Fix context history with `cwatch`'d expressions (#2617)

The output of the expressions section changes even when running `context` multiple times after each other. The output variables in GDB are counted up when reexecuting the watched commands for example. The other sections don't change their output. This caused the history to be extended infinitely when using `ctxp` while having a `cwatch` command executed.

Special case the `expressions` context section in the history handling to avoid reevaluating the watched commands/expressions while browsing the history. This doesn't add the context output to the history when the expressions VALUES change somehow like it is done for the other sections, but since we cannot know if gdb counted up their output variable names from $1 to $2 or the value changed, this is a compromise.
pull/2635/head
peace-maker 12 months ago committed by GitHub
parent 6a983126da
commit 6e4f89b4ff
No known key found for this signature in database
GPG Key ID: B5690EEEBB952194

@ -312,7 +312,18 @@ def serve_context_history(function: Callable[P, List[str]]) -> Callable[P, List[
# Add the current section to the history if it is not already there
current_output = []
if pwndbg.aglib.proc.alive:
current_output = function(*a, **kw)
# Do not reevaluate the expressions section because its content is not deterministic.
# Instead, reuse the last evaluated expression and rely on the other sections to deselect
# the history entry if the output changed.
# https://github.com/pwndbg/pwndbg/issues/2579
if (
section_name == "expressions"
and selected_history_index is not None
and len(context_history[section_name]) > 0
):
current_output = context_history[section_name][-1]
else:
current_output = function(*a, **kw)
if (
len(context_history[section_name]) == 0
or context_history[section_name][-1] != current_output

@ -493,6 +493,28 @@ def test_context_history_prev_next(start_binary):
assert history_ctx != third_ctx
assert "(history " not in third_ctx
# Check if cwatch expressions are also stored in the history
gdb.execute("cwatch $rip")
gdb.execute("cwatch execute 'p/z $rsp'")
fourth_ctx = gdb.execute("ctx", to_string=True)
assert "1: $rip = " in fourth_ctx
assert "2: p/z $rsp\n$1 = 0x" in fourth_ctx
# The next context shows a different output variable $2
gdb.execute("si")
fifth_ctx = gdb.execute("ctx", to_string=True)
assert "1: $rip = " in fifth_ctx
assert "2: p/z $rsp\n$2 = 0x" in fifth_ctx
# Check that the expression section shows the old gdb variable $1 again.
gdb.execute("contextprev")
history_ctx = gdb.execute("ctx", to_string=True)
assert "1: $rip = " in history_ctx
assert "2: p/z $rsp\n$1 = 0x" in history_ctx
gdb.execute("cunwatch 2")
gdb.execute("cunwatch 1")
def test_context_history_search(start_binary):
start_binary(REFERENCE_BINARY)

Loading…
Cancel
Save