ISOFileSystem: fix path table re-read bug and validate directory entry size

Ioctl's ISO9660 path table read re-read sector `block` (the first
sector, already consumed by the preceding ReadBlocks) for the trailing
partial sector instead of `block + blocks`, returning duplicated data
from the start of the table instead of its actual tail.

ReadDirectory() advanced by the raw on-disk dir.size without checking
it's at least as large as the record's own header+identifier. A
crafted directory sector could set dir.size = 1 repeatedly, making the
loop reinterpret the same overlapping bytes as many separate entries -
allocating far more TreeEntry objects than the sector's actual size
should allow.
This commit is contained in:
Henrik Rydgård
2026-08-11 08:57:15 +02:00
parent c329363a7d
commit f15f8453f0
+13 -1
View File
@@ -222,6 +222,16 @@ void ISOFileSystem::ReadDirectory(TreeEntry *root) const {
ERROR_LOG(Log::FileSystem, "Directory entry crosses sectors, corrupt iso?");
return;
}
// dir.size (the record length actually consumed) must cover at least its
// own header and identifier, or a crafted sector could set it to 1 and
// make the loop reinterpret the same overlapping bytes as many separate
// entries, allocating far more TreeEntry objects than the sector's real
// size warrants.
if (dir.size < IDENTIFIER_OFFSET + dir.identifierLength) {
blockDevice->NotifyReadError();
ERROR_LOG(Log::FileSystem, "Directory entry size too small, corrupt iso?");
return;
}
offset += dir.size;
@@ -455,7 +465,9 @@ int ISOFileSystem::Ioctl(u32 handle, u32 cmd, u32 indataPtr, u32 inlen, u32 outd
// The remaining (or, usually, only) partial sector.
if (size > 0) {
u8 temp[2048];
blockDevice->ReadBlock(block, temp);
// `blocks` whole sectors starting at `block` were already consumed by
// ReadBlocks() above, so the trailing partial sector is the next one.
blockDevice->ReadBlock(block + blocks, temp);
memcpy(out, temp, size);
}
return 0;