[GH-ISSUE #2536] fix(go): sectionless PIE loses build info from a later data segment #855

Closed
opened 2026-10-15 18:29:01 +00:00 by bsakaguchi · 1 comment
Owner

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

Summary

Inspecting a Linux/amd64 Go PIE returns no build information after its section-table header fields are cleared, even though its metadata bytes and load mappings remain intact. The same file with section headers returns its compiler version, module identity, and build settings. The metadata sits in a later writable data segment that the sectionless search does not inspect.

Runtime or environment

Source revision 8b181716ac, Node v26.10.0, npm 11.19.1, locked dependencies, and Go 1.27.1 on darwin/arm64 producing linux/amd64 fixtures. Verification exercised the source reader and production provider; it did not run CLI/MCP or execute the generated binary.

Steps to reproduce

  1. In a disposable fixture directory, create go.mod containing module example.test/sectionless and a go 1.27 directive. Create main.go with this complete program.
package main
func main() {}
  1. Build the fixture with the following command.
GOOS=linux GOARCH=amd64 CGO_ENABLED=0 GOMAXPROCS=2 go build -p 2 -trimpath -buildvcs=false -buildmode=pie -o app .
  1. Make a sectionless copy by clearing only the ELF64 section-table header fields. Leave the program headers and metadata bytes unchanged.
from pathlib import Path
original = Path("app").read_bytes()
sectionless = bytearray(original)
sectionless[40:48] = bytes(8)
sectionless[58:64] = bytes(6)
Path("app-sectionless").write_bytes(sectionless)
  1. Add a focused Vitest test beside GoBinaryImage.ts. Load both fixture files with readFileSync and call readGoBinaryImage on each. Require the original build_info to be non-null and the sectionless build_info to equal it. Assert equality of the byte range from original.build_info.header_offset through module_location.offset + module_location.bytes in both files.
  2. In that test, call new GoBinaryProvider().inspect with the absolute path of each fixture. Require both results to be ok and their build_info fields to match. Run npm run test:focused -- src/go/sectionless-pie.test.ts for the created test.

Expected behavior

Removing section-table header fields must not make present, valid build metadata disappear from a supported ELF image. The sectionless file should retain the original compiler string, module bytes, parsed module facts, and exact metadata offsets.

Actual behavior

The original 1998448-byte fixture returned inline build information with go_version go1.27.1 and header_offset 1290240. Its compiler string occupied offset 1290273 for 8 bytes, and its framed module string occupied offset 1290283 for 234 bytes. The sectionless copy returned build_info null from both the reader and successful production-provider inspection. The metadata-region equality assertion passed. A non-PIE build with the same section fields cleared retained its metadata.

Evidence

  • Expected source: docs/go-binaries.md, Supported boundary, supports ELF inline build metadata; tests/boundary/go/goBinaryImage.test.ts:60-67 checks sectionless writable-mapping discovery; scripts/verify-go-binary.mjs:119-125 uses this section-header transformation for supported inspection.
  • Failure source: src/go/GoElfImage.ts:126 and 216 select only the first writable non-executable load mapping when section lookup is unavailable; src/go/GoBinaryImage.ts:53-59 searches that one range; src/go/GoBinaryProvider.ts:137-163 propagates its absent result.
  • Evidence provenance: observed
  • Local verification: reproduced
  • Reproduction completeness: complete

The selected first writable mapping starts at file offset 1204224 and has 82592 bytes. The next writable mapping starts at 1290240 and has 29440 bytes; the build-info record starts at its first byte. Both have flags 6. The original record is outside the selected range and its bytes survive the header-field transformation. The fixture is below the documented resource bounds and both provider calls succeeded. No matching existing issue was found in read-only searches.

Restoration check

Repeat the paired-file test and require identical non-null build information, including compiler bytes, module bytes, parsed module fields, and source locations. Preserve the working non-PIE sectionless control and original PIE result. Metadata discovery must retain existing restrictions against unmapped, unaligned, executable, or overlay decoys.

Triage assessment

  • Impact level: P2
  • Assessment status: supported
  • Impact basis: A supported bounded inspection succeeds while omitting present compiler, module, and build-setting metadata for a sectionless PIE layout. The observed fixture contains no dependencies, so dependency loss was not measured.
  • Workaround status: unverified
  • Workaround basis: Retaining section headers avoids the failure for a producer-owned build, but an analyst given only the sectionless artifact may have no matching original. No result-preserving alternative for that artifact was verified.
Originally created by @coygeek on GitHub (Oct 13, 2026). Original GitHub issue: https://github.com/morluto/rea/issues/2536 ## Summary Inspecting a Linux/amd64 Go PIE returns no build information after its section-table header fields are cleared, even though its metadata bytes and load mappings remain intact. The same file with section headers returns its compiler version, module identity, and build settings. The metadata sits in a later writable data segment that the sectionless search does not inspect. ## Runtime or environment Source revision 8b181716aca2cb467be499cba226abfb5ae19141, Node v26.10.0, npm 11.19.1, locked dependencies, and Go 1.27.1 on darwin/arm64 producing linux/amd64 fixtures. Verification exercised the source reader and production provider; it did not run CLI/MCP or execute the generated binary. ## Steps to reproduce 1. In a disposable fixture directory, create go.mod containing module example.test/sectionless and a go 1.27 directive. Create main.go with this complete program. ```go package main func main() {} ``` 2. Build the fixture with the following command. ```bash GOOS=linux GOARCH=amd64 CGO_ENABLED=0 GOMAXPROCS=2 go build -p 2 -trimpath -buildvcs=false -buildmode=pie -o app . ``` 3. Make a sectionless copy by clearing only the ELF64 section-table header fields. Leave the program headers and metadata bytes unchanged. ```python from pathlib import Path original = Path("app").read_bytes() sectionless = bytearray(original) sectionless[40:48] = bytes(8) sectionless[58:64] = bytes(6) Path("app-sectionless").write_bytes(sectionless) ``` 4. Add a focused Vitest test beside GoBinaryImage.ts. Load both fixture files with readFileSync and call readGoBinaryImage on each. Require the original build_info to be non-null and the sectionless build_info to equal it. Assert equality of the byte range from original.build_info.header_offset through module_location.offset + module_location.bytes in both files. 5. In that test, call new GoBinaryProvider().inspect with the absolute path of each fixture. Require both results to be ok and their build_info fields to match. Run npm run test:focused -- src/go/sectionless-pie.test.ts for the created test. ## Expected behavior Removing section-table header fields must not make present, valid build metadata disappear from a supported ELF image. The sectionless file should retain the original compiler string, module bytes, parsed module facts, and exact metadata offsets. ## Actual behavior The original 1998448-byte fixture returned inline build information with go_version go1.27.1 and header_offset 1290240. Its compiler string occupied offset 1290273 for 8 bytes, and its framed module string occupied offset 1290283 for 234 bytes. The sectionless copy returned build_info null from both the reader and successful production-provider inspection. The metadata-region equality assertion passed. A non-PIE build with the same section fields cleared retained its metadata. ## Evidence - Expected source: docs/go-binaries.md, Supported boundary, supports ELF inline build metadata; tests/boundary/go/goBinaryImage.test.ts:60-67 checks sectionless writable-mapping discovery; scripts/verify-go-binary.mjs:119-125 uses this section-header transformation for supported inspection. - Failure source: src/go/GoElfImage.ts:126 and 216 select only the first writable non-executable load mapping when section lookup is unavailable; src/go/GoBinaryImage.ts:53-59 searches that one range; src/go/GoBinaryProvider.ts:137-163 propagates its absent result. - Evidence provenance: observed - Local verification: reproduced - Reproduction completeness: complete The selected first writable mapping starts at file offset 1204224 and has 82592 bytes. The next writable mapping starts at 1290240 and has 29440 bytes; the build-info record starts at its first byte. Both have flags 6. The original record is outside the selected range and its bytes survive the header-field transformation. The fixture is below the documented resource bounds and both provider calls succeeded. No matching existing issue was found in read-only searches. ## Restoration check Repeat the paired-file test and require identical non-null build information, including compiler bytes, module bytes, parsed module fields, and source locations. Preserve the working non-PIE sectionless control and original PIE result. Metadata discovery must retain existing restrictions against unmapped, unaligned, executable, or overlay decoys. ## Triage assessment - Impact level: P2 - Assessment status: supported - Impact basis: A supported bounded inspection succeeds while omitting present compiler, module, and build-setting metadata for a sectionless PIE layout. The observed fixture contains no dependencies, so dependency loss was not measured. - Workaround status: unverified - Workaround basis: Retaining section headers avoids the failure for a producer-owned build, but an analyst given only the sectionless artifact may have no matching original. No result-preserving alternative for that artifact was verified.
bsakaguchi 2026-10-15 18:29:01 +00:00
Author
Owner

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

Resolved on main by 6ec84d23d and 55dd21a5e. Go ELF discovery retains all eligible writable data mappings and searches each under the cumulative byte budget. Rechecked tests/boundary/go/goBinaryImage.test.ts on current main b8510ee00: 101 tests passed, including build info in a later writable mapping and non-data mapping controls.

<!-- gh-comment-id:6175166147 --> @morluto commented on GitHub (Oct 14, 2026): Resolved on main by 6ec84d23d and 55dd21a5e. Go ELF discovery retains all eligible writable data mappings and searches each under the cumulative byte budget. Rechecked tests/boundary/go/goBinaryImage.test.ts on current main b8510ee00: 101 tests passed, including build info in a later writable mapping and non-data mapping controls.
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#855
No description provided.