[GH-ISSUE #6810] [Bug] Managed runtime classification hides ReadyToRun behind assembly hints #4158

Closed
opened 2026-10-31 00:47:15 +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/6810

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 managed artifact reader admits a ReadyToRun signature but reports runtime_family: "unity-mono" when an AssemblyRef names UnityEngine.CoreModule, or "dotnet-framework" when TargetFrameworkAttribute names .NETFramework. Both results retain implementation: "cil-and-ready-to-run" and complete coverage, but omit ready-to-run-signature-observed from classification evidence. The same fixture without these metadata hints reports modern-dotnet.

Observed through the production byte-reader API with benign synthetic PE fixtures; no CLR or target code was executed.

Reproduction

From this source checkout with locked dependencies installed, save the following as /tmp/rea-r2r.ts and run REA_ROOT="$PWD" node_modules/.bin/tsx /tmp/rea-r2r.ts:

import { pathToFileURL } from 'node:url';
import { resolve } from 'node:path';
async function main() {
  const root = process.env.REA_ROOT ?? process.cwd();
  const fixture = await import(pathToFileURL(resolve(root, 'src/dotnet/ManagedPe.fixture.ts')).href);
  const { inspectManagedArtifactBytes } = await import(pathToFileURL(resolve(root, 'src/dotnet/ManagedArtifactInspector.ts')).href);
  for (const [label, options] of [
    ['r2r baseline', { readyToRun: true, cliFlags: 0xc }],
    ['r2r Unity reference', { readyToRun: true, cliFlags: 0xc, references: ['System.Runtime', 'UnityEngine.CoreModule'] }],
    ['r2r Framework target', { readyToRun: true, cliFlags: 0xc, targetFramework: '.NETFramework,Version=v4.8' }],
  ] as const) {
    const bytes = fixture.buildManagedPeFixture(options);
    const report = inspectManagedArtifactBytes(bytes, fixture.managedPeFixtureTarget(bytes));
    console.log(JSON.stringify({ label, readyToRun: report.pe.cli.ready_to_run_signature,
      runtime_family: report.classification.runtime_family,
      implementation: report.classification.implementation,
      classification_evidence: report.classification.evidence.map((e: {code: string}) => e.code),
      metadata_status: report.metadata.status, coverage: report.coverage.state }));
  }
}
main().catch(error => { console.error(error); process.exitCode = 1; });

One run produced the output below for all three cases.

Expected behavior

An observed ReadyToRun marker should remain part of classification evidence, and a Unity assembly reference or target API attribute should not silently override it with a contradictory runtime family. The existing ReadyToRun classification test expects modern-dotnet for an admitted ReadyToRun signature, including crossgen2 output with ILONLY clear. The header classifier also explicitly returns modern-dotnet first.

Impact and scope

Static inspection can direct analysts toward the wrong runtime family and conceal the stronger observed marker. This reproduced for both reduced fixtures; real Unity-referencing or legacy-targeted ReadyToRun producer files were not tested. The raw pe.cli.ready_to_run_signature and implementation field remain available, so callers can inspect these directly as a workaround.

REA version

6.7.0; source revision e2efd10b70a2b5f15a7ac1b5a457a9a1dbcab4d4.

Runtime and platform

Node.js v24.22.1; Linux x86_64; source imports through the locked tsx dependency. No analysis engine or CLR required.

Target and artifact context

In-memory synthetic PE/CLI fixtures from buildManagedPeFixture, with a ReadyToRun header marker plus either a UnityEngine assembly reference or a .NET Framework target API attribute.

Interface

Other

Earliest failure stage

Native analysis or reconstruction

Relevant output or evidence

Actual output (one run):

{"label":"r2r baseline","readyToRun":true,"runtime_family":"modern-dotnet","implementation":"cil-and-ready-to-run","classification_evidence":["cli-header-observed","ready-to-run-signature-observed","managed-architecture-derived"],"metadata_status":"complete","coverage":"complete"}
{"label":"r2r Unity reference","readyToRun":true,"runtime_family":"unity-mono","implementation":"cil-and-ready-to-run","classification_evidence":["cli-header-observed","unity-reference-observed","managed-architecture-derived"],"metadata_status":"complete","coverage":"complete"}
{"label":"r2r Framework target","readyToRun":true,"runtime_family":"dotnet-framework","implementation":"cil-and-ready-to-run","classification_evidence":["cli-header-observed","target-framework-observed","managed-architecture-derived"],"metadata_status":"complete","coverage":"complete"}

The production classification chain tests Unity references and .NETFramework targets before reaching its ReadyToRun branch. This is source-derived cause attribution; the outputs above are runtime observations of that reader.

Typed outcome or recovery expected

Preserve ready-to-run-signature-observed and classify the runtime consistently with the admitted marker, or explicitly represent conflicting evidence instead of claiming a complete contradictory classification.

Analysis or suspected cause

The header classifier gives ReadyToRun priority, but the subsequent metadata classifier does not. These two interpretations disagree when an earlier metadata hint is present.

Additional context

Searched open inventory and all-state upstream searches for ReadyToRun, ReadyToRun Unity, and runtime family. Related #2584 concerns detection with larger method lists and #1736 concerns .NET Standard, rather than this precedence error. No duplicate found. No real producer file or public CLI/MCP end-to-end run was used; the complete reduced repro calls the production reader directly.

Triage assessment

  • Impact level: P2
  • Assessment status: supported
  • Impact basis: The static artifact result reports a contradictory runtime family and drops ReadyToRun classification evidence for two synthetic metadata combinations, while claiming complete coverage.
  • Workaround status: available
  • Workaround basis: The same observed results retain pe.cli.ready_to_run_signature: true and implementation: "cil-and-ready-to-run", allowing callers to identify the marker without relying on runtime_family.
Originally created by @coygeek on GitHub (Oct 23, 2026). Original GitHub issue: https://github.com/morluto/rea/issues/6810 ### 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 managed artifact reader admits a ReadyToRun signature but reports `runtime_family: "unity-mono"` when an AssemblyRef names `UnityEngine.CoreModule`, or `"dotnet-framework"` when TargetFrameworkAttribute names `.NETFramework`. Both results retain `implementation: "cil-and-ready-to-run"` and complete coverage, but omit `ready-to-run-signature-observed` from classification evidence. The same fixture without these metadata hints reports `modern-dotnet`. Observed through the production byte-reader API with benign synthetic PE fixtures; no CLR or target code was executed. ### Reproduction From this source checkout with locked dependencies installed, save the following as `/tmp/rea-r2r.ts` and run `REA_ROOT="$PWD" node_modules/.bin/tsx /tmp/rea-r2r.ts`: ```ts import { pathToFileURL } from 'node:url'; import { resolve } from 'node:path'; async function main() { const root = process.env.REA_ROOT ?? process.cwd(); const fixture = await import(pathToFileURL(resolve(root, 'src/dotnet/ManagedPe.fixture.ts')).href); const { inspectManagedArtifactBytes } = await import(pathToFileURL(resolve(root, 'src/dotnet/ManagedArtifactInspector.ts')).href); for (const [label, options] of [ ['r2r baseline', { readyToRun: true, cliFlags: 0xc }], ['r2r Unity reference', { readyToRun: true, cliFlags: 0xc, references: ['System.Runtime', 'UnityEngine.CoreModule'] }], ['r2r Framework target', { readyToRun: true, cliFlags: 0xc, targetFramework: '.NETFramework,Version=v4.8' }], ] as const) { const bytes = fixture.buildManagedPeFixture(options); const report = inspectManagedArtifactBytes(bytes, fixture.managedPeFixtureTarget(bytes)); console.log(JSON.stringify({ label, readyToRun: report.pe.cli.ready_to_run_signature, runtime_family: report.classification.runtime_family, implementation: report.classification.implementation, classification_evidence: report.classification.evidence.map((e: {code: string}) => e.code), metadata_status: report.metadata.status, coverage: report.coverage.state })); } } main().catch(error => { console.error(error); process.exitCode = 1; }); ``` One run produced the output below for all three cases. ### Expected behavior An observed ReadyToRun marker should remain part of classification evidence, and a Unity assembly reference or target API attribute should not silently override it with a contradictory runtime family. The existing [ReadyToRun classification test](https://github.com/morluto/rea/blob/e2efd10b70a2b5f15a7ac1b5a457a9a1dbcab4d4/src/dotnet/ManagedArtifactInspector.test.ts#L551) expects `modern-dotnet` for an admitted ReadyToRun signature, including crossgen2 output with ILONLY clear. The header classifier also explicitly [returns `modern-dotnet` first](https://github.com/morluto/rea/blob/e2efd10b70a2b5f15a7ac1b5a457a9a1dbcab4d4/src/dotnet/ManagedArtifactInspector.ts#L163). ### Impact and scope Static inspection can direct analysts toward the wrong runtime family and conceal the stronger observed marker. This reproduced for both reduced fixtures; real Unity-referencing or legacy-targeted ReadyToRun producer files were not tested. The raw `pe.cli.ready_to_run_signature` and implementation field remain available, so callers can inspect these directly as a workaround. ### REA version 6.7.0; source revision `e2efd10b70a2b5f15a7ac1b5a457a9a1dbcab4d4`. ### Runtime and platform Node.js v24.22.1; Linux x86_64; source imports through the locked tsx dependency. No analysis engine or CLR required. ### Target and artifact context In-memory synthetic PE/CLI fixtures from `buildManagedPeFixture`, with a ReadyToRun header marker plus either a UnityEngine assembly reference or a .NET Framework target API attribute. ### Interface Other ### Earliest failure stage Native analysis or reconstruction ### Relevant output or evidence Actual output (one run): ```jsonl {"label":"r2r baseline","readyToRun":true,"runtime_family":"modern-dotnet","implementation":"cil-and-ready-to-run","classification_evidence":["cli-header-observed","ready-to-run-signature-observed","managed-architecture-derived"],"metadata_status":"complete","coverage":"complete"} {"label":"r2r Unity reference","readyToRun":true,"runtime_family":"unity-mono","implementation":"cil-and-ready-to-run","classification_evidence":["cli-header-observed","unity-reference-observed","managed-architecture-derived"],"metadata_status":"complete","coverage":"complete"} {"label":"r2r Framework target","readyToRun":true,"runtime_family":"dotnet-framework","implementation":"cil-and-ready-to-run","classification_evidence":["cli-header-observed","target-framework-observed","managed-architecture-derived"],"metadata_status":"complete","coverage":"complete"} ``` The [production classification chain](https://github.com/morluto/rea/blob/e2efd10b70a2b5f15a7ac1b5a457a9a1dbcab4d4/src/dotnet/ManagedArtifactInspector.ts#L194) tests Unity references and `.NETFramework` targets before reaching its ReadyToRun branch. This is source-derived cause attribution; the outputs above are runtime observations of that reader. ### Typed outcome or recovery expected Preserve `ready-to-run-signature-observed` and classify the runtime consistently with the admitted marker, or explicitly represent conflicting evidence instead of claiming a complete contradictory classification. ### Analysis or suspected cause The header classifier gives ReadyToRun priority, but the subsequent metadata classifier does not. These two interpretations disagree when an earlier metadata hint is present. ### Additional context Searched open inventory and all-state upstream searches for `ReadyToRun`, `ReadyToRun Unity`, and `runtime family`. Related #2584 concerns detection with larger method lists and #1736 concerns .NET Standard, rather than this precedence error. No duplicate found. No real producer file or public CLI/MCP end-to-end run was used; the complete reduced repro calls the production reader directly. ## Triage assessment - Impact level: P2 - Assessment status: supported - Impact basis: The static artifact result reports a contradictory runtime family and drops ReadyToRun classification evidence for two synthetic metadata combinations, while claiming complete coverage. - Workaround status: available - Workaround basis: The same observed results retain `pe.cli.ready_to_run_signature: true` and `implementation: "cil-and-ready-to-run"`, allowing callers to identify the marker without relying on `runtime_family`.
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#4158
No description provided.