mirror of
https://github.com/taglib/taglib.git
synced 2026-08-27 12:47:01 -04:00
Bound recursive Matroska and MP4 element trees (#1425)
The Matroska and MP4 parsers limited child elements per container, but allowed that limit to repeat at every nesting level. A crafted media file could therefore retain millions of small elements and exhaust process memory while it was opened. Carry a shared element count through recursive parsing so the existing per-container limits also have a global bound.
This commit is contained in:
@@ -99,8 +99,15 @@ void EBML::MasterElement::setMinRenderSize(offset_t minimumSize)
|
||||
}
|
||||
|
||||
bool EBML::MasterElement::read(File &file, int depth)
|
||||
{
|
||||
unsigned int elementCount = 0;
|
||||
return read(file, depth, elementCount);
|
||||
}
|
||||
|
||||
bool EBML::MasterElement::read(File &file, int depth, unsigned int &elementCount)
|
||||
{
|
||||
static constexpr int MAX_EBML_DEPTH = 64;
|
||||
static constexpr int MAX_EBML_ELEMENT_COUNT = 50000;
|
||||
static constexpr int MAX_EBML_ELEMENT_COUNT_PER_LEVEL = 50000;
|
||||
if(depth > MAX_EBML_DEPTH) {
|
||||
debug("EBML: Maximum nesting depth exceeded");
|
||||
@@ -109,12 +116,14 @@ bool EBML::MasterElement::read(File &file, int depth)
|
||||
const offset_t maxOffset = file.tell() + dataSize;
|
||||
std::unique_ptr<Element> element;
|
||||
while((element = findNextElement(file, maxOffset))) {
|
||||
if(elements.size() >= MAX_EBML_ELEMENT_COUNT_PER_LEVEL) {
|
||||
if(elementCount >= MAX_EBML_ELEMENT_COUNT ||
|
||||
elements.size() >= MAX_EBML_ELEMENT_COUNT_PER_LEVEL) {
|
||||
debug("EBML: Maximum element count exceeded");
|
||||
return false;
|
||||
}
|
||||
++elementCount;
|
||||
if(auto master = dynamic_cast<MasterElement *>(element.get())) {
|
||||
if(!master->read(file, depth + 1)) {
|
||||
if(!master->read(file, depth + 1, elementCount)) {
|
||||
debug("EBML: Invalid MasterElement");
|
||||
continue;
|
||||
}
|
||||
|
||||
@@ -56,6 +56,7 @@ namespace TagLib
|
||||
|
||||
protected:
|
||||
bool read(File &file, int depth);
|
||||
bool read(File &file, int depth, unsigned int &elementCount);
|
||||
|
||||
offset_t offset;
|
||||
offset_t padding = 0;
|
||||
|
||||
+23
-2
@@ -40,6 +40,7 @@ namespace {
|
||||
"stbl", "minf", "moof", "traf", "trak",
|
||||
"stsd", "stem"
|
||||
};
|
||||
constexpr int MAX_MP4_ATOM_COUNT = 50000;
|
||||
constexpr int MAX_MP4_ATOM_COUNT_PER_LEVEL = 50000;
|
||||
} // namespace
|
||||
|
||||
@@ -56,6 +57,25 @@ public:
|
||||
MP4::Atom::Atom(File *file, int depth)
|
||||
: d(std::make_unique<AtomPrivate>(file->tell()))
|
||||
{
|
||||
unsigned int atomCount = 0;
|
||||
read(file, depth, atomCount);
|
||||
}
|
||||
|
||||
MP4::Atom::Atom(File *file, int depth, unsigned int &atomCount)
|
||||
: d(std::make_unique<AtomPrivate>(file->tell()))
|
||||
{
|
||||
read(file, depth, atomCount);
|
||||
}
|
||||
|
||||
void MP4::Atom::read(File *file, int depth, unsigned int &atomCount)
|
||||
{
|
||||
if(++atomCount > MAX_MP4_ATOM_COUNT) {
|
||||
debug("MP4: Maximum atom count exceeded");
|
||||
d->length = 0;
|
||||
file->seek(0, File::End);
|
||||
return;
|
||||
}
|
||||
|
||||
d->children.setAutoDelete(true);
|
||||
|
||||
ByteVector header = file->readBlock(8);
|
||||
@@ -124,7 +144,7 @@ MP4::Atom::Atom(File *file, int depth)
|
||||
file->seek(0, File::End);
|
||||
return;
|
||||
}
|
||||
auto child = new MP4::Atom(file, depth + 1);
|
||||
auto child = new MP4::Atom(file, depth + 1, atomCount);
|
||||
d->children.append(child);
|
||||
if(child->d->length == 0)
|
||||
return;
|
||||
@@ -232,12 +252,13 @@ MP4::Atoms::Atoms(File *file) :
|
||||
d(std::make_unique<AtomsPrivate>())
|
||||
{
|
||||
d->atoms.setAutoDelete(true);
|
||||
unsigned int atomCount = 0;
|
||||
|
||||
file->seek(0, File::End);
|
||||
offset_t end = file->tell();
|
||||
file->seek(0);
|
||||
while(file->tell() + 8 <= end) {
|
||||
auto atom = new MP4::Atom(file);
|
||||
auto atom = new MP4::Atom(file, 0, atomCount);
|
||||
d->atoms.append(atom);
|
||||
if (atom->length() == 0)
|
||||
break;
|
||||
|
||||
@@ -112,9 +112,12 @@ namespace TagLib {
|
||||
|
||||
protected:
|
||||
Atom(File *file, int depth);
|
||||
Atom(File *file, int depth, unsigned int &atomCount);
|
||||
|
||||
private:
|
||||
friend class Atoms;
|
||||
class AtomPrivate;
|
||||
void read(File *file, int depth, unsigned int &atomCount);
|
||||
TAGLIB_MSVC_SUPPRESS_WARNING_NEEDS_TO_HAVE_DLL_INTERFACE
|
||||
std::unique_ptr<AtomPrivate> d;
|
||||
};
|
||||
|
||||
Reference in New Issue
Block a user