4

Given that function:

void vuln( char * arg ) {
    char buf[256];
    strcpy(buf, arg);
}

Disassembled in:

       0x0804842b      55             push ebp                                                                                                                                                                                          
       0x0804842c      89e5           mov ebp, esp                                                                                                                                                                                      
       0x0804842e      81ec08010000   sub esp, 0x108                                                                                                                                                                                    
       0x08048434      83ec08         sub esp, 8                                                                                                                                                                                        
       0x08048437      ff7508         push dword [arg_8h]                                                                                            
       0x0804843a      8d85f8feffff   lea eax, ebp - 0x108                                                                                                                                                                              
       0x08048440      50             push eax                                                                                                           
       0x08048441      e8bafeffff     call sym.imp.strcpy                                                                                             
       0x08048446      83c410         add esp, 0x10                                                                                                                                                                                     
       0x08048449      c9             leave                                                                                                                                                                                             
       0x0804844a      c3             ret             

It overflows when the argument is 264 = 0x108 chars and I was expecting 256 bytes. Why compiler adds 8 bytes with sub esp,8 ?

  • It's probably because of strcpy – Trey Dec 29 '17 at 19:19
  • 1
    Why ? What ? How ? :-) – Kartone Dec 29 '17 at 19:40
5
  • According to the SYS V i386 ABI the stack must be at minimum operating system word aligned prior to execution of the CALL instruction.

    Excerpt from Peter Cordes' answer to Responsiblity of stack alignment in x86 assembly:

    The i386 System V ABI has guaranteed/required for years that ESP+4 is 16B-aligned on entry to a function. (i.e. ESP must be 16B-aligned before a CALL instruction, so args on the stack start at a 16B boundary. This is the same as for x86-64 System V.)

  • Additionally, GCC aligns the stack to a 16-byte boundary by defualt. To accomplish this, GCC will allocate additional space in an area within a function's stack frame that is considered unrestricted by the ABI:

    Other areas depend on the compiler and the code being compiled. The standard calling sequence does not define a maximum stack frame size, nor does it restrict how a language system uses the ‘‘unspecified’’ area of the standard stack frame.

  • in the ABI the function return address is considered to be part of the current stack frame:

i386 stack frame layout

This is enough information to allow us to determine why the compiler generates code that creates unused space on the stack. Let us examine the code, focusing on instructions that result in stack frame memory allocation:

   0x0804842b      55             push ebp                     ( 1 )                                                                                                                                                                     
   0x0804842c      89e5           mov ebp, esp                                                                                                                                                                                      
   0x0804842e      81ec08010000   sub esp, 0x108               ( 2 )                                                                                                                                                                     
   0x08048434      83ec08         sub esp, 8                   ( 3 )                                                                                                                                                                    
   0x08048437      ff7508         push dword [arg_8h]          ( 4 )                                                                                  
   0x0804843a      8d85f8feffff   lea eax, ebp - 0x108                                                                                                                                                                              
   0x08048440      50             push eax                     ( 5 )                                                                                      
   0x08048441      e8bafeffff     call sym.imp.strcpy          ( 6 )                                                                                   
   0x08048446      83c410         add esp, 0x10                                                                                                                                                                                     
   0x08048449      c9             leave                                                                                                                                                                                             
   0x0804844a      c3             ret             

  1. We now know that the return address is considered to be part of the current stack frame. i386 machines have 32-bit architecture, so the return address takes up 4 bytes of space and execution of the instruction push ebp will decrement the stack pointer by 4 bytes.

    • 4 + 4 = 8, so at this point the stack frame is 8 bytes in size.
  2. As you noted, this creates 264 bytes of space.

    • 8 + 264 = 272
  3. sub esp, 8 - the specific instruction in question. Creates 8 additional bytes of space on the stack frame.

    • 272 + 8 = 280
  4. As before, push has the effect of adding 4 bytes of space.

    • 280 + 4 = 284
  5. Another push.

    • 284 + 4 = 288
  6. Now it is time for execution of the call instruction. At this point the stack frame is 288 bytes in size. Let us check for alignment to a 16-byte boundary:

    • 288 % 16 = 0 (thanks RadLexus). The stack frame is correctly aligned.


Conclusion:

The compiler uses sub esp, 8 to maintain stack frame alignment to a 16-byte boundary.

See also:

| improve this answer | |
  • 1
    @usr2564301 yes, good point. I will change it. Thanks. – julian Dec 29 '17 at 23:49
  • Superb answer @SYS_V. Thank you. Can you explain me why it reserve 0x108 (264) bytes for a char buf[256] and not 0x100 ? – Kartone Dec 30 '17 at 1:07
  • @Kartone The best I can do is guess: if 264 bytes are allocated for the buffer, we have 8 + 264 + 8 + 4 + 4 = 288, where 288 % 16 = 0. However, if 256 bytes are allocated for the buffer, with the given code we instead have 8 + 256 + 8 + 4 + 4 = 280, where 280 % 16 = 8, which means the stack is not correctly aligned to a 16-byte boundary. To correct for this, the compiler would have to generate additional instructions to align the stack. – julian Dec 30 '17 at 1:24
  • @Kartone no problem, you are welcome – julian Dec 30 '17 at 21:44

Your Answer

By clicking “Post Your Answer”, you agree to our terms of service, privacy policy and cookie policy

Not the answer you're looking for? Browse other questions tagged or ask your own question.