[GH-ISSUE #6926] [Bug] Explicit P/Invoke charsets add Windows suffix candidates for ELF imports #4240

Closed
opened 2026-10-31 00:48:00 +00:00 by bsakaguchi · 0 comments
Owner

Originally created by @gensokyo-dev on GitHub (Oct 23, 2026).
Original GitHub issue: https://github.com/morluto/rea/issues/6926

Before submitting

  • I searched open and closed issues for related reports.
  • I removed credentials and sensitive/private target data.

Area

Evidence, artifacts, or reconstruction

Observed behavior

verify-managed-native-boundaries reports inferred / decorated-name-candidate for an explicit ANSI P/Invoke Example against a supplied ELF function named ExampleA, and for explicit Unicode against ExampleW. The declared entry point itself is absent.

The compiled public CLI and stdio MCP returned identical results using real .NET compiler metadata and native-name Evidence derived from Clang-built ELF exports. Separate .NET 10.0.0 Linux executions of those owned artifacts throw EntryPointNotFoundException for both declarations; exact ExampleA/ExampleW controls return 11/22. The inference is not being reported as verified runtime binding: the defect is applying a Windows-only suffix-candidate rule to known ELF context.

Reproduction

From source revision edc805eca2b2e68a45280042b789ecb1c130945a with its locked dependencies, save this executed minimal source probe as /tmp/pinvoke-elf-probe.mjs and run:

node --import ./node_modules/tsx/dist/loader.mjs /tmp/pinvoke-elf-probe.mjs "$PWD"

This reproducer deliberately uses byte-built managed metadata and synthetic native Evidence; it needs no .NET/native execution. The independent real-artifact checks are described below.

import { pathToFileURL } from 'node:url';
const [repo] = process.argv.slice(2);
const load = path => import(pathToFileURL(`${repo}/src/${path}.ts`).href);
const { buildManagedPeFixture, managedPeFixtureTarget } = await load('dotnet/ManagedPe.fixture');
const { inspectManagedNativeBoundariesBytes } = await load('dotnet/ManagedNativeBoundaryInspector');
const { createEvidence } = await load('domain/evidence');
const { functionDossierSchema } = await load('domain/hopperValues');
const { verifyManagedNativeBoundaries } = await load('domain/managed/managedNativeVerification');
for (const [flags, name] of [[0x0202, 'ExampleA'], [0x0204, 'ExampleW']]) {
  const path = '/examples/libnativehelper.so';
  const bytes = buildManagedPeFixture({ targetFramework: null, pinvoke: { mappingFlags: flags, importName: 'Example', moduleName: path } });
  const target = managedPeFixtureTarget(bytes);
  const managed = createEvidence(target, { id: 'rea-dotnet-static', name: 'REA managed static', version: '1' }, {
    operation: 'inspect_managed_native_boundaries', parameters: {},
    result: inspectManagedNativeBoundariesBytes(bytes, target), rawResult: null,
  });
  const native = createEvidence({ path, sha256: '8'.repeat(64), format: 'elf', architecture: 'x86_64' }, { id: 'synthetic-source-probe', name: 'Synthetic source probe', version: '1' }, {
    operation: 'analyze_function', parameters: {}, rawResult: null,
    result: functionDossierSchema.parse({
      procedure: { address: '0x1000', name, classification: { external: false, thunk: false, thunk_target: null, provenance: 'synthetic-source-probe' }, body: { available: false, reason: 'Synthetic source-module reproduction only.' }, signature: null, locals: [] },
      pseudocode: '', assembly: [], comments: [], callers: [], callees: [], incoming_references: [], outgoing_references: [], referenced_strings: [], referenced_names: [], basic_blocks: [], native_api: null, native_value_flow: null,
      limitations: ['Synthetic Evidence; no native provider or runtime executed by this minimal probe.'],
    }),
  });
  const row = verifyManagedNativeBoundaries({ managed_boundaries: managed, native_observations: [native] }).pinvoke_imports[0];
  console.log(JSON.stringify({ charset: row.managed.char_set, native_format: 'elf', status: row.status, basis: row.basis, candidate_names: row.candidates.map(x => x.name) }));
}

Executed output:

{"charset":"ansi","native_format":"elf","status":"inferred","basis":"decorated-name-candidate","candidate_names":["ExampleA"]}
{"charset":"unicode","native_format":"elf","status":"inferred","basis":"decorated-name-candidate","candidate_names":["ExampleW"]}

Expected behavior

Keep the declared name and caller-supplied platform facts. Known ELF context should not acquire Windows A/W suffix-probing candidates merely from the marshalling charset. With only ExampleA/ExampleW supplied, these declarations should remain unresolved / no-native-candidate; exact-name controls should keep matching. Preserve the existing PE default/ANSI→A, Unicode/Auto→W and ExactSpelling controls, provenance and uncertainty.

.NET 10's actual lookup implementation performs a direct lookup of the declared name on non-Windows targets; its A/W probes are in the Windows branch. Microsoft also distinguishes charset marshalling from character-set name matching. A default charset's ANSI representation does not establish Windows suffix probing on Unix.

Impact and scope

Two supported declaration states can acquire an unsupported native-name correspondence on ELF. The observed result is an inferred candidate, not a false claim of proven binding, ABI equivalence or execution safety. Real-world prevalence and the introducing revision are NOT_ENOUGH_INFO. Exact-name selection is an observed control, not an unchanged-input workaround.

REA version

Source/build edc805eca2b2e68a45280042b789ecb1c130945a, package 6.7.0. Current main 3d4f5565 has unchanged matching/application code at these boundaries. No installed-release comparison was run.

Runtime and platform

Linux x86_64, Node 24.22.1/npm 11.16.0; isolated official .NET SDK 10.0.100/runtime 10.0.0, Clang/LLVM 14 and Rust's existing COFF linker. SDK archive SHA-512 was verified against Microsoft's release manifest. No global toolchain or repository dependencies were changed.

Target and artifact context

Owned C functions int ExampleA(void) { return 11; } and int ExampleW(void) { return 22; }, compiled as ELF and PE. Two real C# assemblies contain eight DllImport declarations each: default, ANSI, Unicode, Auto, ExactSpelling controls, and exact A/W names. PE was inspected only. Native function Evidence was test-supplied from LLVM export names/addresses with unavailable bodies; no Hopper/Ghidra/IDA analysis was run.

Real-artifact SHA-256:

  • ELF library: 563057d61b3c5339b51f27a433ede462397c0ec51e71c06884222571949f0ff1
  • Managed ELF-context assembly: 8f0e718b8314e3f8a9ca3e58c842937ec4c8055c7ea46024c4907e30b2fbc455
  • PE library: a9e0bb4a85bfc2f1fc320da14f17628ed02a2254c151b6bea4a5794ef29fa9e2
  • Managed PE-context assembly: 4dcc78d84feaf2a5032af9d6d5b8dce6acf9fe9ac30d1d5c90989b9055732cf6

Interface

CLI and MCP

Earliest failure stage

Native analysis or reconstruction

Relevant output or evidence

Executed real-artifact results, matching through both adapters:

Native context Charset/declaration REA result Separate Linux runtime control
ELF ANSI, entry Example inferred ExampleA entry point not found
ELF Unicode, entry Example inferred ExampleW entry point not found
ELF default or Auto, entry Example no candidate entry point not found
ELF exact ExampleA / ExampleW verified exact name returns 11 / 22
PE default / ANSI inferred ExampleA Windows runtime not run
PE Unicode / Auto inferred ExampleW Windows runtime not run

Two batched verification inputs contained eight declarations and two native-name observations each. CLI/MCP normalized results agreed exactly. All 31 existing conformance/adapter tests passed; those tests do not prevent this explicit-charset ELF behavior. Real producer/CLI/MCP checks and the minimal probe finished before filing; this is not a proposed reproduction.

Typed outcome or recovery expected

No unsupported A/W correspondence for known ELF context. Preserve exact names, module identity, supplied Evidence links and unresolved coverage; keep supported PE suffix candidates inferred rather than declaring runtime binding.

Analysis or suspected cause

Source-derived: candidateNames in src/domain/managed/managedNativeVerificationMatch.ts gates Auto and unspecified charsets on nativeFormat === "pe", but applies explicit ansi/unicode suffixes without that condition. The shared pinvokeCandidates owner already groups candidate generation by native format, so this is not a CLI/MCP transport difference. Other Windows-only decorations at the same boundary warrant inspection; no unexecuted regression is claimed here.

Additional context

Related #6857 concerns missing default/Auto candidates in Windows PE context, already fixed on main. This report concerns unsupported explicit-charset candidates on ELF. #6652 concerns case-distinct ELF module names; #4057 concerns accepted native Evidence inventory types. Searches did not reveal an exact duplicate. Found while reviewing #6877. No tracked source mutation or target execution by REA occurred; only separate self-authored runtime controls were executed. AI-assisted investigation.

Triage assessment

  • Impact level: P2
  • Assessment status: supported
  • Impact basis: a bounded static candidate-generation result contradicts known platform lookup behavior, with matching public adapters and a real runtime control.
  • Workaround status: unverified
  • Workaround basis: manually inspect the exact declared/native names; no public unchanged-input filter that removes only these unsupported candidates was verified.
Originally created by @gensokyo-dev on GitHub (Oct 23, 2026). Original GitHub issue: https://github.com/morluto/rea/issues/6926 ## Before submitting - [x] I searched open and closed issues for related reports. - [x] I removed credentials and sensitive/private target data. ## Area Evidence, artifacts, or reconstruction ## Observed behavior `verify-managed-native-boundaries` reports `inferred / decorated-name-candidate` for an explicit ANSI P/Invoke `Example` against a supplied ELF function named `ExampleA`, and for explicit Unicode against `ExampleW`. The declared entry point itself is absent. The compiled public CLI and stdio MCP returned identical results using real .NET compiler metadata and native-name Evidence derived from Clang-built ELF exports. Separate .NET 10.0.0 Linux executions of those owned artifacts throw `EntryPointNotFoundException` for both declarations; exact `ExampleA`/`ExampleW` controls return 11/22. The inference is not being reported as verified runtime binding: the defect is applying a Windows-only suffix-candidate rule to known ELF context. ## Reproduction From source revision `edc805eca2b2e68a45280042b789ecb1c130945a` with its locked dependencies, save this executed minimal source probe as `/tmp/pinvoke-elf-probe.mjs` and run: ```sh node --import ./node_modules/tsx/dist/loader.mjs /tmp/pinvoke-elf-probe.mjs "$PWD" ``` This reproducer deliberately uses byte-built managed metadata and synthetic native Evidence; it needs no .NET/native execution. The independent real-artifact checks are described below. ```js import { pathToFileURL } from 'node:url'; const [repo] = process.argv.slice(2); const load = path => import(pathToFileURL(`${repo}/src/${path}.ts`).href); const { buildManagedPeFixture, managedPeFixtureTarget } = await load('dotnet/ManagedPe.fixture'); const { inspectManagedNativeBoundariesBytes } = await load('dotnet/ManagedNativeBoundaryInspector'); const { createEvidence } = await load('domain/evidence'); const { functionDossierSchema } = await load('domain/hopperValues'); const { verifyManagedNativeBoundaries } = await load('domain/managed/managedNativeVerification'); for (const [flags, name] of [[0x0202, 'ExampleA'], [0x0204, 'ExampleW']]) { const path = '/examples/libnativehelper.so'; const bytes = buildManagedPeFixture({ targetFramework: null, pinvoke: { mappingFlags: flags, importName: 'Example', moduleName: path } }); const target = managedPeFixtureTarget(bytes); const managed = createEvidence(target, { id: 'rea-dotnet-static', name: 'REA managed static', version: '1' }, { operation: 'inspect_managed_native_boundaries', parameters: {}, result: inspectManagedNativeBoundariesBytes(bytes, target), rawResult: null, }); const native = createEvidence({ path, sha256: '8'.repeat(64), format: 'elf', architecture: 'x86_64' }, { id: 'synthetic-source-probe', name: 'Synthetic source probe', version: '1' }, { operation: 'analyze_function', parameters: {}, rawResult: null, result: functionDossierSchema.parse({ procedure: { address: '0x1000', name, classification: { external: false, thunk: false, thunk_target: null, provenance: 'synthetic-source-probe' }, body: { available: false, reason: 'Synthetic source-module reproduction only.' }, signature: null, locals: [] }, pseudocode: '', assembly: [], comments: [], callers: [], callees: [], incoming_references: [], outgoing_references: [], referenced_strings: [], referenced_names: [], basic_blocks: [], native_api: null, native_value_flow: null, limitations: ['Synthetic Evidence; no native provider or runtime executed by this minimal probe.'], }), }); const row = verifyManagedNativeBoundaries({ managed_boundaries: managed, native_observations: [native] }).pinvoke_imports[0]; console.log(JSON.stringify({ charset: row.managed.char_set, native_format: 'elf', status: row.status, basis: row.basis, candidate_names: row.candidates.map(x => x.name) })); } ``` Executed output: ```jsonl {"charset":"ansi","native_format":"elf","status":"inferred","basis":"decorated-name-candidate","candidate_names":["ExampleA"]} {"charset":"unicode","native_format":"elf","status":"inferred","basis":"decorated-name-candidate","candidate_names":["ExampleW"]} ``` ## Expected behavior Keep the declared name and caller-supplied platform facts. Known ELF context should not acquire Windows A/W suffix-probing candidates merely from the marshalling charset. With only `ExampleA`/`ExampleW` supplied, these declarations should remain `unresolved / no-native-candidate`; exact-name controls should keep matching. Preserve the existing PE default/ANSI→A, Unicode/Auto→W and ExactSpelling controls, provenance and uncertainty. .NET 10's [actual lookup implementation](https://github.com/dotnet/runtime/blob/v10.0.0/src/coreclr/vm/method.cpp#L3134-L3177) performs a direct lookup of the declared name on non-Windows targets; its A/W probes are in the Windows branch. Microsoft also distinguishes [charset marshalling](https://learn.microsoft.com/en-us/dotnet/standard/native-interop/charset) from [character-set name matching](https://learn.microsoft.com/en-us/dotnet/standard/native-interop/specifying-a-character-set). A default charset's ANSI representation does not establish Windows suffix probing on Unix. ## Impact and scope Two supported declaration states can acquire an unsupported native-name correspondence on ELF. The observed result is an inferred candidate, not a false claim of proven binding, ABI equivalence or execution safety. Real-world prevalence and the introducing revision are NOT_ENOUGH_INFO. Exact-name selection is an observed control, not an unchanged-input workaround. ## REA version Source/build `edc805eca2b2e68a45280042b789ecb1c130945a`, package 6.7.0. Current main `3d4f5565` has unchanged matching/application code at these boundaries. No installed-release comparison was run. ## Runtime and platform Linux x86_64, Node 24.22.1/npm 11.16.0; isolated official .NET SDK 10.0.100/runtime 10.0.0, Clang/LLVM 14 and Rust's existing COFF linker. SDK archive SHA-512 was verified against Microsoft's release manifest. No global toolchain or repository dependencies were changed. ## Target and artifact context Owned C functions `int ExampleA(void) { return 11; }` and `int ExampleW(void) { return 22; }`, compiled as ELF and PE. Two real C# assemblies contain eight DllImport declarations each: default, ANSI, Unicode, Auto, ExactSpelling controls, and exact A/W names. PE was inspected only. Native function Evidence was test-supplied from LLVM export names/addresses with unavailable bodies; no Hopper/Ghidra/IDA analysis was run. Real-artifact SHA-256: - ELF library: `563057d61b3c5339b51f27a433ede462397c0ec51e71c06884222571949f0ff1` - Managed ELF-context assembly: `8f0e718b8314e3f8a9ca3e58c842937ec4c8055c7ea46024c4907e30b2fbc455` - PE library: `a9e0bb4a85bfc2f1fc320da14f17628ed02a2254c151b6bea4a5794ef29fa9e2` - Managed PE-context assembly: `4dcc78d84feaf2a5032af9d6d5b8dce6acf9fe9ac30d1d5c90989b9055732cf6` ## Interface CLI and MCP ## Earliest failure stage Native analysis or reconstruction ## Relevant output or evidence Executed real-artifact results, matching through both adapters: | Native context | Charset/declaration | REA result | Separate Linux runtime control | |---|---|---|---| | ELF | ANSI, entry `Example` | inferred `ExampleA` | entry point not found | | ELF | Unicode, entry `Example` | inferred `ExampleW` | entry point not found | | ELF | default or Auto, entry `Example` | no candidate | entry point not found | | ELF | exact `ExampleA` / `ExampleW` | verified exact name | returns 11 / 22 | | PE | default / ANSI | inferred `ExampleA` | Windows runtime not run | | PE | Unicode / Auto | inferred `ExampleW` | Windows runtime not run | Two batched verification inputs contained eight declarations and two native-name observations each. CLI/MCP normalized results agreed exactly. All 31 existing conformance/adapter tests passed; those tests do not prevent this explicit-charset ELF behavior. Real producer/CLI/MCP checks and the minimal probe finished before filing; this is not a proposed reproduction. ## Typed outcome or recovery expected No unsupported A/W correspondence for known ELF context. Preserve exact names, module identity, supplied Evidence links and unresolved coverage; keep supported PE suffix candidates inferred rather than declaring runtime binding. ## Analysis or suspected cause Source-derived: `candidateNames` in `src/domain/managed/managedNativeVerificationMatch.ts` gates Auto and unspecified charsets on `nativeFormat === "pe"`, but applies explicit `ansi`/`unicode` suffixes without that condition. The shared `pinvokeCandidates` owner already groups candidate generation by native format, so this is not a CLI/MCP transport difference. Other Windows-only decorations at the same boundary warrant inspection; no unexecuted regression is claimed here. ## Additional context Related #6857 concerns missing default/Auto candidates in Windows PE context, already fixed on main. This report concerns unsupported explicit-charset candidates on ELF. #6652 concerns case-distinct ELF module names; #4057 concerns accepted native Evidence inventory types. Searches did not reveal an exact duplicate. Found while reviewing #6877. No tracked source mutation or target execution by REA occurred; only separate self-authored runtime controls were executed. AI-assisted investigation. ## Triage assessment - Impact level: P2 - Assessment status: supported - Impact basis: a bounded static candidate-generation result contradicts known platform lookup behavior, with matching public adapters and a real runtime control. - Workaround status: unverified - Workaround basis: manually inspect the exact declared/native names; no public unchanged-input filter that removes only these unsupported candidates was verified.
bsakaguchi 2026-10-31 00:48:00 +00:00
  • closed this issue
  • added the
    bug
    triage
    labels
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#4240
No description provided.