[GH-ISSUE #8170] [Feature] Investigate verified byte reads for Kuna-backed investigations #4696

Open
opened 2026-10-31 00:52:35 +00:00 by bsakaguchi · 9 comments
Owner

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

Before submitting

  • I searched the open and closed issues and pull requests for a related proposal.
  • I understand the contribution scope must be reviewed before integration; open questions are welcome here.
  • This request does not include credentials, private samples, or sensitive target data.

Area

Native analysis or decompilation

Current limitation

An analyst using Kuna may need to check instruction or data bytes after an uncertain decompilation. On a self-built arm64 Mach-O, REA MCP read_bytes for 0x100000410 returned capability_unavailable with operation is not declared by this provider. Direct Kuna 1.833 read <binary> 0x100000410 --addr --bytes 16 --json returned ff4300d1e00f00b9e80f40b929000090; REA with Ghidra returned the same 16 bytes on this fixture. This does not establish equivalence for relocations, unmapped, zero-filled, overlapping, or truncated ranges.

Desired outcome and success criteria

Investigate whether Kuna can satisfy the existing provider-neutral read_bytes question, with explicit source, address and coverage semantics. Compare mapped, uninitialized, unmapped, truncated and relocation cases against Kuna's actual behavior and REA's contract before admission. Return the exact producer bytes and truthful completeness or unavailability; do not present synthetic zero fill as observed file bytes. Avoid adding a duplicate raw Kuna command tool solely for API parity.

Expected interaction with targets

Read existing files and launch the caller-supplied Kuna executable in REA's owned workspace; no target execution or target modification proposed.

Verification available so far

I have a prototype or observed results

Verification details (optional)

One positive direct Kuna and REA/Ghidra comparison and one REA/Kuna capability refusal on macOS arm64, Node 24.22.1, Kuna 1.833 and Ghidra 12.1.4. Target SHA-256 ffad1b70c6a5a7ef3f7c3f59126be516520210777d949b35e97c9067f272aa2d. No other formats, boundary cases, or full real-provider tests were run. Investigate whether REA has sufficient producer observations to establish mapping and completeness before implementing.

References

Additional boundary observation

On the same Mach-O, direct Kuna 1.833 read at 0x10000bff8 for 16 bytes returned 8 zero bytes, truncated: false, and a note saying the listing stops at segment end 0x10000c000. REA/Ghidra read_bytes at the same address returned returned_bytes: 0, complete: false. The program's __LINKEDIT segment extends virtually beyond its file-backed bytes; this observation does not establish equivalent initialization semantics between providers. At 0x123456780, direct Kuna refused the unmapped start while REA/Ghidra returned zero bytes and complete: false. Any adaptation must not infer complete coverage from upstream truncated: false alone and must independently establish which bytes the existing contract permits.

Called address and instruction-shaped data control

On macOS arm64 with Kuna 1.986, Ghidra 12.1.4, Node 24.22.1 and an authored, unexecuted Mach-O (sha256:5a25df84d7dce09e5461d260a5656008f11a51ca3669a0e907f07e08f368f9c3), REA/Kuna reported a direct call to 0x100000330 but read_bytes and inspect_native_instruction were unavailable. Direct Kuna read --addr --bytes 8 --json returned 40058052c0035fd6 there. At a separate explicitly authored data word 0x100000338, direct Kuna returned c0035fd6. Separately selected REA/Ghidra read_bytes returned matching bytes and complete coverage for both addresses. Successful MCP responses were validated against the compact SDK schemas; the target digest was unchanged.

This connects the byte-read question to the code-versus-data conflict in #8352: reading the bytes would help inspect the address, but identical valid instruction bytes at the data word show that byte availability or forced decoding alone does not establish a function. These two in-range reads do not resolve the existing uninitialized, segment-end, truncation, relocation or mapping questions.

Kuna 1.986 mapped-range boundary controls

On the same unexecuted Mach-O 5a25df84d7dce09e5461d260a5656008f11a51ca3669a0e907f07e08f368f9c3, otool -l reports __LINKEDIT at virtual 0x100004000, file offset 0x4000, file size 0x238 and virtual size 0x4000. At 0x100004230, a 16-byte direct Kuna read returned 8 file-backed bytes followed by 8 zeros with truncated: false; REA/Ghidra returned the 8 initialized bytes and complete: false. Starting at 0x100004238, Kuna returned 16 zeros while REA/Ghidra returned zero bytes, incomplete. At 0x100007ff8, Kuna returned 8 zeros, a segment-end note and truncated: false; Ghidra returned no bytes, incomplete. At 0x100008000, Kuna rejected the address and Ghidra returned an incomplete empty read.

The fixture also has a zero-file-size, no-permissions __PAGEZERO segment. At 0x0, Kuna returned 16 zeros while REA/Ghidra returned no bytes, incomplete. These are static loader observations, not runtime memory measurements; zeros cannot be called observed file bytes. Target SHA-256 was unchanged, and successful Ghidra MCP results were validated against compact advertised schemas. This confirms that returned length, truncated and bare virtual segment extent do not alone establish the coverage needed to translate this provider's result into REA's existing contract. No relocation or ELF behavior is established by these controls.

The same five boundary reads were repeated against published Kuna 1.987 with the same results. Upstream source 75c564314e15056d427a27e4fe2b901e39fafe2e after the fixup-provenance change was inspected but not run. The provenance question is tracked upstream at Noelo-Lab/kuna#1401; it distinguishes zero-filled loader-image bytes from observed file bytes and asks about no-permission mappings without claiming runtime behavior.

Post-1.988 upstream provenance control

Upstream Noelo-Lab/kuna#1404 merged as b848b3640c337a292802227f5de2700931d15bf3 after published 1.988. I built that exact source commit in an isolated worktree (cargo build -p kuna-cli -j 2, debug profile; executable reports kuna 0.1.0) and supplied the locally extracted 1.988 SLEIGH specs. On the same owned, unexecuted Mach-O boundary fixture, its read --json kept the same bytes and exits as the published 1.988 executable for code, page-zero, file tail, zero tail, segment end, and unmapped start, while adding provenance: page-zero was zero-fill with --- permissions; the file-tail read split into eight file bytes and eight zero-fill bytes; the segment-end read was only eight bytes despite truncated: false. Real REA/Ghidra 12.1.4 compact MCP returned eight initialized bytes for the split range, empty incomplete results for the three virtual-only/unmapped controls, and the same eight code bytes.

On a separate owned chained-fixup Mach-O, the new Kuna output labeled the pointer slot's backing file but separately reported an applied rebase: original file bytes 1004000000000000, returned image bytes 1004000001000000. Real REA/Ghidra returned the image bytes. A file provenance range therefore does not by itself mean the returned bytes equal the original file: fixup metadata must be interpreted alongside it. Both target digests were unchanged; neither executable ran.

This is one macOS arm64 source-build control, not released-version verification, general coverage, or an REA implementation. The new fields materially improve the feasibility of a typed adapter, but REA still needs an explicit policy for uninitialized/zero-filled/repaired/unknown ranges, permissions, gaps, fixups, short reads and unrecognized metadata; do not use truncated alone or silently equate file-backed and unmodified bytes. Check ELF/PE, overlapping mappings, FAT offsets, producer failures, resource bounds, provenance and session behavior before claiming support.

ELF and PE source-provenance comparison

Using upstream's readprov_elf_x86_64 and readprov_pe_x86_64.exe fixtures (not executed), the same source build at b848b364 split an ELF range into eight file bytes plus eight zero-fill bytes and a PE range likewise. Real REA/Ghidra 12.1.4 compact stdio MCP on the same files returned eight bytes and complete: false for the ELF range, but all 16 bytes and complete: true for the PE range. For separately declared no-read ELF and PE mappings, source Kuna marked the file-backed bytes with permissions ---, while REA/Ghidra returned all eight initialized bytes in each case. Ghidra also returned the PE header bytes. File-backed Kuna ranges were checked against the source files, zero-fill ranges contained zeros, and fixture hashes were unchanged.

A deliberately damaged copy of the upstream ELF was read only by source Kuna: it marked the rewritten ELF header fields as repaired, interleaved with file spans. An upstream relocatable ELF object produced unknown provenance. No REA/Ghidra outcome for those two cases was measured. This is a small selected-fixture control, not a cross-format completeness or runtime-permission claim.

The PE result matters for adapter design: Kuna's zero-fill describes origin, while Ghidra's read_bytes reflects what that provider considers initialized analyzed memory; truncating every zero-fill range to imitate the ELF example would not preserve the observed PE behavior. Likewise, declared read permission is a separate fact from whether a static analyzer can inspect bytes. Preserve origin, provider memory semantics and exact returned coverage rather than treating any one as an equivalent of the others.

Producer allocation and representation expansion

On the source build at b848b364, reading 256, 4,096 and 65,536 bytes from the same zero-filled Mach-O range emitted 4,398, 52,600 and 831,760 stdout bytes, respectively, with 16, 256 and 4,096 hexdump rows and one provenance range. These are serialized byte counts for three controlled requests, not token, time, CPU or memory measurements, and not a worst-case expansion bound. The target digest remained unchanged and it was not executed.

Source inspection: an explicit --bytes creates a non-derived region; walk_data accumulates a vector of rows, then data_result_json constructs the full hex string and per-row JSON before serializing stdout. Thus a REA subprocess stdout cap alone cannot bound producer allocations before the first output byte. An adapter must validate the caller request against a justified resource bound before launch and account for JSON, row and provenance expansion; it should derive completeness from the examined returned span, since truncated: false did not establish the requested length at segment end. This is an integration design concern, not a claim that the modest controls exhausted memory.

Exploratory existing-contract shape

A production-domain control manually converted four real b848b364 Kuna output documents (code, file/zero-fill boundary, segment-end short read, and applied fixup) into nativeByteReadSchema results, with complete calculated from returned versus requested length and Kuna provenance/fixups retained in raw Evidence. The existing schema accepted all four; complete was true for the 16-byte file/zero-fill modeled image and false for the eight-byte segment-end result despite Kuna's truncated: false. Real compact stdio MCP imported the manually assembled Evidence bundle into a target-free session and returned all four records with raw provenance and fixup data intact. This is a manual domain/handoff experiment, not a REA/Kuna provider implementation or proof of downstream consumer safety.

The experiment demonstrates that the existing complete field describes returned length, not file backing or independent verification. A full modeled zero-filled span can be complete by length and still need its source qualifier. Before using this shape, audit consumers such as native-function byte fingerprinting, decide how provenance and placeholders become visible inline and through views, and validate that source limitations are preserved rather than relying on hidden raw metadata alone.

Overlapping ELF segment control

On macOS arm64, I generated two unexecuted ELF64 x86-64 files with two PT_LOAD entries mapping distinct 16-byte file ranges at offsets 0x1000 and 0x2000 to the same 0x401000..0x40100f virtual range. They differ only in program-header order. GNU readelf -l independently confirms the two mappings.

Direct published Kuna 1.988 selects the first mapping in each file: 112233445566778899aabbccddeeffc3 for the first order and ffeeddccbbaa998877665544332211c3 for the reverse. The post-1.988 source build at b848b364 returned the same bytes and reported the selected file offset as file provenance, without reporting the other mapping. These outputs are static loader-model observations, not runtime behavior or proof that either provider is authoritative.

Real REA/Ghidra 12.1.4 compact and full stdio MCP imported the later segment into default address space and preserved the earlier as segment_0 overlay. inspect_native_load_image(detail: complete) exposed both mappings but stated that independent ELF verification is unsupported. read_bytes and address_to_file_offset on 0x401000 returned the later mapping; the same operations on segment_0:0x401000 returned the earlier mapping and its file offset. Both reads reported complete within their selected address spaces, and successful responses validated against the fetched MCP output schemas. This is a working way to inspect both Ghidra observations, not a general ambiguity detector. inspect_binary_layout was unavailable on this macOS host because its real-verified profile supports Linux x64 only.

An adapter must not treat one selected file offset or a complete byte count as proof that the source artifact has one possible mapping, and it must preserve provider/address-space identity. This one fixture does not establish general overlap, architecture, loader or runtime behavior. Targets were unchanged (SHA-256 a9b709dc5c0c5fe54ae489ea619f2ba2b3d2f48259a781e536c5925068b544f6 and c444c0a75cf8cb68ba16b07657fdca0765dcd9c054bfa224ae2c8fdc577e8880). Upstream provenance question: Noelo-Lab/kuna#1426.

Using the first overlapping ELF with real REA/Ghidra compact and full MCP on #8606 head b1f382ce1, I also supplied the same function inventory and its one-byte default-space body to both sides of match_native_functions: the left observations included the default-space read; the right observations included only the overlay read at the same numeric offset. The result recorded one left byte fingerprint and zero right byte fingerprints. A match still appeared from the other available correspondence basis; it was not a byte-based match for the overlay read. This is one positive address-space separation control, not proof of all overlay or cross-provider comparison semantics.

Universal Mach-O byte provenance control

On macOS arm64, I built an unexecuted x86-64/arm64 universal Mach-O with _audit_value in both slices. lipo -detailed_info reported x86-64 at container offset 0x1000 and arm64 at 0x4000. Direct released Kuna 1.988 read --slice returned the expected 16 bytes for each architecture. An unverified debug source build at Kuna merge commit b8eae8b660ceb0dc2a9162d0df2e63272c9280ed, using locally supplied 1.988 specs, returned the same bytes and file provenance offsets 0x1340 for x86-64 and 0x4328 for arm64. Those offsets locate the bytes in the original universal container.

After explicit lipo -thin extraction, the same source build reported slice-file offsets 0x340 and 0x328, respectively. The original container bytes at the reported container offsets and the extracted files at their reported slice offsets matched independently. Real REA/Ghidra 12.1.4 through compact and full stdio MCP returned the same bytes and slice-file offsets for both extracted binaries; successful read_bytes and address_to_file_offset results validated against their advertised schemas. REA explicitly refused the universal file for both Ghidra and verified Kuna 1.988 with provider-specific unsupported-target reasons.

The original universal SHA-256 was d5374df0bffb2d6c05a30d431a37b70cc4877f16bad1c11638d748797c0252ef; extracted arm64 and x86-64 hashes were 967e8358527e0fd4dea88282e6cd4b9cf56604c9f786f5c1d75ed1ba642d991b and b33bfb04782a7b1570eb9d54cf28226dd67e2d4223608d23b979d299c89065f7. All remained unchanged and were not run. This is one constructed container and ordinary file-backed reads, not a claim about all offsets, fixups, malformed containers or released provenance support. It reinforces that an adapter must name the coordinate owner and preserve the selected slice and container identity; it must not add a slice base twice or treat an extracted file's offset as a container offset.

Released Kuna 1.989 overlapping-mapping control

On macOS arm64 I regenerated the two ELF fixtures in Noelo-Lab/kuna#1426, with identical program mappings in opposite table orders. Released Kuna 1.989 read --json selected the first mapping in each and now reported both the chosen file offset and the other mapping in provenance[].overlaps: ab selected 0x1000 with alternate 0x2000; ba selected 0x2000 with alternate 0x1000. Its returned 16 bytes matched the selected file range, and notes described the overlap. This checks the release behavior of the upstream fix; it does not establish a runtime loader choice.

On proposed REA upgrade #8699, real Kuna 1.989 in full and compact stdio MCP still returned capability_unavailable for read_bytes, address_to_file_offset, and inspect_native_load_image. Real Ghidra 12.1.4 in both profiles returned the other (later) mapping in default space and the first mapping in segment_0, with their respective bytes and file offsets for both fixture orders. The successful MCP responses used fetched SDK schemas. An attempted local xcrun llvm-readobj comparison was unavailable; the earlier independent readelf control above applies to these byte-identical SHA-256 fixtures. Both target hashes remained unchanged and neither executable was run.

A Kuna-backed byte design can now report producer-selected and alternate mappings from this released output, but must keep those facts separate from REA/Ghidra address spaces, file offsets, fixups and runtime assumptions. Existing read_bytes completeness for one selected space is not proof of globally unique mapping. Other formats, partial overlaps, zero fill and malformed overlap metadata remain unverified for 1.989.

Partial overlap and zero-fill control on 1.989

I generated an unexecuted x86-64 ELF with a first PT_LOAD at 0x401000 containing eight file bytes and 32 bytes of declared memory, and a second 16-byte file-backed PT_LOAD at 0x401010, overlapping the first segment's virtual zero-fill range. With released Kuna 1.989, read 0x401000 --bytes 32 --json returned 32 bytes with truncated: false: eight file bytes at offset 0x1000, eight zero-fill bytes, then 16 file bytes at 0x2010. Its provenance split the three ranges and listed the first segment's zero-fill mapping as an alternate overlap for the last range.

Real REA/Ghidra 12.1.4 in compact and full stdio MCP returned the first eight initialized bytes with complete: false for the 32-byte request. A read starting in the intervening zero-fill region returned zero bytes and complete: false; a separate read of the later mapping returned its 16 bytes and complete: true. inspect_native_load_image retained the two initialized Ghidra mappings and reported independent ELF verification unsupported. The attempted segment_0 overlay selector was rejected: on this partial-overlap fixture Ghidra imported the two initialized ranges into default space, unlike the fully overlapping fixtures above. Successful calls used fetched SDK schemas.

This is a static, intentionally constructed fixture, not proof of runtime loader behavior. The target SHA-256 68a9257e3a9a314df605c1578772ade58b958780031ca9104203590580f60929 remained unchanged and the target was not executed. A future Kuna adapter must preserve the selected range's file/zero-fill segmentation and alternate mapping rather than turning the 32-byte count or truncated: false into an unqualified file-byte claim. No REA/Kuna byte-read implementation is claimed.

Released 1.989 short-read interpretation

On the original owned boundary Mach-O (SHA-256 5a25df84d7dce09e5461d260a5656008f11a51ca3669a0e907f07e08f368f9c3), released Kuna 1.989 read 0x100007ff8 --addr --json returned eight zero-fill bytes and truncated: false for both --bytes 8 and --bytes 16. The 16-byte request additionally recorded a segment-end note; the returned end address and byte count were otherwise the same. The original target was unchanged and not executed.

The 1.989 CLI documentation describes truncated in the listing context as the implicit instruction cap, and explains that listing stops at the mapped run's end. Therefore this is not asserted as a Kuna bug or a broken promise that truncated means short read. A REA adapter must retain the requested length and compute its own returned-versus-requested completeness, preserve the segment-end note and zero-fill provenance, and not infer complete file-byte coverage from this flag. The previously recorded real REA/Ghidra control returned an empty incomplete read at this address; no new Ghidra run was performed for this note.

Released Kuna 1.990 fixup-qualified read

On macOS arm64, direct Kuna 1.990 read of eight bytes at 0x100004000 in the unexecuted Mach-O fixture from #8510 returned modeled image bytes c003000001000000. Its JSON also reported the underlying file bytes c003000000000000, returned: applied, an eight-byte chained-fixup rebase to 0x1000003c0, and source file offset 0x4000; the text note distinguished the loader-stored pointer from file bytes. Independent llvm-objdump and dyld_info checks of this fixture, and real REA/Ghidra reads showing the same loaded bytes, are recorded in #8510. The source SHA-256 remained 496e188170232c15f1ceeebf33165f060c6a31b46be9571b6f49778ef0e8d910.

This is a direct Kuna observation on one fixup, not a new REA provider capability or proof of runtime memory. A future REA task-level read should retain caller-requested length, mapping, fixup and file-versus-modeled-image distinction rather than flattening these facts into an unqualified byte string. The existing Ghidra read_bytes response describes initialized memory including loader fixups but does not itself expose this per-fixup source record.

Partial fixup-slot read on 1.990

On the same fixture, direct Kuna 1.990 read of one byte at 0x100004004 returned modeled byte 01 and a provenance range for that one byte, while its fixup metadata retained the full eight-byte slot beginning at 0x100004000, original file bytes, modeled image bytes and rebase target. Real REA/Ghidra 12.1.4 read_bytes returned the same one byte and complete: true in both MCP profiles; its completeness is for the requested byte, not the full pointer or original file bytes. The unexecuted target digest remained unchanged.

The observed metadata can extend outside the requested range: preserve both the caller's exact byte span and the complete fixup's separate range and provenance. Do not describe a complete one-byte read as a complete pointer observation. This is one partial slot and does not establish behavior for other relocation encodings or clipped fixups.

Existing Ghidra file-offset handoff

In real REA/Ghidra 12.1.4 compact/full MCP on the same fixup fixture, address_to_file_offset(0x100004000) returned file offset 16384, while read_bytes at the virtual address returned eight modeled image bytes c003000001000000. Independently reading eight bytes from the selected artifact at the returned file offset gave c003000000000000. The read_bytes Evidence explicitly states that its bytes include loader fixups; the file-offset result supplies a location, not a claim that those bytes equal the loaded image. The target SHA-256 remained unchanged and it was not executed.

This is a manual way to correlate both representations for one unambiguous file mapping. It does not surface per-fixup explanation in the Ghidra read, guarantee a contiguous mapping for every requested span, or prove runtime memory. Any composed byte workflow should preserve the separate owners rather than silently substituting one representation for the other.

Originally created by @morluto on GitHub (Oct 30, 2026). Original GitHub issue: https://github.com/morluto/rea/issues/8170 ### Before submitting - [x] I searched the open and closed issues and pull requests for a related proposal. - [x] I understand the [contribution scope](https://github.com/morluto/rea/blob/main/CONTRIBUTING.md#contribution-scope) must be reviewed before integration; open questions are welcome here. - [x] This request does not include credentials, private samples, or sensitive target data. ### Area Native analysis or decompilation ### Current limitation An analyst using Kuna may need to check instruction or data bytes after an uncertain decompilation. On a self-built arm64 Mach-O, REA MCP `read_bytes` for `0x100000410` returned `capability_unavailable` with `operation is not declared by this provider`. Direct Kuna 1.833 `read <binary> 0x100000410 --addr --bytes 16 --json` returned `ff4300d1e00f00b9e80f40b929000090`; REA with Ghidra returned the same 16 bytes on this fixture. This does not establish equivalence for relocations, unmapped, zero-filled, overlapping, or truncated ranges. ### Desired outcome and success criteria Investigate whether Kuna can satisfy the existing provider-neutral `read_bytes` question, with explicit source, address and coverage semantics. Compare mapped, uninitialized, unmapped, truncated and relocation cases against Kuna's actual behavior and REA's contract before admission. Return the exact producer bytes and truthful completeness or unavailability; do not present synthetic zero fill as observed file bytes. Avoid adding a duplicate raw Kuna command tool solely for API parity. ### Expected interaction with targets Read existing files and launch the caller-supplied Kuna executable in REA's owned workspace; no target execution or target modification proposed. ### Verification available so far I have a prototype or observed results ### Verification details (optional) One positive direct Kuna and REA/Ghidra comparison and one REA/Kuna capability refusal on macOS arm64, Node 24.22.1, Kuna 1.833 and Ghidra 12.1.4. Target SHA-256 `ffad1b70c6a5a7ef3f7c3f59126be516520210777d949b35e97c9067f272aa2d`. No other formats, boundary cases, or full real-provider tests were run. Investigate whether REA has sufficient producer observations to establish mapping and completeness before implementing. ### References - [Kuna 1.833 CLI](https://github.com/Noelo-Lab/kuna/blob/v1.833/docs/cli.md) - [REA Kuna capabilities](https://github.com/morluto/rea/blob/47fa6ab3795d547207001ffb01643c0aa39dcad8/src/kuna/KunaProviderCapabilities.ts) - [REA native byte read contract](https://github.com/morluto/rea/blob/47fa6ab3795d547207001ffb01643c0aa39dcad8/src/contracts/hopperMemoryContracts.ts) ### Additional boundary observation On the same Mach-O, direct Kuna 1.833 `read` at `0x10000bff8` for 16 bytes returned 8 zero bytes, `truncated: false`, and a note saying the listing stops at segment end `0x10000c000`. REA/Ghidra `read_bytes` at the same address returned `returned_bytes: 0`, `complete: false`. The program's `__LINKEDIT` segment extends virtually beyond its file-backed bytes; this observation does not establish equivalent initialization semantics between providers. At `0x123456780`, direct Kuna refused the unmapped start while REA/Ghidra returned zero bytes and `complete: false`. Any adaptation must not infer complete coverage from upstream `truncated: false` alone and must independently establish which bytes the existing contract permits. ### Called address and instruction-shaped data control On macOS arm64 with Kuna 1.986, Ghidra 12.1.4, Node 24.22.1 and an authored, unexecuted Mach-O (`sha256:5a25df84d7dce09e5461d260a5656008f11a51ca3669a0e907f07e08f368f9c3`), REA/Kuna reported a direct call to `0x100000330` but `read_bytes` and `inspect_native_instruction` were unavailable. Direct Kuna `read --addr --bytes 8 --json` returned `40058052c0035fd6` there. At a separate explicitly authored data word `0x100000338`, direct Kuna returned `c0035fd6`. Separately selected REA/Ghidra `read_bytes` returned matching bytes and complete coverage for both addresses. Successful MCP responses were validated against the compact SDK schemas; the target digest was unchanged. This connects the byte-read question to the code-versus-data conflict in #8352: reading the bytes would help inspect the address, but identical valid instruction bytes at the data word show that byte availability or forced decoding alone does not establish a function. These two in-range reads do not resolve the existing uninitialized, segment-end, truncation, relocation or mapping questions. ### Kuna 1.986 mapped-range boundary controls On the same unexecuted Mach-O `5a25df84d7dce09e5461d260a5656008f11a51ca3669a0e907f07e08f368f9c3`, `otool -l` reports `__LINKEDIT` at virtual `0x100004000`, file offset `0x4000`, file size `0x238` and virtual size `0x4000`. At `0x100004230`, a 16-byte direct Kuna read returned 8 file-backed bytes followed by 8 zeros with `truncated: false`; REA/Ghidra returned the 8 initialized bytes and `complete: false`. Starting at `0x100004238`, Kuna returned 16 zeros while REA/Ghidra returned zero bytes, incomplete. At `0x100007ff8`, Kuna returned 8 zeros, a segment-end note and `truncated: false`; Ghidra returned no bytes, incomplete. At `0x100008000`, Kuna rejected the address and Ghidra returned an incomplete empty read. The fixture also has a zero-file-size, no-permissions `__PAGEZERO` segment. At `0x0`, Kuna returned 16 zeros while REA/Ghidra returned no bytes, incomplete. These are static loader observations, not runtime memory measurements; zeros cannot be called observed file bytes. Target SHA-256 was unchanged, and successful Ghidra MCP results were validated against compact advertised schemas. This confirms that returned length, `truncated` and bare virtual segment extent do not alone establish the coverage needed to translate this provider's result into REA's existing contract. No relocation or ELF behavior is established by these controls. The same five boundary reads were repeated against published Kuna 1.987 with the same results. Upstream source `75c564314e15056d427a27e4fe2b901e39fafe2e` after the fixup-provenance change was inspected but not run. The provenance question is tracked upstream at [Noelo-Lab/kuna#1401](https://github.com/Noelo-Lab/kuna/issues/1401); it distinguishes zero-filled loader-image bytes from observed file bytes and asks about no-permission mappings without claiming runtime behavior. ### Post-1.988 upstream provenance control Upstream [Noelo-Lab/kuna#1404](https://github.com/Noelo-Lab/kuna/pull/1404) merged as `b848b3640c337a292802227f5de2700931d15bf3` after published 1.988. I built that exact source commit in an isolated worktree (`cargo build -p kuna-cli -j 2`, debug profile; executable reports `kuna 0.1.0`) and supplied the locally extracted 1.988 SLEIGH specs. On the same owned, unexecuted Mach-O boundary fixture, its `read --json` kept the same bytes and exits as the published 1.988 executable for code, page-zero, file tail, zero tail, segment end, and unmapped start, while adding provenance: page-zero was `zero-fill` with `---` permissions; the file-tail read split into eight `file` bytes and eight `zero-fill` bytes; the segment-end read was only eight bytes despite `truncated: false`. Real REA/Ghidra 12.1.4 compact MCP returned eight initialized bytes for the split range, empty incomplete results for the three virtual-only/unmapped controls, and the same eight code bytes. On a separate owned chained-fixup Mach-O, the new Kuna output labeled the pointer slot's backing `file` but separately reported an applied rebase: original file bytes `1004000000000000`, returned image bytes `1004000001000000`. Real REA/Ghidra returned the image bytes. A `file` provenance range therefore does not by itself mean the returned bytes equal the original file: fixup metadata must be interpreted alongside it. Both target digests were unchanged; neither executable ran. This is one macOS arm64 source-build control, not released-version verification, general coverage, or an REA implementation. The new fields materially improve the feasibility of a typed adapter, but REA still needs an explicit policy for uninitialized/zero-filled/repaired/unknown ranges, permissions, gaps, fixups, short reads and unrecognized metadata; do not use `truncated` alone or silently equate file-backed and unmodified bytes. Check ELF/PE, overlapping mappings, FAT offsets, producer failures, resource bounds, provenance and session behavior before claiming support. ### ELF and PE source-provenance comparison Using upstream's `readprov_elf_x86_64` and `readprov_pe_x86_64.exe` fixtures (not executed), the same source build at `b848b364` split an ELF range into eight file bytes plus eight zero-fill bytes and a PE range likewise. Real REA/Ghidra 12.1.4 compact stdio MCP on the same files returned eight bytes and `complete: false` for the ELF range, but all 16 bytes and `complete: true` for the PE range. For separately declared no-read ELF and PE mappings, source Kuna marked the file-backed bytes with permissions `---`, while REA/Ghidra returned all eight initialized bytes in each case. Ghidra also returned the PE header bytes. File-backed Kuna ranges were checked against the source files, zero-fill ranges contained zeros, and fixture hashes were unchanged. A deliberately damaged copy of the upstream ELF was read only by source Kuna: it marked the rewritten ELF header fields as `repaired`, interleaved with `file` spans. An upstream relocatable ELF object produced `unknown` provenance. No REA/Ghidra outcome for those two cases was measured. This is a small selected-fixture control, not a cross-format completeness or runtime-permission claim. The PE result matters for adapter design: Kuna's `zero-fill` describes origin, while Ghidra's `read_bytes` reflects what that provider considers initialized analyzed memory; truncating every zero-fill range to imitate the ELF example would not preserve the observed PE behavior. Likewise, declared read permission is a separate fact from whether a static analyzer can inspect bytes. Preserve origin, provider memory semantics and exact returned coverage rather than treating any one as an equivalent of the others. ### Producer allocation and representation expansion On the source build at `b848b364`, reading 256, 4,096 and 65,536 bytes from the same zero-filled Mach-O range emitted 4,398, 52,600 and 831,760 stdout bytes, respectively, with 16, 256 and 4,096 hexdump rows and one provenance range. These are serialized byte counts for three controlled requests, not token, time, CPU or memory measurements, and not a worst-case expansion bound. The target digest remained unchanged and it was not executed. Source inspection: an explicit `--bytes` creates a non-derived region; `walk_data` accumulates a vector of rows, then `data_result_json` constructs the full hex string and per-row JSON before serializing stdout. Thus a REA subprocess stdout cap alone cannot bound producer allocations before the first output byte. An adapter must validate the caller request against a justified resource bound before launch and account for JSON, row and provenance expansion; it should derive completeness from the examined returned span, since `truncated: false` did not establish the requested length at segment end. This is an integration design concern, not a claim that the modest controls exhausted memory. ### Exploratory existing-contract shape A production-domain control manually converted four real `b848b364` Kuna output documents (code, file/zero-fill boundary, segment-end short read, and applied fixup) into `nativeByteReadSchema` results, with `complete` calculated from returned versus requested length and Kuna provenance/fixups retained in raw Evidence. The existing schema accepted all four; `complete` was true for the 16-byte file/zero-fill modeled image and false for the eight-byte segment-end result despite Kuna's `truncated: false`. Real compact stdio MCP imported the manually assembled Evidence bundle into a target-free session and returned all four records with raw provenance and fixup data intact. This is a manual domain/handoff experiment, not a REA/Kuna provider implementation or proof of downstream consumer safety. The experiment demonstrates that the existing `complete` field describes returned length, not file backing or independent verification. A full modeled zero-filled span can be complete by length and still need its source qualifier. Before using this shape, audit consumers such as native-function byte fingerprinting, decide how provenance and placeholders become visible inline and through views, and validate that source limitations are preserved rather than relying on hidden raw metadata alone. ### Overlapping ELF segment control On macOS arm64, I generated two unexecuted ELF64 x86-64 files with two `PT_LOAD` entries mapping distinct 16-byte file ranges at offsets `0x1000` and `0x2000` to the same `0x401000..0x40100f` virtual range. They differ only in program-header order. GNU `readelf -l` independently confirms the two mappings. Direct published Kuna 1.988 selects the first mapping in each file: `112233445566778899aabbccddeeffc3` for the first order and `ffeeddccbbaa998877665544332211c3` for the reverse. The post-1.988 source build at `b848b364` returned the same bytes and reported the selected file offset as `file` provenance, without reporting the other mapping. These outputs are static loader-model observations, not runtime behavior or proof that either provider is authoritative. Real REA/Ghidra 12.1.4 compact and full stdio MCP imported the later segment into default address space and preserved the earlier as `segment_0` overlay. `inspect_native_load_image(detail: complete)` exposed both mappings but stated that independent ELF verification is unsupported. `read_bytes` and `address_to_file_offset` on `0x401000` returned the later mapping; the same operations on `segment_0:0x401000` returned the earlier mapping and its file offset. Both reads reported complete within their selected address spaces, and successful responses validated against the fetched MCP output schemas. This is a working way to inspect both Ghidra observations, not a general ambiguity detector. `inspect_binary_layout` was unavailable on this macOS host because its real-verified profile supports Linux x64 only. An adapter must not treat one selected file offset or a complete byte count as proof that the source artifact has one possible mapping, and it must preserve provider/address-space identity. This one fixture does not establish general overlap, architecture, loader or runtime behavior. Targets were unchanged (SHA-256 `a9b709dc5c0c5fe54ae489ea619f2ba2b3d2f48259a781e536c5925068b544f6` and `c444c0a75cf8cb68ba16b07657fdca0765dcd9c054bfa224ae2c8fdc577e8880`). Upstream provenance question: [Noelo-Lab/kuna#1426](https://github.com/Noelo-Lab/kuna/issues/1426). Using the first overlapping ELF with real REA/Ghidra compact and full MCP on #8606 head `b1f382ce1`, I also supplied the same function inventory and its one-byte default-space body to both sides of `match_native_functions`: the left observations included the default-space read; the right observations included only the overlay read at the same numeric offset. The result recorded one left byte fingerprint and zero right byte fingerprints. A match still appeared from the other available correspondence basis; it was not a byte-based match for the overlay read. This is one positive address-space separation control, not proof of all overlay or cross-provider comparison semantics. ### Universal Mach-O byte provenance control On macOS arm64, I built an unexecuted x86-64/arm64 universal Mach-O with `_audit_value` in both slices. `lipo -detailed_info` reported x86-64 at container offset `0x1000` and arm64 at `0x4000`. Direct released Kuna 1.988 `read --slice` returned the expected 16 bytes for each architecture. An unverified debug source build at Kuna merge commit `b8eae8b660ceb0dc2a9162d0df2e63272c9280ed`, using locally supplied 1.988 specs, returned the same bytes and `file` provenance offsets `0x1340` for x86-64 and `0x4328` for arm64. Those offsets locate the bytes in the original universal container. After explicit `lipo -thin` extraction, the same source build reported slice-file offsets `0x340` and `0x328`, respectively. The original container bytes at the reported container offsets and the extracted files at their reported slice offsets matched independently. Real REA/Ghidra 12.1.4 through compact and full stdio MCP returned the same bytes and slice-file offsets for both extracted binaries; successful `read_bytes` and `address_to_file_offset` results validated against their advertised schemas. REA explicitly refused the universal file for both Ghidra and verified Kuna 1.988 with provider-specific unsupported-target reasons. The original universal SHA-256 was `d5374df0bffb2d6c05a30d431a37b70cc4877f16bad1c11638d748797c0252ef`; extracted arm64 and x86-64 hashes were `967e8358527e0fd4dea88282e6cd4b9cf56604c9f786f5c1d75ed1ba642d991b` and `b33bfb04782a7b1570eb9d54cf28226dd67e2d4223608d23b979d299c89065f7`. All remained unchanged and were not run. This is one constructed container and ordinary file-backed reads, not a claim about all offsets, fixups, malformed containers or released provenance support. It reinforces that an adapter must name the coordinate owner and preserve the selected slice and container identity; it must not add a slice base twice or treat an extracted file's offset as a container offset. ### Released Kuna 1.989 overlapping-mapping control On macOS arm64 I regenerated the two ELF fixtures in Noelo-Lab/kuna#1426, with identical program mappings in opposite table orders. Released Kuna 1.989 `read --json` selected the first mapping in each and now reported both the chosen file offset and the other mapping in `provenance[].overlaps`: `ab` selected `0x1000` with alternate `0x2000`; `ba` selected `0x2000` with alternate `0x1000`. Its returned 16 bytes matched the selected file range, and notes described the overlap. This checks the release behavior of the upstream fix; it does not establish a runtime loader choice. On proposed REA upgrade #8699, real Kuna 1.989 in full and compact stdio MCP still returned `capability_unavailable` for `read_bytes`, `address_to_file_offset`, and `inspect_native_load_image`. Real Ghidra 12.1.4 in both profiles returned the other (later) mapping in default space and the first mapping in `segment_0`, with their respective bytes and file offsets for both fixture orders. The successful MCP responses used fetched SDK schemas. An attempted local `xcrun llvm-readobj` comparison was unavailable; the earlier independent `readelf` control above applies to these byte-identical SHA-256 fixtures. Both target hashes remained unchanged and neither executable was run. A Kuna-backed byte design can now report producer-selected and alternate mappings from this released output, but must keep those facts separate from REA/Ghidra address spaces, file offsets, fixups and runtime assumptions. Existing `read_bytes` completeness for one selected space is not proof of globally unique mapping. Other formats, partial overlaps, zero fill and malformed overlap metadata remain unverified for 1.989. ### Partial overlap and zero-fill control on 1.989 I generated an unexecuted x86-64 ELF with a first `PT_LOAD` at `0x401000` containing eight file bytes and 32 bytes of declared memory, and a second 16-byte file-backed `PT_LOAD` at `0x401010`, overlapping the first segment's virtual zero-fill range. With released Kuna 1.989, `read 0x401000 --bytes 32 --json` returned 32 bytes with `truncated: false`: eight file bytes at offset `0x1000`, eight zero-fill bytes, then 16 file bytes at `0x2010`. Its provenance split the three ranges and listed the first segment's zero-fill mapping as an alternate overlap for the last range. Real REA/Ghidra 12.1.4 in compact and full stdio MCP returned the first eight initialized bytes with `complete: false` for the 32-byte request. A read starting in the intervening zero-fill region returned zero bytes and `complete: false`; a separate read of the later mapping returned its 16 bytes and `complete: true`. `inspect_native_load_image` retained the two initialized Ghidra mappings and reported independent ELF verification unsupported. The attempted `segment_0` overlay selector was rejected: on this partial-overlap fixture Ghidra imported the two initialized ranges into default space, unlike the fully overlapping fixtures above. Successful calls used fetched SDK schemas. This is a static, intentionally constructed fixture, not proof of runtime loader behavior. The target SHA-256 `68a9257e3a9a314df605c1578772ade58b958780031ca9104203590580f60929` remained unchanged and the target was not executed. A future Kuna adapter must preserve the selected range's file/zero-fill segmentation and alternate mapping rather than turning the 32-byte count or `truncated: false` into an unqualified file-byte claim. No REA/Kuna byte-read implementation is claimed. ### Released 1.989 short-read interpretation On the original owned boundary Mach-O (SHA-256 `5a25df84d7dce09e5461d260a5656008f11a51ca3669a0e907f07e08f368f9c3`), released Kuna 1.989 `read 0x100007ff8 --addr --json` returned eight zero-fill bytes and `truncated: false` for both `--bytes 8` and `--bytes 16`. The 16-byte request additionally recorded a segment-end note; the returned end address and byte count were otherwise the same. The original target was unchanged and not executed. The [1.989 CLI documentation](https://github.com/Noelo-Lab/kuna/blob/v1.989/docs/cli.md) describes `truncated` in the listing context as the implicit instruction cap, and explains that listing stops at the mapped run's end. Therefore this is not asserted as a Kuna bug or a broken promise that `truncated` means short read. A REA adapter must retain the requested length and compute its own returned-versus-requested completeness, preserve the segment-end note and zero-fill provenance, and not infer complete file-byte coverage from this flag. The previously recorded real REA/Ghidra control returned an empty incomplete read at this address; no new Ghidra run was performed for this note. ### Released Kuna 1.990 fixup-qualified read On macOS arm64, direct Kuna 1.990 `read` of eight bytes at `0x100004000` in the unexecuted Mach-O fixture from #8510 returned modeled image bytes `c003000001000000`. Its JSON also reported the underlying file bytes `c003000000000000`, `returned: applied`, an eight-byte chained-fixup rebase to `0x1000003c0`, and source file offset `0x4000`; the text note distinguished the loader-stored pointer from file bytes. Independent `llvm-objdump` and `dyld_info` checks of this fixture, and real REA/Ghidra reads showing the same loaded bytes, are recorded in #8510. The source SHA-256 remained `496e188170232c15f1ceeebf33165f060c6a31b46be9571b6f49778ef0e8d910`. This is a direct Kuna observation on one fixup, not a new REA provider capability or proof of runtime memory. A future REA task-level read should retain caller-requested length, mapping, fixup and file-versus-modeled-image distinction rather than flattening these facts into an unqualified byte string. The existing Ghidra `read_bytes` response describes initialized memory including loader fixups but does not itself expose this per-fixup source record. ### Partial fixup-slot read on 1.990 On the same fixture, direct Kuna 1.990 `read` of one byte at `0x100004004` returned modeled byte `01` and a provenance range for that one byte, while its fixup metadata retained the full eight-byte slot beginning at `0x100004000`, original file bytes, modeled image bytes and rebase target. Real REA/Ghidra 12.1.4 `read_bytes` returned the same one byte and `complete: true` in both MCP profiles; its completeness is for the requested byte, not the full pointer or original file bytes. The unexecuted target digest remained unchanged. The observed metadata can extend outside the requested range: preserve both the caller's exact byte span and the complete fixup's separate range and provenance. Do not describe a complete one-byte read as a complete pointer observation. This is one partial slot and does not establish behavior for other relocation encodings or clipped fixups. ### Existing Ghidra file-offset handoff In real REA/Ghidra 12.1.4 compact/full MCP on the same fixup fixture, `address_to_file_offset(0x100004000)` returned file offset `16384`, while `read_bytes` at the virtual address returned eight modeled image bytes `c003000001000000`. Independently reading eight bytes from the selected artifact at the returned file offset gave `c003000000000000`. The `read_bytes` Evidence explicitly states that its bytes include loader fixups; the file-offset result supplies a location, not a claim that those bytes equal the loaded image. The target SHA-256 remained unchanged and it was not executed. This is a manual way to correlate both representations for one unambiguous file mapping. It does not surface per-fixup explanation in the Ghidra read, guarantee a contiguous mapping for every requested span, or prove runtime memory. Any composed byte workflow should preserve the separate owners rather than silently substituting one representation for the other.
Author
Owner

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

A loader-fixup control on the same source-owned arm64 Mach-O sharpens the contract question. At message address 0x100004008, direct Kuna 1.986 read --bytes 8 --json returned c004000000000000. Independently reading the original file at the Ghidra-reported file offset 16392 returned those same bytes. Real REA/Ghidra 12.1.4 read_bytes at the analysis address returned c004000001000000; its Evidence explicitly says initialized Ghidra memory includes loader fixups. Both returned eight complete bytes.

This is one observed Mach-O pointer, not a claim that every Kuna read equals source bytes or that either byte view is wrong. It shows that a general adapter cannot treat direct Kuna read as identical to Ghidra's initialized-memory read_bytes: source-file bytes, analysis-memory bytes, relocations/fixups, and unmapped/uninitialized coverage need separately established semantics and provenance. The source artifact was not modified or executed.

<!-- gh-comment-id:6533264543 --> @morluto commented on GitHub (Oct 30, 2026): A loader-fixup control on the same source-owned arm64 Mach-O sharpens the contract question. At `message` address `0x100004008`, direct Kuna 1.986 `read --bytes 8 --json` returned `c004000000000000`. Independently reading the original file at the Ghidra-reported file offset 16392 returned those same bytes. Real REA/Ghidra 12.1.4 `read_bytes` at the analysis address returned `c004000001000000`; its Evidence explicitly says initialized Ghidra memory includes loader fixups. Both returned eight complete bytes. This is one observed Mach-O pointer, not a claim that every Kuna read equals source bytes or that either byte view is wrong. It shows that a general adapter cannot treat direct Kuna `read` as identical to Ghidra's initialized-memory `read_bytes`: source-file bytes, analysis-memory bytes, relocations/fixups, and unmapped/uninitialized coverage need separately established semantics and provenance. The source artifact was not modified or executed.
Author
Owner

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

A follow-up exercised the existing REA search and load-image workflows at the same pointer. Restricting Ghidra search_bytes to the eight-byte mapped region, the original file pattern c004000000000000 returned no matches, while the loader-adjusted pattern c004000001000000 matched 0x100004008 with file_offset: 16392. The search explicitly said it covered initialized loader-adjusted memory. That file offset identifies the corresponding source location; on this fixture it does not mean the matched memory bytes occur unchanged in the file. Independent file reading at that offset produced the original pattern.

There is already a useful Ghidra-backed handoff: inspect_native_load_image summary retained one relocation, and a bounded inspect_analysis_view relocation page with explicitly selected original_bytes_hex and memory_bytes_hex returned both sequences at the same address. The summary/page correctly reported independent load-image verification as unsupported for Mach-O and warned that observed relocations do not establish complete runtime behavior. With Kuna selected, read_bytes, search_bytes, address_to_file_offset, and inspect_native_load_image each returned typed capability unavailability. The file digest was unchanged. This is a single pointer on a single fixture; it does not establish general mapping or relocation completeness, nor does it justify silently substituting source bytes for provider memory.

<!-- gh-comment-id:6534553268 --> @morluto commented on GitHub (Oct 30, 2026): A follow-up exercised the existing REA search and load-image workflows at the same pointer. Restricting Ghidra `search_bytes` to the eight-byte mapped region, the original file pattern `c004000000000000` returned no matches, while the loader-adjusted pattern `c004000001000000` matched `0x100004008` with `file_offset: 16392`. The search explicitly said it covered initialized loader-adjusted memory. That file offset identifies the corresponding source location; on this fixture it does not mean the matched memory bytes occur unchanged in the file. Independent file reading at that offset produced the original pattern. There is already a useful Ghidra-backed handoff: `inspect_native_load_image` summary retained one relocation, and a bounded `inspect_analysis_view` relocation page with explicitly selected `original_bytes_hex` and `memory_bytes_hex` returned both sequences at the same address. The summary/page correctly reported independent load-image verification as unsupported for Mach-O and warned that observed relocations do not establish complete runtime behavior. With Kuna selected, `read_bytes`, `search_bytes`, `address_to_file_offset`, and `inspect_native_load_image` each returned typed capability unavailability. The file digest was unchanged. This is a single pointer on a single fixture; it does not establish general mapping or relocation completeness, nor does it justify silently substituting source bytes for provider memory.
Author
Owner

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

A controlled ELF boundary test on Kuna 1.986 and real REA/Ghidra 12.1.4 makes the zero-fill question concrete. The self-built, unexecuted x86-64 ELF has a data PT_LOAD with p_filesz=0x10, p_memsz=0x30: file-backed .data at 0x403000..0x40300f, followed by NOBITS .bss at 0x403010..0x40302f. Independent llvm-readelf confirmed that layout. The original digest stayed unchanged.

Requested address, length Direct Kuna read REA/Ghidra read_bytes
0x403000, 8 1011121314151617, 8 bytes same, complete
0x40300c, 8 1c1d1e1f00000000, 8 bytes 1c1d1e1f, 4 bytes, incomplete
0x403010, 8 eight zero bytes zero bytes, incomplete
0x40302c, 8 four zero bytes; segment-end note; truncated:false zero bytes, incomplete
0x403030, 8 nonzero exit, unmapped zero bytes, incomplete

Direct Kuna also returned four of eight requested text bytes at the end of the text segment with a segment-end note and truncated:false; Ghidra returned those four bytes with complete:false. A REA/Kuna session reported typed capability_unavailable for all seven tested read_bytes cases, as currently designed.

The REA/Ghidra load-image observation independently reported .bss as loaded but uninitialized and without a file offset. A bounded zero-byte search over that area returned no matches, searched.bytes:0, and listed the 32-byte block under skipped_uninitialized; its complete:true describes completion of this initialized-memory search, not examination of the skipped area. address_to_file_offset mapped the last .data byte and refused the first .bss byte. Independent load-image verification remains unsupported for ELF, so these mapping facts are Ghidra observations, corroborated here by llvm-readelf.

This is one ELF/host fixture, not a universal provider rule. It supports preserving source-file, provider memory and zero-fill provenance separately, and deriving returned coverage from the observed range rather than copying Kuna's truncated flag. It does not establish whether Kuna exposes enough mapping facts to adapt its output safely, or authorize substituting Ghidra semantics for Kuna's view.

<!-- gh-comment-id:6535266695 --> @morluto commented on GitHub (Oct 30, 2026): A controlled ELF boundary test on Kuna 1.986 and real REA/Ghidra 12.1.4 makes the zero-fill question concrete. The self-built, unexecuted x86-64 ELF has a data `PT_LOAD` with `p_filesz=0x10`, `p_memsz=0x30`: file-backed `.data` at `0x403000..0x40300f`, followed by NOBITS `.bss` at `0x403010..0x40302f`. Independent `llvm-readelf` confirmed that layout. The original digest stayed unchanged. | Requested address, length | Direct Kuna `read` | REA/Ghidra `read_bytes` | | --- | --- | --- | | `0x403000`, 8 | `1011121314151617`, 8 bytes | same, complete | | `0x40300c`, 8 | `1c1d1e1f00000000`, 8 bytes | `1c1d1e1f`, 4 bytes, incomplete | | `0x403010`, 8 | eight zero bytes | zero bytes, incomplete | | `0x40302c`, 8 | four zero bytes; segment-end note; `truncated:false` | zero bytes, incomplete | | `0x403030`, 8 | nonzero exit, unmapped | zero bytes, incomplete | Direct Kuna also returned four of eight requested text bytes at the end of the text segment with a segment-end note and `truncated:false`; Ghidra returned those four bytes with `complete:false`. A REA/Kuna session reported typed `capability_unavailable` for all seven tested `read_bytes` cases, as currently designed. The REA/Ghidra load-image observation independently reported `.bss` as loaded but uninitialized and without a file offset. A bounded zero-byte search over that area returned no matches, `searched.bytes:0`, and listed the 32-byte block under `skipped_uninitialized`; its `complete:true` describes completion of this initialized-memory search, not examination of the skipped area. `address_to_file_offset` mapped the last `.data` byte and refused the first `.bss` byte. Independent load-image verification remains unsupported for ELF, so these mapping facts are Ghidra observations, corroborated here by `llvm-readelf`. This is one ELF/host fixture, not a universal provider rule. It supports preserving source-file, provider memory and zero-fill provenance separately, and deriving returned coverage from the observed range rather than copying Kuna's `truncated` flag. It does not establish whether Kuna exposes enough mapping facts to adapt its output safely, or authorize substituting Ghidra semantics for Kuna's view.
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:6538783260 --> @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):

