diff options
| author | blasty <blasty@local> | 2026-07-09 19:47:38 +0200 |
|---|---|---|
| committer | blasty <blasty@local> | 2026-07-09 19:47:38 +0200 |
| commit | 737744644f0cc45d73e34731046db51326f3ffdd (patch) | |
| tree | 5755fe79416af315c16b93502570a5c5fd1afe44 | |
| parent | docs: TEXTUAL_NOTES.md — Textual pitfalls, app patterns, testing gotchas (diff) | |
| download | ida-tui-737744644f0cc45d73e34731046db51326f3ffdd.tar.gz ida-tui-737744644f0cc45d73e34731046db51326f3ffdd.tar.xz ida-tui-737744644f0cc45d73e34731046db51326f3ffdd.zip | |
fix: xref-select navigated to the wrong place (ordinary-flow contamination)
xrefs data doesn't distinguish a call/jump from ordinary flow (both type=code),
so _xrefs_disasm's 'first code from-xref' subject picked the fall-through to the
NEXT instruction on a plain line. Its only xref is the flow edge back, so
selecting it jumped to the current function at line 0 instead of a real caller.
Drop the from-xref subject entirely: use the symbol under the cursor if any, else
the current address (xrefs-to a function entry == its callers). Now selecting an
xref lands on the exact referencing function AND line. pilot suite 59/59.
| -rw-r--r-- | idatui/app.py | 7 | ||||
| -rw-r--r-- | tests/test_tui.py | 35 |
2 files changed, 40 insertions, 2 deletions
diff --git a/idatui/app.py b/idatui/app.py index 702f59a..76f9943 100644 --- a/idatui/app.py +++ b/idatui/app.py @@ -1380,8 +1380,11 @@ class IdaTui(App): except Exception: # noqa: BLE001 subj = None if subj is None: - frm = self.program.xrefs_from(ea) - subj = next((x.to for x in frm if x.type == "code" and x.to), ea) + # xrefs to the current address itself. (Do NOT chase a from-xref: on + # a plain instruction the only code from-xref is the ordinary-flow + # fall-through to the next instruction, which would point xrefs at + # the wrong place. At a function entry, xrefs-to == the callers.) + subj = ea self._xrefs_present(subj) @work(thread=True, group="xrefs") diff --git a/tests/test_tui.py b/tests/test_tui.py index f43c4a2..d14b525 100644 --- a/tests/test_tui.py +++ b/tests/test_tui.py @@ -303,6 +303,41 @@ async def run(db): check("Esc closes the xrefs popup", not isinstance(app.screen, XrefsScreen)) + # Selecting an xref must land on the referencing SITE (right function + # AND line), not the function under the cursor. + xf = None + for cand in app.program.functions().all_loaded()[:600]: + codex = [x for x in app.program.xrefs_to(cand.addr) + if x.type == "code" and x.frm] + if codex: + xf = (cand, codex[0]) + break + if xf is None: + check("found a function with a code xref", False) + else: + xfn, xref = xf + xexp = app.program.disasm(xref.fn_addr).index_of_ea(xref.frm) + await pilot.press("g") + await pilot.pause(0.2) + for ch in xfn.name: + await pilot.press(ch) + await pilot.press("enter") + await wait_until(pilot, lambda: dis.total > 0 and app._cur.ea == xfn.addr, + timeout=20) + dis.focus() + dis.cursor, dis.cursor_x = 0, 0 # on the entry (address column) + await pilot.press("x") + await wait_until(pilot, lambda: isinstance(app.screen, XrefsScreen), 25) + app.screen.query_one(OptionList).highlighted = 0 + await pilot.press("enter") + await wait_until(pilot, lambda: not isinstance(app.screen, XrefsScreen), 25) + await wait_until(pilot, lambda: app._cur.ea == xref.fn_addr, timeout=25) + await pilot.pause(0.3) + check("xref-select lands on the referencing function + line", + app._cur.ea == xref.fn_addr and dis.cursor == xexp, + f"cur={app._cur.ea:#x} (want {xref.fn_addr:#x}) " + f"cursor={dis.cursor} (want {xexp})") + # Column cursor: h/l move it; following the symbol under the cursor. dis.focus() dis.cursor = call_idx |
