Repository: pwndbg/pwndbg

Issue:
Context disasm crashes when trying to show dereferenced call address
Enhancing a disassembled instruction fails if it is an indirect call and the address cannot be dereferenced/retrieved.

The issue can be reproduced with a binary created from assembly:
```
global _start
_start:
call qword [r11+0x30]
```

Or, full testcase (assuming `nasm` is installed):
```
printf 'global _start\n_start:\ncall qword [r11+0x30]' > test.asm && nasm -f elf64 test.asm && ld test.o -o test && gdb ./test -ex entry
```


Current result:
```
Traceback (most recent call last):
  File "/home/dc/pwndbg/pwndbg/commands/__init__.py", line 135, in __call__
    return self.function(*args, **kwargs)
  File "/home/dc/pwndbg/pwndbg/commands/__init__.py", line 226, in _OnlyWhenRunning
    return function(*a, **kw)
  File "/home/dc/pwndbg/pwndbg/commands/context.py", line 88, in context
    result.extend(func())
  File "/home/dc/pwndbg/pwndbg/commands/context.py", line 100, in context_regs
    return [pwndbg.ui.banner("registers")] + get_regs()
  File "/home/dc/pwndbg/pwndbg/commands/context.py", line 144, in get_regs
    desc = pwndbg.chain.format(value)
  File "/home/dc/pwndbg/pwndbg/chain.py", line 122, in format
    enhanced = pwndbg.enhance.enhance(chain[-2] + offset, code=code)
  File "/home/dc/pwndbg/pwndbg/enhance.py", line 102, in enhance
    instr = pwndbg.disasm.one(value)
  File "/home/dc/pwndbg/pwndbg/disasm/__init__.py", line 115, in one
    for insn in get(address, 1):
  File "/home/dc/pwndbg/pwndbg/disasm/__init__.py", line 135, in get
    i = get_one_instruction(address)
  File "/home/dc/pwndbg/pwndbg/memoize.py", line 48, in __call__
    value = self.func(*args, **kwargs)
  File "/home/dc/pwndbg/pwndbg/disasm/__init__.py", line 107, in get_one_instruction
    pwndbg.disasm.arch.DisassemblyAssistant.enhance(ins)
  File "/home/dc/pwndbg/pwndbg/disasm/arch.py", line 55, in enhance
    enhancer.enhance_next(instruction)
  File "/home/dc/pwndbg/pwndbg/disasm/arch.py", line 111, in enhance_next
    instruction.target = self.next(instruction, call=True)
  File "/home/dc/pwndbg/pwndbg/disasm/x86.py", line 112, in next
    return super(DisassemblyAssistant, self).next(instruction, call)
  File "/home/dc/pwndbg/pwndbg/disasm/arch.py", line 150, in next
    addr = int(pwndbg.memory.poi(pwndbg.typeinfo.ppvoid, addr))
  File "/home/dc/pwndbg/pwndbg/inthook.py", line 40, in __new__
    value = value.cast(pwndbg.typeinfo.ulong)
gdb.MemoryError: Cannot access memory at address 0x30
```

Expected result - display disasm:
```
call   qword ptr [r11 + 0x30]  # <INVALID>
```

or `INVALID DEREF` or sth like this.


Discussion:

Resolving patch:
diff --git a/pwndbg/disasm/arch.py b/pwndbg/disasm/arch.py
--- a/pwndbg/disasm/arch.py
+++ b/pwndbg/disasm/arch.py
@@ -8,6 +8,7 @@
 import collections
 
 import capstone
+import gdb
 from capstone import *
 
 import pwndbg.memoize
@@ -147,7 +148,12 @@ def next(self, instruction, call=False):
 
             # self.memory may return none, so we need to check it here again
             if addr is not None:
-                addr = int(pwndbg.memory.poi(pwndbg.typeinfo.ppvoid, addr))
+                try:
+                    # fails with gdb.MemoryError if the dereferenced address
+                    # doesn't belong to any of process memory maps
+                    addr = int(pwndbg.memory.poi(pwndbg.typeinfo.ppvoid, addr))
+                except gdb.MemoryError:
+                    return None
         if op.type == CS_OP_REG:
             addr = self.register(instruction, op)
 
@@ -243,6 +249,9 @@ def memory_sz(self, instruction, operand):
         return None # raise NotImplementedError
 
     def dump(self, instruction):
+        """
+        Debug-only method.
+        """
         ins = instruction
         rv  = []
         rv.append('%s %s' % (ins.mnemonic, ins.op_str))


Test patch: