mirror of
https://github.com/taglib/taglib.git
synced 2026-08-14 06:17:00 -04:00
ID3v2: limit embedded frame nesting (#1405)
Limit recursive CHAP and CTOC embedded frame parsing to 64 levels. This prevents crafted ID3v2 tags from exhausting the parser stack. Use one thread-local guard shared by both paths so mixed CHAP and CTOC nesting is bounded as well.
This commit is contained in:
@@ -30,6 +30,7 @@
|
||||
#include "tbytevectorlist.h"
|
||||
#include "tdebug.h"
|
||||
#include "tpropertymap.h"
|
||||
#include "id3v2framefactory.h"
|
||||
#include "unknownframe.h"
|
||||
|
||||
using namespace TagLib;
|
||||
@@ -254,7 +255,7 @@ void ChapterFrame::parseFields(const ByteVector &data)
|
||||
return;
|
||||
|
||||
while(embPos < size - header()->size()) {
|
||||
Frame *frame = FrameFactory::instance()->createFrame(data.mid(pos + embPos), d->tagHeader);
|
||||
Frame *frame = FrameFactory::createEmbeddedFrame(data.mid(pos + embPos), d->tagHeader);
|
||||
|
||||
if(!frame)
|
||||
return;
|
||||
|
||||
@@ -29,6 +29,7 @@
|
||||
|
||||
#include "tpropertymap.h"
|
||||
#include "tdebug.h"
|
||||
#include "id3v2framefactory.h"
|
||||
#include "unknownframe.h"
|
||||
|
||||
using namespace TagLib;
|
||||
@@ -264,7 +265,7 @@ void TableOfContentsFrame::parseFields(const ByteVector &data)
|
||||
return;
|
||||
|
||||
while(embPos < size - header()->size()) {
|
||||
Frame *frame = FrameFactory::instance()->createFrame(data.mid(pos + embPos), d->tagHeader);
|
||||
Frame *frame = FrameFactory::createEmbeddedFrame(data.mid(pos + embPos), d->tagHeader);
|
||||
|
||||
if(!frame)
|
||||
return;
|
||||
|
||||
@@ -55,6 +55,23 @@ using namespace ID3v2;
|
||||
|
||||
namespace
|
||||
{
|
||||
constexpr unsigned int MAX_EMBEDDED_FRAME_DEPTH = 64;
|
||||
thread_local unsigned int embeddedFrameDepth = 0;
|
||||
|
||||
class EmbeddedFrameDepth
|
||||
{
|
||||
public:
|
||||
EmbeddedFrameDepth()
|
||||
{
|
||||
++embeddedFrameDepth;
|
||||
}
|
||||
|
||||
~EmbeddedFrameDepth()
|
||||
{
|
||||
--embeddedFrameDepth;
|
||||
}
|
||||
};
|
||||
|
||||
void updateGenre(TextIdentificationFrame *frame)
|
||||
{
|
||||
StringList fields = frame->fieldList();
|
||||
@@ -190,6 +207,18 @@ Frame *FrameFactory::createFrame(const ByteVector &origData,
|
||||
return createFrame(data, header, tagHeader);
|
||||
}
|
||||
|
||||
Frame *FrameFactory::createEmbeddedFrame(const ByteVector &origData,
|
||||
const Header *tagHeader)
|
||||
{
|
||||
if(embeddedFrameDepth >= MAX_EMBEDDED_FRAME_DEPTH) {
|
||||
debug("ID3v2: Maximum embedded frame nesting depth exceeded");
|
||||
return nullptr;
|
||||
}
|
||||
|
||||
EmbeddedFrameDepth depth;
|
||||
return FrameFactory::instance()->createFrame(origData, tagHeader);
|
||||
}
|
||||
|
||||
Frame *FrameFactory::createFrame(const ByteVector &data, Frame::Header *header,
|
||||
const Header *tagHeader) const {
|
||||
ByteVector frameID = header->frameID();
|
||||
|
||||
@@ -36,6 +36,8 @@ namespace TagLib {
|
||||
namespace ID3v2 {
|
||||
|
||||
class TextIdentificationFrame;
|
||||
class ChapterFrame;
|
||||
class TableOfContentsFrame;
|
||||
|
||||
//! A factory for creating ID3v2 frames during parsing
|
||||
|
||||
@@ -176,6 +178,12 @@ namespace TagLib {
|
||||
const Header *tagHeader) const;
|
||||
|
||||
private:
|
||||
static Frame *createEmbeddedFrame(const ByteVector &origData,
|
||||
const Header *tagHeader);
|
||||
|
||||
friend class ChapterFrame;
|
||||
friend class TableOfContentsFrame;
|
||||
|
||||
static FrameFactory factory;
|
||||
|
||||
class FrameFactoryPrivate;
|
||||
|
||||
Reference in New Issue
Block a user