Is your stacktrace really corrupted?
You may encounter, during your debugging sessions, the `stack corruption’ problem. Usually you will find it out after seeing your program run into a segmentation fault. Otherwise, it must mean that some very malicious and subtle code has been injected into your program, usually through a buffer overrun. What is a buffer overrun? Let’s examine the following short C code:
#include <stdio.h>
void bar(char* str) {
char buf[4];
strcpy( buf, str );
}
void foo() {
printf("Hello from foo!");
}
int main(void) {
bar("This string definitely is too long, sorry!");
foo();
return 0;
}
There’s clearly something wrong with it: as you can see, we are copying `str’ to `buf’ without first checking the size of `str’. First of all there is a security issue, because if `str’ didn’t just come from a fixed string like in this case, but got inputted from somewhere (maybe on a website), then there could be a string long enough to overwrite the code of `foo’, and run malicious code on its behalf. What we have here, anyhow, is just a segmentation fault. Let’s debug the program.
(gdb) file stack
Reading symbols from /home/siovene/stack...done.
(gdb) run
Starting program: /home/siovene/stack
Program received signal SIGSEGV, Segmentation fault.
0x6f6c206f in ?? ()
(gdb) backtrace
#0 0x6f6c206f in ?? ()
#1 0x202c676e in ?? ()
#2 0x72726f73 in ?? ()
#3 0xbf002179 in ?? ()
#4 0xb7df9970 in __libc_start_main ()
from /lib/tls/i686/cmov/libc.so.6
Previous frame inner to this frame (corrupt stack?)
Obviously something must have gone wrong.
In order to better understand what is going on, let’s make a step back, and let’s examine
a working example instead:#include <stdio.h> void bar(char* str) { char buf[4]; strcpy( buf, str ); } void foo() { printf("Hello from foo!"); } int main(void) { bar("abc"); foo(); return 0; }
This is the same code, but it’s been stripped off of the long string that caused the
segmentation fault, and in its place we find a harmless 3 character string: `abc’.
Let’s name the program stack.c anc compile it with debug informaion:
$> gcc -g -o stack stack.c
Now let’s debug it:
(gdb) file stack Reading symbols from /home/siovene/stack...done. (gdb) break bar Breakpoint 1 at 0x80483ca: file stack.c, line 5. (gdb) run Starting program: /home/siovene/stack Breakpoint 1, bar (str=0x8048545 "abc") at stack.c:5 5 strcpy( buf, str );
We have entered the bar() function, let’s examine the backtrace:
(gdb) backtrace #0 bar (str=0x8048545 "abc") at stack.c:5 #1 0x0804840e in main () at stack.c:13
What is the address of the bar() function?
(gdb) print bar $1 = {void (char *)} 0x80483c4
Let’s now be paranoid and check this out producing a dump of our executable:
$> objdump -tD stack > stack.dis
Open the file with your favorite editor and look for `80483c4′, the address of bar():
080483c4 <bar>: 80483c4: 55 push %ebp 80483c5: 89 e5 mov %esp,%ebp 80483c7: 83 ec 28 sub $0x28,%esp 80483ca: 8b 45 08 mov 0x8(%ebp),%eax 80483cd: 89 44 24 04 mov %eax,0x4(%esp) 80483d1: 8d 45 e8 lea 0xffffffe8(%ebp),%eax 80483d4: 89 04 24 mov %eax,(%esp) 80483d7: e8 0c ff ff ff call 80482e8 80483dc: c9 leave 80483dd: c3 ret
Perfect, that’s our function. But now let’s get curious.
Where’s the stack pointer in the CPU registers?
(gdb) info registers eax 0x0 0 ecx 0xb7ed11b4 -1209200204 edx 0xbff04f60 -1074770080 ebx 0xb7ecfe9c -1209205092 esp 0xbff04f10 0xbff04f10 ebp 0xbff04f38 0xbff04f38 esi 0xbff04fd4 -1074769964 edi 0xbff04fdc -1074769956 eip 0x80483ca 0x80483ca eflags 0x282 642 cs 0x73 115 ss 0x7b 123 ds 0x7b 123 es 0x7b 123 fs 0x0 0 gs 0x33 51
The `esp’ register, on the architecture this article is written on, is the stack pointer.
Its address is 0xbff04f10. Let’s examine the memory at that point:
(gdb) x/20xw 0xbff04f10 0xbff04f10: 0x00000000 0x08049638 0xbff04f28 0x080482b5 0xbff04f20: 0xb7ecfe90 0xbff04f34 0xbff04f48 0x0804843b 0xbff04f30: 0xbff04fdc 0xb7ecfe9c 0xbff04f48 0x0804840e 0xbff04f40: 0x08048545 0x08048480 0xbff04fa8 0xb7db3970 0xbff04f50: 0x00000001 0xbff04fd4 0xbff04fdc 0x00000000
With this command we have told GDB to examine 20 words in exadecimal format
at the address 0xbff04f10. That’s because the value of the stack pointer is the
address of the back-chain pointer to the previous stack frame. So address 0×00000000 is the
address of the previous stack frame. But 0×00000000 is put in the stack frame in concurrence of
the program entry point, i.e. the main() function. This agrees with the fact that we know bar()
was called by main()!
Everything looks ok and in place, since the program works perfectly we weren’t expecting anything
different. Let’s now do the same with the faulty program. At the moment of the segmentation fault,
the backtrace looked like this:
(gdb) backtrace #0 0x6f6c206f in ?? () #1 0x202c676e in ?? () #2 0x72726f73 in ?? () #3 0xbf002179 in ?? () #4 0xb7df9970 in __libc_start_main () from /lib/tls/i686/cmov/libc.so.6 Previous frame inner to this frame (corrupt stack?)
To see exactly what goes on, it would be better to debug it more carefully:
(gdb) file stack Reading symbols from /home/siovene/stack...done. (gdb) break bar Breakpoint 1 at 0x80483ca: file stack.c, line 5. (gdb) run Starting program: /home/siovene/stack Breakpoint 1, bar (str=0x8048580 "This string definitely is too long, sorry!") at stack.c:5 5 strcpy( buf, str ); (gdb) next 6 } (gdb) next 0x6f6c206f in ?? () (gdb) next Cannot find bounds of current function
Let’s then try to follow back the stacktrace, as we did previously:(gdb) backtrace #0 0x6f6c206f in ?? () #1 0x202c676e in ?? () #2 0x72726f73 in ?? () #3 0xbf002179 in ?? () #4 0xb7e9b970 in __libc_start_main () from /lib/tls/i686/cmov/libc.so.6 Previous frame inner to this frame (corrupt stack?) (gdb) info registers eax 0xbfeed1e0 -1074867744 ecx 0xb7ea4c5f -1209381793 edx 0x80485ab 134514091 ebx 0xb7fb7e9c -1208254820 esp 0xbfeed200 0xbfeed200 ebp 0x6f742073 0x6f742073 esi 0xbfeed294 -1074867564 edi 0xbfeed29c -1074867556 eip 0x6f6c206f 0x6f6c206f eflags 0x246 582 cs 0x73 115 ss 0x7b 123 ds 0x7b 123 es 0x7b 123 fs 0x0 0 gs 0x33 51 (gdb) x/20xw 0xbfeed200 0xbfeed200: 0x202c676e 0x72726f73 0xbf002179 0xb7e9b970 0xbfeed210: 0x00000001 0xbfeed294 0xbfeed29c 0x00000000 0xbfeed220: 0xb7fb7e9c 0xb7fee540 0x08048480 0xbfeed268 0xbfeed230: 0xbfeed210 0xb7e9b932 0x00000000 0x00000000 0xbfeed240: 0x00000000 0xb7feeca0 0x00000001 0x08048300 (gdb) x/20xw 0x202c676e 0x202c676e: Cannot access memory at address 0x202c676e
There’s only one explanation to that: the stack memory has been overwritten and now contains
gibberish. We have been very unlucky with our example, but this gave us the tools to imagine
another case. Let’s assume the stack got actually corrupted not because it was overwritten
accidentally, but because GDB was failing to build it. In this case you are still able to
navigate it backwards. All you need to do it keep following the value of the stack frames,
starting from the `esp’ register, until you reach 0×000000. Write all the addresses down, and
then use `objdump’ to obtain the disassembly and symbols information from the binary. All is left,
now, is to check the names of the symbols matching the pinned up addresses.
If you can actually do that, than you have successfully reconstructed your stacktrace.
It wasn’t really corrupted by a bug in your program, but simply GDB missed to keep it up with it.
Filed under: Debugging | 1 Comment »