[GH-ISSUE #6518] [Bug] BEAM inspection uses the first repeated chunk while Erlang loads the last #3912

Closed
opened 2026-10-31 00:45:10 +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/6518

Before submitting

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

Area

Evidence, artifacts, or reconstruction

Observed behavior

Source-only finding: when a BEAM form contains repeated chunks with the same four-byte name, REA lists both chunks but silently uses only the first payload to decode atoms, exports, imports, lambdas, and Code-header facts. It reports complete coverage without identifying this selection rule.

Erlang's published loader instead overwrites each matching chunk slot as it scans, selecting the last occurrence. With two AtU8 tables and a common ExpT row, REA can therefore report an export name different from the name resolved by the loader's selected atom table. No runtime reproduction was performed.

Reproduction

Proposed, not run. Build this reduced structural inspection fixture with two valid atom-table payloads. The module name stays demo; atom 2 changes from first to last. Its sole export references atom 2.

from pathlib import Path
from struct import pack

def chunk(name, payload):
    return name + pack('>I', len(payload)) + payload + bytes((-len(payload)) % 4)

def atoms(function):
    names = [b'demo', function]
    return pack('>I', len(names)) + b''.join(bytes([len(n)]) + n for n in names)

body = b'BEAM' + b''.join([
    chunk(b'AtU8', atoms(b'first')),
    chunk(b'AtU8', atoms(b'last')),
    chunk(b'ExpT', pack('>IIII', 1, 2, 0, 1)),
])
Path('repeated-atoms.beam').write_bytes(b'FOR1' + pack('>I', len(body)) + body)

Proposed command:

rea inspect-erlang-beam repeated-atoms.beam --json

Source-derived prediction: chunks contains both AtU8 occurrences, exports[0].function is first, and coverage.status is complete. The loader's scan selects the second table, so the same export index resolves to last. This is a structural fixture without a Code chunk; it is not claimed to be a loadable module. A compiler-produced module with a decoy table prepended is a further proposed check, not an executed reproduction.

Expected behavior

Decoded table facts should follow the format owner's last-occurrence selection, or the result should explicitly identify repeated chunks as ambiguous rather than silently present first-occurrence facts as the selected module's complete interpretation.

REA promises resolved export/import names from its atom table. The published OTP 29 chunk scan assigns the matching slot on every occurrence, and then parses the selected atom/import/export chunks. There is no first-occurrence guard in that scan. This expectation comes from source, not an observed loaded module.

Impact and scope

A bounded evidence-integrity error: repeated atom tables can change resolved module/function names, and repeated table or Code chunks can change the decoded rows/header. REA retains the raw chunk inventory, but its normalized facts select a different occurrence from Erlang's loader without a limitation. Occurrence in real compiler/tool-generated artifacts and end-to-end loaded-module behavior are NOT_ENOUGH_INFO. Provisional P2. Inspecting chunk payloads separately might expose the disagreement, but a practical workaround that restores REA's normalized facts was not verified.

REA version

Source revision 49c9c9d4a14cc7d5509ec28a304dab48f85cb045 (main; package.json says 6.7.0). The parser file is absent from the supplied published-release commit 27dc42d10249f0410904a75c0997842eb03b931c (tag rea-agents-6.7.0). This report does not claim that the published package is affected.

Runtime and platform

NOT_ENOUGH_INFO: no REA or Erlang runtime was executed for this source-only finding; runtime/platform-specific behavior was not tested. No external provider was involved.

Target and artifact context

A synthetic FOR1/BEAM form containing two AtU8 chunks followed by one ExpT row. Every chunk and padding range fits, both atom tables use ordinary one-byte lengths, and export atom index 2 exists in both tables. This is independent of extended compact-length support.

Interface

CLI

Earliest failure stage

Native analysis or reconstruction

Relevant output or evidence

No runtime output is available. Direct source inspection established:

Evidence provenance: source-derived. Local verification: source inspection only. Reproduction completeness: proposed fixture and command supplied, not run. No claim of runtime loading or installed-package reproduction is made.

Additional context

Open and closed issue searches found no matching report. Related open #6503 concerns extended compact atom-length decoding; this report uses ordinary short atom names and concerns a separate chunk-framing rule.

Triage assessment

  • Impact level: P2
  • Assessment status: provisional
  • Impact basis: REA's explicit first-occurrence map disagrees with OTP's last-occurrence chunk selection and can produce different resolved export names for a bounded input class; no runtime reproduction or real-world prevalence was observed.
  • Workaround status: unverified
  • Workaround basis: Raw chunk occurrences remain inventoried, but independently decoding the loader-selected payload or rewriting a separate copy has not been demonstrated as a practical restoration of the normalized result.
Originally created by @coygeek on GitHub (Oct 23, 2026). Original GitHub issue: https://github.com/morluto/rea/issues/6518 ### 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 Evidence, artifacts, or reconstruction ### Observed behavior Source-only finding: when a BEAM form contains repeated chunks with the same four-byte name, REA lists both chunks but silently uses only the first payload to decode atoms, exports, imports, lambdas, and Code-header facts. It reports complete coverage without identifying this selection rule. Erlang's published loader instead overwrites each matching chunk slot as it scans, selecting the last occurrence. With two `AtU8` tables and a common `ExpT` row, REA can therefore report an export name different from the name resolved by the loader's selected atom table. No runtime reproduction was performed. ### Reproduction Proposed, not run. Build this reduced structural inspection fixture with two valid atom-table payloads. The module name stays `demo`; atom 2 changes from `first` to `last`. Its sole export references atom 2. ```python from pathlib import Path from struct import pack def chunk(name, payload): return name + pack('>I', len(payload)) + payload + bytes((-len(payload)) % 4) def atoms(function): names = [b'demo', function] return pack('>I', len(names)) + b''.join(bytes([len(n)]) + n for n in names) body = b'BEAM' + b''.join([ chunk(b'AtU8', atoms(b'first')), chunk(b'AtU8', atoms(b'last')), chunk(b'ExpT', pack('>IIII', 1, 2, 0, 1)), ]) Path('repeated-atoms.beam').write_bytes(b'FOR1' + pack('>I', len(body)) + body) ``` Proposed command: ```sh rea inspect-erlang-beam repeated-atoms.beam --json ``` Source-derived prediction: `chunks` contains both `AtU8` occurrences, `exports[0].function` is `first`, and `coverage.status` is `complete`. The loader's scan selects the second table, so the same export index resolves to `last`. This is a structural fixture without a Code chunk; it is not claimed to be a loadable module. A compiler-produced module with a decoy table prepended is a further proposed check, not an executed reproduction. ### Expected behavior Decoded table facts should follow the format owner's last-occurrence selection, or the result should explicitly identify repeated chunks as ambiguous rather than silently present first-occurrence facts as the selected module's complete interpretation. REA promises [resolved export/import names from its atom table](https://github.com/morluto/rea/blob/49c9c9d4a14cc7d5509ec28a304dab48f85cb045/docs/erlang-beam.md#L28-L36). The published OTP 29 [chunk scan assigns the matching slot on every occurrence](https://github.com/erlang/otp/blob/550d7b7898706c7822362c42e7b93120c1d1f29a/erts/emulator/beam/beam_file.c#L1447-L1469), and [then parses the selected atom/import/export chunks](https://github.com/erlang/otp/blob/550d7b7898706c7822362c42e7b93120c1d1f29a/erts/emulator/beam/beam_file.c#L1537-L1563). There is no first-occurrence guard in that scan. This expectation comes from source, not an observed loaded module. ### Impact and scope A bounded evidence-integrity error: repeated atom tables can change resolved module/function names, and repeated table or Code chunks can change the decoded rows/header. REA retains the raw chunk inventory, but its normalized facts select a different occurrence from Erlang's loader without a limitation. Occurrence in real compiler/tool-generated artifacts and end-to-end loaded-module behavior are `NOT_ENOUGH_INFO`. Provisional P2. Inspecting chunk payloads separately might expose the disagreement, but a practical workaround that restores REA's normalized facts was not verified. ### REA version Source revision `49c9c9d4a14cc7d5509ec28a304dab48f85cb045` (`main`; package.json says 6.7.0). The parser file is absent from the supplied published-release commit `27dc42d10249f0410904a75c0997842eb03b931c` (tag `rea-agents-6.7.0`). This report does not claim that the published package is affected. ### Runtime and platform `NOT_ENOUGH_INFO`: no REA or Erlang runtime was executed for this source-only finding; runtime/platform-specific behavior was not tested. No external provider was involved. ### Target and artifact context A synthetic `FOR1`/`BEAM` form containing two `AtU8` chunks followed by one `ExpT` row. Every chunk and padding range fits, both atom tables use ordinary one-byte lengths, and export atom index 2 exists in both tables. This is independent of extended compact-length support. ### Interface CLI ### Earliest failure stage Native analysis or reconstruction ### Relevant output or evidence No runtime output is available. Direct source inspection established: - [The inventory records each occurrence but retains only the first payload by name](https://github.com/morluto/rea/blob/49c9c9d4a14cc7d5509ec28a304dab48f85cb045/src/erlang/BeamParser.ts#L198-L221): ```ts if (!payloads.has(name)) payloads.set(name, { payload, offset: at }); ``` - [Export/import/lambda decoding and Code selection all reuse that map](https://github.com/morluto/rea/blob/49c9c9d4a14cc7d5509ec28a304dab48f85cb045/src/erlang/BeamParser.ts#L237-L272). Consequently the example's export index is resolved against `first` rather than `last`. - [Module and complete coverage are emitted without a duplicate-selection caveat](https://github.com/morluto/rea/blob/49c9c9d4a14cc7d5509ec28a304dab48f85cb045/src/erlang/BeamParser.ts#L283-L289). Evidence provenance: source-derived. Local verification: source inspection only. Reproduction completeness: proposed fixture and command supplied, not run. No claim of runtime loading or installed-package reproduction is made. ### Additional context Open and closed issue searches found no matching report. Related open [#6503](https://github.com/morluto/rea/issues/6503) concerns extended compact atom-length decoding; this report uses ordinary short atom names and concerns a separate chunk-framing rule. ## Triage assessment - Impact level: P2 - Assessment status: provisional - Impact basis: REA's explicit first-occurrence map disagrees with OTP's last-occurrence chunk selection and can produce different resolved export names for a bounded input class; no runtime reproduction or real-world prevalence was observed. - Workaround status: unverified - Workaround basis: Raw chunk occurrences remain inventoried, but independently decoding the loader-selected payload or rewriting a separate copy has not been demonstrated as a practical restoration of the normalized result.
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#3912
No description provided.