Looking at the difference between the generated assembly.
Foo::Foo() [base object constructor]: <
pushq %rbp <
movq %rsp, %rbp <
movq %rdi, -8(%rbp) <
movq -8(%rbp), %rax <
pxor %xmm0, %xmm0 <
movss %xmm0, (%rax) ; 1. <
nop <
popq %rbp <
ret <
my_func(): my_func():
pushq %rbp pushq %rbp ; Save previous stack frame addr
movq %rsp, %rbp ; 4. movq %rsp, %rbp ; Address of current stack frame as new base ptr
subq $16, %rsp ; save 16 bytes for local data | movl $0, %eax ; move value 0 to function return register
movq %rdi, -8(%rbp) ; 3. | popq %rbp ; unwind the stack to exit function
movq -8(%rbp), %rax <
movq %rax, %rdi ; 2. <
call Foo::Foo() [complete object constructor] <
movq -8(%rbp), %rax <
leave <
ret ret
movss %xmm0, (%rax)
Caused the SIGSEGV. Using gdb: p/x %rax points to 0x4004d6 which is the address of my_func(). Attempt to write to the stack is the problem. Memory pages are write or execute (read) only. So why is %rax pointing there?
-
movq %rax, %rdi
This line was when the %rax was last written to, in my_func. What did %rdi contain? -
movq %rdi, -8(%rbp)
Contained whatever was at the address of %rbp - 8. What's in %rbp? The stack's base pointer of course. This was 0x4004d6.
The mov instructions write things. In the assembly code of int my_func(), besides the standard function setup/teardown, there was only movl $0, %eax which isn't going to cause a problem.
Registers
%rbp : base pointer of current stack frame (called %ebp in 32 bit) %rsp : stack pointer (top element) %eax : return value of a function, 32 bit register %rax : 64 bit register, same use as %eax %rdi : 64 bit general purpose register
Instructions
movss : move scalar single precision floating point value (copies 32 lowest bits from a XMM 128 bit register) pxor : logical exclusive or movl : move long (32 bit) movq : move quad word (64 bit) pushq : push quad word onto the stack
Suffix b : 8b aka byte s : single 32b float w : 16b l : 32b int or 64b float q : 64b t : 80b, 10 bytes
Stack Smashing Summary
Here's the MVP code you need to repro this.
In gdb, I find the reason for this crash.
The
(%rax)
points to0x4004d6
which is where themy_func
begins, so writing to it would overwrite the instruction page. Kernel protection W^X causes SIGSEGV. The cause is from the compiler, which doesn't reserve enough stack space for Foo to be written to, in theindirection
function. I confirmed this by looking at the compiler's assembly code.