[GH-ISSUE #3398] [Bug] An unrelated .go.buildinfo section blocks Rust dependency inspection #1266

Closed
opened 2026-10-16 21:03:32 +00:00 by bsakaguchi · 0 comments
Owner

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

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

inspect-rust-binary rejects a structurally valid ELF containing a valid cargo-auditable dependency section when an unrelated, nonallocated data section is named .go.buildinfo. Renaming only that section to .extra makes the same dependency payload readable. Rust dependency inspection therefore depends on an unrelated section's Go-specific interpretation.

The .go.buildinfo input exits 1 with code: "invalid_request", category invalid_input, and an issue for path whose reason is invalid_format. The .extra control exits 0 and returns auditable_metadata with format_version: 0, package sample at version 1.0.0 with source local, root_package_index: 0, and dependency_edges: []. The compressed dependency payload and section headers are identical between inputs. The section-name change alone determines whether inspection succeeds. Both outcomes were reproduced through the public CLI.

Reproduction

  1. Start from a clean checkout and build the compiled runtime.
git clone https://github.com/morluto/rea.git rea
cd rea
git checkout 8dde6370d0809ad04cda3342d4e6f290f48aa063
npm ci
npm run build:cached
  1. Run the following complete fixture builder and both CLI inspections from the repository root. The builder uses only Node built-ins and creates an ELF64 image with a zlib-compressed dependency payload describing the local sample package and a separate nonallocated SHT_PROGBITS section. A nonallocated section may have virtual address zero. The control changes only the additional section's name in the section-name string table.
work_dir="$(mktemp -d)"
trap 'rm -rf "$work_dir"' EXIT
cat > "$work_dir/create.mjs" <<'EOF'
import { writeFileSync } from 'node:fs';
import { deflateSync } from 'node:zlib';
import { dirname, join } from 'node:path';
import { fileURLToPath } from 'node:url';
const out = process.argv[2] ?? dirname(fileURLToPath(import.meta.url));
const bytes = Buffer.alloc(1024);
bytes.set([0x7f, 0x45, 0x4c, 0x46, 2, 1, 1]);
const u16 = (at, value) => bytes.writeUInt16LE(value, at);
const u32 = (at, value) => bytes.writeUInt32LE(value, at);
const u64 = (at, value) => bytes.writeBigUInt64LE(BigInt(value), at);
u16(16, 2); u16(18, 62); u32(20, 1); u64(32, 64); u64(40, 128);
u16(52, 64); u16(54, 56); u16(56, 1); u16(58, 64); u16(60, 4); u16(62, 1);
u32(64, 1); u32(68, 4); u64(72, 0); u64(80, 0x400000);
u64(96, bytes.length); u64(104, bytes.length); u64(112, 1);
const names = Buffer.from('\0.shstrtab\0.dep-v0\0.go.buildinfo\0');
names.copy(bytes, 512);
u32(192, 1); u32(196, 3); u64(216, 512); u64(224, names.length); u64(240, 1);
const payload = deflateSync(Buffer.from(JSON.stringify({format:0,packages:[{name:'sample',version:'1.0.0',source:'local',root:true}]})));
payload.copy(bytes, 640);
u32(256, 11); u32(260, 1); u64(280, 640); u64(288, payload.length); u64(304, 1);
// A nonallocated SHT_PROGBITS section may have no virtual address.
u32(320, 19); u32(324, 1); u64(344, 800); u64(352, 16); u64(368, 1);
Buffer.from('additional data\0').copy(bytes, 800);
writeFileSync(join(out, 'rust-with-go-section.elf'), bytes);
const control = Buffer.from(bytes);
control.write('.extra\0', 512 + 19, 'latin1');
writeFileSync(join(out, 'rust-with-extra-section.elf'), control);
EOF
node "$work_dir/create.mjs" "$work_dir"
node scripts/rea.mjs inspect-rust-binary "$work_dir/rust-with-go-section.elf" --json
node scripts/rea.mjs inspect-rust-binary "$work_dir/rust-with-extra-section.elf" --json
  1. Observe that the first inspection exits 1 with the format error below, while the second exits 0 with the dependency metadata described above.

Expected behavior

Both inputs should return the embedded sample package at version 1.0.0, with source local, root index 0, and no dependency edges. An unrelated nonallocated section does not make the ELF container or the cargo-auditable payload malformed. Rust inspection should not require that section to satisfy Go build-info virtual-address mapping rules.

docs/rust-binaries.md:24 documents the dependency-section result, and docs/rust-binaries.md:66 supports ELF containers. src/rust/RustBinaryProvider.ts:94 states that only dependency sections and read-only compiler-path strings are inspected.

Impact and scope

P2, supported. The documented Rust metadata operation rejects the reproduced valid ELF input because of an unrelated section. The result is bounded to this additional-section condition; occurrence in real compiler-produced binaries was not measured.

Workaround status is unverified. A different ELF/dependency reader might recover the original metadata, but none was exercised. Renaming the section in a copy produces the passing control while changing the artifact bytes and digest, so it does not preserve the exact selected artifact's evidence identity.

REA version

6.4.0 (main at 8dde6370d0)

Runtime and platform

Node.js v26.10.0 on macOS 27.0.1, using the compiled checkout runtime. Host architecture was not recorded.

Target and artifact context

A synthetic 1024-byte ELF64 little-endian x86-64 executable image contains a valid zlib-compressed .dep-v0 cargo-auditable dependency payload and a separate nonallocated SHT_PROGBITS section holding additional data. The extra section has virtual address zero. The paired control changes only .go.buildinfo to .extra in the section-name string table; the compressed dependency payload and section headers remain identical.

Interface

CLI

Earliest failure stage

Native analysis or reconstruction

Relevant output or evidence

Virtual address or string range is not file mapped

The diagnostic comes from the .go.buildinfo input's observed invalid_request / invalid_input response with path reason invalid_format; its exit code is 1. The .extra control exits 0 and reports format_version: 0, the sample package at 1.0.0 with source local, root_package_index: 0, and dependency_edges: [].

Typed outcome or recovery expected

Inspect both generated inputs without renaming or deleting any section. Both must return the same dependency metadata and preserve their respective original file identities. The .extra input is the discriminating control. Malformed ELF container ranges and malformed dependency payloads must remain failures. This restoration concerns the Rust inspection route and does not require changing Go build-info admission.

Analysis or suspected cause

Source-derived reasoning: src/rust/RustBinaryImage.ts:51 reaches the shared container reader. src/go/GoElfImage.ts:174 through src/go/GoElfImage.ts:215 applies Go-specific section discovery and mapping validation before Rust dependency decoding. The reproduced name-only control is consistent with Rust container reading admitting an unrelated section through those Go-specific rules.

Additional context

Related open issue #2536 concerns Go inspection omitting build information from a later writable mapping in a sectionless PIE; this report concerns Rust inspection failing because an unrelated section triggers Go-specific mapping validation.

Related closed-as-completed issue #2167 concerns Go inspection rejecting Mach-O debug segments because of segment-size validation; this report concerns ELF section-name-dependent rejection on the Rust inspection route.

Originally created by @coygeek on GitHub (Oct 14, 2026). Original GitHub issue: https://github.com/morluto/rea/issues/3398 ### 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 `inspect-rust-binary` rejects a structurally valid ELF containing a valid cargo-auditable dependency section when an unrelated, nonallocated data section is named `.go.buildinfo`. Renaming only that section to `.extra` makes the same dependency payload readable. Rust dependency inspection therefore depends on an unrelated section's Go-specific interpretation. The `.go.buildinfo` input exits 1 with `code: "invalid_request"`, category `invalid_input`, and an issue for `path` whose reason is `invalid_format`. The `.extra` control exits 0 and returns `auditable_metadata` with `format_version: 0`, package `sample` at version `1.0.0` with source `local`, `root_package_index: 0`, and `dependency_edges: []`. The compressed dependency payload and section headers are identical between inputs. The section-name change alone determines whether inspection succeeds. Both outcomes were reproduced through the public CLI. ### Reproduction 1. Start from a clean checkout and build the compiled runtime. ```sh git clone https://github.com/morluto/rea.git rea cd rea git checkout 8dde6370d0809ad04cda3342d4e6f290f48aa063 npm ci npm run build:cached ``` 2. Run the following complete fixture builder and both CLI inspections from the repository root. The builder uses only Node built-ins and creates an ELF64 image with a zlib-compressed dependency payload describing the local `sample` package and a separate nonallocated `SHT_PROGBITS` section. A nonallocated section may have virtual address zero. The control changes only the additional section's name in the section-name string table. ```sh work_dir="$(mktemp -d)" trap 'rm -rf "$work_dir"' EXIT cat > "$work_dir/create.mjs" <<'EOF' import { writeFileSync } from 'node:fs'; import { deflateSync } from 'node:zlib'; import { dirname, join } from 'node:path'; import { fileURLToPath } from 'node:url'; const out = process.argv[2] ?? dirname(fileURLToPath(import.meta.url)); const bytes = Buffer.alloc(1024); bytes.set([0x7f, 0x45, 0x4c, 0x46, 2, 1, 1]); const u16 = (at, value) => bytes.writeUInt16LE(value, at); const u32 = (at, value) => bytes.writeUInt32LE(value, at); const u64 = (at, value) => bytes.writeBigUInt64LE(BigInt(value), at); u16(16, 2); u16(18, 62); u32(20, 1); u64(32, 64); u64(40, 128); u16(52, 64); u16(54, 56); u16(56, 1); u16(58, 64); u16(60, 4); u16(62, 1); u32(64, 1); u32(68, 4); u64(72, 0); u64(80, 0x400000); u64(96, bytes.length); u64(104, bytes.length); u64(112, 1); const names = Buffer.from('\0.shstrtab\0.dep-v0\0.go.buildinfo\0'); names.copy(bytes, 512); u32(192, 1); u32(196, 3); u64(216, 512); u64(224, names.length); u64(240, 1); const payload = deflateSync(Buffer.from(JSON.stringify({format:0,packages:[{name:'sample',version:'1.0.0',source:'local',root:true}]}))); payload.copy(bytes, 640); u32(256, 11); u32(260, 1); u64(280, 640); u64(288, payload.length); u64(304, 1); // A nonallocated SHT_PROGBITS section may have no virtual address. u32(320, 19); u32(324, 1); u64(344, 800); u64(352, 16); u64(368, 1); Buffer.from('additional data\0').copy(bytes, 800); writeFileSync(join(out, 'rust-with-go-section.elf'), bytes); const control = Buffer.from(bytes); control.write('.extra\0', 512 + 19, 'latin1'); writeFileSync(join(out, 'rust-with-extra-section.elf'), control); EOF node "$work_dir/create.mjs" "$work_dir" node scripts/rea.mjs inspect-rust-binary "$work_dir/rust-with-go-section.elf" --json node scripts/rea.mjs inspect-rust-binary "$work_dir/rust-with-extra-section.elf" --json ``` 3. Observe that the first inspection exits 1 with the format error below, while the second exits 0 with the dependency metadata described above. ### Expected behavior Both inputs should return the embedded `sample` package at version `1.0.0`, with source `local`, root index 0, and no dependency edges. An unrelated nonallocated section does not make the ELF container or the cargo-auditable payload malformed. Rust inspection should not require that section to satisfy Go build-info virtual-address mapping rules. `docs/rust-binaries.md:24` documents the dependency-section result, and `docs/rust-binaries.md:66` supports ELF containers. `src/rust/RustBinaryProvider.ts:94` states that only dependency sections and read-only compiler-path strings are inspected. ### Impact and scope P2, supported. The documented Rust metadata operation rejects the reproduced valid ELF input because of an unrelated section. The result is bounded to this additional-section condition; occurrence in real compiler-produced binaries was not measured. Workaround status is unverified. A different ELF/dependency reader might recover the original metadata, but none was exercised. Renaming the section in a copy produces the passing control while changing the artifact bytes and digest, so it does not preserve the exact selected artifact's evidence identity. ### REA version 6.4.0 (main at 8dde6370d0809ad04cda3342d4e6f290f48aa063) ### Runtime and platform Node.js v26.10.0 on macOS 27.0.1, using the compiled checkout runtime. Host architecture was not recorded. ### Target and artifact context A synthetic 1024-byte ELF64 little-endian x86-64 executable image contains a valid zlib-compressed `.dep-v0` cargo-auditable dependency payload and a separate nonallocated `SHT_PROGBITS` section holding `additional data`. The extra section has virtual address zero. The paired control changes only `.go.buildinfo` to `.extra` in the section-name string table; the compressed dependency payload and section headers remain identical. ### Interface CLI ### Earliest failure stage Native analysis or reconstruction ### Relevant output or evidence ```text Virtual address or string range is not file mapped ``` The diagnostic comes from the `.go.buildinfo` input's observed `invalid_request` / `invalid_input` response with `path` reason `invalid_format`; its exit code is 1. The `.extra` control exits 0 and reports `format_version: 0`, the `sample` package at `1.0.0` with source `local`, `root_package_index: 0`, and `dependency_edges: []`. ### Typed outcome or recovery expected Inspect both generated inputs without renaming or deleting any section. Both must return the same dependency metadata and preserve their respective original file identities. The `.extra` input is the discriminating control. Malformed ELF container ranges and malformed dependency payloads must remain failures. This restoration concerns the Rust inspection route and does not require changing Go build-info admission. ### Analysis or suspected cause Source-derived reasoning: `src/rust/RustBinaryImage.ts:51` reaches the shared container reader. `src/go/GoElfImage.ts:174` through `src/go/GoElfImage.ts:215` applies Go-specific section discovery and mapping validation before Rust dependency decoding. The reproduced name-only control is consistent with Rust container reading admitting an unrelated section through those Go-specific rules. ### Additional context Related open issue [#2536](https://github.com/morluto/rea/issues/2536) concerns Go inspection omitting build information from a later writable mapping in a sectionless PIE; this report concerns Rust inspection failing because an unrelated section triggers Go-specific mapping validation. Related closed-as-completed issue [#2167](https://github.com/morluto/rea/issues/2167) concerns Go inspection rejecting Mach-O debug segments because of segment-size validation; this report concerns ELF section-name-dependent rejection on the Rust inspection route.
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#1266
No description provided.