From 62db51159d543cd3eca08d33150621544d7be84e Mon Sep 17 00:00:00 2001 From: Acts1631 <69813585+acts-1631@users.noreply.github.com> Date: Mon, 28 Sep 2026 14:28:19 -0400 Subject: [PATCH] Avoid repeated genre suffix copies in ID3v2 parsing (#1457) A TCON frame with many legacy genre codes copies the remaining text for every code. A small crafted tag can consume seconds of CPU. Walk the original string by offset, compare mapped genres without copying the suffix, and limit decoded codes by input size. --- taglib/mpeg/id3v2/id3v2framefactory.cpp | 25 ++++++++++++++++++------- 1 file changed, 18 insertions(+), 7 deletions(-) diff --git a/taglib/mpeg/id3v2/id3v2framefactory.cpp b/taglib/mpeg/id3v2/id3v2framefactory.cpp index 380db478..74fdf3fe 100644 --- a/taglib/mpeg/id3v2/id3v2framefactory.cpp +++ b/taglib/mpeg/id3v2/id3v2framefactory.cpp @@ -81,22 +81,33 @@ namespace for(auto s : std::as_const(fields)) { int offset = 0; int end = 0; + unsigned int codeCount = 0; + const unsigned int maxCodes = std::min(50000U, std::max(1024U, s.size() / 32)); while(static_cast(s.length()) > offset && s[offset] == '(' && (end = s.find(")", offset + 1)) > offset) { + if(codeCount++ >= maxCodes) { + debug("ID3v2: Maximum genre code count exceeded"); + break; + } // "(12)Genre" - const String genreCode = s.substr(offset + 1, end - 1); - s = s.substr(end + 1); + const String genreCode = s.substr(offset + 1, end - offset - 1); + offset = end + 1; bool ok; int number = genreCode.toInt(&ok); - if((ok && number >= 0 && number <= 255 && - ID3v1::genre(number) != s) || - genreCode == "RX" || genreCode == "CR") + if(ok && number >= 0 && number <= 255) { + const String genre = ID3v1::genre(number); + if(genre.size() != s.size() - offset || + !std::equal(genre.cbegin(), genre.cend(), s.cbegin() + offset)) + newfields.append(genreCode); + } + else if(genreCode == "RX" || genreCode == "CR") { newfields.append(genreCode); + } } - if(!s.isEmpty()) + if(static_cast(offset) < s.size()) // "Genre" or "12" - newfields.append(s); + newfields.append(s.substr(offset)); } if(newfields.isEmpty())