Reject QuickTime chapter samples outside their data bounds (#1427)

A malformed QuickTime chapter track can set stsz's default sample size to a
value larger than the available sample data. MP4::File::qtChapters() passes
that value through QtChapterList::read() to readTextSample(), allowing a
small file to cause gigabytes of allocations and terminate the application.

Check each resolved sample against the file end and the next sample offset
before reading it. Samples that do not fit are rejected without allocating
the attacker-controlled size.
This commit is contained in:
Acts1631
2026-08-21 05:43:54 +02:00
committed by GitHub
parent 5f869755c8
commit 0a3deeac75
+22
View File
@@ -991,6 +991,25 @@ namespace
return String(data.mid(2, textLen), String::UTF8);
}
bool sampleFits(TagLib::File *file, const std::vector<unsigned int> &offsets,
unsigned int sampleIndex, unsigned int sampleSize)
{
const offset_t offset = offsets[sampleIndex];
const offset_t fileLength = file->length();
if(offset < 0 || offset > fileLength ||
static_cast<offset_t>(sampleSize) > fileLength - offset)
return false;
if(sampleIndex + 1 < offsets.size()) {
const offset_t nextOffset = offsets[sampleIndex + 1];
if(nextOffset <= offset ||
static_cast<offset_t>(sampleSize) > nextOffset - offset)
return false;
}
return true;
}
// -- Remove helpers -------------------------------------------------------
//! Removes the tref atom from the audio track.
@@ -1250,6 +1269,9 @@ bool MP4::QtChapterList::read(TagLib::File *file)
if(sampleSize == 0 && sampleIndex < sizeInfo.perSampleSizes.size())
sampleSize = sizeInfo.perSampleSizes[sampleIndex];
if(!sampleFits(file, offsets, sampleIndex, sampleSize))
return false;
String title = readTextSample(file, offsets[sampleIndex], sampleSize);
const auto startTimeMs = static_cast<long long>(