From d391029695adfa9a7ecbd7dc35933eb0d509fbd3 Mon Sep 17 00:00:00 2001 From: Acts1631 <69813585+acts-1631@users.noreply.github.com> Date: Thu, 20 Aug 2026 23:59:27 -0400 Subject: [PATCH] Resolve QuickTime chunk tables linearly (#1428) MIME-Version: 1.0 Content-Type: text/plain; charset=UTF-8 Content-Transfer-Encoding: 8bit A QuickTime chapter track can provide large stco and stsc tables. QtChapterList::resolveSampleOffsets() currently walks every stsc entry for every chunk, so a file with N entries can force O(N²) work during MP4::File::qtChapters(). A 3.2 MB file with 200,000 entries took about 20 seconds in the release build. Keep a cursor into stscEntries and advance it as chunk numbers increase. This resolves the same ordered table in linear time while avoiding attacker-controlled repeated scans. --- taglib/mp4/mp4qtchapterlist.cpp | 14 +++++--------- 1 file changed, 5 insertions(+), 9 deletions(-) diff --git a/taglib/mp4/mp4qtchapterlist.cpp b/taglib/mp4/mp4qtchapterlist.cpp index 98572ce3..4dca59ed 100644 --- a/taglib/mp4/mp4qtchapterlist.cpp +++ b/taglib/mp4/mp4qtchapterlist.cpp @@ -943,19 +943,15 @@ namespace std::vector sampleOffsets; const auto totalChunks = static_cast(chunkOffsets.size()); unsigned int sampleIndex = 0; + std::size_t stscIndex = 0; for(unsigned int chunkIdx = 0; chunkIdx < totalChunks; ++chunkIdx) { // Find which stsc entry applies to this chunk (1-based) const unsigned int chunkNum = chunkIdx + 1; - unsigned int samplesInChunk = stscEntries[0].samplesPerChunk; - for(const auto & stscEntry : stscEntries) { - if(stscEntry.firstChunk <= chunkNum) { - samplesInChunk = stscEntry.samplesPerChunk; - } - else { - break; - } - } + while(stscIndex + 1 < stscEntries.size() && + stscEntries[stscIndex + 1].firstChunk <= chunkNum) + ++stscIndex; + unsigned int samplesInChunk = stscEntries[stscIndex].samplesPerChunk; unsigned int offsetInChunk = 0; if(samplesInChunk > sizeInfo.sampleCount - sampleIndex)