CISOFileBlockDevice: reject frame/block count mismatches

numFrames and numBlocks are derived from the same attacker-controlled
64-bit total_bytes field, but independently truncated to 32 bits using
different divisors (frameSize vs. the fixed 2048-byte block size).
With extreme total_bytes/block_size combinations the two truncations
can disagree so that numBlocks (which gates ReadBlock's bounds check)
describes more blocks than numFrames actually covers - ReadBlock then
indexes the numFrames+1-sized `index` array one or more elements past
its end. Reject any header where this could happen before allocating
anything.
This commit is contained in:
Henrik Rydgård committed 2026-08-11 08:57:15 +02:00
1 parent e94463b4b6
commit a82043eb9d
1 file changed
+12
+12
View File
@@ -556,6 +556,18 @@ CISOFileBlockDevice::CISOFileBlockDevice(FileLoader *fileLoader)
numBlocks = (u32)(totalSize / GetBlockSize());
VERBOSE_LOG(Log::Loader, "CSO numBlocks=%i numFrames=%i align=%i", numBlocks, numFrames, indexShift);
// numFrames and numBlocks are independently truncated to 32 bits from the same
// attacker-controlled 64-bit total_bytes, using different divisors (frameSize vs.
// the fixed 2048-byte block size). With extreme total_bytes/block_size values these
// can disagree so that numBlocks describes more blocks than numFrames actually has
// frames for - ReadBlock() would then index the numFrames+1-sized `index` array
// (via frameNumber+1, with frameNumber derived from a blockNumber < numBlocks) out
// of bounds. Reject any header where that could happen.
if ((u64)numBlocks > (u64)numFrames << blockShift) {
errorString_ = "Invalid CSO header (block/frame size mismatch)";
return;
}
// We might read a bit of alignment too, so be prepared.
readBufferSize = frameSize + (1u << indexShift);
if (readBufferSize < CSO_READ_BUFFER_SIZE)