From 586a658650aed6ea62484470f7878785d9e4bd11 Mon Sep 17 00:00:00 2001 From: Acts1631 <69813585+acts-1631@users.noreply.github.com> Date: Sun, 2 Aug 2026 02:14:43 -0400 Subject: [PATCH] Prevent DSDIFF chunk size wrap (#1388) A crafted DSDIFF chunk size could wrap the parser boundary check, seek backwards, and make the parser loop indefinitely. Compare chunk sizes with remaining bytes before seeking at each DSDIFF chunk nesting level. --- taglib/dsdiff/dsdifffile.cpp | 28 +++++++++++++++++++--------- 1 file changed, 19 insertions(+), 9 deletions(-) diff --git a/taglib/dsdiff/dsdifffile.cpp b/taglib/dsdiff/dsdifffile.cpp index 90d1315b..d4b1e28b 100644 --- a/taglib/dsdiff/dsdifffile.cpp +++ b/taglib/dsdiff/dsdifffile.cpp @@ -69,6 +69,16 @@ namespace [](unsigned char c) { return c < 32 || c > 126; }); } + bool chunkFits(offset_t offset, unsigned long long size, + unsigned long long end) + { + if(offset < 0) + return false; + + const auto position = static_cast(offset); + return position <= end && size <= end - position; + } + enum { ID3v2Index = 0, DIINIndex = 1 @@ -611,7 +621,8 @@ void DSDIFF::File::read(bool readProperties, Properties::ReadStyle propertiesSty // + 12: chunk header at least, fix for additional junk bytes - while(tell() + 12 <= length()) { + const auto fileLength = static_cast(length()); + while(chunkFits(tell(), 12, fileLength)) { ByteVector chunkName = readBlock(4); unsigned long long chunkSize = readBlock(8).toULongLong(bigEndian); @@ -621,8 +632,7 @@ void DSDIFF::File::read(bool readProperties, Properties::ReadStyle propertiesSty break; } - if(static_cast(tell()) + chunkSize > - static_cast(length())) { + if(!chunkFits(tell(), chunkSize, fileLength)) { debug("DSDIFF::File::read() -- Chunk '" + chunkName + "' has invalid size (larger than the file size)"); setValid(false); @@ -671,7 +681,7 @@ void DSDIFF::File::read(bool readProperties, Properties::ReadStyle propertiesSty audioDataSizeinBytes = d->chunks[i].size; - while(static_cast(tell()) + 12 <= dstChunkEnd) { + while(chunkFits(tell(), 12, dstChunkEnd)) { ByteVector dstChunkName = readBlock(4); unsigned long long dstChunkSize = readBlock(8).toULongLong(bigEndian); @@ -681,7 +691,7 @@ void DSDIFF::File::read(bool readProperties, Properties::ReadStyle propertiesSty break; } - if(tell() + dstChunkSize > dstChunkEnd) { + if(!chunkFits(tell(), dstChunkSize, dstChunkEnd)) { debug("DSDIFF::File::read() -- DST Chunk '" + dstChunkName + "' has invalid size (larger than the DST chunk)"); setValid(false); @@ -713,7 +723,7 @@ void DSDIFF::File::read(bool readProperties, Properties::ReadStyle propertiesSty unsigned long long propChunkEnd = d->chunks[i].offset + d->chunks[i].size; // +4 to remove the 'SND ' marker at beginning of 'PROP' chunk seek(d->chunks[i].offset + 4); - while(static_cast(tell()) + 12 <= propChunkEnd) { + while(chunkFits(tell(), 12, propChunkEnd)) { ByteVector propChunkName = readBlock(4); unsigned long long propChunkSize = readBlock(8).toULongLong(bigEndian); @@ -723,7 +733,7 @@ void DSDIFF::File::read(bool readProperties, Properties::ReadStyle propertiesSty break; } - if(tell() + propChunkSize > propChunkEnd) { + if(!chunkFits(tell(), propChunkSize, propChunkEnd)) { debug("DSDIFF::File::read() -- PROP Chunk '" + propChunkName + "' has invalid size (larger than the PROP chunk)"); setValid(false); @@ -759,7 +769,7 @@ void DSDIFF::File::read(bool readProperties, Properties::ReadStyle propertiesSty unsigned long long diinChunkEnd = d->chunks[i].offset + d->chunks[i].size; seek(d->chunks[i].offset); - while(static_cast(tell()) + 12 <= diinChunkEnd) { + while(chunkFits(tell(), 12, diinChunkEnd)) { ByteVector diinChunkName = readBlock(4); unsigned long long diinChunkSize = readBlock(8).toULongLong(bigEndian); @@ -769,7 +779,7 @@ void DSDIFF::File::read(bool readProperties, Properties::ReadStyle propertiesSty break; } - if(tell() + diinChunkSize > diinChunkEnd) { + if(!chunkFits(tell(), diinChunkSize, diinChunkEnd)) { debug("DSDIFF::File::read() -- DIIN Chunk '" + diinChunkName + "' has invalid size (larger than the DIIN chunk)"); setValid(false);