diff --git a/src/artifacts/apple/DyldSharedCacheReader.ts b/src/artifacts/apple/DyldSharedCacheReader.ts index 2b5d5e8ee..33de04e09 100644 --- a/src/artifacts/apple/DyldSharedCacheReader.ts +++ b/src/artifacts/apple/DyldSharedCacheReader.ts @@ -552,18 +552,31 @@ export class DyldSharedCache { ): Promise { 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"); } diff --git a/src/artifacts/apple/MachoLoadCommandReader.test.ts b/src/artifacts/apple/MachoLoadCommandReader.test.ts index 125c0b3f5..1d646c342 100644 --- a/src/artifacts/apple/MachoLoadCommandReader.test.ts +++ b/src/artifacts/apple/MachoLoadCommandReader.test.ts @@ -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); diff --git a/src/artifacts/apple/MachoLoadCommandReader.ts b/src/artifacts/apple/MachoLoadCommandReader.ts index b1e3bafad..ea814444c 100644 --- a/src/artifacts/apple/MachoLoadCommandReader.ts +++ b/src/artifacts/apple/MachoLoadCommandReader.ts @@ -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;