概述
- 目前项目会使用chromium项目的base模块
- 在调用友商一模块时,出现StackOverflow
事件分析
- 在base库的task_runner线程中,调用该模块,会出现StackOverflow
- 在另一种std::thread创建的线程中,调用该模块,正常
- 在main函数中,调用该模块,正常
因此初步推断友商模块可能对base模块的task_runner水土不符?
上IDA进行分析,发现在调用 _alloca_probe函数时出现的崩溃。
参考连接:解析__alloca_probe
该函数有1个参数,表示要展开的栈的大小。
所以 _alloca_probe 是用来开栈的,也就是说,问题不在友商,而是我们,因为线程栈耗尽,而导致崩溃。
在调用友商模块时,分析线程状态:
StackBase: 043a0000
StackLimit: 04396000
esp=0439ad90
根据这几个参数,可以看出:
栈大小为 0x043a0000 - 0x04396000 = 0xA000
栈剩余大小为 0x0439ad90 - 0x04396000 = 0x4D90
继续深入,在 _alloca_probe函数前,发现栈使用了 0x4908,也就是说,在调用 _alloca_probe函数前,栈中剩余空间只有 0x4D90 - 0x4908 = 0x488字节。
我们看调用 _alloca_probe函数前代码:
.text:5C5331F3 mov eax, 4020h
.text:5C5331F8 call __alloca_probe
可知,会使用 _alloca_probe函数开0x4020字节的栈,而此时栈的可用空间只有0x488字节!
那为什么在main函数(主线程)和std::thread线程调用不会崩溃?
在main函数中,观察线程栈状态:
StackBase: 00b40000
StackLimit: 00b3b000
esp=00b3f8b0
栈大小:0x00b40000 - 0x00b3b000 = 0x5000
栈剩余大小:0x00b3f8b0 - 0x00b3b000 = 0x48B0
这不科学!为什么这个可用大小更少,但是不会StackOverflow呢?
我们继续观察调用完模块后,线程栈状态:
StackBase: 00b40000
StackLimit: 00b36000
esp=00b3f8b0
栈大小:0x00b40000 - 0x00b36000 = 0xA000
栈剩余大小:0x00b3f8b0 - 0x00b36000 = 0x98B0
栈大小变大了,也变成了0xA000,那是因为栈最大只能是0xA000而我们用的太多导致的么?
于是我在调用模块前,在栈用申请一块大小为0x7000内存:
StackBase: 00500000
StackLimit: 004f6000
esp=004f8230
栈大小:0x00500000 - 0x004f6000 = 0xA000
栈剩余大小:0x004f8230 - 0x004f6000 = 0x2230
很不错,这下该崩溃了吧😁?然而事与愿违!
StackBase: 00500000
StackLimit: 004ef000
esp=004f8230
栈大小:0x00500000 - 0x004ef000 = 0x11000
栈剩余大小:0x004f8230 - 0x004ef000 = 0x9230
好家伙,又双叒叕变大了!
所以栈是会根据需要自动增长的。但是为什么task_runner线程不会呢?
最后发现同事将线程栈大小设置为10*1024。改为0就没有问题了。
那么问题又双叒叕来了,为什么栈大小设置为10*1024(0x2800),但是实际用到了0xA000呢?
情况较复杂,等有空单独研究。