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.
This commit is contained in:
Acts1631
2026-09-28 20:28:19 +02:00
committed by GitHub
parent f9efbc7ba8
commit 62db51159d
+18 -7
View File
@@ -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<int>(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<unsigned int>(offset) < s.size())
// "Genre" or "12"
newfields.append(s);
newfields.append(s.substr(offset));
}
if(newfields.isEmpty())