summaryrefslogtreecommitdiffstats
path: root/idatui/rpc.py
diff options
context:
space:
mode:
authorblasty <peter@haxx.in>2026-07-11 10:59:48 +0200
committerblasty <peter@haxx.in>2026-07-11 10:59:48 +0200
commite769336aa753639f26208fd7755c1585d59caf41 (patch)
tree4be70a40c201206f4e6286742af5c6ca2ebb1c78 /idatui/rpc.py
parentdrive pc: render pseudocode in the TUI, not just the driver (diff)
downloadida-tui-e769336aa753639f26208fd7755c1585d59caf41.tar.gz
ida-tui-e769336aa753639f26208fd7755c1585d59caf41.tar.xz
ida-tui-e769336aa753639f26208fd7755c1585d59caf41.zip
fix: don't hang drive pc on undecompilable functions
toggle_view's settle predicate (lambda: app._active != before) never fired when tabbing toward pseudocode on a function Hex-Rays can't decompile: App._apply_decomp snaps the view back to disasm, so _active returns to its prior value -> full 20s settle timeout (x2 in _show_decomp, ~40s for drive pc). Recognize the decomp-failed fallback as settled. Also harden two amplifiers surfaced by the same case: - rpcclient: the CLI socket had no read timeout and would block forever on any server slowness; add a bounded settimeout (IDATUI_RPC_TIMEOUT, default 90s) with a clear error. - domain.decompile: pass a bounded 15s timeout and cache failures, so a failing decompile can't sit at the 30s client default or be re-run by transport retries.
Diffstat (limited to 'idatui/rpc.py')
-rw-r--r--idatui/rpc.py21
1 files changed, 20 insertions, 1 deletions
diff --git a/idatui/rpc.py b/idatui/rpc.py
index 426b337..f41c734 100644
--- a/idatui/rpc.py
+++ b/idatui/rpc.py
@@ -587,7 +587,26 @@ class RpcServer:
return await self._press(["escape"], timeout=timeout)
if method == "toggle_view":
before = app._active
- return await self._press(["tab"], lambda: app._active != before, timeout)
+
+ def _toggled():
+ # Normal case: the shown view flipped.
+ if app._active != before:
+ return True
+ # Fallback case: a tab toward pseudocode on a function Hex-Rays
+ # can't decompile snaps `_active` back to disasm (see
+ # App._apply_decomp), so `_active` never changes and the naive
+ # `_active != before` predicate would block for the full
+ # timeout. Treat "requested decomp but it's known-failed" as
+ # settled (the decompile is cached, so this is cheap).
+ cur = app._cur
+ if before == "disasm" and cur is not None:
+ try:
+ return app.program.decompile(cur.ea).failed
+ except Exception: # noqa: BLE001
+ return False
+ return False
+
+ return await self._press(["tab"], _toggled, timeout)
if method == "hex":
return await self._press(["backslash"], lambda: app._active == "hex", timeout)
if method == "xrefs":