From 5bd11b2fb6009eb35712dd6663c22ddea79f6a58 Mon Sep 17 00:00:00 2001 From: Acts1631 <69813585+acts-1631@users.noreply.github.com> Date: Mon, 28 Sep 2026 14:28:51 -0400 Subject: [PATCH] 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. --- taglib/ape/apeitem.cpp | 17 +++++++++++++++-- .../id3v2/frames/textidentificationframe.cpp | 14 +++++++++++++- 2 files changed, 28 insertions(+), 3 deletions(-) diff --git a/taglib/ape/apeitem.cpp b/taglib/ape/apeitem.cpp index a5c8099e..83618500 100644 --- a/taglib/ape/apeitem.cpp +++ b/taglib/ape/apeitem.cpp @@ -25,6 +25,7 @@ #include "apeitem.h" +#include #include #include @@ -232,8 +233,20 @@ void APE::Item::parse(const ByteVector &data) setReadOnly(flags & 1); setType(static_cast((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(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; } diff --git a/taglib/mpeg/id3v2/frames/textidentificationframe.cpp b/taglib/mpeg/id3v2/frames/textidentificationframe.cpp index 4406eaa5..9715ac9f 100644 --- a/taglib/mpeg/id3v2/frames/textidentificationframe.cpp +++ b/taglib/mpeg/id3v2/frames/textidentificationframe.cpp @@ -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(dataLength) / 32)); + const ByteVectorList l = ByteVectorList::split( + data.mid(1, dataLength), textDelimiter(d->textEncoding), byteAlign, + static_cast(maxFields + 1)); + if(l.size() > maxFields) { + debug("ID3v2: Maximum text field count exceeded"); + return; + } d->fieldList.clear();