我一生中见过许多核心转储,但这一次让我不知所措。
背景:
- 在AMD Barcelona CPU群集上运行的多线程Linux/x86_64程序
- 崩溃的代码被执行了 * 很多 *
- 在负载下运行1000个程序示例(完全相同的优化二进制文件)每小时会产生1 - 2次崩溃
- 崩溃发生在不同的机器上(但机器本身是完全相同的)
- 崩溃看起来都一样(相同的确切地址,相同的调用堆栈)
以下是此次坠机的详细情况:
Program terminated with signal 11, Segmentation fault.
#0 0x00000000017bd9fd in Foo()
(gdb) x/i $pc
=> 0x17bd9fd <_Z3Foov+349>: rex.RB orb $0x8d,(%r15)
(gdb) x/6i $pc-12
0x17bd9f1 <_Z3Foov+337>: mov (%rbx),%eax
0x17bd9f3 <_Z3Foov+339>: mov %rbx,%rdi
0x17bd9f6 <_Z3Foov+342>: callq *0x70(%rax)
0x17bd9f9 <_Z3Foov+345>: cmp %eax,%r12d
0x17bd9fc <_Z3Foov+348>: mov %eax,-0x80(%rbp)
0x17bd9ff <_Z3Foov+351>: jge 0x17bd97e <_Z3Foov+222>
您会注意到崩溃发生在0x17bd9fc
指令的 * 中间 *,也就是从0x17bd9f6
调用返回到虚函数之后。
当我检查虚拟表时,我发现它没有以任何方式损坏:
(gdb) x/a $rbx
0x2ab094951f80: 0x3f8c550 <_ZTI4Foo1+16>
(gdb) x/a 0x3f8c550+0x70
0x3f8c5c0 <_ZTI4Foo1+128>: 0x2d3d7b0 <_ZN4Foo13GetEv>
并且它指向这个平凡的函数(通过查看源代码可以预料到):
(gdb) disas 0x2d3d7b0
Dump of assembler code for function _ZN4Foo13GetEv:
0x0000000002d3d7b0 <+0>: push %rbp
0x0000000002d3d7b1 <+1>: mov 0x70(%rdi),%eax
0x0000000002d3d7b4 <+4>: mov %rsp,%rbp
0x0000000002d3d7b7 <+7>: leaveq
0x0000000002d3d7b8 <+8>: retq
End of assembler dump.
此外,当我查看Foo1::Get()
应该返回到的返回地址时:
(gdb) x/a $rsp-8
0x2afa55602048: 0x17bd9f9 <_Z3Foov+345>
我看到它指向了正确的指令,所以就好像在从Foo1::Get()
返回的过程中,出现了一些gremlin,并将%rip
增加了4。
合理的解释?
2条答案
按热度按时间oxcyiej71#
因此,虽然看起来不太可能,但我们似乎遇到了一个真正的CPU bug。
https://web.archive.org/web/20130228081435/http://support.amd.com/us/Processor_TechDocs/41322_10h_Rev_Gd.pdf包含勘误表#721:
721处理器可能错误地更新堆栈指针
描述
在一组非常具体和详细的内部时序条件下,处理器可能会在一长串压入和/或近调用指令,或者一长串弹出和/或近返回指令之后错误地更新堆栈指针。处理器必须处于64位模式,才会出现此错误。
对系统的潜在影响
堆栈指针值以大约1024的值正向或负向跳转。此不正确的堆栈指针会导致不可预知的程序或系统行为,通常表现为程序异常或崩溃(例如,#GP或#UD)。
建议的变通方案
系统软件可设置MSRC001_1029 [0]= 1b。
neskvpey2#
我曾经看到一个"非法操作码"在指令执行过程中崩溃。我当时正在处理一个Linux端口。长话短说,Linux从指令指针中减去,以便重新启动一个系统调用,在我的例子中,这种情况发生了两次(如果两个信号同时到达)。
所以这是一个可能的罪魁祸首:内核在摆弄你的指令指针。2在你的情况下可能有一些其他的原因。
请记住,有时处理器会将正在处理的数据理解为指令,即使它不应该理解为指令。因此,处理器可能在0x17bd9fa执行了"指令",然后移到0x17bd9fd,然后生成了非法操作码异常。(这个数字是我瞎编的,但是用反汇编器进行实验可以显示处理器可能"进入"指令流的位置。)
调试愉快!