fix(il2cpp): validate generic-container extents before index widths

Validate the fixed 16-byte version-39 records before using their count to select the serialized index width. Reject contradictory count and byte extents while preserving valid zero, one and 300-record controls.

Validation: 89 focused reader/provider/compiled CLI and stdio MCP tests; check:ci; seven reproduced CLI and SDK-validated MCP cases with unchanged fixture hashes. Version-specific source and independent parser evidence distinguish fixed records from compressed indices. Synthetic metadata only; no real-game or target-execution qualification.

Fixes #5751
This commit is contained in:
morluto committed 2026-10-21 12:55:35 +08:00
1 parent afa5da745e
commit fa8b87c470
3 files changed
+143 -10

No files matched your search

@@ -261,13 +261,6 @@ describe("rejected IL2CPP global-metadata", () => {
),
"record-size-mismatch",
);
// 300 generic containers widen GenericContainerIndex to two bytes.
rejects(
mutated((fixture, bytes) =>
bytes.writeInt32LE(300, fixture.header("genericContainers", "count")),
),
"record-size-mismatch",
);
rejects(
mutated((fixture, bytes) =>
bytes.writeInt32LE(2, fixture.header("nestedTypes", "count")),
@@ -290,6 +283,62 @@ describe("rejected IL2CPP global-metadata", () => {
});
});
describe("IL2CPP generic-container extents", () => {
it("checks generic-container count against its fixed 16-byte records before resolving widths", () => {
// A positive count with no record bytes.
for (const count of [1, 300])
rejects(
mutated((fixture, bytes) =>
bytes.writeInt32LE(
count,
fixture.header("genericContainers", "count"),
),
),
"record-size-mismatch",
);
const oneRecord = createIl2cppMetadataFixture({
wide: { fillerTypes: 0, genericContainers: 1, parameters: 0 },
});
// A positive count with a partial record.
rejects(
mutated(
(fixture, bytes) =>
bytes.writeInt32LE(1, fixture.header("genericContainers", "size")),
oneRecord,
),
"record-size-mismatch",
);
// Bytes without a corresponding record.
rejects(
mutated(
(fixture, bytes) =>
bytes.writeInt32LE(0, fixture.header("genericContainers", "count")),
oneRecord,
),
"record-size-mismatch",
);
});
it("retains width selection for empty and complete generic-container records", () => {
const empty = readIl2cppMetadata(createIl2cppMetadataFixture().bytes);
expect(empty.index_widths.generic_container).toBe(1);
const one = readIl2cppMetadata(
createIl2cppMetadataFixture({
wide: { fillerTypes: 0, genericContainers: 1, parameters: 0 },
}).bytes,
);
expect(one.index_widths.generic_container).toBe(1);
const wide = readIl2cppMetadata(
createIl2cppMetadataFixture({
wide: { fillerTypes: 0, genericContainers: 300, parameters: 0 },
}).bytes,
);
expect(wide.index_widths.generic_container).toBe(2);
});
});
describe("rejected IL2CPP metadata references", () => {
it("rejects image, type, method and nested-type indices outside their tables", () => {
rejects(