[GH-ISSUE #6577] [Bug] PE export inspection rejects a valid last ordinal when an unused EAT slot follows it #3965

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

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

Main-only finding. The production parseNativeExports boundary rejects an export directory with base 65,535 and two address-table slots, even when only slot zero is used. Its only export has ordinal 65,535; the trailing zero slot is unused. Removing that unused slot makes the same named export succeed.

Observed error: BinaryFormatFailure: PE export ordinal base and address-table size exceed 16-bit ordinals.

This was executed through the production parser using REA's PE fixture constructor. The public CLI/MCP adapters and Windows loader were not exercised.

Reproduction

From the audited checkout root, with its pinned Node toolchain and locked dependencies installed, run this exact reduced probe:

./node_modules/.bin/tsx --eval '
import { peExportImage } from "./tests/fixtures/binaryDiagnostics/nativeExports.ts";
import { parseNativeExports } from "./src/native/exports/NativeExportParser.ts";
for (const functions of [[{ rva: 0x1300 }], [{ rva: 0x1300 }, null]]) {
  const bytes = peExportImage({ordinalBase: 65535, functions, names: [{name: "Kept", index: 0}]});
  try {
    console.log(JSON.stringify(parseNativeExports(bytes, {path: "fixture.dll", sha256: "a".repeat(64), bytes: bytes.length}).exports));
  } catch (e) { console.log(e.name + ": " + e.message); }
}
'

The fixture is an in-memory PE32+ export-directory reduction; its artifact identity is a placeholder passed to the direct parser, not a claim that a real target was acquired. The same failure was observed in both the initial four-case probe and this reduced probe.

Expected behavior

Return the single Kept export at ordinal 65,535 in both cases. A zero address-table slot does not create another export.

Microsoft's PE format specification gives Ordinal Base and Address Table Entries four bytes each. Its export ordinal table definition defines the two-byte entries as unbiased table indexes. The used index here is zero and the resulting exported ordinal fits 16 bits. REA itself skips zero address-table slots rather than treating them as exports.

Impact and scope

A bounded PE export-directory variant makes the entire inventory unavailable, including its ordinary named export. The initial probe also rejected a named slot with base 65,536, which the format represents with a four-byte base; that broader compatibility question is not needed to reproduce the unused-slot failure.

No real-world frequency or Windows loading behavior was measured. NOT_ENOUGH_INFO: an alternative REA workflow or external-tool workaround has not been tested. Trimming the input table is not proposed as a workaround because it changes the selected artifact.

REA version

Current main: 46111dd091f2ae01baab9eb119b53b9bd309a05b (package metadata 6.7.0). The parser does not exist at published rea-agents-6.7.0 source revision 27dc42d10249f0410904a75c0997842eb03b931c; this report does not claim that release shipped it.

Runtime and platform

Node.js v24.22.1; Linux 7.2.8-9-omarchy x86_64. No native analysis provider or target process was used.

Target and artifact context

Synthetic PE32+ x86-64 export directory; one named export at EAT index zero; ordinal base 65,535; optional unused zero slot at index one.

Interface

Other

Earliest failure stage

Native analysis or reconstruction

Relevant output or evidence

Executed reduced probe output:

[{"name":"Kept","ordinal":65535,"address":"0x180001300","forwarder":null}]
BinaryFormatFailure: PE export ordinal base and address-table size exceed 16-bit ordinals

The source rejects the declared slot range before it reads the EAT: NativeExportParser.ts at the audited SHA. Later in the same parser, zero EAT slots are explicitly skipped.

The upstream fixture constructor writes the ordinal base, EAT, name and unbiased ordinal index used by the probe. The source references establish why the two fixture variants differ; the quoted error and success were executed observations.

Typed outcome or recovery expected

Successful export inventory containing Kept, with the unused slot omitted. A declared unused slot should not make the selected artifact malformed.

Analysis or suspected cause

Source-derived: ordinalBase + functionCount > 0x10000 treats every declared slot as an exported ordinal before examining whether it is zero. It also conflates the 16-bit name ordinal-table index with the biased ordinal base. The reduced case needs only the former correction; all used exports remain within the existing output schema.

Additional context

Open-issue title/body searches covered ordinal base, 16-bit ordinal, unused export, export ordinal, 65535 and 65536; none identified this report. Related open #6533 concerns ELF version names, not PE slot admission. The submitting agent additionally searched open and closed issues for the same behavior and found no matching report.

NOT_ENOUGH_INFO: no Windows loader reproduction or complete compiler-produced PE was tested. This report establishes the parser result on a reduced directory and the format-derived expected behavior.

Triage assessment

  • Impact level: P2
  • Assessment status: supported
  • Impact basis: A bounded, otherwise admitted export-directory variant causes complete loss of its valid export inventory. The used named export still has a 16-bit ordinal, and the control succeeds; no broader runtime impact is claimed.
  • Workaround status: unverified
  • Workaround basis: No alternative REA path or external-tool extraction was executed. Editing the source image would change its identity and is not a preservation-compatible workaround.
Originally created by @coygeek on GitHub (Oct 23, 2026). Original GitHub issue: https://github.com/morluto/rea/issues/6577 ### 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 **Main-only finding.** The production `parseNativeExports` boundary rejects an export directory with base 65,535 and two address-table slots, even when only slot zero is used. Its only export has ordinal 65,535; the trailing zero slot is unused. Removing that unused slot makes the same named export succeed. Observed error: `BinaryFormatFailure: PE export ordinal base and address-table size exceed 16-bit ordinals`. This was executed through the production parser using REA's PE fixture constructor. The public CLI/MCP adapters and Windows loader were not exercised. ### Reproduction From the audited checkout root, with its pinned Node toolchain and locked dependencies installed, run this exact reduced probe: ```sh ./node_modules/.bin/tsx --eval ' import { peExportImage } from "./tests/fixtures/binaryDiagnostics/nativeExports.ts"; import { parseNativeExports } from "./src/native/exports/NativeExportParser.ts"; for (const functions of [[{ rva: 0x1300 }], [{ rva: 0x1300 }, null]]) { const bytes = peExportImage({ordinalBase: 65535, functions, names: [{name: "Kept", index: 0}]}); try { console.log(JSON.stringify(parseNativeExports(bytes, {path: "fixture.dll", sha256: "a".repeat(64), bytes: bytes.length}).exports)); } catch (e) { console.log(e.name + ": " + e.message); } } ' ``` The fixture is an in-memory PE32+ export-directory reduction; its artifact identity is a placeholder passed to the direct parser, not a claim that a real target was acquired. The same failure was observed in both the initial four-case probe and this reduced probe. ### Expected behavior Return the single `Kept` export at ordinal 65,535 in both cases. A zero address-table slot does not create another export. [Microsoft's PE format specification](https://learn.microsoft.com/en-us/windows/win32/debug/pe-format#export-directory-table) gives `Ordinal Base` and `Address Table Entries` four bytes each. Its [export ordinal table definition](https://learn.microsoft.com/en-us/windows/win32/debug/pe-format#export-ordinal-table) defines the two-byte entries as **unbiased table indexes**. The used index here is zero and the resulting exported ordinal fits 16 bits. REA itself skips zero address-table slots rather than treating them as exports. ### Impact and scope A bounded PE export-directory variant makes the entire inventory unavailable, including its ordinary named export. The initial probe also rejected a named slot with base 65,536, which the format represents with a four-byte base; that broader compatibility question is not needed to reproduce the unused-slot failure. No real-world frequency or Windows loading behavior was measured. NOT_ENOUGH_INFO: an alternative REA workflow or external-tool workaround has not been tested. Trimming the input table is not proposed as a workaround because it changes the selected artifact. ### REA version Current main: `46111dd091f2ae01baab9eb119b53b9bd309a05b` (package metadata 6.7.0). The parser does not exist at published `rea-agents-6.7.0` source revision `27dc42d10249f0410904a75c0997842eb03b931c`; this report does not claim that release shipped it. ### Runtime and platform Node.js v24.22.1; Linux 7.2.8-9-omarchy x86_64. No native analysis provider or target process was used. ### Target and artifact context Synthetic PE32+ x86-64 export directory; one named export at EAT index zero; ordinal base 65,535; optional unused zero slot at index one. ### Interface Other ### Earliest failure stage Native analysis or reconstruction ### Relevant output or evidence Executed reduced probe output: ```text [{"name":"Kept","ordinal":65535,"address":"0x180001300","forwarder":null}] BinaryFormatFailure: PE export ordinal base and address-table size exceed 16-bit ordinals ``` The source rejects the declared slot range before it reads the EAT: [NativeExportParser.ts at the audited SHA](https://github.com/morluto/rea/blob/46111dd091f2ae01baab9eb119b53b9bd309a05b/src/native/exports/NativeExportParser.ts#L132-L140). Later in the same parser, [zero EAT slots are explicitly skipped](https://github.com/morluto/rea/blob/46111dd091f2ae01baab9eb119b53b9bd309a05b/src/native/exports/NativeExportParser.ts#L198-L207). The [upstream fixture constructor](https://github.com/morluto/rea/blob/46111dd091f2ae01baab9eb119b53b9bd309a05b/tests/fixtures/binaryDiagnostics/nativeExports.ts#L20) writes the ordinal base, EAT, name and unbiased ordinal index used by the probe. The source references establish why the two fixture variants differ; the quoted error and success were executed observations. ### Typed outcome or recovery expected Successful export inventory containing `Kept`, with the unused slot omitted. A declared unused slot should not make the selected artifact `malformed`. ### Analysis or suspected cause Source-derived: `ordinalBase + functionCount > 0x10000` treats every declared slot as an exported ordinal before examining whether it is zero. It also conflates the 16-bit name ordinal-table index with the biased ordinal base. The reduced case needs only the former correction; all used exports remain within the existing output schema. ### Additional context Open-issue title/body searches covered `ordinal base`, `16-bit ordinal`, `unused export`, `export ordinal`, `65535` and `65536`; none identified this report. Related open #6533 concerns ELF version names, not PE slot admission. The submitting agent additionally searched open and closed issues for the same behavior and found no matching report. NOT_ENOUGH_INFO: no Windows loader reproduction or complete compiler-produced PE was tested. This report establishes the parser result on a reduced directory and the format-derived expected behavior. ## Triage assessment - Impact level: P2 - Assessment status: supported - Impact basis: A bounded, otherwise admitted export-directory variant causes complete loss of its valid export inventory. The used named export still has a 16-bit ordinal, and the control succeeds; no broader runtime impact is claimed. - Workaround status: unverified - Workaround basis: No alternative REA path or external-tool extraction was executed. Editing the source image would change its identity and is not a preservation-compatible workaround.
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#3965
No description provided.