QA测试过程发现一个比较奇怪的crash,只会在特定机型特定素材上出现,而且出现crash的位置也比较诡异,具体堆栈如下:
0xxxxxxx split_buffer::construct_at_end
0xxxxxxx XXX::doProcess
crash的具体原因是除0。
首先就是根据上面的堆栈分析可能crash的原因,因为是在vector
在已经分配的内存上创建对象时崩溃,我们基本可以断定,这个vector
的内存出现了问题。然后就去看代码排查调用链中可能导致堆损坏的地方(因为开发机无法复现,因此只能通过分析+日志来排查),在排查代码过程中发现了几处可能越界的操作以及除0。因为是堆损坏就朝着越界的方向去排查了,但是加了新的日志之后发现可能存在越界的代码实际上没有越界,而且实际崩溃的地方大概如下:
std::sort(vec.begin(), vec.end(), [](const bbox& a, const bbox& b){
return a.p > b.p; //崩溃的地方
});
具体崩溃是在访问a
时出现的,此时就有点儿匪夷所思了,崩溃堆栈是construct_at_end
这个只有需要在vector
中新建元素才会调用的函数,但是std::sort
只涉及元素的交换(因为对应的vector
比较小只有20个元素)。
然后在排查是通过日志发现vector
中的元素一部分的p
是inf
,比较反常,因此排查了该值的计算来源发现是有double除0。然后将该处加了check后就不会崩溃了。
到现在为止崩溃的原因基本已经确定了,除0UB导致的问题。
我个人一直以为除0一定会出现除0异常,所以最初并没有怀疑此处,而简单测试发现:
IEEE754规定了浮点除0运算的结果为+INF或者-INF,但是C++标准明确规定了该行为未定义的,使用可能会导致UB。实际测试过程中发现只有低版本的机器浮点除0是不支持的,而新机器无论是windows还是Mac浮点除0编译器只会报warning但是结果是INF不会出错。
具体原因是,C++并不强制编译器实现IEEE 754标准,因为可能受限于硬件平台无法实现。所以上面问题的结论是: