0

I have a code like this :

#include <stdio.h>
#include <string.h>

void overflow_me(char* dizi){
    char buff_array[100];
    strcpy(buff_array,dizi);
    printf("Hosgeldin %s",buff_array);
}


int main(int argc, char *argv[]){
    overflow_me(argv[1]);
    return 0;
}

I compile this by using gcc -g -o overflow overflow.c -m32 -mpreferred-stack-boundary=2 this. Then I open the overflow file with gdb and disassemble the overflow_me function.

                      endbr32
 0x00001211 <+4>:     push   %ebp
 0x00001212 <+5>:     mov    %esp,%ebp
 0x00001214 <+7>:     push   %ebx
 0x00001215 <+8>:     sub    $0x6c,%esp

I wonder why the stack allocates 108 bytes. I expected that would be 0x64 instead of 0x6c.

Whole disassembled function :

 0x0000120d <+0>:     endbr32
 0x00001211 <+4>:     push   %ebp
 0x00001212 <+5>:     mov    %esp,%ebp
 0x00001214 <+7>:     push   %ebx
 0x00001215 <+8>:     sub    $0x6c,%esp
 0x00001218 <+11>:    call   0x1110 <__x86.get_pc_thunk.bx>
 0x0000121d <+16>:    add    $0x2db3,%ebx
 0x00001223 <+22>:    mov    0x8(%ebp),%eax
 0x00001226 <+25>:    mov    %eax,-0x70(%ebp)
 0x00001229 <+28>:    mov    %gs:0x14,%eax
 0x0000122f <+34>:    mov    %eax,-0x8(%ebp)
 0x00001232 <+37>:    xor    %eax,%eax
 0x00001234 <+39>:    pushl  -0x70(%ebp)
 0x00001237 <+42>:    lea    -0x6c(%ebp),%eax
 0x0000123a <+45>:    push   %eax
 0x0000123b <+46>:    call   0x10b0 <strcpy@plt>
 0x00001240 <+51>:    add    $0x8,%esp
 0x00001243 <+54>:    lea    -0x6c(%ebp),%eax
 0x00001246 <+57>:    push   %eax
 0x00001247 <+58>:    lea    -0x1fc8(%ebx),%eax
 0x0000124d <+64>:    push   %eax
 0x0000124e <+65>:    call   0x1090 <printf@plt>
 0x00001253 <+70>:    add    $0x8,%esp
 0x00001256 <+73>:    nop
 0x00001257 <+74>:    mov    -0x8(%ebp),%eax
 0x0000125a <+77>:    xor    %gs:0x14,%eax
 0x00001261 <+84>:    je     0x1268 <overflow_me+91>
 0x00001263 <+86>:    call   0x1320 <__stack_chk_fail_local>
 0x00001268 <+91>:    mov    -0x4(%ebp),%ebx
 0x0000126b <+94>:    leave
 0x0000126c <+95>:    ret
Peter Cordes
  • 328,167
  • 45
  • 605
  • 847

2 Answers2

1

Looks like the extra space is for a stack cookie (-fstack-protector=strong on by default, along with -fpie also complicating the code), and from copying the stack arg from above EBP to below for no reason.

Use -fno-stack-protector -fno-pie to simplify the asm. They're off by default on Godbolt, and newer GCC doesn't waste instructions copying from EBP+8 to another local, so https://godbolt.org/z/7bMzxGKsd illustrates that you do get only a 100 bytes of stack space reserved when you compile differently with a newer GCC. I also used -fverbose-asm to comment the asm with var names. 32-bit PIE code sucks (PC-relative addressing was new in x86-64 64-bit mode), so it's hard to read, that's why I mention -fno-pie even though it doesn't affect stack usage. (Other than for a moment when calling the thunk to get the current EIP into an integer register.)


I found what was going on by looking for n(%ebp) with |n| > 0x6c, so I spotted the mov %eax,-0x70(%ebp) (which is preceded by a load from 8(%ebp), i.e. the arg.) and the pushl -0x70(%ebp) which also made it clear this was a copy of dizi being pushed as an arg for printf.

Also, mov %gs:0x14,%eax and call 0x1320 <__stack_chk_fail_local> made it obvious this was compiled with some form of -fstack-protector, so I looked and found it storing the stack cookie to mov %eax,-0x8(%ebp). (Right below the saved EBX, which itself is below the saved EBP, which the EBP frame pointer points at after bothering to set it up.)


Comments mentions EBX, but space for the saved EBP and EBX are allocated by push %ebp and push %ebx which themselves modify ESP, not part of the sub $0x6c,%esp.

Note that GCC does sometimes allocate more stack space than it needs for locals + alignment (Why does GCC allocate more space than necessary on the stack, beyond what's needed for alignment?) but that's not what it's doing here: it is using every byte of the stack space it reserves. Not usefully, but you asked it not to optimize so it made dumb code. :P

Peter Cordes
  • 328,167
  • 45
  • 605
  • 847
  • I compiled simple code with "gcc -g -o buff buff.c -m32 -fno-stack-protector". Function contains buffer1[5] and buffer2[10] I expected that first buffer take 8 bytes and second buffer takes 12 bytes and total 20 bytes. However when I disassemble the code with gdb I see it takes only 16 bytes but why ? `void function(int a, int b, int c) { char buffer1[5]; char buffer2[10]; } void main() { function(1,2,3); }` – ogulcanhgul Apr 23 '21 at 13:29
  • 1
    @ogulcanhgul: alignof(char) = 1 so GCC doesn't need to align the arrays. And they're smaller than 16 bytes each, so it wouldn't *choose* to even without `-m32` (the x86-64 SysV ABI "requires" 16-byte alignment for local arrays that are 16 bytes or larger, even though that's internal to the function so the ABI really has no business there. I forget if the i386 SysV ABI says the same thing. But anyway, GCC's heuristics can certainly choose when to align or not align any array.) Anyway, they can just pack head to tail. – Peter Cordes Apr 23 '21 at 13:34
0

Looking at your disassembled code, it appears that it is checking for data overrun in the strcpy function. At <+28> and <+34>, it writes a magic number gs:0x14 to the address just past the end of buff_array. Then after strcpy returns, at <+74>-<+84>,it checks that this magic number hasn't been overwritten. If it has, it knows that more than 100 bytes were copied, and it signals an error with __stack_chk_fail_local.

TonyK
  • 16,761
  • 4
  • 37
  • 72
  • The position of the check is actually right before return (when it truly matters), not specifically after strcpy. GCC `-fstack-protector=strong` doesn't care what functions you call, just that you have an array or stack locals of some size limit. (I forget if its heuristic for when to use a stack cookie includes you passing pointers to them to functions; I think sometimes it makes a stack cookie even if all the array access is within the current function.) – Peter Cordes Apr 23 '21 at 11:04
  • @PeterCordes: Yes, it looks like you are right. But that still leaves four bytes unaccounted for. That would presumably be to align SP on an 8-byte boundary -- do you agree? – TonyK Apr 23 '21 at 11:08
  • No, see my answer :P GCC doesn't seem to be leaving any unused space in *this* case. `-mpreferred-stack-boundary=2` tells it to only maintain 2^2 = 4 byte stack alignment, and it is respecting that. (There are no locals with alignas(x) > 4 so it doesn't need to align any more than that, and doesn't choose to do so.) – Peter Cordes Apr 23 '21 at 11:13
  • (The extra 4 bytes is a copy of the incoming stack arg, at `-0x70(%ebp)`, below the array). – Peter Cordes Apr 23 '21 at 11:19