一次内存越界的 bug
最近我们的手游上线, 一开始稳定的跑了好几天,最近两天开始平均 30 小时崩溃一次。这个周末我们一直在查这个 bug 。
第一次崩溃发生在周六凌晨 1:00 左右,没有收集到 core 文件,只有事故现场的寄存器的值(通过查看 kern.log 和 dmesg )。通过 EIP 寄存器最终定位到是在内存分配器中出的错。
我们用的 jemalloc ,崩溃发生在一段红黑树插入节点的代码中。 这里的 pathp 指针变成了 NULL 。
昨天在岩馆攀岩的时候,我脑子里一直在转这段代码,想为什么这个局部变量会变成 NULL 。反复推测的结果是 path 这个局部数组越界了,导致把 pathp 改写。
这段代码是用来展开一颗红黑树的,如果树的数据结构正常,128 一定够用,不会越界。比较可能的解释是插入了相同的节点,导致树结构绕接。虽然 jemalloc 在这里加了 assert ,但似乎在最终的程序中没有生效。
我推测是在内存释放的时候调整红黑树的,虽然我们的崩溃没有产生 core 文件,但从网上搜索到另一条类似的信息可以佐证我的推测:这条 bug report 报告了一个类似的崩溃。从调用栈来看,的确是在内存释放时发生的。
我的第一次推测是我们有些代码对指针做了 double free ,导致了红黑树节点绕接,间接导致后续的 free 操作不正常,让树展开代码越界。如果这个推测成立,那么即使我们拿到完整的 core 文件,对我们 debug 也没有什么帮助。因为数据结构是事先被破坏掉,才会导致后面的操作出错。
今天上午 10 点左右发生了第二次崩溃。这次收集到了 core 文件。
这次的错误点和上一次相同,但现象有所不同,并没有访问 NULL 地址。double free 的猜测应该是不成立的。但和我的推测相同,core 文件中可以看到调用栈的确是从 free 过来的,其它信息则没有什么用。数据结构早就被破坏了,事故现场是一次正常的 lua state 关闭操作(引发大量内存回收)。
但可以肯定的是这起 bug 最近连发两次,每次间隔都是 30 小时左右,一定是最近代码的修改造成的。我们翻看了最近的修改。对 C 代码的修改只有一两个位置,之前反复看了好几次都没有发现问题。不得以再看一次。
最后终于发现了问题,是一个简单的内存越界造成的:
我们上周发现有玩家客户端发过来的数据包解包失败,但不知道原因,所以增加了一个函数把错误的数据包以 16 进制输出到 log 的函数。晓靖同学是这样写的:
char *buffer = calloc(sz*2+1, sizeof(char));
先分配一块内存,长度是要 dump 的数据长度两倍加一。然后循环
sprintf(buffer+i*2, "%02x", data[i]);
这就是我们看了几次没留意的 bug 所在:data 是 const char 类型,有符号的。当 data[i] 是一个负数时, %02x 不一定只输出 3 个字节(别忘记字符串结尾的 \0)。buffer 这块内存就被写越界了。
Comments
Posted by: Anonymous | (38) December 29, 2014 01:14 PM
Posted by: cc | (37) July 15, 2014 02:42 PM
Posted by: 十年草木 | (36) April 2, 2014 09:35 PM
Posted by: simon | (35) March 4, 2014 01:17 PM
Posted by: ChinaHome | (34) February 20, 2014 06:21 PM
Posted by: asdfg | (33) February 20, 2014 04:20 PM
Posted by: 观雨 | (32) February 8, 2014 02:41 PM
Posted by: marmot | (31) February 7, 2014 10:26 PM
Posted by: marmot | (30) February 5, 2014 09:30 PM
Posted by: anthony | (29) January 22, 2014 12:24 PM
Posted by: wellmv | (28) January 21, 2014 11:35 PM
Posted by: cyberscorpio | (27) January 14, 2014 01:49 AM
Posted by: Liigo | (26) January 11, 2014 06:09 PM
Posted by: lite3 | (25) January 10, 2014 09:06 AM
Posted by: yanbin | (24) January 9, 2014 08:17 PM
Posted by: starshine | (23) January 8, 2014 11:36 AM
Posted by: Julius | (22) January 8, 2014 09:53 AM
Posted by: Julius | (21) January 8, 2014 09:42 AM
Posted by: Smite | (20) January 8, 2014 12:05 AM
Posted by: jacky | (19) January 7, 2014 11:01 PM
Posted by: yang | (18) January 7, 2014 11:44 AM
Posted by: chengli | (17) January 7, 2014 10:51 AM
Posted by: Julius | (16) January 7, 2014 10:36 AM
Posted by: hemaolong | (15) January 7, 2014 09:39 AM
Posted by: Cloud | (14) January 7, 2014 09:37 AM
Posted by: johnzz | (13) January 7, 2014 09:10 AM
Posted by: injur | (12) January 7, 2014 08:56 AM
Posted by: injur | (11) January 7, 2014 08:51 AM
Posted by: 小邓 | (10) January 7, 2014 01:04 AM
Posted by: dwing | (9) January 6, 2014 10:20 PM
Posted by: xx | (8) January 6, 2014 09:01 PM
Posted by: Cloud | (7) January 6, 2014 08:53 PM
Posted by: hedengcheng | (6) January 6, 2014 07:36 PM
Posted by: hamo | (5) January 6, 2014 07:16 PM
Posted by: Fenng | (4) January 6, 2014 06:56 PM
Posted by: Rexliao | (3) January 6, 2014 06:56 PM
Posted by: lichking | (2) January 6, 2014 06:18 PM
Posted by: RVoid | (1) January 6, 2014 06:03 PM