summaryrefslogtreecommitdiffstats
path: root/idatui
diff options
context:
space:
mode:
authorblasty <blasty@local>2026-08-07 00:18:55 +0200
committerblasty <blasty@local>2026-08-07 00:18:55 +0200
commitc70acf6881c83d6de1f33022dc6210b93ed86f2e (patch)
tree9b3f613e9d5bf5d82218050bdda5831cc923e782 /idatui
parentworker_client: tests, and stop resurrecting a closed worker (diff)
downloadida-tui-c70acf6881c83d6de1f33022dc6210b93ed86f2e.tar.gz
ida-tui-c70acf6881c83d6de1f33022dc6210b93ed86f2e.tar.xz
ida-tui-c70acf6881c83d6de1f33022dc6210b93ed86f2e.zip
launch: the sweep could delete the file it was asked to open
_sweep_locks removes the scratch IDA unpacks beside a .i64 (.id0/.id1/.id2/ .nam/.til) when an open fails, keyed on both the full name and the stem. It never touched the .i64, which is the dangerous one everybody thinks of. It did delete the input. '.til' is an unpacked-DB suffix AND the extension of an IDA type library, so 'ida-tui mylib.til' swept its own argument out of existence -- irreversibly, on a path that runs automatically. Same for anything named *.id0/*.id1/*.id2/*.nam. Now the sweep skips whatever it was asked to open, compared as an absolute path so a relative argument is covered too. tests/test_launch.py pins the whole contract: what it takes, what it must never take (the .i64, the input, the neighbours), and what it reports. Pure, in the --fast tier. It is the right shape of test for code whose failure mode is deleting the wrong file. Also: _load_args parsed the base with bare int(), which raises on the '0x8000000' string a project file writes. Unreachable from our own CLI (which int()s first) but the asymmetry with project._as_addr was a trap, so both go through the same parser now. 813 checks; --fast is 324 in 3.4s.
Diffstat (limited to 'idatui')
-rw-r--r--idatui/launch.py26
1 files changed, 22 insertions, 4 deletions
diff --git a/idatui/launch.py b/idatui/launch.py
index 1f9c51c..35221a1 100644
--- a/idatui/launch.py
+++ b/idatui/launch.py
@@ -24,9 +24,16 @@ _LOCK_SUFFIXES = (".id0", ".id1", ".id2", ".nam", ".til")
def _load_args(load: dict) -> str:
- """``load`` as IDA switches, for the single-binary path (no project ref)."""
+ """``load`` as IDA switches, for the single-binary path (no project ref).
+
+ The base goes through project._as_addr rather than bare int(): our own CLI
+ hands over an int, but a project file writes "0x8000000" as a string and
+ int() raises on that. One parser, so the two paths can't disagree about what
+ an address looks like.
+ """
from .formats import load_args
- return load_args(load.get("processor", ""), int(load.get("base", 0) or 0),
+ from .project import _as_addr
+ return load_args(load.get("processor", ""), _as_addr(load.get("base", 0)),
str(load.get("ida_args", "") or ""))
@@ -35,13 +42,24 @@ def _log(msg: str) -> None:
def _sweep_locks(binary: str) -> int:
- """Remove stale unpacked DB files next to ``binary``. Returns how many."""
+ """Remove stale unpacked DB files next to ``binary``. Returns how many.
+
+ Never touches the ``.i64`` -- that is the real database, and nothing is
+ saved unless ``idb_save`` was called -- and never the input file itself.
+ The second guard is not theoretical: ``.til`` is both an unpacked-DB suffix
+ and the extension of an IDA type library, so ``ida-tui mylib.til`` swept its
+ own argument out of existence. Same for anything named ``*.id0``/``*.nam``.
+ """
+ keep = os.path.abspath(binary)
stem = os.path.splitext(binary)[0]
n = 0
for base in (binary, stem): # IDA may key on the full name or the stem
for suf in _LOCK_SUFFIXES:
+ victim = base + suf
+ if os.path.abspath(victim) == keep:
+ continue # that's what the user asked us to open
try:
- os.remove(base + suf)
+ os.remove(victim)
n += 1
except OSError:
pass