diff options
| author | blasty <peter@haxx.in> | 2026-07-11 10:59:48 +0200 |
|---|---|---|
| committer | blasty <peter@haxx.in> | 2026-07-11 10:59:48 +0200 |
| commit | e769336aa753639f26208fd7755c1585d59caf41 (patch) | |
| tree | 4be70a40c201206f4e6286742af5c6ca2ebb1c78 /idatui/rpc.py | |
| parent | drive pc: render pseudocode in the TUI, not just the driver (diff) | |
| download | ida-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.py | 21 |
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": |
