此英特尔至强mov指令如何破坏我的应用程序的内存?

时间:2019-03-12 23:51:14

标签: c++ gcc x86-64 compiler-bug stack-corruption

我有一个使用gcc v7.3.0构建并在Intel(R) Xeon(R) CPU E3-1220 v6芯片上执行的应用程序,该芯片确定性地破坏了堆栈内存并由于执行mov指令而获得SEGV。我运行了valgrind,没有报告内存损坏。

我在堆栈上有一个struct SolutionPattern,并且在我开始调用成员函数const float * const &时,它的成员变量inline void RoleToEquation(float threshold)的类型变得越来越混乱。在GDB中,当进入成员函数时,第四条指令mov %rdi,-0x18(%rbp)导致损坏,如调试器中所示:

Dump of assembler code for function SolutionPattern::RoleToEquation(float):
   0x00007fff9556b684 <+0>: push   %rbp
   0x00007fff9556b685 <+1>: mov    %rsp,%rbp
   0x00007fff9556b688 <+4>: sub    $0x20,%rsp
=> 0x00007fff9556b68c <+8>: mov    %rdi,-0x18(%rbp)
   0x00007fff9556b690 <+12>:    movss  %xmm0,-0x1c(%rbp)
   0x00007fff9556b695 <+17>:    mov    -0x18(%rbp),%rax
   0x00007fff9556b699 <+21>:    pxor   %xmm0,%xmm0
   0x00007fff9556b69d <+25>:    movss  %xmm0,0x88(%rax)
   0x00007fff9556b6a5 <+33>:    mov    -0x18(%rbp),%rax
   [...]
End of assembler dump.
(gdb) si
Hardware watchpoint 99: *$285

Old value = (const float * const) 0x7fffafb6087a <fflush+106>
New value = (const float * const) 0x7fffffff9ac0
0x00007fff9556b690 in SolutionPattern::RoleToEquation (this=0x7fffffff9ac0, threshold=4.59163468e-41) at calculate_edge.h:250
(gdb) disas
Dump of assembler code for function SolutionPattern::RoleToEquation(float):
   0x00007fff9556b684 <+0>: push   %rbp
   0x00007fff9556b685 <+1>: mov    %rsp,%rbp
   0x00007fff9556b688 <+4>: sub    $0x20,%rsp
   0x00007fff9556b68c <+8>: mov    %rdi,-0x18(%rbp)
=> 0x00007fff9556b690 <+12>:    movss  %xmm0,-0x1c(%rbp)
   0x00007fff9556b695 <+17>:    mov    -0x18(%rbp),%rax
   0x00007fff9556b699 <+21>:    pxor   %xmm0,%xmm0
   0x00007fff9556b69d <+25>:    movss  %xmm0,0x88(%rax)
   0x00007fff9556b6a5 <+33>:    mov    -0x18(%rbp),%rax
   [...]
End of assembler dump.

成员函数源代码如下所示:

inline void RoleToEquation(float threshold) {
    A = B = 0.f;
    C = threshold;
    D = E = 0.f;
    F = threshold;

    [...]
}

ABCDEF是该结构的float成员变量在损坏的内存之后出现几个成员变量。

我不完全了解反汇编,但是似乎功能设置仍在运行,并且AB0.f的设置尚未开始。我在调试器中注意到,在执行第五条指令threshold之前,参数movss尚未初始化。在堆栈上设置参数变量threshold的代码如何导致位于调用帧的堆栈存储器中的结构损坏?

这里可能会发生什么,我该如何找出这种腐败的根源。拆卸看起来合适吗?这看起来像是编译器错误吗?请注意,只有调试版本会崩溃,优化的版本不会崩溃。该成员函数的前四个指令正在执行什么操作?是否为正在调用的新功能设置了框架?

1 个答案:

答案 0 :(得分:1)

mov %rdi,-0x18(%rbp)仅溢出第一个函数arg。 (this指针,因为它是成员函数。)

这并不是在“破坏”任何东西,您只是在禁用优化的情况下进行编译,因此所有都会溢出到C ++语句之间的内存中,包括函数入口上的所有函数args(隐式和显式)。

-0x18(%rbp)在此函数自己的堆栈帧的0x20字节之内,由sub $0x20,%rsp保留(在mov %rsp, %rbp之后,将帧指针设置为指向return下方的qword)地址)。

  

在堆栈上设置参数变量阈值的代码如何导致位于调用帧堆栈内存中的结构损坏?

不是。 movss %xmm0,-0x1c(%rbp)还在此函数自己的堆栈框架中存储。在溢出的this指针下面是一个4字节的存储区。 (这些是负偏移量,0x1c - 0x18 = 4 =商店的宽度。)


如果您通过设置硬件监视点来发现此问题,则可能是在本地地址上。 包含该本地函数的函数已返回,现在堆栈内存已被重用作为用于不同功能的不同调用的堆栈框架。