Repository: pwndbg/pwndbg

Issue:
`context` sometimes gets printed with the `set` command
`set $rax=0` sometimes causes `context` to be immediately called afterwards. I think don't think this is always reproducible, but will keep investigating it.


Discussion:
I can reproduce this with `gdbserver :1234 /bin/ls` and `gdb --quiet -ex 'target remote :1234'` in another terminal.

It seems to me this may be connected to https://github.com/pwndbg/pwndbg/blob/77380eb63c6cfdb721f2a2c4448f9d544fa03bd7/pwndbg/gdblib/events.py#L98-L100 but I am not sure yet
Actually I can also reproduce this without gdbserver too
Hmm, I added some crasher to `context.context()` 2nd call:
```
pwndbg> set exception-debugger on
Set whether to debug exceptions raised in Pwndbg commands to True
pwndbg> set debug-events on
Set display internal event debugging info to True
'before_prompt' pwndbg.gdblib.hooks.memoize_before_prompt ()
pwndbg> set $rax=1
'reg_changed' pwndbg.gdblib.hooks.memoize_on_stop (<gdb.RegisterChangedEvent object at 0x7fde0cb6b030>,)
'before_prompt' pwndbg.gdblib.hooks.memoize_before_prompt ()
Traceback (most recent call last):
  File "/home/user/pwndbg/pwndbg/commands/__init__.py", line 145, in __call__
    return self.function(*args, **kwargs)
  File "/home/user/pwndbg/pwndbg/commands/__init__.py", line 216, in _OnlyWhenRunning
    return function(*a, **kw)
  File "/home/user/pwndbg/pwndbg/commands/context.py", line 358, in context
    asdf
NameError: name 'asdf' is not defined
```

And also checked this later on:
<img width="1751" alt="image" src="https://user-images.githubusercontent.com/10009354/193487112-94cea41f-f428-40a7-91b4-3d38585232fe.png">


Seems that setting a register triggers a prompt hook event, while other `set` commands do not.
**EDIT: See my next post for better explanation**

--- 
This happens due to this hook called when reg/mem is changed:
https://github.com/pwndbg/pwndbg/blob/32166d0ed3d73a403e2b53d8649fb8b0a5b0de6b/pwndbg/gdblib/hooks.py#L29-L33

It clears all "stop" caches and also the `prompt_hook_on_stop` cache which calls the context command and returns None:
https://github.com/pwndbg/pwndbg/blob/32166d0ed3d73a403e2b53d8649fb8b0a5b0de6b/pwndbg/gdblib/prompt.py#L64-L66

Fwiw if we comment out the `@pwndbg.lib.memoize.reset_on_stop` decorator on the `prompt_hook_on_stop` we will make it so that any command execution will print the context :P.

I think this might have been done on purpose to display the context on direct changes to regs or memory?

We can easily change this by removing the `reg_changed/mem_changed` decorators.
> I think this might have been done on purpose to display the context on direct changes to regs or memory?

If this is the case, we should run `context regs` on register changes, not `context`. For memory changes, I think for now we should probably just not print anything.
After more thinking and Discord discussions, I don't think we should display any context on reg/mem changes.

Also, here is a full writeup summary of this issue:

    The fact that we displayed ctx on regs/mem changes was a result of the
    fact that we cleared the cached result of `prompt_hook_on_stop`:

    ```python
     @pwndbg.lib.memoize.reset_on_stop
     def prompt_hook_on_stop(*a):
         pwndbg.commands.context.context()
    ```

    And that this function is used in the `prompt_hook` that we set up:

    ```python
    def prompt_hook(*a):
        global cur

        new = (gdb.selected_inferior(), gdb.selected_thread())

        if cur != new:
            pwndbg.gdblib.events.after_reload(start=cur is None)
            cur = new

        if pwndbg.proc.alive and pwndbg.proc.thread_is_stopped:
            prompt_hook_on_stop(*a)
    ```

    We cleared the cache through the `memoize_on_stop` function:

    ```
     @pwndbg.gdblib.events.stop
     @pwndbg.gdblib.events.mem_changed
     @pwndbg.gdblib.events.reg_changed
     def memoize_on_stop():
         reset_on_stop._reset()
    ```

    Now, it most likely makes sense to keep reseting the stop caches on
    mem/reg changed events. Otherwise, we may display wrong things if user
    sets a register/memory and redisplays the context through the `ctx`
    command or uses other command that relies on register/memory state.

    So we must either change the way the `prompt_hook_on_stop` is cached,
    which I believe is done this way to display the context ONLY when
    program execution advances (when breakpoints are hit, or when
    stepping?), or, we have to clear the `prompt_hook_on_stop` cache ONLY
    when stop event is triggered, but not when mem/reg events are triggered.

Resolving patch:
diff --git a/pwndbg/gdblib/prompt.py b/pwndbg/gdblib/prompt.py
--- a/pwndbg/gdblib/prompt.py
+++ b/pwndbg/gdblib/prompt.py
@@ -50,8 +50,11 @@ def initial_hook(*a):
     gdb.prompt_hook = prompt_hook
 
 
+context_shown = False
+
+
 def prompt_hook(*a):
-    global cur
+    global cur, context_shown
 
     new = (gdb.selected_inferior(), gdb.selected_thread())
 
@@ -59,13 +62,15 @@ def prompt_hook(*a):
         pwndbg.gdblib.events.after_reload(start=cur is None)
         cur = new
 
-    if pwndbg.proc.alive and pwndbg.proc.thread_is_stopped:
-        prompt_hook_on_stop(*a)
+    if pwndbg.proc.alive and pwndbg.proc.thread_is_stopped and not context_shown:
+        pwndbg.commands.context.context()
+        context_shown = True
 
 
-@pwndbg.lib.memoize.reset_on_stop
-def prompt_hook_on_stop(*a):
-    pwndbg.commands.context.context()
+@pwndbg.gdblib.events.cont
+def reset_context_shown(*a):
+    global context_shown
+    context_shown = False
 
 
 @pwndbg.config.Trigger([message.config_prompt_color, disable_colors])


Test patch: