From 9c1f55ed765ddbde376a3588d2b17b90fbda06f3 Mon Sep 17 00:00:00 2001 From: =?UTF-8?q?Henrik=20Rydg=C3=A5rd?= Date: Fri, 4 Sep 2026 12:59:24 -0600 Subject: [PATCH] ISOFileSystem: Clamp file sizes to what the image actually contains The size in an ISO directory record is untrusted, and callers allocate host buffers from it - GetISOGameID and ReadFileToString did so directly until the previous commit, and ReadFile clamps reads to the claimed size rather than to the image. We've warned about out-of-range extents since c766536914, but deliberately kept the file, and rounded down so the warning wouldn't fire on borderline images. Keep that behavior and just clamp the recorded size to the bytes that really exist. For a well-formed file the extent always fits within its sectors, so this never triggers; for a truncated one the game keeps booting instead of losing the file entirely. --- Core/FileSystems/ISOFileSystem.cpp | 14 ++++++++++++++ 1 file changed, 14 insertions(+) diff --git a/Core/FileSystems/ISOFileSystem.cpp b/Core/FileSystems/ISOFileSystem.cpp index 20994af8b4..4a96d9c1a7 100644 --- a/Core/FileSystems/ISOFileSystem.cpp +++ b/Core/FileSystems/ISOFileSystem.cpp @@ -302,6 +302,20 @@ void ISOFileSystem::ReadDirectory(TreeEntry *root) const { ERROR_LOG(Log::FileSystem, "File '%s' starts or ends outside ISO. firstDataSector: %d len: %d", entry->BuildPath().c_str(), (int)dir.firstDataSector, (int)dir.dataLength); } + // The directory record is untrusted, and callers size host buffers from entry->size, so + // don't let it claim more data than the image actually contains. We clamp rather than + // drop the entry - truncated ISOs are common and used to work with just the warning + // above, and dropping EBOOT.BIN would turn that into an unbootable game. For a sane + // file this is a no-op, since the extent always fits in its sectors. + if (isFile) { + const u64 numBlocks = blockDevice->GetNumBlocks(); + const u64 firstSector = dir.firstDataSector; + const s64 availableBytes = firstSector >= numBlocks ? 0 : (s64)((numBlocks - firstSector) * (u64)sectorSize); + if (entry->size > availableBytes) { + entry->size = availableBytes; + } + } + if (entry->isDirectory && !relative) { if (entry->startsector == root->startsector) { blockDevice->NotifyReadError();