From 5f5c2ba0621e53ae380dc434e578c6f67e9b8deb Mon Sep 17 00:00:00 2001 From: agent Date: Sat, 1 Aug 2026 18:20:13 +0200 Subject: drive: a note went to address 0, and a slow edit was reported as a failure Two bugs found driving a 65KB ARM firmware image (one flat 42k-line listing, no ELF sections to break it up): `note` did goto + `cursor line=0` before commenting. Line 0 is the top of the function only in the DECOMPILER; in the listing it is the top of the SEGMENT, so every note landed at address 0 -- and scrolling a 42k-line listing there took so long the call timed out, which read as "comments are broken". goto already lands on the function's first line, so the cursor call just goes; note now also reports where it landed. The client's 90s timeout was too tight for the same reason: comments on that listing take 26-106s (the rebuild has no function boundary to stop at), so the CLI reported "no response ... server busy or the op is hung" for edits that had already been applied. Believing a successful edit failed is the worse error -- the driver redoes it, or "fixes" what was never broken. Default is now 300s; IDATUI_RPC_TIMEOUT still overrides. --- idatui/drive.py | 11 ++++++++--- idatui/rpcclient.py | 10 +++++++++- 2 files changed, 17 insertions(+), 4 deletions(-) diff --git a/idatui/drive.py b/idatui/drive.py index 0e82b0d..531b558 100644 --- a/idatui/drive.py +++ b/idatui/drive.py @@ -219,10 +219,15 @@ def cmd_mv(c, args): def cmd_note(c, args): if len(args) < 2: raise SystemExit("usage: note ") - c.call("goto", target=args[0], delay_ms=0) - c.call("cursor", line=0, col=0) + st = c.call("goto", target=args[0], delay_ms=0) + # goto already lands on the function's first line. The old `cursor line=0` + # meant "the top of the function" only in the decompiler; in the listing + # line 0 is the top of the whole SEGMENT, so the note landed at address 0 -- + # and on a 42k-line firmware listing the scroll to get there timed the + # caller out, which read as "comments are broken". c.call("comment", text=" ".join(args[1:]), delay_ms=0) - return f" noted {args[0]}" + cur = (st.get("function") or {}).get("name") or args[0] + return f" noted {cur} @ {(st.get('cursor') or {}).get('ea', 0):#x}" def cmd_retype(c, args): diff --git a/idatui/rpcclient.py b/idatui/rpcclient.py index e0a5602..4231d62 100644 --- a/idatui/rpcclient.py +++ b/idatui/rpcclient.py @@ -28,7 +28,15 @@ class RpcClient: #: Default read timeout (s). Bounds any single call so a slow/hung server #: (e.g. Hex-Rays grinding on an undecompilable function) can't block the #: CLI forever. Override via ctor or the IDATUI_RPC_TIMEOUT env var. - DEFAULT_TIMEOUT = 90.0 + #: + #: 90s was too tight on real firmware: a comment on a 42k-line flat listing + #: (one segment, no function boundaries to limit the rebuild) took 26-106s, + #: so the client reported "no response ... server busy or the op is hung" + #: for edits that had in fact been applied. A driver that believes a + #: successful edit failed is worse than a slow one -- it redoes the work, or + #: "fixes" something that was never broken. Real hangs still get caught, + #: just later. + DEFAULT_TIMEOUT = 300.0 def __init__(self, sock_path: str, timeout: float | None = None): self.path = sock_path -- cgit v1.3.1-sl0p