Home > front end >  Segfault pushing to stack in C inline assembly
Segfault pushing to stack in C inline assembly

Time:12-13

I am having an issue with some inline assembly. I am writing a compiler, and it is compiling to assembly, and for portability i made it add the main function in C and just use inline assembly. Though even the simplest inline assembly is giving me a segfault. Thanks for your help

int main(int argc, char** argv) {
  __asm__(
"push $1\n"
  );
  return 0;
}

CodePudding user response:

TLDR at bottom. Note: everything here is assuming x86_64.

The issue here is that compilers will effectively never use push or pop in a function body (except for prologues/epilogues).

Consider this example.

When the function begins, room is made on the stack in the prologue with:

push rbp
mov rbp, rsp
sub rsp, 32

This creates 32 bytes of room for main. Then notice how throughout the function, instead of pushing items to the stack, they are mov'd to the stack through offsets from rbp:

        mov     DWORD PTR [rbp-20], edi
        mov     QWORD PTR [rbp-32], rsi
        mov     DWORD PTR [rbp-4], 2
        mov     DWORD PTR [rbp-8], 5

The reason for this is it allows for variables to be stored anywhere at anytime, and loaded from anywhere at anytime without requiring a huge amount of push/pops.

Consider the case where variables are stored using push and pop. Say a variable is stored early on in the function, let's call this foo. 8 variables on the stack later, you need foo, how should you access it?

Well, you can pop everything until foo, and then push everything back, but that's costly.

It also doesn't work when you have conditional statements. Say a variable is only ever stored if foo is some certain value. Now you have a conditional where the stack pointer could be at one of two locations after it!

For this reason, compilers always prefer to use rbp - N to store variables, as at any point in the function, the variable will still live at rbp - N.

NB: On different ABIs (such as i386 system V), parameters to arguments may be passed on the stack, but this isn't too much of an issue, as ABIs will generally specify how this should be handled. Again, using i386 system V as an example, the calling convention for a function will go something like:

push edi ; 2nd argument to the function.
push eax ; 1st argument to the function.
call my_func
; here, it can be assumed that the stack has been corrected

So, why does push actually cause an issue? Well, I'll add a small asm snippet to the code

At the end of the function, we now have the following:

        push 64

        mov     eax, 0
        leave
        ret

There's 2 things that fail now due to pushing to the stack.

The first is the leave instruction (see this thread)

The leave instruction will attempt to pop the value of rbp that was stored at the beginning of the function (notice the only push that the compiler generates is at the start: push rbp).

This is so that the stack frame of the caller is preserved following main. By pushing to the stack, in our case rbp is now going to be set to 64, since the last value pushed is 64. When the callee of main resumes it's execution, and tries to access a value at say, rbp - 8, a crash will occur, as rbp - 8 is 0x38 in hex, which is an invalid address.

But that assumes the callee even get's execution back!

After rbp has it's value restored with the invalid value, the next thing on the stack will be the original value of rbp.

The ret instruction will pop a value from the stack, and return to that address...

Notice how this might be slightly problematic?

The CPU is going to try and jump to the value of rbp stored at the start of the function!

On nearly every modern program, the stack is a "no execute" zone (see here), and attempting to execute code from there will immediately cause a crash.

So, TLDR: Pushing to the stack violates assumptions made by the compiler, most importantly about the return address of the function. This violation causes program execution to end up on the stack (generally), which will cause a crash

  • Related