GDB reverse debugging
Let's start with a simple C++ program in the file gdb-test.cpp
:
#include <iostream> const unsigned int SIZE = 10; int main() { int b[SIZE]; // intitialize for(int i=0; i <= SIZE; i++) { b[i]=0; } std::cout << "done!" << std::endl; return 0; }
Let's comple and run it.
$ g++ -g gdb-test.cpp -o gdb-test $ ./gdb-test
??? It doesn't stop!
Let's load it into gdb
.
$ gdb ./gdb-test GNU gdb (GDB) 7.1-ubuntu Copyright (C) 2010 Free Software Foundation, Inc. License GPLv3+: GNU GPL version 3 or later <http://gnu.org/licenses/gpl.html> [...] Reading symbols from /home/larsr/gdb-test...done. (gdb)
Now run it:
(gdb) r Starting program: /home/larsr/gdb-test
Still dosn't stop! Stop it with ctrl-c:
^C Program received signal SIGINT, Interrupt. 0x08048704 in main () at gdb-test.cpp:11 11 for(int i=0; i <= SIZE; i++) { (gdb)
Let's look at the variable i.
(gdb) print i $1 = 4 (gdb)
Looks normal enough! But strange that it didn't get further than 4... Well, let's run some more, break, and look at i.
(gdb) continue Continuing. ^C Program received signal SIGINT, Interrupt. 0x080486fc in main () at gdb-test.cpp:11 11 for(int i=0; i <= SIZE; i++) { (gdb) print i $2 = 7 (gdb) c Continuing. ^C Program received signal SIGINT, Interrupt. 0x080486f8 in main () at gdb-test.cpp:11 11 for(int i=0; i <= SIZE; i++) { (gdb) print i $4 = 6 (gdb)
What? First i
was 7 and then it was 6
! How did that happen? Lets start to record, and run some more!
(gdb) record (gdb) c Continuing. ^C Program received signal SIGINT, Interrupt. main () at gdb-test.cpp:11 11 for(int i=0; i <= SIZE; i++) { (gdb) print i $6 = 2 (gdb)
Something has modified i
to become smaller! When did this happen? Let's watch i
and go backwards.
(gdb) reverse-continue Continuing. Hardware watchpoint 1: i Old value = 2 New value = 1 main () at gdb-test.cpp:11 11 for(int i=0; i <= SIZE; i++) {
This makes sense. We are walking backswards, and in that direction we had a value of 2 which turned into 1. Let's keep going backwards.
(gdb) reverse-continue Continuing. Hardware watchpoint 1: i Old value = 1 New value = 0 main () at gdb-test.cpp:11 11 for(int i=0; i <= SIZE; i++) { (gdb) reverse-continue Continuing. Hardware watchpoint 1: i Old value = 0 New value = 10 0x080486eb in main () at gdb-test.cpp:12 12 b[i]=0; (gdb)
Here we see that as we went backwards, we went from 0 to 10. That means that when we ran forward, i
went from 10 to 0, and it happened when we executed
b[i]=0;
Could it be that writing to b[10]
overwrites the memory of i
? Where are they located in memory?
(gdb) print &i $7 = (int *) 0xbffff7bc (gdb) print &b[i] $8 = (int *) 0xbffff7bc (gdb)
Yup, they are at the same address. Why? Doh! An array indexing error! b[10]
is not within the array bounds! The last element is b[9]
.
Line 11 should use <
instead of <=
and read
for(int i=0; i < SIZE; i++) {
Case closed.
An ugly observation is that programs like this bug out silently and "work" if you change the program to be initialized to 100 (or something bigger than SIZE).