fix(il2cpp): validate retained declaring type relationships (#7946)

This commit is contained in:
morluto authored and GitHub committed 2026-10-29 08:16:57 +08:00
1 parent 5147b6e306
commit ef9ab91e42
5 files changed
+124 -24

No files matched your search

+26
View File
@@ -0,0 +1,26 @@
/** Find a cycle in positional declaring-type links, yielding during long walks. */
export function* firstDeclaringTypeCycle(
types: readonly { readonly declaring_type: number }[],
): Generator<void, number | null> {
// 0 unvisited, 1 on the current chain, 2 already examined.
const state = new Uint8Array(types.length);
let work = 0;
for (let first = 0; first < types.length; first += 1) {
if (work++ % 256 === 0) yield;
const chain: number[] = [];
let current = first;
while (current >= 0 && current < types.length && state[current] === 0) {
if (work++ % 256 === 0) yield;
state[current] = 1;
chain.push(current);
current = types[current]!.declaring_type;
}
if (current >= 0 && current < types.length && state[current] === 1)
return current;
for (const index of chain) {
if (work++ % 256 === 0) yield;
state[index] = 2;
}
}
return null;
}
+24 -1
View File
@@ -5,6 +5,7 @@ import {
localPathStringSchema,
} from "../localPath.js";
import { canonicalBase64Schema } from "../stringPatterns.js";
import { firstDeclaringTypeCycle } from "./il2cppDeclaringTypes.js";
/** Unsigned little-endian signature observed at file offset zero. */
export const IL2CPP_METADATA_MAGIC = 0xfab11baf;
@@ -452,7 +453,29 @@ const validateMetadataFacts = (
"Only a code row carries an address and RVA; both are then present.",
});
});
for (const collection of ["fields", "properties", "events"] as const)
report.types.forEach((row, index) => {
if (row.declaring_type >= report.types.length)
context.addIssue({
code: "custom",
path: ["types", index, "declaring_type"],
message: "Enclosing type is outside the retained type definitions.",
});
});
const cycleWalk = firstDeclaringTypeCycle(report.types);
let cycle = cycleWalk.next();
while (!cycle.done) cycle = cycleWalk.next();
if (cycle.value !== null)
context.addIssue({
code: "custom",
path: ["types", cycle.value, "declaring_type"],
message: "Enclosing type relationships contain a cycle.",
});
for (const collection of [
"methods",
"fields",
"properties",
"events",
] as const)
report[collection]?.forEach((row, index) => {
if (row.declaring_type >= report.types.length)
context.addIssue({