Existing source-byte handoff and current Kuna control

On the owned arm64 Mach-O at REA b43c329, I kept a real Kuna 1.986 MCP session open and requested read_bytes at the known pointer 0x100004008; REA correctly returned capability_unavailable. In that same session, inspect_macho({detail:"complete"}) returned an exhaustive thin-image segment inventory and an Evidence ID. Its __DATA VM/file ranges uniquely contained the full requested eight-byte range; using those reported coordinates and a separate local read of the caller-selected file, I derived file offset 16392 and source bytes c004000000000000. The file buffer's digest matched the opened target and its on-disk digest remained unchanged afterward. binary_session still reported Kuna open. This is an explicit analyst-side file read based on REA's mapping, not a new REA read_bytes capability or a separately retained byte-read Evidence record.

For a negative boundary control, the requested bytes at 0x10000bff8 lay in the __LINKEDIT virtual range but outside its file-backed extent; the same bounded mapping check refused to derive source bytes. In a separate Ghidra session, read_bytes at the pointer returned loader-adjusted c004000001000000. This confirms why the first result must be described as source-file bytes, not silently substituted for initialized provider memory. The check covered one thin image, unique mapping and small local file; it does not establish a general FAT, overlapping-segment, stale-file, partial-range or atomic file-identity algorithm.

