Repository: pwndbg/pwndbg

Issue:
The reverse- commands of rr stop working after a SIGSEGV when used with pwndbg
### Description

When running rr with pwndbg and encountering a SIGSEGV in the debugged application, the reverse- commands of rr stop working. After executing them, the same context is printed as before, rip and the other registers/memory remains in the same state, etc. (see the attached session below).

If I disable pwndbg, they work fine in the same situation. They also work with pwndbg when there's no SIGSEGV encountered. Another thing I noticed is that I can step back from SIGABRTs, so it might be specific to segmentation faults?

I also think I've seen this issue in the not-so-recent past (last year maybe), so it's likely not something new.

### Steps to reproduce

```
tukan@farm:~/t$ cat sigsegv.c
int main() {
    *(int *)0x414141 = 0x41;
    return 0;
}
tukan@farm:~/t$ gcc -O0 -o sigsegv sigsegv.c 
tukan@farm:~/t$ rr record ./sigsegv
rr: Saving execution to trace directory `/home/tukan/.local/share/rr/sigsegv-1'.
Segmentation fault
tukan@farm:~/t$ rr replay
```
rr session attached: [rr-session.txt](https://github.com/pwndbg/pwndbg/files/2053502/rr-session.txt)


### My setup

* Ubuntu 16.04 amd64
* Python 3.5.2 (default, Nov 23 2017, 16:37:01) 
* GNU gdb (Ubuntu 7.11.1-0ubuntu1~16.5) 7.11.1
* rr latest (f3bde22)
* pwndbg latest (f56f271)





Discussion:
Hey, thanks for reporting this. I will take a look on that tomorrow.
We've both investigated it today and talked on irc, the problem is caused by `gdb.parse_and_eval` invoked here:
https://github.com/pwndbg/pwndbg/blob/dev/pwndbg/commands/context.py#L371-L375

We use this code to display to the user that something has happend with debugging program (it exited, segaulted etc) AFTER we display context. GDB does it by default but it does it before the context and so most of the time the user doesn't see that.

If we remove them, everything works fine.

Resolving patch:
diff --git a/pwndbg/commands/context.py b/pwndbg/commands/context.py
--- a/pwndbg/commands/context.py
+++ b/pwndbg/commands/context.py
@@ -5,6 +5,7 @@
 from __future__ import print_function
 from __future__ import unicode_literals
 
+import ast
 import codecs
 import sys
 
@@ -367,12 +368,20 @@ def save_signal(signal):
 
     elif isinstance(signal, gdb.SignalEvent):
         msg = 'Program received signal %s' % signal.stop_signal
+
         if signal.stop_signal == 'SIGSEGV':
-            try:
-                si_addr = gdb.parse_and_eval("$_siginfo._sifields._sigfault.si_addr")
-                msg += ' (fault address %#x)' % int(si_addr or 0)
-            except gdb.error:
-                pass
+
+            # When users use rr (https://rr-project.org or https://github.com/mozilla/rr)
+            # we can't access $_siginfo, so lets just show current pc
+            # see also issue 476
+            if _is_rr_present():
+                msg += ' (current pc: %#x)' % pwndbg.regs.pc
+            else:
+                try:
+                    si_addr = gdb.parse_and_eval("$_siginfo._sifields._sigfault.si_addr")
+                    msg += ' (fault address %#x)' % int(si_addr or 0)
+                except gdb.error:
+                    pass
         result.append(message.signal(msg))
 
     elif isinstance(signal, gdb.BreakpointEvent):
@@ -396,3 +405,17 @@ def context_signal():
     's': context_stack,
     'b': context_backtrace
 }
+
+
+@pwndbg.memoize.forever
+def _is_rr_present():
+    """
+    Checks whether rr project is present (so someone launched e.g. `rr replay <some-recording>`)
+    """
+
+    # this is ugly but I couldn't find a better way to do it
+    # feel free to refactor it
+    globals_list_literal_str = gdb.execute('python print(list(globals().keys()))', to_string=True)
+    interpreter_globals = ast.literal_eval(globals_list_literal_str)
+
+    return 'RRCmd' in interpreter_globals and 'RRWhere' in interpreter_globals


Test patch: