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

QVector::isEmpty这个巨坑!!!

2024-3-21 17:50
2501

软件最近收集回来的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文件没有正确收集到,还需要继续分析原因。


[培训]二进制漏洞攻防(第3期);满10人开班;模糊测试与工具使用二次开发;网络协议漏洞挖掘;Linux内核漏洞挖掘与利用;AOSP漏洞挖掘与利用;代码审计。

最后于 2024-3-21 22:36 被_THINCT编辑 ,原因:
收藏
点赞0
打赏
分享
最新回复 (17)
雪    币: 221
活跃值: (2057)
能力值: ( LV2,RANK:10 )
在线值:
发帖
回帖
粉丝
contain_of 2024-3-21 19:46
2
0
不太清楚Qt中QVector类是怎么定义的  但在std::vector里 空容器上调用pop_back导致未定义行为
如果容器为空 begin() == end() 为了程序的通用性 迭代器比较建议使用 != 而不是 <>
所以我认为不是isEmpty的坑 而是空容器上调用pop_back的坑
雪    币: 2659
活跃值: (3787)
能力值: ( LV2,RANK:10 )
在线值:
发帖
回帖
粉丝
值得怀疑 2024-3-21 21:16
3
0
是空也进不到循环里啊, 怎么会调用pop?
雪    币: 3530
活跃值: (7860)
能力值: ( LV3,RANK:20 )
在线值:
发帖
回帖
粉丝
_THINCT 2024-3-21 22:35
4
0
contain_of 不太清楚Qt中QVector类是怎么定义的 但在std::vector里 空容器上调用pop_back导致未定义行为 如果容器为空 begin() == end() 为了程序的通用性 迭代器比较建 ...
我没有试过std::vector,使用QVector为空的时候,继续pop_back会使其size一直减小,变成负数,导致end在begin前面。
雪    币: 3530
活跃值: (7860)
能力值: ( LV3,RANK:20 )
在线值:
发帖
回帖
粉丝
_THINCT 2024-3-21 22:36
5
0
值得怀疑 是空也进不到循环里啊, 怎么会调用pop?
代码前面有pop_back导致了QVector的size为负数,而isEmpty的实现是size == 0.
雪    币: 221
活跃值: (2057)
能力值: ( LV2,RANK:10 )
在线值:
发帖
回帖
粉丝
contain_of 2024-3-21 23:47
6
1
_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)
雪    币: 221
活跃值: (2057)
能力值: ( LV2,RANK:10 )
在线值:
发帖
回帖
粉丝
contain_of 2024-3-22 00:02
7
0
_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
雪    币: 1915
活跃值: (1902)
能力值: ( LV2,RANK:10 )
在线值:
发帖
回帖
粉丝
xuezhimeng 2024-3-22 08:29
8
0
_THINCT 代码前面有pop_back导致了QVector的size为负数,而isEmpty的实现是size == 0.
那这也不能算QVector吧,毕竟没有元素的时候进行了pop操作。
雪    币: 3530
活跃值: (7860)
能力值: ( LV3,RANK:20 )
在线值:
发帖
回帖
粉丝
_THINCT 2024-3-22 08:36
9
0
xuezhimeng 那这也不能算QVector吧,毕竟没有元素的时候进行了pop操作。
关键点是pop没有元素了,isEmpty返回的为true。
雪    币: 3530
活跃值: (7860)
能力值: ( LV3,RANK:20 )
在线值:
发帖
回帖
粉丝
_THINCT 2024-3-22 08:40
10
0

// 实验环境为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编辑 ,原因:
雪    币: 221
活跃值: (2057)
能力值: ( LV2,RANK:10 )
在线值:
发帖
回帖
粉丝
contain_of 2024-3-22 10:18
11
0
都空了 就别去pop_back了 当然会导致未定义行为啦
雪    币:
能力值: ( LV1,RANK:0 )
在线值:
发帖
回帖
粉丝
linglonger 2024-3-22 10:25
12
0
自己菜,怪Qt
雪    币: 3530
活跃值: (7860)
能力值: ( LV3,RANK:20 )
在线值:
发帖
回帖
粉丝
_THINCT 2024-3-22 10:31
13
0
linglonger 自己菜,怪Qt

大佬厉害

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

.

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