I also re-ran the earlier ELF data/BSS/segment-end controls against both released Kuna 1.986 and a debug build of current upstream facd4d4369b3b37ddb5f70b67591c219664b939b, using the release compiled SLEIGH tree. Both returned the same observed file-backed bytes, synthesized zeros in the tested BSS, and four of eight requested bytes at segment end with a boundary note and truncated:false; both refused an unmapped start. Their Mach-O pointer read matched the source bytes above. Kuna's current CLI documentation describes clipping at mapped-segment ends and the listing cap separately, so truncated:false should not be treated as a byte-coverage guarantee or automatically reported as an upstream defect. The SLEIGH tree was not rebuilt at current main. Both targets retained their hashes and were not executed.

<!-- gh-comment-id:6544211192 --> @morluto commented on GitHub (Oct 31, 2026): ### Existing source-byte handoff and current Kuna control On the owned arm64 Mach-O at REA `b43c329`, I kept a real Kuna 1.986 MCP session open and requested `read_bytes` at the known pointer `0x100004008`; REA correctly returned `capability_unavailable`. In that same session, `inspect_macho({detail:"complete"})` returned an exhaustive thin-image segment inventory and an Evidence ID. Its `__DATA` VM/file ranges uniquely contained the full requested eight-byte range; using those reported coordinates and a separate local read of the caller-selected file, I derived file offset 16392 and source bytes `c004000000000000`. The file buffer's digest matched the opened target and its on-disk digest remained unchanged afterward. `binary_session` still reported Kuna open. This is an explicit analyst-side file read based on REA's mapping, **not** a new REA `read_bytes` capability or a separately retained byte-read Evidence record. For a negative boundary control, the requested bytes at `0x10000bff8` lay in the `__LINKEDIT` virtual range but outside its file-backed extent; the same bounded mapping check refused to derive source bytes. In a separate Ghidra session, `read_bytes` at the pointer returned loader-adjusted `c004000001000000`. This confirms why the first result must be described as source-file bytes, not silently substituted for initialized provider memory. The check covered one thin image, unique mapping and small local file; it does not establish a general FAT, overlapping-segment, stale-file, partial-range or atomic file-identity algorithm. I also re-ran the earlier ELF data/BSS/segment-end controls against both released Kuna 1.986 and a debug build of current upstream `facd4d4369b3b37ddb5f70b67591c219664b939b`, using the release compiled SLEIGH tree. Both returned the same observed file-backed bytes, synthesized zeros in the tested BSS, and four of eight requested bytes at segment end with a boundary note and `truncated:false`; both refused an unmapped start. Their Mach-O pointer read matched the source bytes above. Kuna's current CLI documentation describes clipping at mapped-segment ends and the listing cap separately, so `truncated:false` should not be treated as a byte-coverage guarantee or automatically reported as an upstream defect. The SLEIGH tree was not rebuilt at current main. Both targets retained their hashes and were not executed.
Author
Owner

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

