Bound text value counts in ID3v2 and APE tags (#1458)

Null-separated text values create a ByteVector and String for each
tiny field. A 1 MB tag can allocate over 100 MB and abort
memory-limited applications.

Limit decoded values based on input size and stop before creating
String objects when the value count exceeds the budget.
This commit is contained in:
Acts1631
2026-09-28 20:28:51 +02:00
committed by GitHub
parent 62db51159d
commit 5bd11b2fb6
2 changed files with 28 additions and 3 deletions
+15 -2
View File
@@ -25,6 +25,7 @@
#include "apeitem.h"
#include <algorithm>
#include <utility>
#include <numeric>
@@ -232,8 +233,20 @@ void APE::Item::parse(const ByteVector &data)
setReadOnly(flags & 1);
setType(static_cast<ItemTypes>((flags >> 1) & 3));
if(Text == d->type)
d->text = StringList(ByteVectorList::split(val, '\0'), String::UTF8);
if(Text == d->type) {
// Short, null-separated values otherwise allocate far more memory than
// the item occupies on disk.
static constexpr unsigned int MAX_TEXT_VALUE_COUNT = 50000;
const unsigned int maxValues = std::min(MAX_TEXT_VALUE_COUNT,
std::max(1024U, val.size() / 32));
const ByteVectorList values = ByteVectorList::split(
val, '\0', 1, static_cast<int>(maxValues + 1));
if(values.size() > maxValues) {
debug("APE::Item::parse() -- Maximum text value count exceeded");
return;
}
d->text = StringList(values, String::UTF8);
}
else
d->value = val;
}
@@ -31,6 +31,7 @@
#include "tutils.h"
#include "tpropertymap.h"
#include "tdebug.h"
#include "id3v1genres.h"
#include "id3v2tag.h"
@@ -236,7 +237,18 @@ void TextIdentificationFrame::parseFields(const ByteVector &data)
while(dataLength % byteAlign != 0)
dataLength++;
const ByteVectorList l = ByteVectorList::split(data.mid(1, dataLength), textDelimiter(d->textEncoding), byteAlign);
// Short, null-separated values otherwise allocate far more memory than the
// frame occupies on disk. Allow at least 1024 fields in small frames.
static constexpr unsigned int MAX_TEXT_FIELD_COUNT = 50000;
const unsigned int maxFields = std::min(MAX_TEXT_FIELD_COUNT,
std::max(1024U, static_cast<unsigned int>(dataLength) / 32));
const ByteVectorList l = ByteVectorList::split(
data.mid(1, dataLength), textDelimiter(d->textEncoding), byteAlign,
static_cast<int>(maxFields + 1));
if(l.size() > maxFields) {
debug("ID3v2: Maximum text field count exceeded");
return;
}
d->fieldList.clear();