From 6e58e9e4ea72771619ded48b0ac2390a58d31cfb Mon Sep 17 00:00:00 2001 From: blasty Date: Fri, 7 Aug 2026 12:56:27 +0200 Subject: codemode: fix DatabaseHandle.open kwarg, and check kwargs against the real signature ida-codemode is now cloned at ../ida-codemode (0.3.1) and installed into ~/ida-venv, so the adapter can be checked against the library instead of against assumptions. First thing it found: connect() passed loading_address=, which DatabaseHandle.open() does not have. The real parameter is image_base, and it already wants the natural 16-byte-aligned address we compute, so this is a rename. Every connect would have died with TypeError on the first call. The port's own contract test could not catch it: its fake handle takes **kwargs, so any keyword at all looks accepted. The test now also validates the keywords we send against inspect.signature(DatabaseHandle.open) when the library is importable, and skips that one check when it is not. Offline suite: 302 passed with the library installed, 302 without it. --- tests/test_codemode_client.py | 24 +++++++++++++++++++++++- 1 file changed, 23 insertions(+), 1 deletion(-) (limited to 'tests/test_codemode_client.py') diff --git a/tests/test_codemode_client.py b/tests/test_codemode_client.py index 0954918..2f70ba3 100644 --- a/tests/test_codemode_client.py +++ b/tests/test_codemode_client.py @@ -76,6 +76,21 @@ class FakeDatabaseHandle: return FakeHandle(path) +def _open_kwargs_are_real(sent: dict): + """(ok, detail) for the kwargs the adapter passes to DatabaseHandle.open. + + Skips (passes) when ida_codemode is not installed, so the file stays pure. + """ + try: + import inspect + from ida_codemode.client import DatabaseHandle as Real + except ImportError: + return True, "ida_codemode not installed - signature not checked" + accepted = set(inspect.signature(Real.open).parameters) + unknown = sorted(set(sent) - accepted) + return not unknown, f"open() rejects {unknown}" + + def main() -> int: proc, base, file_type = _parse_load_args("-parm:ARMv7-M -b800000 -TRaw") check("legacy switches map to typed Code Mode options", @@ -103,8 +118,15 @@ def main() -> int: FakeDatabaseHandle.opened == path and handle is not None) check("typed loader options cross the dependency boundary", FakeDatabaseHandle.kwargs["processor"] == "arm:ARMv7-A" - and FakeDatabaseHandle.kwargs["loading_address"] == 0x1000, + and FakeDatabaseHandle.kwargs["image_base"] == 0x1000, FakeDatabaseHandle.kwargs) + # A fake that swallows **kwargs cannot catch a keyword the real + # library does not have -- which is exactly how this port shipped + # `loading_address` (the real name is `image_base`) and would have + # raised TypeError on the very first connect. Check the names we + # send against the real signature whenever it is importable. + check("every open() keyword exists in the real library", + *_open_kwargs_are_real(FakeDatabaseHandle.kwargs)) check("connect waits for Code Mode autoanalysis", handle.waited == 42, getattr(handle, "waited", None)) check("progress distinguishes discovery and backend attachment", -- cgit v1.3.1-sl0p