[Matroska] Check element length before constructing

By checking the Element size against the maximum offset we can find
out of bounds elements early and not try to read them at all.
This is useful for the next patch.

Signed-off-by: Anthony Brandon <anthony.brandon@gmail.com>
This commit is contained in:
Anthony Brandon
2026-06-24 14:03:06 +02:00
committed by Urs Fleisch
parent 2185b47d5f
commit 13239fde26
5 changed files with 14 additions and 7 deletions

View File

@@ -41,7 +41,7 @@ using namespace TagLib;
#define RETURN_ELEMENT_FOR_CASE(eid) \
case (eid): return make_unique_element<eid>(id, sizeLength, dataSize, offset)
std::unique_ptr<EBML::Element> EBML::Element::factory(File &file)
std::unique_ptr<EBML::Element> EBML::Element::factory(File &file, offset_t maxOffset)
{
// Get the element ID
const offset_t offset = file.tell();
@@ -56,6 +56,13 @@ std::unique_ptr<EBML::Element> EBML::Element::factory(File &file)
if(!sizeLength)
return nullptr;
if(const offset_t currentOffset = file.tell();
static_cast<offset_t>(dataSize) > maxOffset - currentOffset) {
debug(Utils::formatString("EBML: datasize too great: %lu > (%lld - %lld)",
dataSize, maxOffset, currentOffset));
return nullptr;
}
// Return the subclass
// The enum switch without default will give us a warning if an ID is missing
auto id = static_cast<Id>(uintId);

View File

@@ -122,7 +122,7 @@ namespace TagLib
int64_t getDataSize() const;
ByteVector renderId() const;
virtual ByteVector render();
static std::unique_ptr<Element> factory(File &file);
static std::unique_ptr<Element> factory(File &file, offset_t maxOffset);
static unsigned int readId(File &file);
protected:

View File

@@ -45,7 +45,7 @@ std::unique_ptr<ElementType> readElementAt(File &file,
}
file.seek(offset);
auto element = EBML::Element::factory(file);
auto element = EBML::Element::factory(file, maxOffset);
if(!element || element->getId() != Id) {
return nullptr;
}

View File

@@ -34,7 +34,7 @@ std::unique_ptr<EBML::Element> EBML::findElement(File &file, Element::Id id, off
{
std::unique_ptr<Element> element;
while(file.tell() < maxOffset) {
element = Element::factory(file);
element = Element::factory(file, maxOffset);
if(!element || element->getId() == id)
return element;
element->skipData(file);
@@ -45,7 +45,7 @@ std::unique_ptr<EBML::Element> EBML::findElement(File &file, Element::Id id, off
std::unique_ptr<EBML::Element> EBML::findNextElement(File &file, offset_t maxOffset)
{
return file.tell() < maxOffset ? Element::factory(file) : nullptr;
return file.tell() < maxOffset ? Element::factory(file, maxOffset) : nullptr;
}
template <int maxSizeLength>

View File

@@ -368,7 +368,7 @@ void Matroska::File::read(bool readProperties, Properties::ReadStyle readStyle)
// Find the EBML Header
const auto head = EBML::element_cast<EBML::Element::Id::EBMLHeader>(
EBML::Element::factory(*this));
EBML::Element::factory(*this, fileLength));
if(!head || head->getId() != EBML::Element::Id::EBMLHeader) {
debug("Failed to find EBML head");
setValid(false);
@@ -381,7 +381,7 @@ void Matroska::File::read(bool readProperties, Properties::ReadStyle readStyle)
head->skipData(*this);
}
offset_t maxOffset = fileLength - tell();
offset_t maxOffset = fileLength;
if (readStyle == Properties::ReadStyle::Fast && maxOffset > FAST_SCAN_LIMIT) {
maxOffset = FAST_SCAN_LIMIT;
}