1

In this method

void lame(void) {
 char small[30];
 gets(small);
 printf("%s\n",small);
}

I would expect the stack to get increased by 0x20

but instead gdb shows it getting increased by 0x38

0x08048450 <+3>:  sub $0x38, %esp

I'm not really sure what info is needed to answer this question so if I miss something please let me know and I'll add it:

  • Ubuntu 140.04 32bit
  • Compile command : cc -ggdb -fno-stack-protector blah.c -o blah
  • CC --version - 4.8.2
  • Running in Oracle Virtual Box 4.3.20

What I am doing Just wanted to play around with buffer overflows and found this example that I am following

disas lame

disas lame output

Wjdavis5
  • 3,952
  • 7
  • 35
  • 63
  • You forgot to enable optimizations. Also outgoing argument may be allocated directly from the stack, and alignment comes to play. – Jester Jan 13 '15 at 01:22
  • @Jester thanks, I tried recompiling with 3 different options -O, -O1, -O2 and they all produce the same assembly – Wjdavis5 Jan 13 '15 at 01:28
  • "gdb shows it getting increased by 0x38": increased relative to *what*? An empty function? – Cornstalks Jan 13 '15 at 01:40
  • 4
    Stack pointer must be kept 16 byte aligned, so that leaves either `0x28` or `0x38` as possibilities (4 bytes are used by the `push %ebx`, and an additional 4 by the `call`, so that's where the `8` comes from). Your buffer is rounded up to `0x20` and we need an additional 4 bytes for the outgoing arguments. It isn't obvious why 0x28 wouldn't have worked. – Jester Jan 13 '15 at 01:40
  • @Jester, the 16byte alignment is for x64, is it not? Nothing in the asm seems to indicate 64-bit (such as use of rax) and OP states it's Ubuntu 32-bit. – paxdiablo Jan 13 '15 at 01:47
  • No, it's also for 32 bit nowadays. – Jester Jan 13 '15 at 01:48
  • @Jester You should post as answer. – Wjdavis5 Jan 13 '15 at 02:03

2 Answers2

1

For starters, your buffer is rounded up to 0x20 bytes, no surprise there. The outgoing arguments need 4 more bytes. So we have a net requirement of 0x24 bytes.

The updated calling convention mandates that stack pointer must be kept 16 byte aligned. Since the push %ebx uses 4 bytes and an additional 4 are used for the return address put on the stack by the call instruction, the adjustment has to be 8 bytes off from a multiple of 16. This combined with our minimum requirement of 0x24 bytes means allocating 0x28 should be enough. gcc instead allocating 16 extra bytes for a total of 0x38 seems to be a bug or peculiarity exhibited by at least gcc versions 4.4 through 4.9. Note that clang and llvm-gcc both allocate the expected 0x28 bytes.

My testing ruled out the extra space being related to use of gets or the compiler optimizing a 2 argument printf into a 1 argument puts.

Jester
  • 56,577
  • 4
  • 81
  • 125
0

Your best bet is to simply look at the assembler code of the function and find out what it's doing with the areas offset from esp and ebp (depending on your calling convention).

It may be using them for various temporaries, for example. Or it may be pre-allocating the space for parameters to be sent to gets and/or printf. Or it may be trying to align the stack pointer to some "resolution", for example, easing SSE object alignments.

Or it may be recognising that people who use gets tend to not understand the dangers of buffer overruns, and it's giving you a small amount of extra room as breathing space :-)

Bottom line, we can't tell without seeing what the compiler has generated. Even with that information, however, it may not be obvious as to why the compiler allocates more than requested, since we may still be working with incomplete information.

Based on your added assembler dump, the buffer for gets and printf (which has sneakily been optimised to puts) is eighteen bytes above the stack pointer and there's no immediate use made of the intervening area.

So, unless gets/puts use it for something (unlikely) or the procedure linkage table does (still unlikely but it's a convoluted beast), you may just have to assume it's overkill or alignment requirements for your platform, generated from the compiler.

You could of course test it with various array sizes to see how that affects the space allocated. That's likely to at least give some extra information which may make a theory more viable.

Based on your comment that char[15] allocates 0x28 and char[16] allocates 0x38, that seems to be a 16-byte alignment requirement for the stack pointer (it ends with an 8 because there are eight bytes already used as part of the call: the return address itself, and the previous ebx).

And, indeed, if you search the web for gcc 16 byte alignment stack pointer, you'll find some very spirited discussion as to whether gcc is doing the right thing here but, amongst all that discussion is the basic fact that gcc does align the stack to 16 bytes (rightly or wrongly).

Community
  • 1
  • 1
paxdiablo
  • 854,327
  • 234
  • 1,573
  • 1,953
  • I believe you may be correct that this is the compiler trying to compensate for my potential stupidity. I decreased the array to 20 and it still used 0x38, I decreased the array to 10 and it used 0x28. It seems the line is an array of 16 bumps the allocation to 0x38 – Wjdavis5 Jan 13 '15 at 01:49
  • @Wjdavis5, if it was simply compensating, I'd expect it to go up and down with your value rather then a quantum value. Suggest you try the values between 10 and 20 to see if it _ever_ gives you 0x30. If not, it's likely to be the alignment issue as suggested, the compiler attempting to keep 16-byte alignment for stuff like SSE. – paxdiablo Jan 13 '15 at 01:52
  • Does the stack have to be 16-byte aligned _before_ or _after_ `call`? That may cause a tiny bit more memory use. – Iwillnotexist Idonotexist Jan 13 '15 at 01:54
  • FYI, my testing shows it's not related to `gets`, and also not related to compiler optimizing a 2 argument `printf` into a 1 argument `puts`. – Jester Jan 13 '15 at 01:54
  • @paxdiablo Ok I see what you mean. char[15] is 0x20 and char[16] is 0x38 – Wjdavis5 Jan 13 '15 at 01:56
  • 2
    Also, `clang` uses `0x28` which matches my explanation in the comment, above. Looks like it's a `gcc` bug/peculiarity. – Jester Jan 13 '15 at 01:56