A universal Mach-O control extends the earlier thin-image source-byte handoff. inspect_macho selected arm64 at container offset 32768; its normalized __TEXT.file_offset is slice-relative 0. For handler VM address 0x100000784, the slice-relative file offset is 1924 and the input-container offset is 34692. Local file reads at those offsets produced different bytes; only 34692 matched the selected thin image and a successful Hopper address_to_file_offset observation. Evidence locations were already container-relative. This confirms the existing documented rules for one universal fixture, rather than a changed inspect_macho numeric result.

The full advertised output schema described the distinct bases. With explicitly selected compact output schemas, that description was absent from the advertised schema, tool description and observed result limitations; I opened #8302 for the discoverability gap, linked to the earlier resolved #1973. Both successful inspect_macho responses were validated by the installed TypeScript MCP SDK against their respective advertised schemas. Target hash stayed unchanged and it was not executed. A separate full-profile Hopper mapping attempt failed before bridge readiness and reported cleanup incomplete; that failed call is not used as the mapping evidence. No general FAT, overlapping-range or concurrent-file guarantee follows.

<!-- gh-comment-id:6544256418 --> @morluto commented on GitHub (Oct 31, 2026): A universal Mach-O control extends the earlier thin-image source-byte handoff. `inspect_macho` selected arm64 at container offset 32768; its normalized `__TEXT.file_offset` is slice-relative 0. For handler VM address `0x100000784`, the slice-relative file offset is 1924 and the input-container offset is 34692. Local file reads at those offsets produced different bytes; only 34692 matched the selected thin image and a successful Hopper `address_to_file_offset` observation. Evidence locations were already container-relative. This confirms the existing documented rules for one universal fixture, rather than a changed `inspect_macho` numeric result. The full advertised output schema described the distinct bases. With explicitly selected compact output schemas, that description was absent from the advertised schema, tool description and observed result limitations; I opened #8302 for the discoverability gap, linked to the earlier resolved #1973. Both successful `inspect_macho` responses were validated by the installed TypeScript MCP SDK against their respective advertised schemas. Target hash stayed unchanged and it was not executed. A separate full-profile Hopper mapping attempt failed before bridge readiness and reported cleanup incomplete; that failed call is not used as the mapping evidence. No general FAT, overlapping-range or concurrent-file guarantee follows.
Author
Owner

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

