fix(managed): classify ReadyToRun images without ILONLY as modern .NET (#2075)

* fix(managed): classify ReadyToRun images without ILONLY as modern .NET

crossgen2 clears COMIMAGE_FLAGS_ILONLY and sets IL_LIBRARY on ReadyToRun output. inspect_managed_artifact checked the clear ILONLY flag before the ReadyToRun signature, so real ReadyToRun assemblies were reported as runtime_family mixed-clr-native and implementation cpp-cli-mixed, and cil-and-ready-to-run was unreachable for crossgen2 output. Check the ReadyToRun signature first; a clear ILONLY flag without it still reports mixed CLR/native code.

* test(managed): exhaust capacity through inspected assembly references

---------

Co-authored-by: morluto <76467478+morluto@users.noreply.github.com>
This commit is contained in:
TonyWu 2026-10-13 21:13:18 +08:00 committed by GitHub
parent 9c14999efd
commit cdc41fc86c
No known key found for this signature in database
GPG Key ID: B5690EEEBB952194
2 changed files with 71 additions and 4 deletions

View File

@ -547,6 +547,63 @@ describe("managed fixture data layout", () => {
});
});
describe("managed implementation classification", () => {
it("classifies crossgen2 ReadyToRun output without ILONLY as modern .NET", () => {
// crossgen2 writes IL_LIBRARY | STRONGNAMESIGNED and clears ILONLY.
const bytes = buildManagedPeFixture({ readyToRun: true, cliFlags: 0xc });
const result = inspectManagedArtifactBytes(
bytes,
managedPeFixtureTarget(bytes),
);
expect(result.classification).toMatchObject({
runtime_family: "modern-dotnet",
implementation: "cil-and-ready-to-run",
});
expect(result.classification.evidence).toContainEqual(
expect.objectContaining({ code: "ready-to-run-signature-observed" }),
);
expect(result.classification.evidence).not.toContainEqual(
expect.objectContaining({ code: "cli-il-only-clear" }),
);
});
it("preserves ReadyToRun runtime identity after metadata capacity is exhausted", () => {
const bytes = buildManagedPeFixture({
readyToRun: true,
cliFlags: 0xc,
references: ["x".repeat(12 * 1024 * 1024)],
});
const result = inspectManagedArtifactBytes(
bytes,
managedPeFixtureTarget(bytes),
);
expect(result.classification).toMatchObject({
status: "managed",
runtime_family: "modern-dotnet",
});
expect(result.coverage.issues).toContainEqual(
expect.objectContaining({ code: "resource-limit" }),
);
});
it("keeps a clear ILONLY flag without ReadyToRun as mixed CLR/native", () => {
const bytes = buildManagedPeFixture({ cliFlags: 0 });
const result = inspectManagedArtifactBytes(
bytes,
managedPeFixtureTarget(bytes),
);
expect(result.classification).toMatchObject({
runtime_family: "mixed-clr-native",
implementation: "cpp-cli-mixed",
});
expect(result.classification.evidence).toContainEqual(
expect.objectContaining({ code: "cli-il-only-clear" }),
);
});
});
describe("managed artifact metadata identity", () => {
it("accepts CLI metadata GUIDs without RFC UUID version or variant bits", () => {
const bytes = buildManagedPeFixture({

View File

@ -158,6 +158,15 @@ interface RuntimeClassificationInput {
readonly inventory: ManagedMetadataInventory;
}
const runtimeFamilyFromCliHeader = (
cli: ManagedCliHeader | null,
): ManagedArtifactInspection["classification"]["runtime_family"] =>
cli?.readyToRunSignature === true
? "modern-dotnet"
: cli !== null && (cli.flags & 1) === 0
? "mixed-clr-native"
: "unknown";
const runtimeFamilyFor = (
cli: ManagedCliHeader,
inventory: ManagedMetadataInventory,
@ -180,7 +189,9 @@ const runtimeFamilyFor = (
);
let runtimeFamily: ManagedArtifactInspection["classification"]["runtime_family"] =
"unknown";
if ((cli.flags & 1) === 0) {
// crossgen2 clears ILONLY on ReadyToRun output, so only a clear flag
// without the ReadyToRun signature indicates mixed CLR/native code.
if (runtimeFamilyFromCliHeader(cli) === "mixed-clr-native") {
runtimeFamily = "mixed-clr-native";
evidence.push({
code: "cli-il-only-clear",
@ -239,8 +250,8 @@ const implementationFor = (
cli: ManagedCliHeader,
metadata: ManagedMetadataLayout,
): ManagedArtifactInspection["classification"]["implementation"] => {
if ((cli.flags & 1) === 0) return "cpp-cli-mixed";
if (cli.readyToRunSignature) return "cil-and-ready-to-run";
if ((cli.flags & 1) === 0) return "cpp-cli-mixed";
return (metadata.rowCounts[6] ?? 0) === 0 ? "metadata-only" : "cil";
};
@ -303,8 +314,7 @@ const partialMetadataResult = ({
classification: {
status: issue.code === "resource-limit" ? "managed" : "malformed",
container: "pe",
runtime_family:
(layout.cli?.flags ?? 1) & 1 ? "unknown" : "mixed-clr-native",
runtime_family: runtimeFamilyFromCliHeader(layout.cli),
implementation: "unknown",
managed_architecture: "unknown",
evidence: [