fix(apple): map Intel version-min to simulators; fail truncated cache hashes

This commit is contained in:
morluto
2026-10-08 16:23:05 +08:00
parent 24c8e13909
commit d4ba8f661b
3 changed files with 38 additions and 4 deletions
+15 -2
View File
@@ -552,18 +552,31 @@ export class DyldSharedCache {
): Promise<string> {
const hash = createHash("sha256");
const buffer = Buffer.alloc(HASH_CHUNK_BYTES);
for (let position = 0; position < file.size;) {
let position = 0;
for (;;) {
signal?.throwIfAborted();
if (position >= file.size) break;
const { bytesRead } = await file.handle.read(
buffer,
0,
Math.min(buffer.length, file.size - position),
position,
);
if (bytesRead === 0) break;
// A file truncated after open must not yield the surviving prefix's
// digest as the identity of the recorded size.
if (bytesRead === 0)
throw new ArtifactReaderFailure(
"integrity",
"dyld cache file changed while its digest was read",
);
hash.update(buffer.subarray(0, bytesRead));
position += bytesRead;
}
if (position !== file.size)
throw new ArtifactReaderFailure(
"integrity",
"dyld cache file changed while its digest was read",
);
return hash.digest("hex");
}
@@ -186,6 +186,13 @@ describe("Mach-O load command reader inputs", () => {
{ id: 2, name: "ios" },
{ id: 99, name: null },
]);
// Legacy version-min commands on Intel slices denote simulators.
const intel = await read(
machoImage({ cpu: CPU.x86_64, commands: [versionMin] }),
);
expect(intel.status === "parsed" && intel.slices[0]?.platforms).toEqual([
{ id: 7, name: "ios-simulator" },
]);
// A 16-byte LC_BUILD_VERSION, and one that declares a tool it lacks.
const truncated = buildVersionCommand(1).slice(0, 16);
new DataView(truncated.buffer).setUint32(4, 16, true);
+16 -2
View File
@@ -268,7 +268,7 @@ const decodeCommands = (
`load command ${index} at offset ${offset} has invalid cmdsize ${size}`,
);
const body = bytes.subarray(offset, offset + size);
decodeCommand(command, body, index, slice);
decodeCommand(command, body, index, slice, header.cpuType);
offset += size;
}
// A header that declares fewer commands than its sizeofcmds could hide
@@ -286,6 +286,7 @@ const decodeCommand = (
body: Uint8Array,
index: number,
slice: MachoSlice,
cpuType: number,
): void => {
if (command === LC_ID_DYLIB) {
slice.install_name = commandString(body, 24, index);
@@ -324,7 +325,20 @@ const decodeCommand = (
"malformed",
`load command ${index} is too short for a platform version`,
);
const id = implied ?? viewOf(body).getUint32(8, true);
const declared = implied ?? viewOf(body).getUint32(8, true);
// Legacy LC_VERSION_MIN_* commands on Intel slices denote simulator
// platforms; Apple's decoder branches on the CPU type for these commands.
const intel = cpuType === 7 || cpuType === (7 | 0x01000000);
const id =
implied !== undefined && intel
? declared === 2
? 7
: declared === 3
? 8
: declared === 4
? 9
: declared
: declared;
if (!slice.platforms.some((platform) => platform.id === id))
slice.platforms.push(applePlatform(id));
return;