I have an application built with gcc v7.3.0 and executing on an Intel(R) Xeon(R) CPU E3-1220 v6
chip that corrupts stack memory deterministically and gets a SEGV due to executing a mov
instruction. I ran valgrind
and no memory corruption was reported.
I have a struct SolutionPattern
on the stack and its member variable, of type const float * const &
, is getting clobbered when I begin calling the member function, inline void RoleToEquation(float threshold)
. In GDB
, when enetring the member function, the forth instruction, mov %rdi,-0x18(%rbp)
, causes the corruption as shown in the debugger:
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.
The member function source code begins like this:
inline void RoleToEquation(float threshold) {
A = B = 0.f;
C = threshold;
D = E = 0.f;
F = threshold;
[...]
}
A
, B
, C
, D
, E
, and F
are float
member variables of the struct that occur several member variables after the memory that is being corrupted.
I don't fully understand the disassembly, but it appears that the function setup is still being run and the setting of A
and B
to 0.f
has not started yet. I notice in the debugger that the argument threshold
is uninitialized until the fifth instruction, movss
, executes. How does the code to set up the argument variable threshold
on the stack cause the struct to be corrupted that is located in the calling frame's stack memory?
What is likely going on here and how can I go about figuring out the source of this corruption. Does the disassembly look proper? Does this look like a compiler bug? Note, only the debug build crashes, optimized build does not crash. What is being performed by the first four instructions of this member function? Is it improperly setting up the frame for the new function being called?
mov %rdi,-0x18(%rbp)
is just spilling the first function arg. (The this
pointer since this is a member function.)
It's not "corrupting" anything, you just compiled with optimization disabled so everything gets spilled to memory between C++ statements, including all the function args (implicit and explicit) on function entry.
-0x18(%rbp)
is inside the 0x20
bytes of this function's own stack frame, reserved with sub $0x20,%rsp
(after mov %rsp, %rbp
set the frame pointer to point to the qword below the return address).
How does the code to set up the argument variable threshold on the stack cause the struct to be corrupted that is located in the calling frame's stack memory?
It doesn't. movss %xmm0,-0x1c(%rbp)
also stores inside this function's own stack frame. That's a 4-byte store right below the spilled this
pointer. (These are negative offsets, and 0x1c - 0x18
= 4 = the width of the store.)
If you found this by setting a HW watchpoint, probably that was on the address of a local. The function containing that local has returned, and now that stack memory is being reused as the stack frame for a different invocation of a different function.