1
0
Fork 0
mirror of https://github.com/OpenMW/openmw.git synced 2025-10-18 07:46:38 +00:00

Fix potential overflows on reading BSA header

This commit is contained in:
elsid 2025-10-05 17:17:57 +02:00
parent c87cc643d1
commit 16abb436e2
No known key found for this signature in database
GPG key ID: B845CB9FEE18AB40

View file

@ -115,7 +115,8 @@ void BSAFile::readHeader(std::istream& input)
fail("File too small to be a valid BSA archive"); fail("File too small to be a valid BSA archive");
// Get essential header numbers // Get essential header numbers
size_t dirsize, filenum; std::streamsize dirsize;
std::streamsize filenum;
{ {
// First 12 bytes // First 12 bytes
uint32_t head[3]; uint32_t head[3];
@ -139,7 +140,7 @@ void BSAFile::readHeader(std::istream& input)
// Each file must take up at least 21 bytes of data in the bsa. So // Each file must take up at least 21 bytes of data in the bsa. So
// if files*21 overflows the file size then we are guaranteed that // if files*21 overflows the file size then we are guaranteed that
// the archive is corrupt. // the archive is corrupt.
if ((filenum * 21 > unsigned(fsize - 12)) || (dirsize + 8 * filenum > unsigned(fsize - 12))) if (filenum * 21 > fsize - 12 || dirsize + 8 * filenum > fsize - 12)
fail("Directory information larger than entire archive"); fail("Directory information larger than entire archive");
// Read the offset info into a temporary buffer // Read the offset info into a temporary buffer
@ -168,20 +169,22 @@ void BSAFile::readHeader(std::istream& input)
// Calculate the offset of the data buffer. All file offsets are // Calculate the offset of the data buffer. All file offsets are
// relative to this. 12 header bytes + directory + hash table // relative to this. 12 header bytes + directory + hash table
// (skipped) // (skipped)
size_t fileDataOffset = 12 + dirsize + 8 * filenum; const std::streamsize fileDataOffset = 12 + dirsize + 8 * filenum;
// Set up the the FileStruct table // Set up the the FileStruct table
mFiles.reserve(filenum); mFiles.reserve(filenum);
size_t endOfNameBuffer = 0; size_t endOfNameBuffer = 0;
for (size_t i = 0; i < filenum; i++) for (std::streamsize i = 0; i < filenum; i++)
{ {
FileStruct& fs = mFiles.emplace_back();
const uint32_t fileSize = offsets[i * 2]; const uint32_t fileSize = offsets[i * 2];
const uint32_t offset = offsets[i * 2 + 1] + static_cast<uint32_t>(fileDataOffset); const std::streamsize offset = static_cast<std::streamsize>(offsets[i * 2 + 1]) + fileDataOffset;
if (fileSize + offset > fsize) if (fileSize + offset > fsize)
fail("Archive contains offsets outside itself"); fail(std::format("Archive contains offsets outside itself: {} + {} > {}", fileSize, offset, fsize));
if (offset > std::numeric_limits<uint32_t>::max())
fail(std::format(
"Absolute file {} offset is too large: {} > {}", i, offset, std::numeric_limits<uint32_t>::max()));
const uint32_t nameOffset = offsets[2 * filenum + i]; const uint32_t nameOffset = offsets[2 * filenum + i];
@ -196,13 +199,17 @@ void BSAFile::readHeader(std::istream& input)
const std::size_t nameSize = end - begin; const std::size_t nameSize = end - begin;
FileStruct fs;
fs.mFileSize = fileSize; fs.mFileSize = fileSize;
fs.mOffset = offset; fs.mOffset = static_cast<uint32_t>(offset);
fs.mHash = hashes[i]; fs.mHash = hashes[i];
fs.mNameOffset = nameOffset; fs.mNameOffset = nameOffset;
fs.mNameSize = static_cast<uint32_t>(nameSize); fs.mNameSize = static_cast<uint32_t>(nameSize);
fs.mNamesBuffer = &mStringBuf; fs.mNamesBuffer = &mStringBuf;
mFiles.push_back(fs);
endOfNameBuffer = std::max(endOfNameBuffer, nameOffset + nameSize + 1); endOfNameBuffer = std::max(endOfNameBuffer, nameOffset + nameSize + 1);
assert(endOfNameBuffer <= mStringBuf.size()); assert(endOfNameBuffer <= mStringBuf.size());
} }