When defining (and exec) commands that contain a "continue", pwndbg gets stuck (while vanilla gdb does not). Here is a test case.
Consider this simple program:
#include <stdio.h>
void foo() {
printf("foo\n");
}
int main() {
int i;
printf("Attach the debugger...\n");
getchar();
for (i=0; i<10; i++) {
foo();
}
}
Consider this simple gdb script:
break foo
commands
silent
echo hit foo breakpoint\n
continue
end
Current result: when foo is called and the breakpoint is hit, pwndbg gets stuck beyond recovery (and the program's execution is not resumed).
Expected result: I would expect to see "hit foo breakpoint" 10 times, with no additional interaction.
Notes:
Pwndbg will print "breakpoint is hit" and get stuck there. Let me know if you have problems reproducing it.
I'm running Ubuntu 16.04. I've tried with both stable and dev version of pwndbg.
pwndbg> version
Gdb: GNU gdb (Ubuntu 7.11.1-0ubuntu1~16.5) 7.11.1
Python: 3.5.2 (default, Nov 23 2017, 16:37:01) [GCC 5.4.0 20160609]
Pwndbg: 1.0.0 build: 4b2781f
and
pwndbg> version
Gdb: GNU gdb (Ubuntu 7.11.1-0ubuntu1~16.5) 7.11.1
Python: 3.5.2 (default, Nov 23 2017, 16:37:01) [GCC 5.4.0 20160609]
Pwndbg: 1.0.0 build: f595ba3
Capstone: 3.0.768
Unicorn: 1.0.0
The bug occurs only if silent is present in the script. I am not sure yet what does it change.
I seem to be encountering this without silent:
tukan@farm:~t$ cat repro.c
int main() {
return 42;
}
tukan@farm:~t$ gcc -o repro -ggdb repro.c
tukan@farm:~t$ cat gdb2.cmd
break main
commands
continue
end
tukan@farm:~t$ gdb -x gdb2.cmd ./repro
pwndbg: loaded 168 commands. Type pwndbg [filter] for a list.
pwndbg: created $rebase, $ida gdb functions (can be used with print/break)
Reading symbols from ./repro...done.
Breakpoint 1 at 0x4004da: file repro.c, line 2.
pwndbg> r
Starting program: ~t/repro
Breakpoint 1, main () at repro.c:2
2 return 42;
And gdb cannot be recovered. After disabling pwndbg:
tukan@farm:~t$ sed -ie '1 s/^/#/' ~/.gdbinit
tukan@farm:~t$ gdb -x gdb2.cmd ./repro
Reading symbols from ./repro...done.
Breakpoint 1 at 0x4004da: file repro.c, line 2.
(gdb) r
Starting program: ~t/repro
Breakpoint 1, main () at repro.c:2
2 return 42;
[Inferior 1 (process 14376) exited with code 052]
(gdb)
I was able to work around this by replacing continue with python gdb.execute('continue') but this isn't recommended as far as I'm aware.
I looked at this a little more. You don't need pwndbg to reproduce this bug, just any python program that runs gdb.execute() during a stop event and continue in your commands to run after a breakpoint is hit.
From my analysis, it appears that running gdb.execute inside a handler for a stop event results in the commands the user defined to run after the breakpoint being hit executing. If the user requested for something to continue, this results in the program running while the handler is still executing.
My guess is that gdb more or less puts the commands received textually into some queue, and gdb.execute just adds to the end of the queue.
Here is my reproduction case:
My ~/.gdbinit sources only the following file:
import gdb
def stop_handler (event):
print("Python stop handler started")
print("Thread is stopped: " + str(gdb.selected_thread().is_stopped()))
print("Thread is running: " + str(gdb.selected_thread().is_running()))
(gdb.execute("",to_string=True))
print("Thread is stopped: " + str(gdb.selected_thread().is_stopped()))
print("Thread is running: " + str(gdb.selected_thread().is_running()))
print("Python Stop handler finished\n")
gdb.events.stop.connect (stop_handler)
If you comment out the gdb.execute(""), it should run with no trouble.
You can use either of the previous c test cases. I think the only important thing is that the commands contains a continue.
This seems like a flaw in the gdb API, in that the state could totally change from running a seemingly unrelated command. I don't know of any way pwndbg can work around this. I'm also not sure why this results in gdb hanging, because it seems like the handler finishes running.