Carefullcars
Carefullcars

Reputation: 181

Is the stack growing the wrong way when you compile C code with GCC

I have the following C code:

#include <stdio.h>
#include <string.h>
int main(int argc, char *argv[]) {
   int value = 5;
   char buffer_one[8], buffer_two[8];
   strcpy(buffer_one, "one"); /* Put "one" into buffer_one. */
   strcpy(buffer_two, "two"); /* Put "two" into buffer_two. */

   return 0;
}

From my knowledge of the stack, the buffer_one array should start at a higher adress than the buffer_two array, since the stack grows towards lower adresse and buffer_two is at the top of the stack. However, when i compiler the code using gcc and use GDB to step through the code, it tells me that the opposite is true:

eman@eman:~/Documents/CFiles/bufferOverflow/source$ gcc -g -o example overflow_example.c 
eman@eman:~/Documents/CFiles/bufferOverflow/source$ gdb -q example
Reading symbols from example...done.
(gdb) list
1   #include <stdio.h>
2   #include <string.h>
3   int main() {
4   char buffer_one[8];
5   char buffer_two[8];
6   
7   strcpy(buffer_one, "one"); /* Put "one" into buffer_one. */
8   strcpy(buffer_two, "two"); /* Put "two" into buffer_two. */
9   
10  return 0;
(gdb) break 9
Breakpoint 1 at 0x400571: file overflow_example.c, line 9.
(gdb) run
Starting program: /home/eman/Documents/CFiles/bufferOverflow/source/example 

Breakpoint 1, main () at overflow_example.c:10
10  return 0;
(gdb) print &buffer_one
$1 = (char (*)[8]) 0x7fffffffdd50
(gdb) print &buffer_two
$2 = (char (*)[8]) 0x7fffffffdd60
(gdb) 

What is going on here?

extra question: Why does the arrays take up 10 bytes when it is initialized with 8 bytes?

Upvotes: 0

Views: 273

Answers (1)

John Bode
John Bode

Reputation: 123448

Several things:

  1. The compiler is not required to lay out variables in the order in which they were declared (or any other specific order). Members of a struct instance are laid out in the order declared, although there may be padding bytes between each member (see 2).

  2. The platform may require that objects be aligned on specific addresses (for example, addresses that are evenly divisible by 8 or 16). Thus, it's possible for there to be unused bytes between objects. If the addresses of buffer_one and buffer_two differ by 16, that doesn't mean that the compiler set aside 16 bytes for buffer_one, it means there are 8 bytes of padding between the end of buffer_one and the beginning of buffer_two, and the behavior on attempting to read/write those bytes is undefined. In practical terms, it means your code can tolerate a small buffer overflow in buffer_one with no ill effects, but you don't want to rely on that.

  3. You cannot rely on the ordering between objects to be meaningful. You certainly cannot rely on it being repeatable across implementations.

  4. Although you'll be hard-pressed to find a C implementation that doesn't use one, the language definition doesn't require the use of a runtime stack, nor does it require that the stack grow upwards (towards increasing addresses) or downwards (towards decreasing addresses). That's strictly an implementation detail.

Upvotes: 4

Related Questions