Files
reverse-engineering-anything/docs/kuna-provider.md
T
Kaoru0822andClaude Opus 5.5 a5024a9710 fix(kuna): report a failed export as not retryable in its session (#4070)
* fix(kuna): report a failed export as not retryable in its session

A session never rebuilds a failed project export, yet requests that need it
returned the export's raw failure. For a timeout that projected as
`retryable: true` with "Try a smaller request", and every retry returned the
same stored failure. Those requests now fail with a non-retryable
capability error that keeps the export failure as its cause and diagnostics
and names what to change (the deadline or jobs for a timeout) before
reopening the target. Cancellation and unproven cleanup keep their own
errors.

Refs #1957

Co-Authored-By: Claude Opus 5.5 <noreply@anthropic.com>

* fix(kuna): keep a failed export's output and name the restart it needs

The capability error for a failed export dropped the export's captured Kuna
output, which the MCP projection only reads from the top-level error. It
also told callers to change a setting and reopen the target, but REA reads
Kuna settings once at startup. The error now carries the captured output
and partial observation, and its recovery text says to change the setting
where REA starts and restart REA.

Co-Authored-By: Claude Opus 5.5 <noreply@anthropic.com>

* fix(kuna): keep resource limits on a failed export's capability error

An export larger than REA_KUNA_CACHE_MAX_BYTES, or a Kuna command at its
output cap, fails with a resource constraint whose resource and reported
limits the projection exposes only on the top-level error. The wrapper now
copies them into its diagnostics under the same names.

Co-Authored-By: Claude Opus 5.5 <noreply@anthropic.com>

* refactor(kuna): carry a failed export's whole projection on its wrapper

The capability error for a failed export copied the cause's facts field by
field (captured output, resource limits), and each missing field, such as an
artifact change's path and boundary, dropped evidence callers could not
reach through `cause`. The wrapper now carries the export failure's own
projected code and details in its diagnostics, so every typed field survives
in the shape its own projection gives it, and the duplicated top-level
copies are removed.

Co-Authored-By: Claude Opus 5.5 <noreply@anthropic.com>

---------

Co-authored-by: Claude Opus 5.5 <noreply@anthropic.com>
2026-10-15 05:20:08 -07:00

15 KiB
Raw Blame History

Kuna provider

Kuna is an Apache-2.0 decompiler distributed as one kuna executable. REA uses a kuna you supply. It does not download, install, update or bundle Kuna. REA verified this provider with Kuna 1.833; it accepts other releases and reports them as unverified in rea doctor and in each analysis profile.

Kuna is static: it reads the selected image and never executes it.

Configure

export REA_KUNA_COMMAND=/absolute/path/to/kuna
rea doctor --provider kuna

Select it explicitly with --provider kuna, MCP's open_binary.provider_id: "kuna", or REA_ANALYSIS_PROVIDER=kuna. With Kuna and another provider configured for the same target, auto reports ambiguous instead of choosing one.

Setting Default Meaning
REA_KUNA_COMMAND none (required) Absolute path of the kuna executable.
REA_KUNA_CACHE_DIR ~/Library/Caches/rea/kuna (macOS), $XDG_CACHE_HOME/rea/kuna (Linux; ~/.cache/rea/kuna when XDG_CACHE_HOME is unset or relative) Absolute directory REA owns for project exports. Kuna's own spec cache also lives here, not in your Kuna cache.
REA_KUNA_CACHE_MAX_BYTES 4 GiB Size cap; least recently used exports are evicted above it.
REA_KUNA_PROJECT_TIMEOUT_MS 30 minutes Deadline for one project export.
REA_KUNA_TIMEOUT_MS 120 s Deadline for one single-function decompile, and for the inventory run REA makes when the export ends without one.
REA_KUNA_JOBS 1 decompile-project --jobs. 1 is Kuna's reference answer; more jobs finish sooner but lose callee-first prototypes, and change about 40–60% of function bodies on the measured binaries.

Windows is not supported yet. Supported targets are ELF and thin Mach-O executables for x86-64, AArch64 and ARM; PE and universal Mach-O are rejected with a typed reason.

What Kuna answers

Tool Source
list_procedures, search_procedures, procedure_address Kuna's callable inventory (kuna functions --json), including import slots it does not decompile
address_name Function entries, then the data labels Kuna's export assigns
procedure_pseudo_code, batch_decompile The project export, or a single-function run while the export builds
procedure_assembly The project export's labeled listing
procedure_callers, procedure_callees Call-kind edges from a lazy, body-free Kuna graph; synthetic import nodes are accepted by address
xrefs Exact-address incoming reference sites, with forwarding aliases filtered explicitly
analyze_function Pseudocode and assembly from the export, plus call relationships; typed references, comments, strings and CFG remain unavailable

Typed procedure references, basic blocks, raw-memory tools, annotations and type recovery are not available from this provider. Use another provider for those operations.

How answers are produced

The first text or assembly request in a session looks for this target's export in the cache. list_documents reads the selected path without initializing analysis. Inventory, function lookup, call relationships and xrefs do not start a C export. A data-name lookup starts it only when the address is absent from the function inventory. The export is keyed by the target's SHA-256, the Kuna executable's SHA-256, its version and --jobs.

  • Warm cache: analysis reads reuse the verified export. A fresh server verifies its file digests once, even for concurrent first requests; that cost scales with export size.
  • Cold cache:
    • REA starts kuna decompile-project in the background on a private copy of the target.
    • Meanwhile, pseudocode comes from one kuna decompile --addr run. Name and address lookups, and listings, use the inventory the export takes with kuna functions --json before it decompiles. The session runs kuna functions itself, under REA_KUNA_TIMEOUT_MS, only when no export is building (an inventory-only request does not start one) or the export ends without an inventory.
    • Requests for the same function share one run; it stops when its last waiting request is cancelled. Each run analyzes the whole image again in its own process, so one REA process runs at most as many at once as the host has cores beyond REA_KUNA_JOBS (at least one). Further requests wait in order, and a waiting request is answered from the export if it finishes first. A run whose stop REA cannot prove keeps its slot until close_binary proves it stopped.
    • Assembly and data names wait for the export; progress notifications report the wait every ten seconds. A session does not rebuild a failed export: requests that need it fail with retryable: false, the export's failure, its captured Kuna output and what to change before reopening the target. A setting such as REA_KUNA_PROJECT_TIMEOUT_MS or REA_KUNA_JOBS is read when REA starts, so changing it also needs a restart. Pseudocode keeps using single-function runs.
    • A single-function answer says so in its limitations. It lacks the export's whole-program prototypes, so a later request can return different text for the same function.

Pseudocode, assembly, analyze_function and data-name answers record where their text came from in raw_result.source: single-function, or project-export with the cache directory. procedure_pseudo_code and procedure_assembly answers from the export also name the file, byte span and the file's SHA-256. Combined analyze_function answers retain both facet records in raw_result.source.files, each with its file, digest and byte span. Both texts come from one export; losing that project retries the complete report. Listings and procedure lookups carry no raw_result. Agents can open or search the export's .c and .asm there directly when that is cheaper than calling a tool per function. Text read that way is target-derived, like any decompiler output, but it bypasses the untrusted-content marker that REA's tool results carry.

The background export belongs to the session. close_binary, or the end of a CLI command, cancels it unless another session still waits for it. A one-shot CLI decompile therefore answers from a single-function run; any request that needs the export, such as procedure_assembly or analyze_function, keeps it.

The assembly label index is scanned once per export reader. Cancelling a request stops its wait without interrupting other readers; closing the session aborts and drains the shared scan before releasing its files.

Regex procedure search uses ECMAScript Unicode matching in a cancellable worker with a five-second matching deadline. Literal matching remains the default. Cancellation or a matching deadline leaves the session available.

Single-function subprocess output and each cached source facet share a 16 MiB buffered-text capacity. Cached spans are checked before allocation; larger text remains in the local export and the error identifies the file and range. analyze_function initially returns the complete dossier, which can still be large; later retained views do not remove that first transfer. Prefer the individual text tools for a focused question.

Calls and references

The first call query runs kuna decompile-graph --functions '', admitting no C or assembly bodies. One validated graph and forward/reverse indexes serve later call queries and existing get_call_graph / trace_call_path workflows. Numeric u64 addresses are parsed from their original JSON tokens. Only call edges become callers/callees; jumps and data references retain their distinct meaning. Graph counts, edge endpoints and ordering are validated before any relationship is returned. A forwarding address may be an instruction inside a function, so it is retained separately and never promoted to a function node or call edge. Reported indirect calls make affected relationship coverage partial; no target is invented for them.

xrefs runs native xrefs --to ADDRESS --json on demand. Kuna unifies forwarding aliases and suppresses edges internal to their class; REA filters to the requested address and records the excluded-row count. Duplicate source sites appear once. An empty result cannot establish the absence of references. Addresses are explicit u64 values or exact, unambiguous procedure names; Kuna has no cursor.

Graph JSON admits at most 32 MiB before parsing; xref JSON at most 16 MiB. The graph is cached for the selected target and executable profile within one session. Xref reuse is an LRU charged by source bytes plus metadata, limited to 16 MiB and 128 entries; at most eight distinct native xref queries may be pending. Reference commands run one at a time per session and share the provider's foreground budget with single-function decompiles. An unproven process stop retains its admission slots until cleanup succeeds. Concurrent callers share work, individual cancellation releases only that waiter, and the last departing waiter cancels its producer and receives any unproven cleanup. Session close aborts and drains producers, releases parsed caches and retries any retained process/file cleanup.

Evidence includes only selected addresses or dossier neighbors, the producing JSON's SHA-256/byte count, coverage and relevant function facts; unrequested program-wide JSON and instruction text are omitted. Reference caches are in memory and expire on close: a fresh CLI process or MCP server rebuilds them. A first call query therefore costs a whole-program reference walk even when a C export is warm. These operations do not establish full Ghidra/IDA feature parity.

Provenance and limitations

  • Addresses and bytes are Kuna's observations of the image. Procedure entries and extents are its derivations: extents are upper bounds clipped at the next entry, and Kuna can split one function at an entry its own analysis infers. Pseudocode is its reconstruction, never original source.
  • Pseudocode and assembly are exact Kuna text; text from Kuna and from another provider is not comparable as equal or different.
  • Results are re-read rather than replayed from REA's analysis snapshots, because a cold answer can differ from the export's.

Cache ownership

REA writes Kuna's exports only inside REA_KUNA_CACHE_DIR:

  • projects/<key>/ holds published, immutable exports with a manifest of file digests.
  • staging/ holds builds whose processes may still be live.
  • trash/ holds retired builds and evicted exports awaiting removal.
  • home/ is Kuna's HOME.

Builds publish by one rename, so a reader sees a complete export or none. A session keeps the export it uses from eviction only within its own REA process. If another REA process sharing the cache evicts it, the session rebuilds it; meanwhile pseudocode falls back to single-function runs. A damaged or edited export is removed and rebuilt. Staging moves to trash/ for removal only after REA proves its Kuna process stopped. Staging of a process it cannot prove stopped is never swept; REA keeps it owned, retries the stop on close_binary, and reports it until the stop is proven. Failed file removal also stays bound to its export and is retried by close_binary; closing does not report success while that cleanup remains. rea doctor --provider kuna reports the cache directory without creating it.

Verify with a real Kuna

REA_KUNA_COMMAND=/absolute/path/to/kuna npm run verify:kuna
# Compare with Ghidra on caller-built binaries
REA_KUNA_COMMAND=/absolute/path/to/kuna GHIDRA_INSTALL_DIR=/absolute/path/to/ghidra \
  npm run verify:kuna -- --targets DIR --functions inflate,crc32 --compare-ghidra

The lane compiles a small source-owned fixture with the host C compiler and verifies the MCP workflow end to end. --targets DIR --functions a,b adds caller-built binaries. --compare-ghidra (with GHIDRA_INSTALL_DIR) runs those same functions through Ghidra; it requires --targets and --functions and refuses empty matrices or requested function names missing from every target. See testing for coverage.

Failed runs also report process-lineage evidence and exit unsuccessfully. If provider or transport cleanup is unconfirmed, the report says cleanup: "incomplete" and records retained_workspace; that directory is preserved for investigation and cleanup retries.