| Commit message (Collapse) | Author | Age | Files | Lines |
| |
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
| |
Three separate wastes, all of the same family: waiting on a guess instead of a
signal, and paying for work that never had to be repeated.
1. The 64KB blob was built with os.urandom into a fresh TemporaryDirectory on
every run. New bytes at a new path means the pristine-database cache can
never apply, so full auto-analysis of 64KB of AArch64-decoded noise was paid
every single run. It is now built from a seeded PRNG at a stable path
(tests/.synthetic/, gitignored) and staged through the existing cache.
Determinism is also a correctness fix: whether 64KB of chance bytes contains
something IDA reads as a function is luck, and this suite asserts "and really
has no functions".
2. `wait(lambda: lst.model is not old, ..., 30)` after commenting. The perf work
made an item edit KEEP the listing's walk and re-render in place, so the
model object is never replaced and this waited out its full 30s timeout on
every run -- and then "commenting leaves the view where it was" passed
vacuously, because nothing had happened at all. A test that burns 30s to
check nothing is worse than no test.
3. Two `pause(2.0)`/`pause(2.5)` after a carve, replaced with settle() on a real
condition. The second one deliberately has NO predicate: that spot is random
data, so the carve may legitimately produce nothing, and "the row became
code" would never hold -- gating on it cost another 30s timeout. What that
check is about is the VIEW not moving, so the gate is "the app finished
reacting".
Fixing (1) exposed a real bug in the client, fixed here too: reopening a
database that already exists while passing loader switches is FATAL in IDA --
FATAL ERROR: Switch '-b400' can be used only when loading a new file
which kills the worker before it can report anything. Loader switches describe
an IMPORT and are recorded in the database they produce, so they are now sent
only when there is an import to describe. This was never reachable from the old
suite (a fresh random blob never had a database to reopen), but it is reachable
by any user who opens a raw blob with --ida-args twice.
30 passed, 0 failed.
|
| |
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
| |
Found by tests/test_rawimage_rpc.py, which the earlier runs had not covered:
every rename_many check failed with
{"ok": 0, "failed": 2, "errors": [{"addr": null,
"error": "list indices must be integers or slices, not str"}]}
The port's rename read each category as a single edit (edit["addr"]), but the
batch shape is {func: [{addr,name}, ...], data: [...], local/stack: [...]} --
a list per category, with a single dict accepted as shorthand. Indexing the
list with "addr" raised, and because the whole category was one try block the
error came back attached to addr=null, naming nothing.
That is the entire point of the rename_many RPC verb: a firmware image arrives
with hundreds of names from a loader map or an emulator's symbols.json, and
applying them one at a time costs a navigation plus two prompt round trips
each. Only the single-rename UI path worked.
Now mirrors the real tool: one row per EDIT (addr/old/name plus a per-row
error), a summary counting edits rather than categories, conflict detection
before the write, and dry_run/allow_overwrite/stop_on_error. Renaming a
function refreshes Hex-Rays' ctext, whose cache is per function and persisted
in the .i64 -- without it the pseudocode keeps calling the old name forever
while every other readback reports the new one. Clearing a label with an empty
new name is kept as a real request (the scenarios revert with it) rather than
being rejected as a missing argument.
tests/test_rawimage_rpc.py: 14 passed/7 failed -> 21 passed, 0 failed.
|
| |
|
|
|
|
| |
Two core dumps were swept into 7e4b593 by an 'add -A' commit. idalib segfaults
readily under differential probing (running two revisions of a tool against one
cfunc drops two SWIG item objects on the same ctree), so this will recur.
|
| |
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
| |
The suite edits the database — defines code, undefines items, renames, comments —
and IDA saves all of it. Running that against targets/echo.i64 meant every run
inherited the last one's damage.
That cost real time twice. decomp_follow_self "started failing" with no code
change, and stayed failing until the .i64 was deleted; an edit-position check
looked flaky about one run in three and I nearly reported it as an async race.
Both were the database drifting. A suite whose result depends on its own history
cannot be trusted to accuse the code — and it had been quietly laundering bad
conclusions for however long.
Now the suite copies the binary into a temp dir and seeds it from a golden
database (<target>.pristine.i64) that nothing ever writes back to. Every run
starts from identical bytes; the tracked target is never opened.
The golden copy is built once, on first run, by analysing and saving before any
scenario runs — so it costs one analysis rather than one per run. Rebuilt
automatically if the binary is newer.
Verified: two consecutive full runs both 209/0; targets/echo.i64 no longer
exists after a run; a deliberately corrupted targets/echo.i64 is ignored
completely (11/0 with junk in place, and the junk untouched afterwards); no temp
directories leak.
The other suites were already clean for the same reason, by different means:
test_blob_ui builds a throwaway binary, test_project_ui stages copies, and
test_thumb_ui deletes the .i64 before each phase because the T flag and the
segment's bitness are saved in it.
|
| |
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
| |
A caveman entry point so you don't hand-craft the plumbing every time. It:
* ensures the ida-pro-mcp supervisor is up — starts spawn.sh detached
(start_new_session, tmux-free) and waits for the port if it's down;
* recovers a binary wedged by a hard-killed worker — sweeps the stale
unpacked .id0/.id1/.id2/.nam/.til next to the .i64 and retries (the packed
.i64 is never touched);
* adopts an already-open session for the same binary (idempotent), else
idb_opens it with a sane idle-TTL;
* launches the TUI attached to that session with keepalive on.
./ida-tui /path/to/binary # open a binary and drive it
./ida-tui # attach to the sole session
./ida-tui --db <id> # attach to a specific session
Pieces: idatui/launch.py (logic, reuses pane.py's server probe), a repo-root
`ida-tui` sh wrapper (resolves ~/ida-venv python, keeps idatui importable from
any cwd), and an `ida-tui` console-script in pyproject. --help works without
textual (app imported late). README documents both the one-liner and the
manual recovery. gitignore bin/ (binary targets, like targets/).
Verified live: ensure_server up-detection, open, adopt (same session id), and
lock-sweep all work against a running supervisor.
|
| |
|
|
|
|
|
|
|
|
|
|
|
|
|
| |
Measured against libcrypto.so.3 (10,092 funcs, biggest 52,120 insns):
- list_* count cap ~700, disasm max_instructions cap ~500, then SILENT
collapse to 10 (not clamped) -> must clamp client-side (use 500).
- pagination must advance by len(data); next_offset=offset+count skips data.
- disasm offset paging is O(offset): 6ms@0 -> 180ms@50k, no resumable cursor
-> domain layer must cache windows + prefetch + over-fetch.
- include_total scans whole func (~217ms on monster) -> fetch once, cache.
- decompile hard-fails on huge funcs as a soft error (code=null) -> handle.
- idb_open needs a writable path for the .i64.
tests/stress_paging.py: durable paging benchmark harness
docs/PAGING_FINDINGS.md: constraints that drive the paging layer
|
|
|
thread-safe
- IDAClient: single handshake, ~7ms warm calls (vs ~60ms cold CLI)
- keep-alive connection pool over http.client; concurrent-safe (40 threads OK)
- grounded error taxonomy: IDAToolError on isError, soft per-item errors as data
- session-expiry recovery (404 -> re-handshake -> retry), bounded transport retries
- auto db-injection + resolve_db (ignores stale empty-id sessions)
- live smoke test: 13/13 pass
|