[GH-ISSUE #6652] [Bug] Managed/native verification equates case-distinct ELF module names #4030

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

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

The production managed/native declaration verifier reports verified, confidence: observed, and basis: exact-function-name when a P/Invoke names caseprobe but the supplied ELF function Evidence belongs to libCaseProbe.so. It returns the same result for the case-matching libcaseprobe.so control. The differently named libother.so control is correctly contradicted.

This was reproduced through the source domain API with a generated PE/CLI fixture and authenticated synthetic native Evidence. No CLR or native analysis engine was run, and this report does not claim that a runtime call occurred.

Reproduction

From a checkout of the revision below with its locked dependencies installed, save the following as module-case.mts in the repository root and run node_modules/.bin/tsx module-case.mts:

import { buildManagedPeFixture, managedPeFixtureTarget } from './src/dotnet/ManagedPe.fixture.ts';
import { inspectManagedNativeBoundariesBytes } from './src/dotnet/ManagedNativeBoundaryInspector.ts';
import { createEvidence } from './src/domain/evidence.ts';
import { verifyManagedNativeBoundaries } from './src/domain/managed/managedNativeVerification.ts';
import { functionDossierSchema } from './src/domain/hopperValues.ts';

const provider = {id:'fixture',name:'Synthetic fixture',version:'1'};
const bytes=buildManagedPeFixture({pinvoke:{moduleName:'caseprobe',importName:'probe',mappingFlags:0x0101}});
const target=managedPeFixtureTarget(bytes);
const result=inspectManagedNativeBoundariesBytes(bytes,target);
const managed=createEvidence(target,provider,{operation:'inspect_managed_native_boundaries',parameters:{},result,rawResult:null});
const dossier=functionDossierSchema.parse({
  procedure:{address:'0x1000',name:'probe',classification:{external:false,thunk:false,thunk_target:null,provenance:'synthetic-provider'},body:{available:false,reason:'Synthetic fixture'},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:[]
});
for(const name of ['libcaseprobe.so','libCaseProbe.so','libother.so']){
  const native=createEvidence({path:'/tmp/'+name,sha256:'1'.repeat(64),format:'elf'},provider,{operation:'analyze_function',parameters:{},result:dossier,rawResult:null});
  const verified=verifyManagedNativeBoundaries({managed_boundaries:managed,native_observations:[native]});
  const item=verified.pinvoke_imports[0];
  console.log(JSON.stringify({declared:item.managed.import_scope_name,module:name,status:item.status,basis:item.basis,coverage:verified.coverage.status}));
}

The single run produced all three results shown below.

Expected behavior

A case-only mismatch between known ELF module identities must not establish an observed exact declaration link. Preserve the supplied spelling and either report the mismatch or retain uncertainty when the target platform cannot establish the applicable name equivalence.

The managed/native boundary rules say that verification compares declared module and entry-point identities. The .NET loading rules describe Unix lib/extension variants, not arbitrary case folding. Linux shared-library filenames can be case-distinct.

Impact and scope

A caller can receive an observed declaration link to the wrong case-distinct ELF module. The demonstrated scope is the deterministic domain verifier for one P/Invoke and supplied function Evidence. Real CLR binding and provider collection were not tested. Manual comparison of the original module strings can reveal the discrepancy; no public case-sensitive verification option was demonstrated.

REA version

Source revision 23def04096869b85c4d3c14a73ed09168483c9f6 (package metadata: 6.7.0). The published package was not executed.

Runtime and platform

Node.js v26.12.1; Linux x86_64.

Target and artifact context

Generated PE/CLI fixture declaring probe in caseprobe; synthetic, authenticated analyze_function Evidence for ELF modules under /tmp. No external target or sensitive sample.

Interface

Other

Earliest failure stage

Native analysis or reconstruction

Relevant output or evidence

Observed output of the script above:

{"declared":"caseprobe","module":"libcaseprobe.so","status":"verified","basis":"exact-function-name","coverage":"complete-within-inputs"}
{"declared":"caseprobe","module":"libCaseProbe.so","status":"verified","basis":"exact-function-name","coverage":"complete-within-inputs"}
{"declared":"caseprobe","module":"libother.so","status":"contradicted","basis":"module-mismatch","coverage":"complete-within-inputs"}

Typed outcome or recovery expected

Do not return verified / observed solely because case-distinct ELF module names become equal after lowercasing.

Analysis or suspected cause

Source-derived explanation: normalizeModule and nativeModuleIdentities lowercase every module name before matching, without target-specific case semantics.

Additional context

#3351 concerns Windows path separators interpreted on another host. This report concerns a false positive after case folding of ELF module names.

Triage assessment

  • Impact level: P2
  • Assessment status: supported
  • Impact basis: The executed verifier links known case-distinct ELF module names with observed confidence, producing incorrect analysis evidence.
  • Workaround status: available
  • Workaround basis: The result retains the original declared and candidate module names, so callers can compare their case explicitly before relying on the link.
Originally created by @coygeek on GitHub (Oct 23, 2026). Original GitHub issue: https://github.com/morluto/rea/issues/6652 ### 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 The production managed/native declaration verifier reports `verified`, `confidence: observed`, and `basis: exact-function-name` when a P/Invoke names `caseprobe` but the supplied ELF function Evidence belongs to `libCaseProbe.so`. It returns the same result for the case-matching `libcaseprobe.so` control. The differently named `libother.so` control is correctly contradicted. This was reproduced through the source domain API with a generated PE/CLI fixture and authenticated synthetic native Evidence. No CLR or native analysis engine was run, and this report does not claim that a runtime call occurred. ### Reproduction From a checkout of the revision below with its locked dependencies installed, save the following as `module-case.mts` in the repository root and run `node_modules/.bin/tsx module-case.mts`: ```ts import { buildManagedPeFixture, managedPeFixtureTarget } from './src/dotnet/ManagedPe.fixture.ts'; import { inspectManagedNativeBoundariesBytes } from './src/dotnet/ManagedNativeBoundaryInspector.ts'; import { createEvidence } from './src/domain/evidence.ts'; import { verifyManagedNativeBoundaries } from './src/domain/managed/managedNativeVerification.ts'; import { functionDossierSchema } from './src/domain/hopperValues.ts'; const provider = {id:'fixture',name:'Synthetic fixture',version:'1'}; const bytes=buildManagedPeFixture({pinvoke:{moduleName:'caseprobe',importName:'probe',mappingFlags:0x0101}}); const target=managedPeFixtureTarget(bytes); const result=inspectManagedNativeBoundariesBytes(bytes,target); const managed=createEvidence(target,provider,{operation:'inspect_managed_native_boundaries',parameters:{},result,rawResult:null}); const dossier=functionDossierSchema.parse({ procedure:{address:'0x1000',name:'probe',classification:{external:false,thunk:false,thunk_target:null,provenance:'synthetic-provider'},body:{available:false,reason:'Synthetic fixture'},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:[] }); for(const name of ['libcaseprobe.so','libCaseProbe.so','libother.so']){ const native=createEvidence({path:'/tmp/'+name,sha256:'1'.repeat(64),format:'elf'},provider,{operation:'analyze_function',parameters:{},result:dossier,rawResult:null}); const verified=verifyManagedNativeBoundaries({managed_boundaries:managed,native_observations:[native]}); const item=verified.pinvoke_imports[0]; console.log(JSON.stringify({declared:item.managed.import_scope_name,module:name,status:item.status,basis:item.basis,coverage:verified.coverage.status})); } ``` The single run produced all three results shown below. ### Expected behavior A case-only mismatch between known ELF module identities must not establish an observed exact declaration link. Preserve the supplied spelling and either report the mismatch or retain uncertainty when the target platform cannot establish the applicable name equivalence. [The managed/native boundary rules](https://github.com/morluto/rea/blob/23def04096869b85c4d3c14a73ed09168483c9f6/docs/managed-code-analysis.md#managed-native-boundary-rules) say that verification compares declared module and entry-point identities. [The .NET loading rules](https://learn.microsoft.com/en-us/dotnet/standard/native-interop/native-library-loading) describe Unix `lib`/extension variants, not arbitrary case folding. Linux shared-library filenames can be case-distinct. ### Impact and scope A caller can receive an observed declaration link to the wrong case-distinct ELF module. The demonstrated scope is the deterministic domain verifier for one P/Invoke and supplied function Evidence. Real CLR binding and provider collection were not tested. Manual comparison of the original module strings can reveal the discrepancy; no public case-sensitive verification option was demonstrated. ### REA version Source revision `23def04096869b85c4d3c14a73ed09168483c9f6` (package metadata: 6.7.0). The published package was not executed. ### Runtime and platform Node.js v26.12.1; Linux x86_64. ### Target and artifact context Generated PE/CLI fixture declaring `probe` in `caseprobe`; synthetic, authenticated `analyze_function` Evidence for ELF modules under `/tmp`. No external target or sensitive sample. ### Interface Other ### Earliest failure stage Native analysis or reconstruction ### Relevant output or evidence Observed output of the script above: ```jsonl {"declared":"caseprobe","module":"libcaseprobe.so","status":"verified","basis":"exact-function-name","coverage":"complete-within-inputs"} {"declared":"caseprobe","module":"libCaseProbe.so","status":"verified","basis":"exact-function-name","coverage":"complete-within-inputs"} {"declared":"caseprobe","module":"libother.so","status":"contradicted","basis":"module-mismatch","coverage":"complete-within-inputs"} ``` ### Typed outcome or recovery expected Do not return `verified` / `observed` solely because case-distinct ELF module names become equal after lowercasing. ### Analysis or suspected cause Source-derived explanation: [`normalizeModule` and `nativeModuleIdentities`](https://github.com/morluto/rea/blob/23def04096869b85c4d3c14a73ed09168483c9f6/src/domain/managed/managedNativeVerificationMatch.ts#L47-L64) lowercase every module name before matching, without target-specific case semantics. ### Additional context [#3351](https://github.com/morluto/rea/issues/3351) concerns Windows path separators interpreted on another host. This report concerns a false positive after case folding of ELF module names. ## Triage assessment - Impact level: P2 - Assessment status: supported - Impact basis: The executed verifier links known case-distinct ELF module names with observed confidence, producing incorrect analysis evidence. - Workaround status: available - Workaround basis: The result retains the original declared and candidate module names, so callers can compare their case explicitly before relying on the link.
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#4030
No description provided.