[GH-ISSUE #6533] [Bug] inspect-native-exports loses known versions of copy-relocated ELF symbols #3924

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

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

Source-only REA finding, corroborated by reading a real ELF with GNU readelf; REA runtime was not run.

inspect_native_exports resolves ELF symbol-version indices only through SHT_GNU_VERDEF. A normal executable's copy-relocated, defined dynamic symbols can instead name versions in SHT_GNU_VERNEED. The parser retains those symbols but produces version.name: null, a version_unresolved issue, and partial coverage despite the version name being available in the same input.

For the inspected /usr/bin/ls, readelf reports defined stdout@GLIBC_2.2.5, version index 3, at 0x28268, and a corresponding R_X86_64_COPY relocation. Its version-needs table maps index 3 to GLIBC_2.2.5; it has no version-definitions table. Following the REA source predicts a null version name for this row. That REA result is a derivation, not captured output.

Reproduction

Actually ran on Linux x64:

readelf --dyn-syms --wide /usr/bin/ls
readelf --version-info --wide /usr/bin/ls
readelf --relocs --wide /usr/bin/ls | rg 'COPY|stdout|stderr|optarg|optind'

These establish the valid input facts shown below. Each inspection was run once; no REA execution was performed.

Proposed, unexecuted public reproduction on an equivalent glibc executable containing these tables:

rea inspect-native-exports /usr/bin/ls

Inspect the stdout export's version name and coverage.status. The equivalent unexecuted MCP call is inspect_native_exports with {"path":"/usr/bin/ls"}.

Expected behavior

Retain GLIBC_2.2.5 as the version name of the defined stdout symbol by resolving its version-needs entry. A readable version name in the artifact should not become an unresolved-version issue merely because the symbol is copy-relocated.

The public contract promises defined default/protected global and weak dynamic symbols with their symbol versions. Input admission explicitly accepts executables as well as libraries.

Impact and scope

Provisional P2: a bounded metadata-loss defect. Source-derived scope is defined, visible global/weak/unique dynamic symbols whose version index is supplied by the ELF version-needs table rather than the version-definitions table. The export name and value remain available; its known version name is lost and coverage becomes partial.

GNU readelf is an available read-only workaround: the executed commands recovered the missing name from this input. NOT_ENOUGH_INFO: REA runtime output, frequency across distributions, and other architectures were not measured. This report makes no claim of binary mutation or runtime linking failure.

REA version

rea-agents 6.7.0; inspected source revision 28a33cae8f.

Runtime and platform

Linux x64 source inspection; GNU readelf (GNU Binutils) 2.47. No REA runtime execution or external analysis engine was run. NOT_ENOUGH_INFO: no Node.js runtime was used for this finding.

Target and artifact context

System ls: stripped, dynamically linked ELF64 little-endian x86-64 PIE, GNU build ID d6281af586ce362d9c1029002636397df0cf21f1. It has .dynsym, .gnu.version, .gnu.version_r, and defined copy-relocated data symbols. No private artifact is required.

Interface

MCP

Earliest failure stage

Native analysis or reconstruction

Relevant output or evidence

Selected output from the executed readelf commands:

Symbol table '.dynsym' contains 132 entries:
   Num:    Value          Size Type    Bind   Vis      Ndx Name
   123: 0000000000028268     8 OBJECT  GLOBAL DEFAULT   25 stdout@GLIBC_2.2.5 (3)
   124: 00000000000282a0     8 OBJECT  GLOBAL DEFAULT   25 stderr@GLIBC_2.2.5 (3)

Version needs section '.gnu.version_r' contains 1 entry:
  000000: Version: 1  File: libc.so.6  Cnt: 12
  0x00b0:   Name: GLIBC_2.2.5  Flags: none  Version: 3

0000000000028268  0000007b00000005 R_X86_64_COPY          0000000000028268 stdout@GLIBC_2.2.5 + 0

The provider reads the selected file directly into this parser. The parser's only version-name map reads SHT_GNU_VERDEF. Its symbol loop admits defined visible symbols, then looks up every version index in that map. For index 3 in this file, that map is empty, so name becomes null and version_unresolved is appended.

Typed outcome or recovery expected

Restoration signal: the stdout row retains version: {"index":3,"name":"GLIBC_2.2.5","hidden":false} and no unresolved-version issue is generated for that readable requirement. If no independent table issue exists, coverage remains complete. This is a proposed acceptance check, not a tested fix.

Analysis or suspected cause

Source-derived cause: readVersionDefinitions ignores SHT_GNU_VERNEED entirely. Being defined in the executable does not imply that a copy-relocated symbol's version index must come from the executable's version-definitions table. Both tables need to contribute appropriately validated version-name facts.

Additional context

Open and closed searches included "inspect-native-exports", "export" "GLIBC", "exports" "version", "native exports", "verneed", and "copy relocation". No equivalent report was found. The related #4177 concerns Ghidra incorrectly constant-folding copy-relocated stream pointers; this report concerns the independent portable export parser losing symbol-version metadata. Closed #4057 requested export-inventory integration, rather than reporting this failure.

NOT_ENOUGH_INFO: no known-good REA revision was established. No source files were edited and no fix, suite, build, or REA runtime reproduction was run.

Triage assessment

  • Impact level: P2
  • Assessment status: provisional
  • Impact basis: The source discards available ELF version-name metadata for a demonstrated valid table arrangement and marks coverage partial. REA runtime behavior and distribution-wide frequency were not measured; export names and values remain available.
  • Workaround status: available
  • Workaround basis: The executed GNU readelf symbol and version-table inspections recovered the version name from the same input without modifying it.
Originally created by @coygeek on GitHub (Oct 23, 2026). Original GitHub issue: https://github.com/morluto/rea/issues/6533 ### 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 Source-only REA finding, corroborated by reading a real ELF with GNU readelf; REA runtime was not run. `inspect_native_exports` resolves ELF symbol-version indices only through `SHT_GNU_VERDEF`. A normal executable's copy-relocated, defined dynamic symbols can instead name versions in `SHT_GNU_VERNEED`. The parser retains those symbols but produces `version.name: null`, a `version_unresolved` issue, and partial coverage despite the version name being available in the same input. For the inspected `/usr/bin/ls`, readelf reports defined `stdout@GLIBC_2.2.5`, version index 3, at `0x28268`, and a corresponding `R_X86_64_COPY` relocation. Its version-needs table maps index 3 to `GLIBC_2.2.5`; it has no version-definitions table. Following the REA source predicts a null version name for this row. That REA result is a derivation, not captured output. ### Reproduction Actually ran on Linux x64: ```sh readelf --dyn-syms --wide /usr/bin/ls readelf --version-info --wide /usr/bin/ls readelf --relocs --wide /usr/bin/ls | rg 'COPY|stdout|stderr|optarg|optind' ``` These establish the valid input facts shown below. Each inspection was run once; no REA execution was performed. Proposed, unexecuted public reproduction on an equivalent glibc executable containing these tables: ```sh rea inspect-native-exports /usr/bin/ls ``` Inspect the `stdout` export's version name and `coverage.status`. The equivalent unexecuted MCP call is `inspect_native_exports` with `{"path":"/usr/bin/ls"}`. ### Expected behavior Retain `GLIBC_2.2.5` as the version name of the defined `stdout` symbol by resolving its version-needs entry. A readable version name in the artifact should not become an unresolved-version issue merely because the symbol is copy-relocated. The [public contract](https://github.com/morluto/rea/blob/28a33cae8f8d4de8e11f7a266ff0d756432ab092/src/contracts/native/binaryDiagnosticsToolContracts.ts#L122-L128) promises defined default/protected global and weak dynamic symbols with their symbol versions. [Input admission](https://github.com/morluto/rea/blob/28a33cae8f8d4de8e11f7a266ff0d756432ab092/src/domain/native/nativeExports.ts#L13-L25) explicitly accepts executables as well as libraries. ### Impact and scope Provisional P2: a bounded metadata-loss defect. Source-derived scope is defined, visible global/weak/unique dynamic symbols whose version index is supplied by the ELF version-needs table rather than the version-definitions table. The export name and value remain available; its known version name is lost and coverage becomes partial. GNU readelf is an available read-only workaround: the executed commands recovered the missing name from this input. NOT_ENOUGH_INFO: REA runtime output, frequency across distributions, and other architectures were not measured. This report makes no claim of binary mutation or runtime linking failure. ### REA version rea-agents 6.7.0; inspected source revision 28a33cae8f8d4de8e11f7a266ff0d756432ab092. ### Runtime and platform Linux x64 source inspection; GNU readelf (GNU Binutils) 2.47. No REA runtime execution or external analysis engine was run. NOT_ENOUGH_INFO: no Node.js runtime was used for this finding. ### Target and artifact context System `ls`: stripped, dynamically linked ELF64 little-endian x86-64 PIE, GNU build ID `d6281af586ce362d9c1029002636397df0cf21f1`. It has `.dynsym`, `.gnu.version`, `.gnu.version_r`, and defined copy-relocated data symbols. No private artifact is required. ### Interface MCP ### Earliest failure stage Native analysis or reconstruction ### Relevant output or evidence Selected output from the executed readelf commands: ```text Symbol table '.dynsym' contains 132 entries: Num: Value Size Type Bind Vis Ndx Name 123: 0000000000028268 8 OBJECT GLOBAL DEFAULT 25 stdout@GLIBC_2.2.5 (3) 124: 00000000000282a0 8 OBJECT GLOBAL DEFAULT 25 stderr@GLIBC_2.2.5 (3) Version needs section '.gnu.version_r' contains 1 entry: 000000: Version: 1 File: libc.so.6 Cnt: 12 0x00b0: Name: GLIBC_2.2.5 Flags: none Version: 3 0000000000028268 0000007b00000005 R_X86_64_COPY 0000000000028268 stdout@GLIBC_2.2.5 + 0 ``` The [provider reads the selected file directly into this parser](https://github.com/morluto/rea/blob/28a33cae8f8d4de8e11f7a266ff0d756432ab092/src/native/exports/NativeExportsProvider.ts#L64-L81). The parser's [only version-name map reads `SHT_GNU_VERDEF`](https://github.com/morluto/rea/blob/28a33cae8f8d4de8e11f7a266ff0d756432ab092/src/native/exports/NativeExportParser.ts#L254-L262). Its [symbol loop admits defined visible symbols, then looks up every version index in that map](https://github.com/morluto/rea/blob/28a33cae8f8d4de8e11f7a266ff0d756432ab092/src/native/exports/NativeExportParser.ts#L407-L448). For index 3 in this file, that map is empty, so `name` becomes null and `version_unresolved` is appended. ### Typed outcome or recovery expected Restoration signal: the `stdout` row retains `version: {"index":3,"name":"GLIBC_2.2.5","hidden":false}` and no unresolved-version issue is generated for that readable requirement. If no independent table issue exists, coverage remains complete. This is a proposed acceptance check, not a tested fix. ### Analysis or suspected cause Source-derived cause: `readVersionDefinitions` ignores `SHT_GNU_VERNEED` entirely. Being defined in the executable does not imply that a copy-relocated symbol's version index must come from the executable's version-definitions table. Both tables need to contribute appropriately validated version-name facts. ### Additional context Open and closed searches included `"inspect-native-exports"`, `"export" "GLIBC"`, `"exports" "version"`, `"native exports"`, `"verneed"`, and `"copy relocation"`. No equivalent report was found. The related [#4177](https://github.com/morluto/rea/issues/4177) concerns Ghidra incorrectly constant-folding copy-relocated stream pointers; this report concerns the independent portable export parser losing symbol-version metadata. Closed [#4057](https://github.com/morluto/rea/issues/4057) requested export-inventory integration, rather than reporting this failure. NOT_ENOUGH_INFO: no known-good REA revision was established. No source files were edited and no fix, suite, build, or REA runtime reproduction was run. ## Triage assessment - Impact level: P2 - Assessment status: provisional - Impact basis: The source discards available ELF version-name metadata for a demonstrated valid table arrangement and marks coverage partial. REA runtime behavior and distribution-wide frequency were not measured; export names and values remain available. - Workaround status: available - Workaround basis: The executed GNU readelf symbol and version-table inspections recovered the version name from the same input without modifying it.
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#3924
No description provided.