Merge pull request #22208 from acts-1631/security/fix-cso-index-overflow

Reject overflowing CSO index sizes
This commit is contained in:
Henrik Rydgård authored and GitHub committed 2026-09-03 20:36:15 +02:00
commit bdaf6dcaf4
1 file changed
+23 -5
+23 -5
View File
@@ -18,6 +18,7 @@
#include <algorithm>
#include <array>
#include <cstring>
#include <limits>
#include <vector>
#include "Common/Data/Text/I18n.h"
@@ -552,8 +553,17 @@ CISOFileBlockDevice::CISOFileBlockDevice(FileLoader *fileLoader)
return;
}
const u64 totalSize = hdr.total_bytes;
numFrames = (u32)((totalSize + frameSize - 1) / frameSize);
numBlocks = (u32)(totalSize / GetBlockSize());
// Compute the counts without overflowing the 64-bit total size, then
// validate them before truncating to the 32-bit fields used by the CSO
// block reader. In particular, numFrames + 1 must remain representable.
const u64 numFrames64 = totalSize / frameSize + (totalSize % frameSize != 0);
const u64 numBlocks64 = totalSize / GetBlockSize();
if (numFrames64 >= 0xFFFFFFFFull || numBlocks64 > 0xFFFFFFFFull) {
errorString_ = "Invalid CSO header (image is too large)";
return;
}
numFrames = (u32)numFrames64;
numBlocks = (u32)numBlocks64;
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
@@ -568,6 +578,16 @@ CISOFileBlockDevice::CISOFileBlockDevice(FileLoader *fileLoader)
return;
}
const size_t headerEnd = hdr.ver > 1 ? (size_t)hdr.header_size : sizeof(hdr);
const u64 indexSize64 = numFrames64 + 1;
const u64 indexBytes = indexSize64 * sizeof(u32);
const u64 fileSize = fileLoader->FileSize();
if (indexBytes > (u64)std::numeric_limits<size_t>::max() ||
headerEnd > fileSize || indexBytes > fileSize - headerEnd) {
errorString_ = "Invalid CSO header (index table is truncated)";
return;
}
// We might read a bit of alignment too, so be prepared.
readBufferSize = frameSize + (1u << indexShift);
if (readBufferSize < CSO_READ_BUFFER_SIZE)
@@ -576,8 +596,7 @@ CISOFileBlockDevice::CISOFileBlockDevice(FileLoader *fileLoader)
zlibBuffer = new u8[frameSize + (1u << indexShift)];
zlibBufferFrame = numFrames;
const u32 indexSize = numFrames + 1;
const size_t headerEnd = hdr.ver > 1 ? (size_t)hdr.header_size : sizeof(hdr);
const u32 indexSize = (u32)indexSize64;
#if COMMON_LITTLE_ENDIAN
index = new u32[indexSize];
@@ -603,7 +622,6 @@ CISOFileBlockDevice::CISOFileBlockDevice(FileLoader *fileLoader)
ver_ = hdr.ver;
// Double check that the CSO is not truncated. In most cases, this will be the exact size.
u64 fileSize = fileLoader->FileSize();
u64 lastIndexPos = index[indexSize - 1] & 0x7FFFFFFF;
u64 expectedFileSize = lastIndexPos << indexShift;
if (expectedFileSize > fileSize) {