首页
社区
课程
招聘
QVector::isEmpty这个巨坑!!!
发表于: 2024-3-21 17:50 3907

QVector::isEmpty这个巨坑!!!

2024-3-21 17:50
3907

软件最近收集回来的dump和pdb无法匹配,和可能是因为软件打包时使用了签名文件所导致的,但是这个事情不大。通过反汇编特征也能轻松定位到事故发生点。

接下来分析的dump信息如下:

根据信息很快能定位到发生事故的地方是447行。但是什么条件下产生的呢?使用WinDbg分析堆栈内存,发现因为MiniDump的缘故无法分析堆上的对象。

根据源码的反汇编,知道容器对象operStack的内存存放位置[ebp-14h],源码变量声明为 QVector<char> operStack; ,于是查找栈内存:
图片描述
然后分析源码反汇编,发现operStack迭代器begin位置是该对象指针偏移10h的位置,即76aaf910 = 76aaf900+10。回到问题发生的最后时刻:

1
2
3
4
5
6
CONTEXT:  (.ecxr)
eax=1357d000 ebx=788ea788 ecx=13668560 edx=00000000 esi=0cc28248 edi=0cc29358
eip=0057ca61 esp=00fbcbf4 ebp=00fbccb4 iopl=0         nv up ei pl nz na pe cy
cs=0023  ss=002b  ds=002b  es=002b  fs=0053  gs=002b             efl=00210207
LaserMaker!CEnterWhWidget::calcResult+0x421:
0057ca61 8a48ff          mov     cl,byte ptr [eax-1]        ds:002b:1357cfff=??

知道最后operStack迭代器end位置是1357d000. 于是问题就清晰了end在begin的前面,所以就产生越界了。

一开始看源代码,并没有发现端倪:

1
2
3
4
5
6
7
while (!operStack.isEmpty())
{
    auto begin = operStack.begin();
    //auto end   = operStack.end();
    NumStringList.append(QChar(*(operStack.end() - 1)));
    operStack.pop_back();
}

原因是一直陷入思维定式,理解isEmpty保证了operStack大于0的操作。后面测试发现 operStack.pop_back() 竟然在无元素的情况下,还不停的-1,于是造成了operStack的size为负数,这个时候,operStack.isEmpty()的巨坑就体现了。所以涉及迭代器判断为空的情况,最好还是用迭代器来实现isEmpty(),修改代码如下:
图片描述
问题解决了。

本次分析dump,没有log文件,不知道程序到底发生了什么,导致log文件没有正确收集到,还需要继续分析原因。


[培训]内核驱动高级班,冲击BAT一流互联网大厂工作,每周日13:00-18:00直播授课

最后于 2024-3-21 22:36 被_THINCT编辑 ,原因:
收藏
免费 0
支持
分享
最新回复 (17)
雪    币: 354
活跃值: (2637)
能力值: ( LV2,RANK:10 )
在线值:
发帖
回帖
粉丝
2
不太清楚Qt中QVector类是怎么定义的  但在std::vector里 空容器上调用pop_back导致未定义行为
如果容器为空 begin() == end() 为了程序的通用性 迭代器比较建议使用 != 而不是 <>
所以我认为不是isEmpty的坑 而是空容器上调用pop_back的坑
2024-3-21 19:46
0
雪    币: 2325
活跃值: (4878)
能力值: ( LV2,RANK:10 )
在线值:
发帖
回帖
粉丝
3
是空也进不到循环里啊, 怎么会调用pop?
2024-3-21 21:16
0
雪    币: 4290
活跃值: (8650)
能力值: ( LV3,RANK:20 )
在线值:
发帖
回帖
粉丝
4
contain_of 不太清楚Qt中QVector类是怎么定义的 但在std::vector里 空容器上调用pop_back导致未定义行为 如果容器为空 begin() == end() 为了程序的通用性 迭代器比较建 ...
我没有试过std::vector,使用QVector为空的时候,继续pop_back会使其size一直减小,变成负数,导致end在begin前面。
2024-3-21 22:35
0
雪    币: 4290
活跃值: (8650)
能力值: ( LV3,RANK:20 )
在线值:
发帖
回帖
粉丝
5
值得怀疑 是空也进不到循环里啊, 怎么会调用pop?
代码前面有pop_back导致了QVector的size为负数,而isEmpty的实现是size == 0.
2024-3-21 22:36
0
雪    币: 354
活跃值: (2637)
能力值: ( LV2,RANK:10 )
在线值:
发帖
回帖
粉丝
6
_THINCT 我没有试过std::vector,使用QVector为空的时候,继续pop_back会使其size一直减小,变成负数,导致end在begin前面。
#include <iostream>
#include <vector>
using namespace std;

