Hi folks! I stumbled across some strange stuff on the stack when calling a function with gcc-3.4.3. I wanted to learn something about the stack and buffer overflows. So I wrote this little piece of c code (s1.c): #include <string.h> void f( char *args) { char buf1[10]; char buf2[4] = "ABC"; strcpy (buf1, args); } int main (int argc, char *argv[]) { if (argc > 1) { printf("Input: %s\n", argv[1]); f(argv[1]); } return 0; } Compiled it and fired up gdb: $ gcc -g -o s1 s1.c $ gdb s1 [...] (gdb) list 1 #include <string.h> 2 void f( char *args) { 3 char buf1[10]; 4 char buf2[4] = "ABC"; 5 strcpy (buf1, args); 6 } 7 8 int main (int argc, char *argv[]) { 9 if (argc > 1) { 10 printf("Input: %s\n", argv[1]); (gdb) break 6 Breakpoint 1 at 0x80483e4: file s1.c, line 6. (gdb) run `python -c 'print "A"*10'` Starting program: /mnt/data/studium/vi/rlp/bo/s1 `python -c 'print "A"*10'` Input: AAAAAAAAAA Breakpoint 1, f (args=0xbffff3c3 "AAAAAAAAAA") at s1.c:6 6 } (gdb) Note: I know that 10 A's is to long (10 A's + \0), but this is not the problem here. In fact, the program should throw a segmentation fault if it gets more than 11 A's (and it does that on a debian box with gcc-2.95.4). But on my system, it works with up to 23 A's. AFAIK, when a funktion is called, the calling function pushes the parameters and the adress of the next command after the function call (Return Instruction Pointer, RIP) on the stack. The function that is called saves the frame pointer (FP) on the stack and then reserves space on the stack for local variables by decrementing the stack pointer. As the stack grows from top to bottom, at the breakpoint the stack should look this: RIP FP buf1 [..] buf1 buf2 buf2 buf2 But in fact, it looks like this: (gdb) info frame 0 Stack frame at 0xbffff170: eip = 0x80483e4 in f (s1.c:6); saved eip 0x8048430 called by frame at 0xbffff190 source language c. Arglist at 0xbffff168, args: args=0xbffff3c3 "AAAAAAAAAA" Locals at 0xbffff168, Previous frame's sp is 0xbffff170 Saved registers: ebp at 0xbffff168, eip at 0xbffff16c (gdb) x/12x buf2 0xbffff14c: 0x00434241 0x41414141 0x41414141 0xb7004141 0xbffff15c: 0xb7fd45e0 0x08048558 0xbffff178 0xbffff188 0xbffff16c: 0x08048430 0xbffff3c3 0xbffff3c3 0x00000000 (gdb) As you can see buf2 is on the lowest adress, followed by buf1. But what happend to the saved ebp (at 0xbffff168, value 0xbffff188) and saved eip (at 0xbffff16c, value 0x8048430)? Why are they not located at 0xbffff15c and 0xbffff160? Why is there an 8 byte "hole"? What is stored there? Having a look at the assembler code: (gdb) disass Dump of assembler code for function f: 0x080483a4 <f+0>: push %ebp 0x080483a5 <f+1>: mov %esp,%ebp 0x080483a7 <f+3>: sub $0x38,%esp 0x080483aa <f+6>: mov 0x8048538,%eax 0x080483af <f+11>: mov %eax,0xffffffe4(%ebp) 0x080483b2 <f+14>: mov 0x8(%ebp),%eax 0x080483b5 <f+17>: mov %eax,0x4(%esp) 0x080483b9 <f+21>: lea 0xffffffe8(%ebp),%eax 0x080483bc <f+24>: mov %eax,(%esp) 0x080483bf <f+27>: call 0x80482dc <_init+72> 0x080483c4 <f+32>: leave 0x080483c5 <f+33>: ret I've tested this on a gcc-2.95.4 on a Debian system and everything was at the expected location in memory. Assembler looks like this: (gdb) disass Dump of assembler code for function f: 0x8048430 <f>: push %ebp 0x8048431 <f+1>: mov %esp,%ebp 0x8048433 <f+3>: sub $0x18,%esp 0x8048436 <f+6>: mov 0x80484f4,%eax 0x804843b <f+11>: mov %eax,0xfffffff0(%ebp) 0x804843e <f+14>: add $0xfffffff8,%esp 0x8048441 <f+17>: mov 0x8(%ebp),%eax 0x8048444 <f+20>: push %eax 0x8048445 <f+21>: lea 0xfffffff4(%ebp),%eax 0x8048448 <f+24>: push %eax 0x8048449 <f+25>: call 0x8048338 <strcpy> 0x804844e <f+30>: add $0x10,%esp 0x8048451 <f+33>: leave 0x8048452 <f+34>: ret End of assembler dump. Line <f+3> seems to be the key: gcc-3.4.3 just "wastes" some space. But why? It may be an alignment thing for cache optimization, but it doesn't go away with -O0... Here are some specs about my system: $ uname -a Linux d6k 2.6.11-gentoo-r6 #8 SMP Wed May 11 02:12:58 CEST 2005 i686 Intel(R) Pentium(R) M processor 1.86GHz GenuineIntel GNU/Linux $ gcc -v Lese Spezifikationen von /usr/lib/gcc/i686-pc-linux-gnu/3.4.3-20050110/specs Konfiguriert mit: /var/tmp/portage/gcc-3.4.3.20050110-r2/work/gcc-3.4.3/configure --enable-version-specific-runtime-libs --prefix=/usr --bindir=/usr/i686-pc-linux-gnu/gcc-bin/3.4.3-20050110 --includedir=/usr/lib/gcc/i686-pc-linux-gnu/3.4.3-20050110/include --datadir=/usr/share/gcc-data/i686-pc-linux-gnu/3.4.3-20050110 --mandir=/usr/share/gcc-data/i686-pc-linux-gnu/3.4.3-20050110/man --infodir=/usr/share/gcc-data/i686-pc-linux-gnu/3.4.3-20050110/info --with-gxx-include-dir=/usr/lib/gcc/i686-pc-linux-gnu/3.4.3-20050110/include/g++-v3 --host=i686-pc-linux-gnu --disable-altivec --enable-nls --without-included-gettext --with-system-zlib --disable-checking --disable-werror --disable-libunwind-exceptions --disable-multilib --disable-libgcj --enable-languages=c,c++,f77 --enable-shared --enable-threads=posix --enable-__cxa_atexit --enable-clocale=gnu Thread-Modell: posix gcc-Version 3.4.3-20050110 (Gentoo 3.4.3.20050110-r2, ssp-3.4.3.20050110-0, pie-8.7.7) I've tried gcc with -no-pie and -no-stack-protector-all -march=i386 and also gcc-3.3.5, but got the same behaviour. I don't know if this behaviour is caused by some Gentoo-specific configuration or by gcc >3, unfortunately, I've no access to another system to do more testing. I would be really thankful if someone could explain me this behaviour. Greets, Daniel Hepper