[GH-ISSUE #6590] [Bug] Go inspection rejects compiler-built NaCl amd64p32 binaries because ELF class differs from pointer width #3977

Closed
opened 2026-10-31 00:45:44 +00:00 by bsakaguchi · 0 comments
Owner

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

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

The public inspect-go-binary command rejects an unmodified Go 1.13.15 GOOS=nacl GOARCH=amd64p32 executable with invalid_request, issue reason invalid_format, and Go build-info pointer width disagrees with the image. The artifact is ELF64 with four-byte Go pointers, a valid combination for this historical target. Go 1.27.3's go version -m reads its compiler and module metadata successfully. A NaCl/386 control built from identical source with the same legacy compiler succeeds in both readers.

Reproduction

Run with the REA checkout and runtime listed below. Set GO113 to an absolute path to the official Go 1.13.15 compiler executable and REA_CHECKOUT to the REA checkout. The legacy compiler was unpacked into a disposable directory for this investigation; no installed tool or configuration was replaced. Run the following from an empty disposable directory, with Node 24.22.1 and Go 1.27.3 on PATH:

mkdir legacy-fixture
cat > legacy-fixture/go.mod <<'EOF'
module example.test/legacy

go 1.13
EOF
cat > legacy-fixture/main.go <<'EOF'
package main
func main() {}
EOF
(
  cd legacy-fixture
  GO111MODULE=on CGO_ENABLED=0 GOOS=nacl GOARCH=amd64p32 "$GO113" build -trimpath -o ../nacl-amd64p32.nexe .
  GO111MODULE=on CGO_ENABLED=0 GOOS=nacl GOARCH=386 "$GO113" build -trimpath -o ../nacl-386.nexe .
)
node "$REA_CHECKOUT/scripts/rea.mjs" inspect-go-binary "$PWD/nacl-amd64p32.nexe" --json
go version -m nacl-amd64p32.nexe
node "$REA_CHECKOUT/scripts/rea.mjs" inspect-go-binary "$PWD/nacl-386.nexe" --json
go version -m nacl-386.nexe

These compiler invocations and public inspections were executed once per target, through a subprocess harness using the same commands and arguments. The harness additionally isolated Go caches, temporary output and module lookup, and set GOMAXPROCS=2; these are fixture-production settings, not a REA configuration. Both builds exited 0. Neither target executable was run.

Expected behavior

Decode the historical pointer record using its recorded pointer size and return compiler/module metadata with exact source ranges, without treating a valid pointer width that differs from ELF class as malformed.

The supported boundary explicitly covers ELF images with historical pointer encoding. The observations contract describes compiler/module facts and their source ranges. The successful compiler build and independent Go reader establish that this input contains valid historical Go build metadata.

Impact and scope

This blocks REA's build-metadata inspection for the demonstrated NaCl/amd64p32 executable. The failure reproduced in the one tested artifact; the NaCl/386 control succeeded. This establishes a bounded historical-format compatibility defect, not a failure of current ordinary Go targets. No data loss, target execution, or security consequence was observed. For compiler/module identification, go version -m is a demonstrated read-only workaround, but it does not return REA Evidence or its source ranges. Other old compilers and targets were not tested.

REA version

Source checkout 0154141304028645d767bad32a1f7568b5d4815b, package version 6.7.0, using its built dist runtime through scripts/rea.mjs. This is main-source verification. The latest published release supplied for this investigation is rea-agents-6.7.0 at 27dc42d10249f0410904a75c0997842eb03b931c; that release revision was not tested.

Runtime and platform

Node.js v24.22.1, Linux x86_64, kernel 7.2.8-9-omarchy. Fixture producer: go version go1.13.15 linux/amd64. Independent metadata reader: go version go1.27.3 linux/amd64. No Hopper, Ghidra, or IDA provider was used.

Target and artifact context

Self-owned minimal module example.test/legacy, with only package main and an empty main function. Failing target: NaCl/amd64p32, ELF64 little-endian, e_machine=62, 1,046,664 bytes. The build-info record starts at file offset 917504, records pointer size 4 and flags 0, and uses historical pointer encoding. Control: NaCl/386, ELF32 little-endian, e_machine=3, pointer size 4 and flags 0.

Interface

CLI

Earliest failure stage

Native analysis or reconstruction

Relevant output or evidence

Evidence provenance: observed. Local verification: reproduced. Reproduction completeness: complete. The public CLI and Go oracle were executed on compiler-built artifacts; this is not a synthetic-header or source-only reproduction.

Fixture                 ELF class  Go pointer bytes  REA exit  go version -m exit
nacl-amd64p32.nexe       ELF64      4                 1         0
nacl-386.nexe            ELF32      4                 0         0

REA failing issue reason: invalid_format
REA failing message suffix: Go build-info pointer width disagrees with the image
REA control: encoding=pointer, go_version=go1.13.15, module.path=example.test/legacy, module.complete=true

Independent reader output for the failing artifact:

nacl-amd64p32.nexe: go1.13.15
        path    example.test/legacy
        mod     example.test/legacy    (devel)

The last block preserves the meaningful fields from the tab-separated go version -m output; columns are spaced for readability.

Typed outcome or recovery expected

Successful inspect_go_binary Evidence for the valid artifact, with compiler string go1.13.15, package/main-module identity example.test/legacy, and original file-backed source ranges. Advising the caller to correct the path or request does not recover this case because the selected artifact and arguments are valid.

Analysis or suspected cause

Source-derived explanation: the pointer branch rejects any ptrSize * 8 !== bits, conflating Go pointer size with native container class. Pointer-backed string decoding also selects its string-header word width from container bits. Both belong to this same pointer-width compatibility failure; simply removing the initial rejection is not evidence that decoding has been restored.

Additional context

A restoration check would read both compiler-built artifacts successfully, preserve the ELF64 identity of the amd64p32 target, and recover its compiler/module strings using their actual four-byte string headers and correct source ranges. This is a proposed repair check; no repaired implementation was tested. The source findings and all runtime evidence above were captured before this report was drafted.

Triage assessment

  • Impact level: P2
  • Assessment status: supported
  • Impact basis: Public CLI inspection rejects a valid, unmodified compiler-built historical target while the independent Go reader extracts its metadata; the demonstrated compatibility failure is bounded to NaCl/amd64p32.
  • Workaround status: available
  • Workaround basis: Go 1.27.3's go version -m nacl-amd64p32.nexe exited 0 and printed the compiler, package and main-module identity for the exact rejected artifact. This recovers those metadata facts outside REA, not REA Evidence or exact source ranges.
Originally created by @coygeek on GitHub (Oct 23, 2026). Original GitHub issue: https://github.com/morluto/rea/issues/6590 ## 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 The public `inspect-go-binary` command rejects an unmodified Go 1.13.15 `GOOS=nacl GOARCH=amd64p32` executable with `invalid_request`, issue reason `invalid_format`, and `Go build-info pointer width disagrees with the image`. The artifact is ELF64 with four-byte Go pointers, a valid combination for this historical target. Go 1.27.3's `go version -m` reads its compiler and module metadata successfully. A NaCl/386 control built from identical source with the same legacy compiler succeeds in both readers. ## Reproduction Run with the REA checkout and runtime listed below. Set `GO113` to an absolute path to the official Go 1.13.15 compiler executable and `REA_CHECKOUT` to the REA checkout. The legacy compiler was unpacked into a disposable directory for this investigation; no installed tool or configuration was replaced. Run the following from an empty disposable directory, with Node 24.22.1 and Go 1.27.3 on PATH: ```sh mkdir legacy-fixture cat > legacy-fixture/go.mod <<'EOF' module example.test/legacy go 1.13 EOF cat > legacy-fixture/main.go <<'EOF' package main func main() {} EOF ( cd legacy-fixture GO111MODULE=on CGO_ENABLED=0 GOOS=nacl GOARCH=amd64p32 "$GO113" build -trimpath -o ../nacl-amd64p32.nexe . GO111MODULE=on CGO_ENABLED=0 GOOS=nacl GOARCH=386 "$GO113" build -trimpath -o ../nacl-386.nexe . ) node "$REA_CHECKOUT/scripts/rea.mjs" inspect-go-binary "$PWD/nacl-amd64p32.nexe" --json go version -m nacl-amd64p32.nexe node "$REA_CHECKOUT/scripts/rea.mjs" inspect-go-binary "$PWD/nacl-386.nexe" --json go version -m nacl-386.nexe ``` These compiler invocations and public inspections were executed once per target, through a subprocess harness using the same commands and arguments. The harness additionally isolated Go caches, temporary output and module lookup, and set `GOMAXPROCS=2`; these are fixture-production settings, not a REA configuration. Both builds exited 0. Neither target executable was run. ## Expected behavior Decode the historical pointer record using its recorded pointer size and return compiler/module metadata with exact source ranges, without treating a valid pointer width that differs from ELF class as malformed. The [supported boundary](https://github.com/morluto/rea/blob/0154141304028645d767bad32a1f7568b5d4815b/docs/go-binaries.md#L110-L116) explicitly covers ELF images with historical pointer encoding. The [observations contract](https://github.com/morluto/rea/blob/0154141304028645d767bad32a1f7568b5d4815b/docs/go-binaries.md#L74-L93) describes compiler/module facts and their source ranges. The successful compiler build and independent Go reader establish that this input contains valid historical Go build metadata. ## Impact and scope This blocks REA's build-metadata inspection for the demonstrated NaCl/amd64p32 executable. The failure reproduced in the one tested artifact; the NaCl/386 control succeeded. This establishes a bounded historical-format compatibility defect, not a failure of current ordinary Go targets. No data loss, target execution, or security consequence was observed. For compiler/module identification, `go version -m` is a demonstrated read-only workaround, but it does not return REA Evidence or its source ranges. Other old compilers and targets were not tested. ## REA version Source checkout `0154141304028645d767bad32a1f7568b5d4815b`, package version `6.7.0`, using its built `dist` runtime through `scripts/rea.mjs`. This is main-source verification. The latest published release supplied for this investigation is `rea-agents-6.7.0` at `27dc42d10249f0410904a75c0997842eb03b931c`; that release revision was not tested. ## Runtime and platform Node.js `v24.22.1`, Linux x86_64, kernel `7.2.8-9-omarchy`. Fixture producer: `go version go1.13.15 linux/amd64`. Independent metadata reader: `go version go1.27.3 linux/amd64`. No Hopper, Ghidra, or IDA provider was used. ## Target and artifact context Self-owned minimal module `example.test/legacy`, with only `package main` and an empty `main` function. Failing target: NaCl/amd64p32, ELF64 little-endian, `e_machine=62`, 1,046,664 bytes. The build-info record starts at file offset 917504, records pointer size 4 and flags 0, and uses historical pointer encoding. Control: NaCl/386, ELF32 little-endian, `e_machine=3`, pointer size 4 and flags 0. ## Interface CLI ## Earliest failure stage Native analysis or reconstruction ## Relevant output or evidence Evidence provenance: observed. Local verification: reproduced. Reproduction completeness: complete. The public CLI and Go oracle were executed on compiler-built artifacts; this is not a synthetic-header or source-only reproduction. ```text Fixture ELF class Go pointer bytes REA exit go version -m exit nacl-amd64p32.nexe ELF64 4 1 0 nacl-386.nexe ELF32 4 0 0 REA failing issue reason: invalid_format REA failing message suffix: Go build-info pointer width disagrees with the image REA control: encoding=pointer, go_version=go1.13.15, module.path=example.test/legacy, module.complete=true ``` Independent reader output for the failing artifact: ```text nacl-amd64p32.nexe: go1.13.15 path example.test/legacy mod example.test/legacy (devel) ``` The last block preserves the meaningful fields from the tab-separated `go version -m` output; columns are spaced for readability. ## Typed outcome or recovery expected Successful `inspect_go_binary` Evidence for the valid artifact, with compiler string `go1.13.15`, package/main-module identity `example.test/legacy`, and original file-backed source ranges. Advising the caller to correct the path or request does not recover this case because the selected artifact and arguments are valid. ## Analysis or suspected cause Source-derived explanation: [the pointer branch](https://github.com/morluto/rea/blob/0154141304028645d767bad32a1f7568b5d4815b/src/go/GoBinaryImage.ts#L115-L137) rejects any `ptrSize * 8 !== bits`, conflating Go pointer size with native container class. [Pointer-backed string decoding](https://github.com/morluto/rea/blob/0154141304028645d767bad32a1f7568b5d4815b/src/go/GoBinaryImage.ts#L266-L275) also selects its string-header word width from container `bits`. Both belong to this same pointer-width compatibility failure; simply removing the initial rejection is not evidence that decoding has been restored. ## Additional context A restoration check would read both compiler-built artifacts successfully, preserve the ELF64 identity of the amd64p32 target, and recover its compiler/module strings using their actual four-byte string headers and correct source ranges. This is a proposed repair check; no repaired implementation was tested. The source findings and all runtime evidence above were captured before this report was drafted. ## Triage assessment - Impact level: P2 - Assessment status: supported - Impact basis: Public CLI inspection rejects a valid, unmodified compiler-built historical target while the independent Go reader extracts its metadata; the demonstrated compatibility failure is bounded to NaCl/amd64p32. - Workaround status: available - Workaround basis: Go 1.27.3's `go version -m nacl-amd64p32.nexe` exited 0 and printed the compiler, package and main-module identity for the exact rejected artifact. This recovers those metadata facts outside REA, not REA Evidence or exact source ranges.
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#3977
No description provided.