int main() {
  std::vector<int> numbers;
  numbers.pop_back();
  cout << numbers.size() << "\n";
  return 0;
} // 输出18446744073709551615  
  // Apple clang version 13.1.6 (clang-1316.0.21.2.5)
2024-3-21 23:47
1
雪    币: 354
活跃值: (2637)
能力值: ( LV2,RANK:10 )
在线值:
发帖
回帖
粉丝
7
_THINCT 代码前面有pop_back导致了QVector的size为负数,而isEmpty的实现是size == 0.
代码前面有pop_back 这个前面指哪里
#include <iostream>
#include <vector>
using namespace std;

int main() {
  std::vector<int> numbers{1, 2, 3, 4};
  while (!numbers.empty()) {
    numbers.pop_back();
    cout << numbers.size() << "\n";
  }
  cout << "last " << numbers.size() << "\n";
  return 0;
} // 
3
2
1
0
last 0
2024-3-22 00:02
0
雪    币: 2788
活跃值: (2816)
能力值: ( LV2,RANK:10 )
在线值:
发帖
回帖
粉丝
8
_THINCT 代码前面有pop_back导致了QVector的size为负数,而isEmpty的实现是size == 0.
那这也不能算QVector吧,毕竟没有元素的时候进行了pop操作。
2024-3-22 08:29
0
雪    币: 4290
活跃值: (8650)
能力值: ( LV3,RANK:20 )
在线值:
发帖
回帖
粉丝
9
xuezhimeng 那这也不能算QVector吧,毕竟没有元素的时候进行了pop操作。
关键点是pop没有元素了,isEmpty返回的为true。
2024-3-22 08:36
0
雪    币: 4290
活跃值: (8650)
能力值: ( LV3,RANK:20 )
在线值:
发帖
回帖
粉丝
10

// 实验环境为32位

vector<int*> vec;

std::cout << (vec.empty()) << std::endl;  // 输出 1

vec.pop_back();

std::cout << (vec.empty()) << std::endl;  // 输出 0, 其实没有元素的

std::cout << (vec.size()) << std::endl;     // 输出 4294967295

std::cout << (int)vec.size() << std::endl;  // 输出 -1


最后于 2024-3-22 08:47 被_THINCT编辑 ,原因:
2024-3-22 08:40
0
雪    币: 354
活跃值: (2637)
能力值: ( LV2,RANK:10 )
在线值:
发帖
回帖
粉丝
11
都空了 就别去pop_back了 当然会导致未定义行为啦
2024-3-22 10:18
0
雪    币: 0
能力值: ( LV1,RANK:0 )
在线值:
发帖
回帖
粉丝
12
自己菜,怪Qt
2024-3-22 10:25
0
雪    币: 4290
活跃值: (8650)
能力值: ( LV3,RANK:20 )
在线值:
发帖
回帖
粉丝
13
linglonger 自己菜,怪Qt

大佬厉害

最后于 2024-3-22 10:55 被_THINCT编辑 ,原因:
2024-3-22 10:31
0
雪    币: 10217
活跃值: (4386)
能力值: ( LV2,RANK:10 )
在线值:
发帖
回帖
粉丝
14
是不是多线程环境?没有加锁是有可能出现这种情况的。
2024-3-22 13:43
1
雪    币: 4290
活跃值: (8650)
能力值: ( LV3,RANK:20 )
在线值:
发帖
回帖
粉丝
15
superlover 是不是多线程环境?没有加锁是有可能出现这种情况的。
倒没有用到多线程。是一个计算器功能的实现,里面的逻辑分支太多了,出现了逻辑错误。如果是多线程,估计更加难以发现了。
2024-3-22 14:39
0
雪    币: 10217
活跃值: (4386)
能力值: ( LV2,RANK:10 )
在线值:
发帖
回帖
粉丝
16
_THINCT 倒没有用到多线程。是一个计算器功能的实现,里面的逻辑分支太多了,出现了逻辑错误。如果是多线程,估计更加难以发现了。
while (!operStack.isEmpty())的时候其他地方有没有operStack的push操作,一般情况下是不会出现这种低级错误的
2024-3-22 14:53
0
雪    币: 3004
活跃值: (30861)
能力值: ( LV2,RANK:10 )
在线值:
发帖
回帖
粉丝
17
感谢分享
2024-3-23 20:47
1
雪    币: 575
活跃值: (2171)
能力值: ( LV3,RANK:30 )
在线值:
发帖
回帖
粉丝
18

.

最后于 2024-3-25 10:51 被Istaroth编辑 ,原因: 1
2024-3-25 10:48
0
游客
登录 | 注册 方可回帖
返回
//