[GH-ISSUE #6745] [Bug] ELF export inventory corrupts UTF-8 symbol version names #4107

Closed
opened 2026-10-31 00:46:51 +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/6745

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 production NativeExportsService.inspect returns version.name: "libcafé.so" for a shared library whose GNU linker-generated version is libcafé.so. The same result correctly decodes the Unicode export symbol libcafé.so, so the symbol-name correction from #6647 is present while version-name decoding remains incorrect. Coverage is complete and issues is empty.

This is an executed source-service reproduction, without loading or executing the shared library. CLI and MCP transport were not exercised.

Reproduction

From the source checkout with locked dependencies installed, build and inspect the following fixture. These are the executed source contents, linker options and service invocation, with temporary paths parameterized for a checkout-local reproduction:

work=$(mktemp -d)
printf 'int plain(void) { return 7; }\n' > "$work/versioned.c"
cc -shared -fPIC -Wl,--default-symver,-soname,libcafé.so \
  -o "$work/versioned.so" "$work/versioned.c"
readelf --dyn-syms --wide --version-info "$work/versioned.so"
REA_EXPORT_FIXTURE="$work/versioned.so" node --import tsx --input-type=module <<'JS'
import { NativeExportsProvider } from './src/native/exports/NativeExportsProvider.ts';
import { NativeExportsService } from './src/application/binaryDiagnostics/NativeExportsService.ts';
const service = new NativeExportsService(new NativeExportsProvider());
try {
  const r = await service.inspect({path: process.env.REA_EXPORT_FIXTURE});
  console.log(JSON.stringify(r.ok ? r.value.normalized_result : r.error, null, 2));
} finally {
  await service.close();
}
JS

One successful producer build and one service invocation reproduced the discrepancy. GNU ld's --default-symver assigns the SONAME as the default symbol version; this does not depend on accepting a Unicode version-script identifier.

Expected behavior

Preserve an intelligible version identity for this known-UTF-8 producer value: return libcafé.so, or explicitly identify its byte representation/encoding and expose exact bytes. ELF does not universally prescribe UTF-8, so this expectation does not require lossy conversion of arbitrary ELF strings.

The export inventory contract describes version names resolved from the GNU version-definition or requirement tables. Its existing symbol-name encoding fields do not describe version-name encoding.

Impact and scope

A valid linker-produced version identity becomes different ordinary UTF-8 JSON text, which can mislead analysts or consumers reusing it as a version identifier. Verified only for the version-definition path on this Linux fixture. The input bytes and symbol addresses were preserved; no runtime linking failure, data loss, regression range or population frequency was established.

For this demonstrated known-UTF-8 input, Buffer.from(version.name, 'latin1').toString('utf8') recovers the producer name. That conversion was separately checked before drafting and requires outside knowledge of the original encoding.

REA version

Source revision bc411c24befceced5a9ea1ca1c1ce123fd02bf48, manifest version 6.7.0. The separately published npm artifact was not executed.

Runtime and platform

Node.js v24.22.1; Linux 7.2.8-9-omarchy x86_64; GCC 16.2.1; GNU ld/readelf 2.47. No native analysis engine was used.

Target and artifact context

Self-authored ELF64 x86-64 shared library containing one ordinary C function. GNU ld generated the version definitions from the supplied SONAME. The observed artifact was 15,256 bytes.

Interface

Other

Earliest failure stage

Native analysis or reconstruction

Relevant output or evidence

Observed readelf function row and version-definition entry:

plain@@libcafé.so
Index: 2  Cnt: 1  Name: libcafé.so

Selected service result:

{
  "exports": [
    {
      "name": "libcafé.so",
      "name_bytes_base64": "bGliY2Fmw6kuc28=",
      "name_utf8_decodable": true,
      "version": {"index": 2, "name": "libcafé.so", "hidden": false}
    },
    {
      "name": "plain",
      "version": {"index": 2, "name": "libcafé.so", "hidden": false}
    }
  ],
  "coverage": {"status": "complete", "reason": null, "examined_entries": 6, "exports": 2},
  "issues": []
}
  • Evidence provenance: observed producer build/readelf output and production source-service output.
  • Local verification: reproduced.
  • Reproduction completeness: complete for the stated source-service fixture; transport and released-package behavior remain untested.

Typed outcome or recovery expected

The same producer fixture should expose the exact version identity, or a documented byte representation that recovers it, while retaining both exports, version index 2 and hidden: false.

Analysis or suspected cause

Source-derived: readVersionNames in src/native/exports/NativeExportParser.ts still calls readElfString, which unconditionally decodes as Latin-1 in src/native/metadata/ElfSymbol.ts. The export-symbol path separately uses readElfStringBytes and isUtf8. Version names have no equivalent encoding/raw-byte fields.

The version-requirement path uses the same helper, but that path was not runtime-tested.

Additional context

Related closed report #6647 reproduced unversioned symbol-name corruption. Its symbol-name correction does not restore this separately exposed version-name field. No reproduction was rerun after this draft was written.

Triage assessment

  • Impact level: P2
  • Assessment status: supported
  • Impact basis: An executed production-service inspection changes a valid producer's version identity in analyst-facing JSON while reporting complete coverage. The demonstrated failure is bounded to non-ASCII UTF-8 version text.
  • Workaround status: available
  • Workaround basis: A separately executed Latin-1-to-UTF-8 conversion recovered the exact producer name for this fixture; it requires known source encoding and should not be applied blindly to arbitrary ELF bytes.
Originally created by @coygeek on GitHub (Oct 23, 2026). Original GitHub issue: https://github.com/morluto/rea/issues/6745 ## 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 production `NativeExportsService.inspect` returns `version.name: "libcafé.so"` for a shared library whose GNU linker-generated version is `libcafé.so`. The same result correctly decodes the Unicode export symbol `libcafé.so`, so the symbol-name correction from #6647 is present while version-name decoding remains incorrect. Coverage is complete and `issues` is empty. This is an executed source-service reproduction, without loading or executing the shared library. CLI and MCP transport were not exercised. ## Reproduction From the source checkout with locked dependencies installed, build and inspect the following fixture. These are the executed source contents, linker options and service invocation, with temporary paths parameterized for a checkout-local reproduction: ```sh work=$(mktemp -d) printf 'int plain(void) { return 7; }\n' > "$work/versioned.c" cc -shared -fPIC -Wl,--default-symver,-soname,libcafé.so \ -o "$work/versioned.so" "$work/versioned.c" readelf --dyn-syms --wide --version-info "$work/versioned.so" REA_EXPORT_FIXTURE="$work/versioned.so" node --import tsx --input-type=module <<'JS' import { NativeExportsProvider } from './src/native/exports/NativeExportsProvider.ts'; import { NativeExportsService } from './src/application/binaryDiagnostics/NativeExportsService.ts'; const service = new NativeExportsService(new NativeExportsProvider()); try { const r = await service.inspect({path: process.env.REA_EXPORT_FIXTURE}); console.log(JSON.stringify(r.ok ? r.value.normalized_result : r.error, null, 2)); } finally { await service.close(); } JS ``` One successful producer build and one service invocation reproduced the discrepancy. GNU ld's `--default-symver` assigns the SONAME as the default symbol version; this does not depend on accepting a Unicode version-script identifier. ## Expected behavior Preserve an intelligible version identity for this known-UTF-8 producer value: return `libcafé.so`, or explicitly identify its byte representation/encoding and expose exact bytes. ELF does not universally prescribe UTF-8, so this expectation does not require lossy conversion of arbitrary ELF strings. The [export inventory contract](https://github.com/morluto/rea/blob/bc411c24befceced5a9ea1ca1c1ce123fd02bf48/docs/binary-diagnostics.md#L332-L350) describes version names resolved from the GNU version-definition or requirement tables. Its existing symbol-name encoding fields do not describe version-name encoding. ## Impact and scope A valid linker-produced version identity becomes different ordinary UTF-8 JSON text, which can mislead analysts or consumers reusing it as a version identifier. Verified only for the version-definition path on this Linux fixture. The input bytes and symbol addresses were preserved; no runtime linking failure, data loss, regression range or population frequency was established. For this demonstrated known-UTF-8 input, `Buffer.from(version.name, 'latin1').toString('utf8')` recovers the producer name. That conversion was separately checked before drafting and requires outside knowledge of the original encoding. ## REA version Source revision `bc411c24befceced5a9ea1ca1c1ce123fd02bf48`, manifest version 6.7.0. The separately published npm artifact was not executed. ## Runtime and platform Node.js v24.22.1; Linux 7.2.8-9-omarchy x86_64; GCC 16.2.1; GNU ld/readelf 2.47. No native analysis engine was used. ## Target and artifact context Self-authored ELF64 x86-64 shared library containing one ordinary C function. GNU ld generated the version definitions from the supplied SONAME. The observed artifact was 15,256 bytes. ## Interface Other ## Earliest failure stage Native analysis or reconstruction ## Relevant output or evidence Observed `readelf` function row and version-definition entry: ```text plain@@libcafé.so Index: 2 Cnt: 1 Name: libcafé.so ``` Selected service result: ```json { "exports": [ { "name": "libcafé.so", "name_bytes_base64": "bGliY2Fmw6kuc28=", "name_utf8_decodable": true, "version": {"index": 2, "name": "libcafé.so", "hidden": false} }, { "name": "plain", "version": {"index": 2, "name": "libcafé.so", "hidden": false} } ], "coverage": {"status": "complete", "reason": null, "examined_entries": 6, "exports": 2}, "issues": [] } ``` - Evidence provenance: observed producer build/readelf output and production source-service output. - Local verification: reproduced. - Reproduction completeness: complete for the stated source-service fixture; transport and released-package behavior remain untested. ## Typed outcome or recovery expected The same producer fixture should expose the exact version identity, or a documented byte representation that recovers it, while retaining both exports, version index 2 and `hidden: false`. ## Analysis or suspected cause Source-derived: `readVersionNames` in `src/native/exports/NativeExportParser.ts` still calls `readElfString`, which unconditionally decodes as Latin-1 in `src/native/metadata/ElfSymbol.ts`. The export-symbol path separately uses `readElfStringBytes` and `isUtf8`. Version names have no equivalent encoding/raw-byte fields. The version-requirement path uses the same helper, but that path was not runtime-tested. ## Additional context Related closed report [#6647](https://github.com/morluto/rea/issues/6647) reproduced unversioned symbol-name corruption. Its symbol-name correction does not restore this separately exposed version-name field. No reproduction was rerun after this draft was written. ## Triage assessment - Impact level: P2 - Assessment status: supported - Impact basis: An executed production-service inspection changes a valid producer's version identity in analyst-facing JSON while reporting complete coverage. The demonstrated failure is bounded to non-ASCII UTF-8 version text. - Workaround status: available - Workaround basis: A separately executed Latin-1-to-UTF-8 conversion recovered the exact producer name for this fixture; it requires known source encoding and should not be applied blindly to arbitrary ELF bytes.
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#4107
No description provided.