Version-sensitive follow-up to the earlier Mach-O fixup control: upstream merged Kuna #1358, and I re-ran the same unexecuted fixture at pointer 0x100004008 with both release 1.986 and a local source build of upstream 8c74a944d74f8d3349755e05441a9628b4753111.

  • Direct 1.986 read --addr --bytes 8 --json: c004000000000000, matching the original file bytes in the earlier control.
  • Direct current source build, using the release's compiled SLEIGH tree: c004000001000000, matching this fixture's earlier Ghidra initialized-memory observation and observed applied relocation. Both commands returned eight bytes; neither result establishes a general byte-provenance rule.
  • A separate real REA main d54a4661d session using that source build successfully opened the fixture and decompiled _evaluate; binary_session identified Kuna version 0.1.0 with verified_release: false. REA still returned typed capability_unavailable for read_bytes. The source build is not a verified release or an upgrade recommendation.

The target SHA-256 remained unchanged and it was not executed. This is one Mach-O chained-fixup observation; other relocation types, virtual-only ranges, boundary clipping, partial reads and source/analysis byte semantics must be rechecked for any future version before admitting the existing REA contract. In particular, matching Ghidra on this pointer does not establish equivalent initialized-memory semantics in general.

<!-- gh-comment-id:6547861635 --> @morluto commented on GitHub (Oct 31, 2026): Version-sensitive follow-up to the earlier Mach-O fixup control: upstream merged [Kuna #1358](https://github.com/Noelo-Lab/kuna/pull/1358), and I re-ran the same unexecuted fixture at pointer `0x100004008` with both release 1.986 and a local source build of upstream `8c74a944d74f8d3349755e05441a9628b4753111`. - Direct 1.986 `read --addr --bytes 8 --json`: `c004000000000000`, matching the original file bytes in the earlier control. - Direct current source build, using the release's compiled SLEIGH tree: `c004000001000000`, matching this fixture's earlier Ghidra initialized-memory observation and observed applied relocation. Both commands returned eight bytes; neither result establishes a general byte-provenance rule. - A separate real REA main `d54a4661d` session using that source build successfully opened the fixture and decompiled `_evaluate`; `binary_session` identified Kuna version `0.1.0` with `verified_release: false`. REA still returned typed `capability_unavailable` for `read_bytes`. The source build is not a verified release or an upgrade recommendation. The target SHA-256 remained unchanged and it was not executed. This is one Mach-O chained-fixup observation; other relocation types, virtual-only ranges, boundary clipping, partial reads and source/analysis byte semantics must be rechecked for any future version before admitting the existing REA contract. In particular, matching Ghidra on this pointer does not establish equivalent initialized-memory semantics in general.
Author
Owner

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

Real Ghidra byte-read resource and recovery control, relevant when defining a bounded provider-neutral byte workflow:

  • On an owned ARM64 Mach-O with 3 MiB of initialized data, read_bytes for that range completed under REA d54a4661da20382ad8456564a08569fdd24131c5 / Ghidra 12.1.4, but the full MCP response exceeded an explicitly configured 10 MiB stdio budget. REA returned resource_constraint, said the analysis completed without truncation, and supplied the retained Evidence ID and export recovery.
  • A follow-up 16-byte read succeeded in the same session. get_evidence_bundle summary listed the complete large record; export recovered all 3,145,728 bytes, whose SHA-256 matched the source-owned data. The public rea read-bytes ... --format json command independently returned the same complete bytes and hash. A separate 1.5 MiB read fit this MCP budget. The executable was not executed or changed.
  • Source-derived, not a measured failure: the public and Ghidra input schemas admit lengths up to the safe-integer limit, while the bridge's readMemoryBytes accumulates mapped bytes and hex-encodes the result before MCP delivery checks its response budget. This observation does not establish actual OOM, a safe maximum, or a performance measurement.

If this broader byte workflow is designed, consider how caller-selected ranges, representation expansion, provider and transport bounds, retained Evidence and partial coverage interact before committing to a limit or pagination; a transport overflow is not evidence of a failed or truncated read. No resource-limit change is proposed from this single test. Target SHA-256 b63ee6b146d35792c46c62ecdb442fdcb6a63b08236bf463e026740c5487c7bb; data SHA-256 e8e84dbb009358317aa287d4ee8fe1099f6d9b58e84d6cd3c7de0c26f08b0f8e.

<!-- gh-comment-id:6548151199 --> @morluto commented on GitHub (Oct 31, 2026): Real Ghidra byte-read resource and recovery control, relevant when defining a bounded provider-neutral byte workflow: - On an owned ARM64 Mach-O with 3 MiB of initialized data, `read_bytes` for that range completed under REA `d54a4661da20382ad8456564a08569fdd24131c5` / Ghidra 12.1.4, but the full MCP response exceeded an explicitly configured 10 MiB stdio budget. REA returned `resource_constraint`, said the analysis completed without truncation, and supplied the retained Evidence ID and export recovery. - A follow-up 16-byte read succeeded in the same session. `get_evidence_bundle` summary listed the complete large record; export recovered all 3,145,728 bytes, whose SHA-256 matched the source-owned data. The public `rea read-bytes ... --format json` command independently returned the same complete bytes and hash. A separate 1.5 MiB read fit this MCP budget. The executable was not executed or changed. - Source-derived, not a measured failure: the public and Ghidra input schemas admit lengths up to the safe-integer limit, while the bridge's [`readMemoryBytes`](https://github.com/morluto/rea/blob/d54a4661da20382ad8456564a08569fdd24131c5/bridge/ghidra/ReaGhidraBridge.java#L738-L774) accumulates mapped bytes and hex-encodes the result before MCP delivery checks its response budget. This observation does not establish actual OOM, a safe maximum, or a performance measurement. If this broader byte workflow is designed, consider how caller-selected ranges, representation expansion, provider and transport bounds, retained Evidence and partial coverage interact before committing to a limit or pagination; a transport overflow is not evidence of a failed or truncated read. No resource-limit change is proposed from this single test. Target SHA-256 `b63ee6b146d35792c46c62ecdb442fdcb6a63b08236bf463e026740c5487c7bb`; data SHA-256 `e8e84dbb009358317aa287d4ee8fe1099f6d9b58e84d6cd3c7de0c26f08b0f8e`.
Author
Owner

@morluto commented on GitHub (Nov 1, 2026):

A chained import bind control on the owned arm64 Mach-O from Noelo-Lab/kuna#1376 adds a different case from the earlier rebase control. At GOT slot 0x4000, independent file bytes are 0000000000000080 and dyld_info declares a libSystem/_malloc bind.

  • Released Kuna 1.986 read --addr --bytes 8 --json returned the original encoding. A separate unverified source build of 8c74a944d74f8d3349755e05441a9628b4753111 returned eight zero bytes. Both reported truncated: false and no notes. This source build is not a verified release.
  • REA/Ghidra 12.1.4 read_bytes returned analyzed-memory bytes 00c0000000000000 and its existing limitation correctly says loader fixups are included. inspect_native_load_image separately retained the original bytes and the applied memory bytes, while independent Mach-O load-image verification remained unsupported. The 0xc000 value is Ghidra's synthetic external coordinate, not a runtime address.
  • An eight-byte read starting halfway through the slot at 0x4004 returned 0000008000000000 in release Kuna versus zeros from the source build and Ghidra. Ordinary function bytes at 0x1358 matched across these readers. At 0xc000, both Kuna builds refused an unmapped address; Ghidra returned zero of four requested bytes with complete: false for its uninitialized external block.

The source build's loader explicitly writes zero for chained binds; this is a source-derived explanation consistent with the observation, not runtime instrumentation. Upstream main e27d885452316848c5a8ad17ba25169c5caa3d2a contains the same zero-write line, but was not run. I opened Noelo-Lab/kuna#1379 to ask about exposing that provenance, not to assert the placeholder itself is wrong. REA/Kuna still refuses read_bytes, so none of the direct Kuna outputs is presented as an existing REA result.

This reinforces the design boundary: byte count and truncated: false alone do not distinguish original file bytes, provider-initialized bytes, or an unresolved import placeholder. Range intersections with fixups matter, not just a query starting exactly at a slot. The target SHA-256 remained 62da83d290c7b276d8708937930ef12f469f41782710fed439001c01c2747c76 and it was not executed. One fixture and host; no general loader, runtime or current-upstream-release claim.

<!-- gh-comment-id:6551802352 --> @morluto commented on GitHub (Nov 1, 2026): A chained **import bind** control on the owned arm64 Mach-O from Noelo-Lab/kuna#1376 adds a different case from the earlier rebase control. At GOT slot `0x4000`, independent file bytes are `0000000000000080` and `dyld_info` declares a `libSystem/_malloc` bind. - Released Kuna 1.986 `read --addr --bytes 8 --json` returned the original encoding. A separate unverified source build of `8c74a944d74f8d3349755e05441a9628b4753111` returned eight zero bytes. Both reported `truncated: false` and no notes. This source build is not a verified release. - REA/Ghidra 12.1.4 `read_bytes` returned analyzed-memory bytes `00c0000000000000` and its existing limitation correctly says loader fixups are included. `inspect_native_load_image` separately retained the original bytes and the applied memory bytes, while independent Mach-O load-image verification remained `unsupported`. The `0xc000` value is Ghidra's synthetic external coordinate, not a runtime address. - An eight-byte read starting halfway through the slot at `0x4004` returned `0000008000000000` in release Kuna versus zeros from the source build and Ghidra. Ordinary function bytes at `0x1358` matched across these readers. At `0xc000`, both Kuna builds refused an unmapped address; Ghidra returned zero of four requested bytes with `complete: false` for its uninitialized external block. The source build's loader explicitly writes zero for chained binds; this is a source-derived explanation consistent with the observation, not runtime instrumentation. Upstream main `e27d885452316848c5a8ad17ba25169c5caa3d2a` contains the same zero-write line, but was not run. I opened Noelo-Lab/kuna#1379 to ask about exposing that provenance, not to assert the placeholder itself is wrong. REA/Kuna still refuses `read_bytes`, so none of the direct Kuna outputs is presented as an existing REA result. This reinforces the design boundary: byte count and `truncated: false` alone do not distinguish original file bytes, provider-initialized bytes, or an unresolved import placeholder. Range intersections with fixups matter, not just a query starting exactly at a slot. The target SHA-256 remained `62da83d290c7b276d8708937930ef12f469f41782710fed439001c01c2747c76` and it was not executed. One fixture and host; no general loader, runtime or current-upstream-release claim.
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#4696
No description provided.