From be424957dae963d9735933ae557e5cffee508fd5 Mon Sep 17 00:00:00 2001 From: manuaudio Date: Sat, 15 Aug 2026 09:29:59 -0700 Subject: [PATCH] MPEG: do not convert out of range properties to int The Xing header supplies both the frame count and the byte count as raw 32 bit values, and TagLib multiplies the frame count by the per-frame duration without checking the result. At MPEG 2.5 Layer III / 8 kHz a frame is 72 ms, so 2^32-1 declared frames puts the length at ~3.1e11 ms and the conversion to int is undefined: taglib/mpeg/mpegproperties.cpp:166:35: runtime error: 3.09238e+11 is outside the range of representable values of type 'int' The bitrate on the next line is reachable the other way round. One declared frame of MPEG 1 Layer I at 48 kHz is 8 ms, and the declared size is not the real file size, so a 1 KB file can claim 2^32-1 bytes: taglib/mpeg/mpegproperties.cpp:167:35: runtime error: 4.29497e+09 is outside the range of representable values of type 'int' The length computed on the non-Xing path has the same shape but is bounded by the real stream length rather than a declared count, so it needs a file of a couple of gigabytes rather than a crafted header. I have not built one; that guard is there for consistency, not on demonstrated evidence. Leave the field at its default rather than converting. Two reports before the change, none after. The 23 MPEG files in tests/data report identical channels, sample rate, bitrate and length before and after, and the suite runs 576 tests either way. Assisted-By: Claude Code (Claude Opus 5) --- taglib/mpeg/mpegproperties.cpp | 26 ++++++++++++++++++++++---- 1 file changed, 22 insertions(+), 4 deletions(-) diff --git a/taglib/mpeg/mpegproperties.cpp b/taglib/mpeg/mpegproperties.cpp index 8a64ff26..ff3e5df4 100644 --- a/taglib/mpeg/mpegproperties.cpp +++ b/taglib/mpeg/mpegproperties.cpp @@ -25,6 +25,8 @@ #include "mpegproperties.h" +#include + #include "taglib_config.h" #include "tdebug.h" #include "mpegfile.h" @@ -163,8 +165,18 @@ void MPEG::Properties::read(File *file, ReadStyle readStyle) const double timePerFrame = firstHeader.samplesPerFrame() * 1000.0 / firstHeader.sampleRate(); const double length = timePerFrame * d->xingHeader->totalFrames(); - d->length = static_cast(length + 0.5); - d->bitrate = static_cast(d->xingHeader->totalSize() * 8.0 / length + 0.5); + // Both the frame count and the byte count come straight from the Xing + // header, so the millisecond length can land outside int, and a single + // declared frame does the same to the bitrate. Converting a double the + // destination type cannot represent is undefined, so leave the field at + // its default instead. + if(length > 0.0 && length < static_cast(std::numeric_limits::max())) { + d->length = static_cast(length + 0.5); + + const double bitrate = d->xingHeader->totalSize() * 8.0 / length; + if(bitrate >= 0.0 && bitrate < static_cast(std::numeric_limits::max())) + d->bitrate = static_cast(bitrate + 0.5); + } } else { int bitRate = firstHeader.bitrate(); @@ -239,8 +251,14 @@ void MPEG::Properties::read(File *file, ReadStyle readStyle) { const Header lastHeader(file, lastFrameOffset, false); if(const offset_t streamLength = lastFrameOffset - firstFrameOffset + lastHeader.frameLength(); - streamLength > 0) - d->length = static_cast(static_cast(streamLength) * 8.0 / d->bitrate + 0.5); + streamLength > 0) { + // Same shape as the Xing path above, but bounded by the real stream + // length rather than a declared count, so it needs a very large file + // rather than a crafted header. Guarded for consistency. + const double length = static_cast(streamLength) * 8.0 / d->bitrate; + if(length > 0.0 && length < static_cast(std::numeric_limits::max())) + d->length = static_cast(length + 0.5); + } } } }