Clamp oversized RIFF chunk to available bytes instead of rejecting it (#1329)

Some encoders write a valid data chunk but with a slightly too-large
declared chunkSize, or place the data chunk beyond the declared RIFF
boundary. The previous behaviour called break, abandoning all remaining
chunks and making the file appear empty to taglib.

Lenient parsers (ffmpeg, QuickTime) handle this case by clamping the
chunk size to the bytes that actually remain in the file. Adopt the
same strategy: when chunkSize would exceed the file length, clamp it
and continue parsing rather than stopping early.
This commit is contained in:
Ryan Francesconi
2026-04-04 03:47:49 -07:00
committed by GitHub
parent abadbb6768
commit d6a2134cf3
2 changed files with 8 additions and 4 deletions

View File

@ -298,7 +298,7 @@ void RIFF::File::read()
seek(offset);
const ByteVector chnkName = readBlock(4);
const unsigned int chunkSize = readBlock(4).toUInt(bigEndian);
unsigned int chunkSize = readBlock(4).toUInt(bigEndian);
if(!isValidChunkName(chnkName)) {
debug("RIFF::File::read() -- Chunk '" + chnkName + "' has invalid ID");
@ -306,8 +306,12 @@ void RIFF::File::read()
}
if(static_cast<long long>(offset) + 8 + chunkSize > length()) {
debug("RIFF::File::read() -- Chunk '" + chnkName + "' has invalid size (larger than the file size)");
break;
// Clamp to available bytes rather than rejecting the chunk outright.
// Some encoders write a correct data chunk but with a slightly too-large
// declared size, or place the data chunk outside the declared RIFF boundary.
// Lenient parsers (ffmpeg, QuickTime) handle this by clamping; we do the same.
debug("RIFF::File::read() -- Chunk '" + chnkName + "' is truncated; clamping size to available bytes.");
chunkSize = static_cast<unsigned int>(length() - offset - 8);
}
Chunk chunk;

View File

@ -320,7 +320,7 @@ public:
{
FileStream stream(copy.fileName().c_str());
stream.seek(0, IOStream::End);
constexpr char garbage[] = "12345678";
constexpr char garbage[] = "\r2345678";
stream.writeBlock(ByteVector(garbage, sizeof(garbage) - 1));
stream.seek(0);
contentsBeforeModification = stream.readBlock(stream.length());