diff --git a/src/imageformats/chunks.cpp b/src/imageformats/chunks.cpp index 4446fe6..6344ca9 100644 --- a/src/imageformats/chunks.cpp +++ b/src/imageformats/chunks.cpp @@ -10,6 +10,8 @@ #include +#define RECURSION_PROTECTION 10 + IFFChunk::~IFFChunk() { @@ -20,6 +22,7 @@ IFFChunk::IFFChunk() , _size{0} , _align{2} , _dataPos{0} + , _recursionCnt{0} { } @@ -49,12 +52,16 @@ qint32 IFFChunk::alignBytes() const bool IFFChunk::readStructure(QIODevice *d) { auto ok = readInfo(d); - ok = ok && innerReadStructure(d); + if (recursionCounter() > RECURSION_PROTECTION - 1) { + ok = ok && IFFChunk::innerReadStructure(d); // force default implementation (no more recursion) + } else { + ok = ok && innerReadStructure(d); + } if (ok) { auto pos = _dataPos + _size; if (auto align = pos % alignBytes()) pos += alignBytes() - align; - ok = d->seek(pos); + ok = pos < d->pos() ? false : d->seek(pos); } return ok; } @@ -169,7 +176,17 @@ void IFFChunk::setChunks(const ChunkList &chunks) _chunks = chunks; } -IFFChunk::ChunkList IFFChunk::innerFromDevice(QIODevice *d, bool *ok, qint32 alignBytes) +qint32 IFFChunk::recursionCounter() const +{ + return _recursionCnt; +} + +void IFFChunk::setRecursionCounter(qint32 cnt) +{ + _recursionCnt = cnt; +} + +IFFChunk::ChunkList IFFChunk::innerFromDevice(QIODevice *d, bool *ok, qint32 alignBytes, qint32 recursionCnt) { auto tmp = false; if (ok == nullptr) { @@ -181,6 +198,10 @@ IFFChunk::ChunkList IFFChunk::innerFromDevice(QIODevice *d, bool *ok, qint32 ali return {}; } + if (recursionCnt > RECURSION_PROTECTION) { + return {}; + } + IFFChunk::ChunkList list; for (; !d->atEnd();) { auto cid = d->peek(4); @@ -229,6 +250,7 @@ IFFChunk::ChunkList IFFChunk::innerFromDevice(QIODevice *d, bool *ok, qint32 ali chunk->setAlignBytes(alignBytes); } + chunk->setRecursionCounter(recursionCnt + 1); if (!chunk->readStructure(d)) { *ok = false; return {}; @@ -243,7 +265,7 @@ IFFChunk::ChunkList IFFChunk::innerFromDevice(QIODevice *d, bool *ok, qint32 ali IFFChunk::ChunkList IFFChunk::fromDevice(QIODevice *d, bool *ok) { - return innerFromDevice(d, ok, 2); + return innerFromDevice(d, ok, 2, 0); } @@ -754,7 +776,7 @@ bool FORMChunk::innerReadStructure(QIODevice *d) _type = d->read(4); auto ok = true; if (_type == QByteArray("ILBM")) { - setChunks(IFFChunk::innerFromDevice(d, &ok, alignBytes())); + setChunks(IFFChunk::innerFromDevice(d, &ok, alignBytes(), recursionCounter())); } return ok; } @@ -857,9 +879,9 @@ bool FOR4Chunk::innerReadStructure(QIODevice *d) _type = d->read(4); auto ok = true; if (_type == QByteArray("CIMG")) { - setChunks(IFFChunk::innerFromDevice(d, &ok, alignBytes())); + setChunks(IFFChunk::innerFromDevice(d, &ok, alignBytes(), recursionCounter())); } else if (_type == QByteArray("TBMP")) { - setChunks(IFFChunk::innerFromDevice(d, &ok, alignBytes())); + setChunks(IFFChunk::innerFromDevice(d, &ok, alignBytes(), recursionCounter())); } return ok; } diff --git a/src/imageformats/chunks_p.h b/src/imageformats/chunks_p.h index 862b658..c77deaa 100644 --- a/src/imageformats/chunks_p.h +++ b/src/imageformats/chunks_p.h @@ -256,6 +256,14 @@ protected: */ void setChunks(const ChunkList &chunks); + /*! + * \brief recursionCounter + * Protection against stack overflow due to broken data. + * \return The current recursion counter. + */ + qint32 recursionCounter() const; + void setRecursionCounter(qint32 cnt); + inline quint16 ui16(quint8 c1, quint8 c2) const { return (quint16(c2) << 8) | quint16(c1); } @@ -272,7 +280,7 @@ protected: return qint32(ui32(c1, c2, c3, c4)); } - static ChunkList innerFromDevice(QIODevice *d, bool *ok, qint32 alignBytes); + static ChunkList innerFromDevice(QIODevice *d, bool *ok, qint32 alignBytes, qint32 recursionCnt); private: char _chunkId[4]; @@ -287,6 +295,8 @@ private: ChunkList _chunks; + qint32 _recursionCnt; + };