[GH-ISSUE #8168] [Bug] Kuna procedure inventory includes Mach-O data symbols as functions #4695

Closed
opened 2026-10-31 00:52:33 +00:00 by bsakaguchi · 15 comments
Owner

Originally created by @morluto on GitHub (Oct 30, 2026).
Original GitHub issue: https://github.com/morluto/rea/issues/8168

Originally assigned to: @kaoru0822-kitauji on GitHub.

Before submitting

  • I searched the open and closed issues for a related report.
  • I removed credentials, tokens, private paths, and sensitive target data.

Area

Native analysis or decompilation

Observed behavior

On a self-built arm64 Mach-O, list_procedures with Kuna includes global_count and message at 0x100004000 and 0x100004008. nm -m identifies both as __DATA,__data symbols; the target also has three actual functions. Kuna 1.833 functions --json itself includes those data symbols, so this originates in the upstream inventory and REA currently projects its rows as procedure entries. REA decompile_all selected six entries, wrote three bodies and reported three no_pseudocode failures, including those two data symbols and __mh_execute_header.

Reproduction

On macOS arm64 with clang, compile a C program containing volatile int global_count = 3;, const char *message = "REA audit distinctive message";, a noinline helper reading global_count, a noinline function calling the helper, and main referencing message. In an MCP session call open_binary with the compiled binary and provider_id: "kuna", then list_procedures and decompile_all with a new output_directory. The observed fixture SHA-256 was ffad1b70c6a5a7ef3f7c3f59126be516520210777d949b35e97c9067f272aa2d; nm -m and direct kuna functions --json independently showed the classifications and upstream rows. This ran once; a different build may produce different addresses or symbol ordering.

Expected behavior

docs/kuna-provider.md describes a callable inventory, including import slots. Data symbols should not be represented as known procedure entries or be counted as failed decompilations solely because they have no function body. Preserve uncertain classification and genuine import slots; investigate format-aware evidence at the owning boundary rather than filtering by name or zero size.

Impact and scope

On this fixture, inventory and overview counts included data and the bulk decompile manifest reported three failures for non-body entries. The behavior of other binary formats, stripped targets and Kuna versions is unknown. This report does not claim the code produced by Kuna is incorrect.

REA version

Source checkout 47fa6ab3795d547207001ffb01643c0aa39dcad8; running server reported 6.8.0.

Runtime and platform

Node 24.22.1; macOS arm64; Kuna 1.833.

Target and artifact context

Self-built arm64 Mach-O with globals, a string and a call chain.

Interface

MCP

Earliest failure stage

Native analysis or reconstruction

Relevant output or evidence

  • Direct Kuna functions --json: _adjust at 0x100000410, _evaluate at 0x100000430, global_count at 0x100004000, message at 0x100004008.
  • nm -m: _global_count and _message in (__DATA,__data).
  • REA list_procedures returned all of the above; decompile_all selected 6, wrote 3, failed 3 with no_pseudocode.
  • Inventory parser and provider guidance.

Additional release check

On the same fixture, direct Kuna 1.986 functions --json also returned the two data symbols and header in its six-entry inventory. An REA MCP run with that executable returned the same six procedure rows, and the resumed decompile_all again wrote three functions and reported three no_pseudocode entries. REA labels 1.986 as an unverified release in its analysis profile; this single fixture does not constitute general version verification.

Originally created by @morluto on GitHub (Oct 30, 2026). Original GitHub issue: https://github.com/morluto/rea/issues/8168 Originally assigned to: @kaoru0822-kitauji on GitHub. ### Before submitting - [x] I searched the open and closed issues for a related report. - [x] I removed credentials, tokens, private paths, and sensitive target data. ### Area Native analysis or decompilation ### Observed behavior On a self-built arm64 Mach-O, `list_procedures` with Kuna includes `global_count` and `message` at `0x100004000` and `0x100004008`. `nm -m` identifies both as `__DATA,__data` symbols; the target also has three actual functions. Kuna 1.833 `functions --json` itself includes those data symbols, so this originates in the upstream inventory and REA currently projects its rows as procedure entries. REA `decompile_all` selected six entries, wrote three bodies and reported three `no_pseudocode` failures, including those two data symbols and `__mh_execute_header`. ### Reproduction On macOS arm64 with clang, compile a C program containing `volatile int global_count = 3;`, `const char *message = "REA audit distinctive message";`, a noinline helper reading `global_count`, a noinline function calling the helper, and `main` referencing `message`. In an MCP session call `open_binary` with the compiled binary and `provider_id: "kuna"`, then `list_procedures` and `decompile_all` with a new `output_directory`. The observed fixture SHA-256 was `ffad1b70c6a5a7ef3f7c3f59126be516520210777d949b35e97c9067f272aa2d`; `nm -m` and direct `kuna functions --json` independently showed the classifications and upstream rows. This ran once; a different build may produce different addresses or symbol ordering. ### Expected behavior `docs/kuna-provider.md` describes a callable inventory, including import slots. Data symbols should not be represented as known procedure entries or be counted as failed decompilations solely because they have no function body. Preserve uncertain classification and genuine import slots; investigate format-aware evidence at the owning boundary rather than filtering by name or zero size. ### Impact and scope On this fixture, inventory and overview counts included data and the bulk decompile manifest reported three failures for non-body entries. The behavior of other binary formats, stripped targets and Kuna versions is unknown. This report does not claim the code produced by Kuna is incorrect. ### REA version Source checkout `47fa6ab3795d547207001ffb01643c0aa39dcad8`; running server reported 6.8.0. ### Runtime and platform Node 24.22.1; macOS arm64; Kuna 1.833. ### Target and artifact context Self-built arm64 Mach-O with globals, a string and a call chain. ### Interface MCP ### Earliest failure stage Native analysis or reconstruction ### Relevant output or evidence - Direct Kuna `functions --json`: `_adjust` at `0x100000410`, `_evaluate` at `0x100000430`, `global_count` at `0x100004000`, `message` at `0x100004008`. - `nm -m`: `_global_count` and `_message` in `(__DATA,__data)`. - REA `list_procedures` returned all of the above; `decompile_all` selected 6, wrote 3, failed 3 with `no_pseudocode`. - [Inventory parser](https://github.com/morluto/rea/blob/47fa6ab3795d547207001ffb01643c0aa39dcad8/src/kuna/KunaProjectFiles.ts) and [provider guidance](https://github.com/morluto/rea/blob/47fa6ab3795d547207001ffb01643c0aa39dcad8/docs/kuna-provider.md). ### Additional release check On the same fixture, direct Kuna 1.986 `functions --json` also returned the two data symbols and header in its six-entry inventory. An REA MCP run with that executable returned the same six procedure rows, and the resumed `decompile_all` again wrote three functions and reported three `no_pseudocode` entries. REA labels 1.986 as an unverified release in its analysis profile; this single fixture does not constitute general version verification.
Author
Owner

@morluto commented on GitHub (Oct 30, 2026):

Further real MCP reproduction on current origin/main (d48e472fa) plus the Kuna 1.986 baseline branch, macOS arm64 / Kuna 1.986, using the same source-owned Mach-O (ffad1b70c6a5a7ef3f7c3f59126be516520210777d949b35e97c9067f272aa2d):

  1. search_procedures({"pattern":"global_count"}) returns global_count at 0x100004000.
  2. While the project export was building, procedure_pseudo_code({"procedure":"global_count"}) returned void global_count(void) with an UndefinedInstructionException at that data address. The Evidence correctly disclosed that this came from the single-function run and that later export text can differ.
  3. decompile_all filtered by global_count subsequently selected one entry and reported no_pseudocode; its journal says the export has no body for the entry. A direct kuna decompile ... 0x100004000 --json independently produced the same single-function pseudo-code.
  4. In the same REA session, inspect_macho reported that address as the start of the __DATA,__data section in a segment with initprot: 3; the independently compiled source defines it as data. Kuna-backed procedure_info, inspect_native_instruction, and inspect_native_load_image were unavailable.

This is one observed cold-to-export sequence, not a claim that every data symbol gets pseudocode. It shows that failure counts are not the only impact: an agent requesting the apparent procedure before export completes can receive a successful reconstruction of bytes that are known data. Any mitigation should consider source-backed mapping/section facts and preserve unknowns, imports, and executable mappings rather than assume a section name or successful decompilation proves function identity.

<!-- gh-comment-id:6532937014 --> @morluto commented on GitHub (Oct 30, 2026): Further real MCP reproduction on current `origin/main` (`d48e472fa`) plus the Kuna 1.986 baseline branch, macOS arm64 / Kuna 1.986, using the same source-owned Mach-O (`ffad1b70c6a5a7ef3f7c3f59126be516520210777d949b35e97c9067f272aa2d`): 1. `search_procedures({"pattern":"global_count"})` returns `global_count` at `0x100004000`. 2. While the project export was building, `procedure_pseudo_code({"procedure":"global_count"})` returned `void global_count(void)` with an `UndefinedInstructionException` at that data address. The Evidence correctly disclosed that this came from the single-function run and that later export text can differ. 3. `decompile_all` filtered by `global_count` subsequently selected one entry and reported `no_pseudocode`; its journal says the export has no body for the entry. A direct `kuna decompile ... 0x100004000 --json` independently produced the same single-function pseudo-code. 4. In the same REA session, `inspect_macho` reported that address as the start of the `__DATA,__data` section in a segment with `initprot: 3`; the independently compiled source defines it as data. Kuna-backed `procedure_info`, `inspect_native_instruction`, and `inspect_native_load_image` were unavailable. This is one observed cold-to-export sequence, not a claim that every data symbol gets pseudocode. It shows that failure counts are not the only impact: an agent requesting the apparent procedure before export completes can receive a successful reconstruction of bytes that are known data. Any mitigation should consider source-backed mapping/section facts and preserve unknowns, imports, and executable mappings rather than assume a section name or successful decompilation proves function identity.
Author
Owner

@morluto commented on GitHub (Oct 30, 2026):

A separate owned arm64 Mach-O importing _audit_transform from an owned dylib produced another inventory ambiguity with REA/Kuna 1.986. list_procedures returned audit_transform at local stub 0x1000004b4 and again at 0x100004000, the start of its __DATA_CONST,__got section as observed by inspect_macho. Exact name lookup rejected the two entries as ambiguous and listed both addresses. Pseudocode at the stub rendered a jump-as-call, while pseudocode for the GOT entry returned null. Ghidra separately classified the stub as a thunk to an external symbol. The fixture was not executed and its digest stayed unchanged. This supports preserving section/ownership evidence and ambiguity, not treating every same-named inventory entry as a function body; #8221 tracks the separate cross-library handoff question.

<!-- gh-comment-id:6534412146 --> @morluto commented on GitHub (Oct 30, 2026): A separate owned arm64 Mach-O importing `_audit_transform` from an owned dylib produced another inventory ambiguity with REA/Kuna 1.986. `list_procedures` returned `audit_transform` at local stub `0x1000004b4` and again at `0x100004000`, the start of its `__DATA_CONST,__got` section as observed by `inspect_macho`. Exact name lookup rejected the two entries as ambiguous and listed both addresses. Pseudocode at the stub rendered a `jump-as-call`, while pseudocode for the GOT entry returned null. Ghidra separately classified the stub as a thunk to an external symbol. The fixture was not executed and its digest stayed unchanged. This supports preserving section/ownership evidence and ambiguity, not treating every same-named inventory entry as a function body; #8221 tracks the separate cross-library handoff question.
Author
Owner

@morluto commented on GitHub (Oct 31, 2026):

Looping in @gensokyo-dev @kaoru0822-kitauji @N0zoM1z0 for visibility on the Ghidra, Kuna, and REA tool-design investigation.

<!-- gh-comment-id:6538782017 --> @morluto commented on GitHub (Oct 31, 2026): Looping in @gensokyo-dev @kaoru0822-kitauji @N0zoM1z0 for visibility on the Ghidra, Kuna, and REA tool-design investigation.
Author
Owner

@morluto commented on GitHub (Oct 31, 2026):

A deliberately adversarial x86-64 ELF control extends the Mach-O observation, but does not show that ordinary ELF OBJECT symbols are misclassified. It was source-owned, analyzed without execution, and its SHA-256 remained unchanged. Independent llvm-readelf -l -s showed:

  • false_func is declared STT_FUNC at linked address 0x34a0, but its bytes were deliberately placed in a read/write, non-executable .data segment. normal_data is a separate STT_OBJECT there.
  • no_type_code is declared STT_NOTYPE at 0x1401, but the fixture's entry contains a direct call to it. A separate STT_OBJECT data_in_text follows it in executable .text. Two STT_FUNC symbols alias the same real code address.

Direct Kuna 1.986 functions --json listed false_func as a zero-size procedure, omitted both STT_OBJECT names, and reported the reachable no_type_code as sub_1401 with an upper-bound size spanning the adjacent object bytes. Real REA/Kuna MCP list_procedures projected those same four entries. A cold procedure_pseudo_code(false_func) returned return 0x2a from Kuna's single-function route; after a completed decompile_all, the warm query returned null and the directory export recorded one no_pseudocode failure for it. Both runs preserve their respective source/limitations. This reproduces the cold/warm inconsistency on one deliberately misleading ELF symbol, not a prevalence claim.

Real REA/Ghidra 12.1.4 also inventoried false_func as an imported function and returned pseudocode for it. Its load-image observation reported the containing .data block as non-executable; inspect_native_instruction reported not-in-listing there, stating that it did not attempt to decode. The actual decompiler can still return a reconstruction; neither result establishes runtime execution. Ghidra separately listed and decoded the called no_type_code. REA inspect_native_exports preserved the file's FUNC, OBJECT, and NOTYPE distinctions; the linked Kuna address and rebased Ghidra address differ, so cross-provider joins require verified mapping.

This control argues against treating STT_FUNC alone, a decoded pseudocode body, or an executable-section check as definitive runtime function identity. Preserve the symbol declaration, static mapping/permissions, observed calls and unresolved cases separately; a NOTYPE entry or code in unusual memory is not automatically absent. It does not change the original ordinary Mach-O OBJECT reproduction.

<!-- gh-comment-id:6540463299 --> @morluto commented on GitHub (Oct 31, 2026): A deliberately adversarial x86-64 ELF control extends the Mach-O observation, but does **not** show that ordinary ELF `OBJECT` symbols are misclassified. It was source-owned, analyzed without execution, and its SHA-256 remained unchanged. Independent `llvm-readelf -l -s` showed: - `false_func` is declared `STT_FUNC` at linked address `0x34a0`, but its bytes were deliberately placed in a read/write, non-executable `.data` segment. `normal_data` is a separate `STT_OBJECT` there. - `no_type_code` is declared `STT_NOTYPE` at `0x1401`, but the fixture's entry contains a direct call to it. A separate `STT_OBJECT data_in_text` follows it in executable `.text`. Two `STT_FUNC` symbols alias the same real code address. Direct Kuna 1.986 `functions --json` listed `false_func` as a zero-size procedure, omitted both `STT_OBJECT` names, and reported the reachable `no_type_code` as `sub_1401` with an upper-bound size spanning the adjacent object bytes. Real REA/Kuna MCP `list_procedures` projected those same four entries. A cold `procedure_pseudo_code(false_func)` returned `return 0x2a` from Kuna's single-function route; after a completed `decompile_all`, the warm query returned null and the directory export recorded one `no_pseudocode` failure for it. Both runs preserve their respective source/limitations. This reproduces the cold/warm inconsistency on one deliberately misleading ELF symbol, not a prevalence claim. Real REA/Ghidra 12.1.4 also inventoried `false_func` as an imported function and returned pseudocode for it. Its load-image observation reported the containing `.data` block as non-executable; `inspect_native_instruction` reported `not-in-listing` there, stating that it did not attempt to decode. The actual decompiler can still return a reconstruction; neither result establishes runtime execution. Ghidra separately listed and decoded the called `no_type_code`. REA `inspect_native_exports` preserved the file's `FUNC`, `OBJECT`, and `NOTYPE` distinctions; the linked Kuna address and rebased Ghidra address differ, so cross-provider joins require verified mapping. This control argues against treating `STT_FUNC` alone, a decoded pseudocode body, or an executable-section check as definitive runtime function identity. Preserve the symbol declaration, static mapping/permissions, observed calls and unresolved cases separately; a `NOTYPE` entry or code in unusual memory is not automatically absent. It does not change the original ordinary Mach-O `OBJECT` reproduction.
Author
Owner

@morluto commented on GitHub (Oct 31, 2026):

A downstream call-path control on the same unexecuted Mach-O, now using REA b43c329 and Kuna 1.986: search_procedures({pattern:"global_count"}) returned the writable __DATA,__data symbol at 0x100004000, and procedure_address accepted that name. trace_call_path({start:"0x100000480",goal:"0x100004000"}) then returned goal_status:"not_reached", three visited procedure nodes, truncated:false, and complete relationship coverage for those nodes. Its limitations do say this is a call-kind graph, excluding data edges. A positive control with goal _evaluate returned reached. analyze_function on the global refused it because the export has no code at that address. Ghidra on the same binary rejected it as not a procedure.

The negative is about Kuna's reported call graph, not a claim that the global is unused: the source-owned _adjust reads it, and REA/Ghidra reports the load reference. REA/Kuna xrefs returned address-forming sites, but these are not necessarily accesses to the named object: independent disassembly shows 0x1000004a4 forms the same page base and the next instruction loads at offset 8. This strengthens the impact of the inventory classification issue without treating call and data references as equivalent. Target and original project hashes were unchanged; the fixture was not executed.

For maintainer visibility: @gensokyo-dev @kaoru0822-kitauji @N0zoM1z0

<!-- gh-comment-id:6541893222 --> @morluto commented on GitHub (Oct 31, 2026): A downstream call-path control on the same unexecuted Mach-O, now using REA `b43c329` and Kuna 1.986: `search_procedures({pattern:"global_count"})` returned the writable `__DATA,__data` symbol at `0x100004000`, and `procedure_address` accepted that name. `trace_call_path({start:"0x100000480",goal:"0x100004000"})` then returned `goal_status:"not_reached"`, three visited procedure nodes, `truncated:false`, and complete relationship coverage for those nodes. Its limitations do say this is a call-kind graph, excluding data edges. A positive control with goal `_evaluate` returned `reached`. `analyze_function` on the global refused it because the export has no code at that address. Ghidra on the same binary rejected it as not a procedure. The negative is about Kuna's reported call graph, not a claim that the global is unused: the source-owned `_adjust` reads it, and REA/Ghidra reports the load reference. REA/Kuna xrefs returned address-forming sites, but these are not necessarily accesses to the named object: independent disassembly shows `0x1000004a4` forms the same page base and the next instruction loads at offset 8. This strengthens the impact of the inventory classification issue without treating call and data references as equivalent. Target and original project hashes were unchanged; the fixture was not executed. For maintainer visibility: @gensokyo-dev @kaoru0822-kitauji @N0zoM1z0
Author
Owner

@morluto commented on GitHub (Oct 31, 2026):

A second minimal, source-owned arm64 Mach-O reproduces a concrete downstream consequence with real REA b43c329 and Kuna 1.986. nm -m identifies audit_global at 0x100004000 as __DATA,__data; both release Kuna and a source-built upstream main list it as a function. On a fresh REA/Kuna cache, search_procedures returned it and the first procedure_pseudo_code returned a reconstructed function containing UndefinedInstructionException(3,0x100004000). Its Evidence correctly said this was a single-function result while project export was building. After querying real code assembly and allowing the project export to complete, the same procedure query returned null with the explicit limitation that the inventory entry had no body.

Both outputs carry their source limitations; the concern is that the initial text is nevertheless successful pseudocode for a writable data object, while the later result is no body. The result is one controlled example and does not establish prevalence or prove that source format symbols alone classify every entry. Fixture SHA-256 4bbf47cc358081b633e7ad377433e3c26525a862a40d87a7429f1746ca380787 was unchanged; it was not executed. Upstream producer reproduction: https://github.com/Noelo-Lab/kuna/issues/1339.

For maintainer visibility: @gensokyo-dev @kaoru0822-kitauji @N0zoM1z0

<!-- gh-comment-id:6542180953 --> @morluto commented on GitHub (Oct 31, 2026): A second minimal, source-owned arm64 Mach-O reproduces a concrete downstream consequence with real REA `b43c329` and Kuna 1.986. `nm -m` identifies `audit_global` at `0x100004000` as `__DATA,__data`; both release Kuna and a source-built upstream main list it as a function. On a fresh REA/Kuna cache, `search_procedures` returned it and the first `procedure_pseudo_code` returned a reconstructed function containing `UndefinedInstructionException(3,0x100004000)`. Its Evidence correctly said this was a single-function result while project export was building. After querying real code assembly and allowing the project export to complete, the same procedure query returned `null` with the explicit limitation that the inventory entry had no body. Both outputs carry their source limitations; the concern is that the initial text is nevertheless successful pseudocode for a writable data object, while the later result is no body. The result is one controlled example and does not establish prevalence or prove that source format symbols alone classify every entry. Fixture SHA-256 `4bbf47cc358081b633e7ad377433e3c26525a862a40d87a7429f1746ca380787` was unchanged; it was not executed. Upstream producer reproduction: https://github.com/Noelo-Lab/kuna/issues/1339. For maintainer visibility: @gensokyo-dev @kaoru0822-kitauji @N0zoM1z0
Author
Owner

@morluto commented on GitHub (Oct 31, 2026):

Higher-level batch_decompile control on the minimal fixture from upstream https://github.com/Noelo-Lab/kuna/issues/1339, using real REA b43c329 / Kuna 1.986 with separately created empty caches:

  • In a cold session, a batch containing the writable data symbol 0x100004000 and actual function 0x100000410 returned two status:"ok" items. The data item's text was the UndefinedInstructionException(3,...) pseudocode. Batch-wide Evidence included the single-function/export-building limitation.
  • After an assembly request established that the project export was ready, the identical batch returned one success for the real function and an unreadable_output item for the data address. Evidence carried the producer's no-body limitation; the item itself said only provider returned empty pseudocode and suggested doctor.
  • An independently warmed cache returned the latter result on the first batch. Both sessions closed and the unexecuted target hash stayed unchanged.

This demonstrates a task-level consequence of the inventory classification and makes the export-stage difference visible; it is one controlled call order, not a frequency claim. The separate per-item limitation association question is already tracked in #8226. Source-level data classification or explicit uncertainty is preferable to inferring safety from a generic success count or filtering all zero-sized entries.

<!-- gh-comment-id:6542191887 --> @morluto commented on GitHub (Oct 31, 2026): Higher-level `batch_decompile` control on the minimal fixture from upstream https://github.com/Noelo-Lab/kuna/issues/1339, using real REA `b43c329` / Kuna 1.986 with separately created empty caches: - In a cold session, a batch containing the writable data symbol `0x100004000` and actual function `0x100000410` returned two `status:"ok"` items. The data item's text was the `UndefinedInstructionException(3,...)` pseudocode. Batch-wide Evidence included the single-function/export-building limitation. - After an assembly request established that the project export was ready, the identical batch returned one success for the real function and an `unreadable_output` item for the data address. Evidence carried the producer's no-body limitation; the item itself said only `provider returned empty pseudocode` and suggested doctor. - An independently warmed cache returned the latter result on the first batch. Both sessions closed and the unexecuted target hash stayed unchanged. This demonstrates a task-level consequence of the inventory classification and makes the export-stage difference visible; it is one controlled call order, not a frequency claim. The separate per-item limitation association question is already tracked in #8226. Source-level data classification or explicit uncertainty is preferable to inferring safety from a generic success count or filtering all zero-sized entries.
Author
Owner

@morluto commented on GitHub (Oct 31, 2026):

Real CLI control on the minimal fixture from upstream https://github.com/Noelo-Lab/kuna/issues/1339, with REA b43c329 and a newly created Kuna 1.986 cache:

  1. rea decompile <fixture> audit_global --provider kuna --format json exited 0 with the integer bytes rendered as UndefinedInstructionException pseudocode. Evidence identified single-function output while project export was building and warned a later result could differ.
  2. A separate rea function <fixture> _audit_read --provider kuna --format json completed successfully using the project export.
  3. Repeating the same rea decompile command exited 0 with normalized_result:null; Evidence identified the project export and said the inventory entry had no body.
  4. rea decompile <fixture> 0x100004000 --provider ghidra --format json exited 1 with an invalid procedure input. nm -m independently locates the global in writable __DATA,__data.

The target SHA-256 was unchanged and it was not executed. This extends the MCP finding to separate one-shot CLI processes and one shared cache; the observed state transition and source limitations do not imply a general cross-provider classification rule or a frequency estimate.

<!-- gh-comment-id:6542239071 --> @morluto commented on GitHub (Oct 31, 2026): Real CLI control on the minimal fixture from upstream https://github.com/Noelo-Lab/kuna/issues/1339, with REA `b43c329` and a newly created Kuna 1.986 cache: 1. `rea decompile <fixture> audit_global --provider kuna --format json` exited 0 with the integer bytes rendered as `UndefinedInstructionException` pseudocode. Evidence identified `single-function` output while project export was building and warned a later result could differ. 2. A separate `rea function <fixture> _audit_read --provider kuna --format json` completed successfully using the project export. 3. Repeating the same `rea decompile` command exited 0 with `normalized_result:null`; Evidence identified the project export and said the inventory entry had no body. 4. `rea decompile <fixture> 0x100004000 --provider ghidra --format json` exited 1 with an invalid procedure input. `nm -m` independently locates the global in writable `__DATA,__data`. The target SHA-256 was unchanged and it was not executed. This extends the MCP finding to separate one-shot CLI processes and one shared cache; the observed state transition and source limitations do not imply a general cross-provider classification rule or a frequency estimate.
Author
Owner

@morluto commented on GitHub (Oct 31, 2026):

The real-provider verification gap is narrower than “no E2E tests.” At REA b43c329 (the inspected verifier is unchanged on the current local origin/main), verify:kuna builds symbolized and stripped fixtures and uses the public MCP workflow to verify known procedures, pseudocode, assembly, references, project export, reuse, and cleanup. It identifies its expected procedures using nm text-symbol types and checks that those selected addresses are present in list_procedures; it does not assert that independently identified writable data is excluded or honestly classified. It also does not invoke batch_decompile or decompile_all in this verifier. The existing provider-boundary inventory tests use a controlled fake Kuna inventory, so they do not establish how the real upstream inventory classifies data.

The 1.986 real-provider lane passed earlier on this macOS arm64 host, including both fixture variants. That is consistent with the gap: a presence assertion can pass in an inventory containing extra rows. For a separate check I rebuilt the verifier's exact C source without executing the resulting target. nm -m identified its two marker arrays as __TEXT,__const; direct Kuna 1.986 did not include those arrays in its eight returned rows. Consequently this observation does not establish that the verifier's current fixture reproduces the writable-global failure. The separate writable-global fixture in the reproduction does.

A useful regression after deciding the desired contract would add independently identified writable globals alongside real procedures and exercise real public MCP/CLI with a fresh Kuna cache and again after project export, including mixed batch results. Check that data is not presented as a successful procedure body, and that limitations and unresolved classifications remain truthful; retain positive coverage for real functions and callable import slots. Do not equate zero size with data or assume every format has the same section semantics. Supplement with controlled boundary cases only where real-provider conditions cannot be reproduced reliably.

<!-- gh-comment-id:6542253955 --> @morluto commented on GitHub (Oct 31, 2026): The real-provider verification gap is narrower than “no E2E tests.” At REA `b43c329` (the inspected verifier is unchanged on the current local `origin/main`), `verify:kuna` builds symbolized and stripped fixtures and uses the public MCP workflow to verify known procedures, pseudocode, assembly, references, project export, reuse, and cleanup. It identifies its expected procedures using `nm` text-symbol types and checks that those selected addresses are present in `list_procedures`; it does not assert that independently identified writable data is excluded or honestly classified. It also does not invoke `batch_decompile` or `decompile_all` in this verifier. The existing provider-boundary inventory tests use a controlled fake Kuna inventory, so they do not establish how the real upstream inventory classifies data. The 1.986 real-provider lane passed earlier on this macOS arm64 host, including both fixture variants. That is consistent with the gap: a presence assertion can pass in an inventory containing extra rows. For a separate check I rebuilt the verifier's exact C source without executing the resulting target. `nm -m` identified its two marker arrays as `__TEXT,__const`; direct Kuna 1.986 did not include those arrays in its eight returned rows. Consequently this observation does **not** establish that the verifier's current fixture reproduces the writable-global failure. The separate writable-global fixture in the reproduction does. A useful regression after deciding the desired contract would add independently identified writable globals alongside real procedures and exercise real public MCP/CLI with a fresh Kuna cache and again after project export, including mixed batch results. Check that data is not presented as a successful procedure body, and that limitations and unresolved classifications remain truthful; retain positive coverage for real functions and callable import slots. Do not equate zero size with data or assume every format has the same section semantics. Supplement with controlled boundary cases only where real-provider conditions cannot be reproduced reliably.
Author
Owner

@morluto commented on GitHub (Oct 31, 2026):

Current-main check: on merged REA a989c0e458c91af177e097abd7be0fd021b0401d with Kuna 1.986 and a fresh owned cache, search_procedures("audit_global") still returned the known __DATA,__data address. A cold procedure_pseudo_code("audit_global") returned the UndefinedInstructionException reconstruction from single-function; after an assembly request completed the project export, the same request returned null with the project-export source and no-body limitation. The real fixture's hash was unchanged and it was not executed.

Direct Kuna auto-disassembly provided another useful upstream control: selecting the exact global address returned kind: code, whereas an explicit range beginning at the same address returned kind: data and a non-executable-section note. Details and controls are in upstream #1339. REA does not call that auto-disassembly path in this test; this is not a proposal to add a raw CLI wrapper or assume section membership proves function identity.

<!-- gh-comment-id:6544983370 --> @morluto commented on GitHub (Oct 31, 2026): Current-main check: on merged REA `a989c0e458c91af177e097abd7be0fd021b0401d` with Kuna 1.986 and a fresh owned cache, `search_procedures("audit_global")` still returned the known `__DATA,__data` address. A cold `procedure_pseudo_code("audit_global")` returned the `UndefinedInstructionException` reconstruction from `single-function`; after an assembly request completed the project export, the same request returned null with the project-export source and no-body limitation. The real fixture's hash was unchanged and it was not executed. Direct Kuna auto-disassembly provided another useful upstream control: selecting the exact global address returned `kind: code`, whereas an explicit range beginning at the same address returned `kind: data` and a non-executable-section note. Details and controls are in [upstream #1339](https://github.com/Noelo-Lab/kuna/issues/1339#issuecomment-6544979875). REA does not call that auto-disassembly path in this test; this is not a proposal to add a raw CLI wrapper or assume section membership proves function identity.
Author
Owner

@morluto commented on GitHub (Oct 31, 2026):

A visibility control and likely upstream producing boundary: on a new owned Mach-O with two globals in the same __DATA,__data section, dyld_info -exports identified only audit_exported as exported; the other global had hidden visibility. On current REA a989c0e with Kuna 1.986, search_procedures("audit_") returned the exported data global and real reader function, but not the hidden data global. inspect_macho independently returned the exported global in the export inventory; a cold decompile of it returned UndefinedInstructionException pseudocode. The fixture was not executed or modified.

Source inspection suggests why: Kuna's Mach-O loader appends export records to its function-symbol stream without first establishing that each export is callable. The relevant source and a matching direct control on the latest tested upstream build are documented at upstream #1339. This is evidence for fixing classification at its producer; it does not justify treating every export as a function or every symbol in a data section as definitely non-callable.

<!-- gh-comment-id:6545021805 --> @morluto commented on GitHub (Oct 31, 2026): A visibility control and likely upstream producing boundary: on a new owned Mach-O with two globals in the same `__DATA,__data` section, `dyld_info -exports` identified only `audit_exported` as exported; the other global had hidden visibility. On current REA `a989c0e` with Kuna 1.986, `search_procedures("audit_")` returned the exported data global and real reader function, but not the hidden data global. `inspect_macho` independently returned the exported global in the export inventory; a cold decompile of it returned `UndefinedInstructionException` pseudocode. The fixture was not executed or modified. Source inspection suggests why: Kuna's Mach-O loader appends export records to its function-symbol stream without first establishing that each export is callable. The relevant source and a matching direct control on the latest tested upstream build are documented at [upstream #1339](https://github.com/Noelo-Lab/kuna/issues/1339#issuecomment-6545018447). This is evidence for fixing classification at its producer; it does not justify treating every export as a function or every symbol in a data section as definitely non-callable.
Author
Owner

@morluto commented on GitHub (Oct 31, 2026):

A controlled upstream intervention now supports the suspected producing boundary for this fixture: using two separately built d85e7cc Kuna CLIs, identical except that the diagnostic variant suppresses the Mach-O export append, current REA search_procedures("audit_") returned the data global only with the unmodified CLI; both returned the known code function. Both source-built executables reported 0.1.0/unverified to REA, and each caller-owned launcher supplied the same compiled SLEIGH tree. The target was unchanged and not executed. Details and limitations are in upstream #1339.

The suppression was an experiment, not a candidate fix: exported function discovery must remain intact, including where other symbol sources are absent. This reinforces fixing or exposing producer-owned classification rather than filtering REA results by names or assuming section membership alone proves callable identity.

<!-- gh-comment-id:6545057115 --> @morluto commented on GitHub (Oct 31, 2026): A controlled upstream intervention now supports the suspected producing boundary for this fixture: using two separately built `d85e7cc` Kuna CLIs, identical except that the diagnostic variant suppresses the Mach-O export append, current REA `search_procedures("audit_")` returned the data global only with the unmodified CLI; both returned the known code function. Both source-built executables reported 0.1.0/unverified to REA, and each caller-owned launcher supplied the same compiled SLEIGH tree. The target was unchanged and not executed. Details and limitations are in [upstream #1339](https://github.com/Noelo-Lab/kuna/issues/1339#issuecomment-6545055035). The suppression was an experiment, not a candidate fix: exported function discovery must remain intact, including where other symbol sources are absent. This reinforces fixing or exposing producer-owned classification rather than filtering REA results by names or assuming section membership alone proves callable identity.
Author
Owner

@morluto commented on GitHub (Oct 31, 2026):

Real shipped-binary first-use control on REA 60149282fcfc05ac32fbc94434c107710120da6f, Kuna 1.986 and macOS arm64: Homebrew Brotli 1.2.0 libbrotlicommon.1.2.0.dylib SHA-256 3426742c78df5c3b523071df603f07f2bb8dea6bd8e65c366a2c6adf2bf0a3ad. nm -gm places two exported symbols at 0x11c4 and 0x122c in __TEXT,__const; the upstream source declares the first as a range array. Ghidra's load-image observation marks the __const block non-executable, while inspect_macho retains both exports (with non-exhaustive export coverage). Export names alone do not establish function type.

In a fresh Kuna session, search_procedures and procedure_address treated the table entries as procedures. A cold procedure_pseudo_code(0x11c4) returned a successful single-function reconstruction containing halt_baddata() and an explicit bad-instruction warning. A cold batch of the two tables plus _BrotliGetDictionary reported 3/3 successful, with the two warned table bodies. After procedure_assembly waited for the project export, the same table returned null with an explicit no-body limitation; the same batch then had two capability_unavailable items with per-item caveats and the real function succeeded. Separate CLI invocations with an empty cache and the prepared warm cache showed the same respective text/null outcomes, both exit 0. The cold/warm behavior is timing and export state on this sample, not a frequency claim.

Ghidra's procedure lookup rejected the data addresses while its list_names retained the imported label; read_bytes at 0x11c4 returned the expected initial table bytes. Both direct Kuna release and upstream d85e7cc exhibited the inventory issue (Noelo-Lab/kuna#1339). MCP successes were SDK-validated against compact schemas. The file hash was unchanged, and it was not executed. An initial inspect_macho call incorrectly included path; the format observation above comes from a corrected separate call.

<!-- gh-comment-id:6546153555 --> @morluto commented on GitHub (Oct 31, 2026): Real shipped-binary first-use control on REA `60149282fcfc05ac32fbc94434c107710120da6f`, Kuna 1.986 and macOS arm64: Homebrew Brotli 1.2.0 `libbrotlicommon.1.2.0.dylib` SHA-256 `3426742c78df5c3b523071df603f07f2bb8dea6bd8e65c366a2c6adf2bf0a3ad`. `nm -gm` places two exported symbols at `0x11c4` and `0x122c` in `__TEXT,__const`; the [upstream source](https://github.com/google/brotli/blob/v1.2.0/c/common/constants.c#L7-L13) declares the first as a range array. Ghidra's load-image observation marks the `__const` block non-executable, while `inspect_macho` retains both exports (with non-exhaustive export coverage). Export names alone do not establish function type. In a fresh Kuna session, `search_procedures` and `procedure_address` treated the table entries as procedures. A cold `procedure_pseudo_code(0x11c4)` returned a successful single-function reconstruction containing `halt_baddata()` and an explicit bad-instruction warning. A cold batch of the two tables plus `_BrotliGetDictionary` reported 3/3 successful, with the two warned table bodies. After `procedure_assembly` waited for the project export, the same table returned null with an explicit no-body limitation; the same batch then had two `capability_unavailable` items with per-item caveats and the real function succeeded. Separate CLI invocations with an empty cache and the prepared warm cache showed the same respective text/null outcomes, both exit 0. The cold/warm behavior is timing and export state on this sample, not a frequency claim. Ghidra's procedure lookup rejected the data addresses while its `list_names` retained the imported label; `read_bytes` at `0x11c4` returned the expected initial table bytes. Both direct Kuna release and upstream `d85e7cc` exhibited the inventory issue (Noelo-Lab/kuna#1339). MCP successes were SDK-validated against compact schemas. The file hash was unchanged, and it was not executed. An initial `inspect_macho` call incorrectly included `path`; the format observation above comes from a corrected separate call.
Author
Owner

@morluto commented on GitHub (Oct 31, 2026):

Further real-provider check on REA main 60149282, Kuna 1.986, and Homebrew Brotli 1.2.0 libbrotlicommon.1.2.0.dylib (SHA-256 3426742c78df5c3b523071df603f07f2bb8dea6bd8e65c366a2c6adf2bf0a3ad): in the same Kuna MCP session, search_procedures returns _kBrotliPrefixCodeRanges at 0x11c4 and _kBrotliContextLookupTable at 0x122c. The provider-independent inspect_macho succeeds and reports those exports plus __TEXT,__const beginning at 0x11c4 and spanning both addresses. The export records do not themselves include symbol type. The enclosing __TEXT segment has execute permission, so filtering function candidates on segment execute permissions would not catch this example. These facts provide an analyst a way to investigate, but REA does not connect them or warn on the returned procedure entries in this workflow.

Separately, a logging-only build of upstream Kuna main 957a1eec identified both original symbols as Data, size zero, then showed both arriving as Export at the function-inventory boundary before their addresses were seen. The same build's JSON exactly matched unmodified upstream main. Source shows the export append and function-symbol insertion. This diagnoses this sample, not every source of dubious procedure entries. Related upstream report: https://github.com/Noelo-Lab/kuna/issues/1339.

The target digest remained unchanged. The MCP check exercised REA's published tools with the released Kuna provider; upstream instrumentation was a separate direct Kuna check. Avoid assuming every export is code or that every symbol in an executable segment is a function. Any proposed cross-provider classification needs to preserve uncertain cases and legitimate exported code.

<!-- gh-comment-id:6546199096 --> @morluto commented on GitHub (Oct 31, 2026): Further real-provider check on REA main `60149282`, Kuna 1.986, and Homebrew Brotli 1.2.0 `libbrotlicommon.1.2.0.dylib` (SHA-256 `3426742c78df5c3b523071df603f07f2bb8dea6bd8e65c366a2c6adf2bf0a3ad`): in the **same Kuna MCP session**, `search_procedures` returns `_kBrotliPrefixCodeRanges` at `0x11c4` and `_kBrotliContextLookupTable` at `0x122c`. The provider-independent `inspect_macho` succeeds and reports those exports plus `__TEXT,__const` beginning at `0x11c4` and spanning both addresses. The export records do not themselves include symbol type. The enclosing `__TEXT` segment has execute permission, so filtering function candidates on segment execute permissions would not catch this example. These facts provide an analyst a way to investigate, but REA does not connect them or warn on the returned procedure entries in this workflow. Separately, a logging-only build of upstream Kuna main `957a1eec` identified both original symbols as `Data`, size zero, then showed both arriving as `Export` at the function-inventory boundary before their addresses were seen. The same build's JSON exactly matched unmodified upstream main. Source shows the [export append](https://github.com/Noelo-Lab/kuna/blob/957a1eec3c1f164db342d6583f81d2e91abaf353/decompiler/crates/kuna-analysis/src/loader/macho_stubs.rs#L285-L306) and [function-symbol insertion](https://github.com/Noelo-Lab/kuna/blob/957a1eec3c1f164db342d6583f81d2e91abaf353/decompiler/crates/kuna-analysis/src/loadimage_object.rs#L928-L933). This diagnoses this sample, not every source of dubious procedure entries. Related upstream report: https://github.com/Noelo-Lab/kuna/issues/1339. The target digest remained unchanged. The MCP check exercised REA's published tools with the released Kuna provider; upstream instrumentation was a separate direct Kuna check. Avoid assuming every export is code or that every symbol in an executable segment is a function. Any proposed cross-provider classification needs to preserve uncertain cases and legitimate exported code.
Author
Owner

@morluto commented on GitHub (Oct 31, 2026):

Real Kuna validation of proposed PR #8335 is recorded at https://github.com/morluto/rea/pull/8335#issuecomment-6547264405. The change fixes the ordinary data-symbol fixture and a shipped Brotli table control, but a constructed Mach-O with a statically called instruction sequence in an executable segment's unflagged section is omitted and categorically refused as data. The same real Kuna session reports it as a callee. This is one static counterexample, not an estimate of prevalence or runtime reachability; it illustrates why a section declaration and callable evidence need distinct treatment. The PR's existing simulated-provider boundary tests pass but do not cover that combination.

<!-- gh-comment-id:6547266060 --> @morluto commented on GitHub (Oct 31, 2026): Real Kuna validation of proposed PR #8335 is recorded at https://github.com/morluto/rea/pull/8335#issuecomment-6547264405. The change fixes the ordinary data-symbol fixture and a shipped Brotli table control, but a constructed Mach-O with a statically called instruction sequence in an executable segment's unflagged section is omitted and categorically refused as data. The same real Kuna session reports it as a callee. This is one static counterexample, not an estimate of prevalence or runtime reachability; it illustrates why a section declaration and callable evidence need distinct treatment. The PR's existing simulated-provider boundary tests pass but do not cover that combination.
Sign in to join this conversation.
No milestone
No project
No assignees
1 participant
Notifications
Due date
The due date is invalid or out of range. Please use the format "yyyy-mm-dd".

No due date set.

Dependencies

No dependencies set.

Reference
morluto/rea#4695
No description provided.