|
It gets worse if you have a stack smash, e.g: #include <string.h>
void foo() {
int array[1];
memset(array, 0, 100); /\* Oh no, trash the stack! */
}
int main() {
foo();
}
This gets us: Program received signal SIGSEGV, Segmentation fault.
0x0000000000000000 in ?? ()
(gdb) bt
#0 0x0000000000000000 in ?? ()
#1 0x0000000000000000 in ?? ()
But with a time travel debugger (I'm using UDB because - disclaimer - it's what I work on. `rr` would work just as well): Program received signal SIGSEGV, Segmentation fault.
0x0000000000000000 in ?? ()
recording 10,617> backtrace
#0 0x0000000000000000 in ?? ()
#1 0x0000000000000000 in ?? ()
^ Because we returned to NULL we have segfaulted but the stack is also trashed due to the memset. We don't know how we got here. recording 10,617> reverse-stepi
0x0000000000401146 6 }
99% 10,616> bt
#0 0x0000000000401146 in foo () at smash.c:6
#1 0x0000000000000000 in ?? ()
^ We've stepped back before the return, so we can now see how we got to NULL. Still incomplete stack because it's still trashed. 99% 10,616> reverse-step
5 memset(array, 0, 100); /* Oh no, trash the stack! \*/
99% 10,586> bt
#0 foo () at smash.c:5
#1 0x0000000000401155 in main () at smash.c:9
99% 10,586>
^ We've gone back before the stack smash happened, so now we get the full backtrace. |