diff options
author | Thiago Macieira <thiago.macieira@intel.com> | 2016-06-09 14:42:31 -0700 |
---|---|---|
committer | Thiago Macieira <thiago.macieira@intel.com> | 2016-09-07 14:35:58 +0000 |
commit | 39a2eed039f8cb335f72caaf3b35d5e6832c2c5b (patch) | |
tree | 58dfd7dae8f4f36e8ed93f731c10c03ee826620e /src | |
parent | 42f974f56b18e365c3343545a9491452683fed30 (diff) |
QByteArray: Clean up qUncompress
qUncompress has code to deal with OOM situations without crashing. This
code is highly convoluted. It achieves that by realloc()ing QByteArray's
buffer. Instead, let's operate on QByteArray::Data and use RAII to
manage the lifetime of the buffer.
Change-Id: I1cc7601489634e96833cfffd145688433b76f447
Reviewed-by: Olivier Goffart (Woboq GmbH) <ogoffart@woboq.com>
Diffstat (limited to 'src')
-rw-r--r-- | src/corelib/tools/qbytearray.cpp | 73 |
1 files changed, 37 insertions, 36 deletions
diff --git a/src/corelib/tools/qbytearray.cpp b/src/corelib/tools/qbytearray.cpp index 5b5fbaa94b..71bbd80980 100644 --- a/src/corelib/tools/qbytearray.cpp +++ b/src/corelib/tools/qbytearray.cpp @@ -662,6 +662,20 @@ QByteArray qCompress(const uchar* data, int nbytes, int compressionLevel) */ #ifndef QT_NO_COMPRESS +namespace { +struct QByteArrayDataDeleter +{ + static inline void cleanup(QTypedArrayData<char> *d) + { if (d) QTypedArrayData<char>::deallocate(d); } +}; +} + +static QByteArray invalidCompressedData() +{ + qWarning("qUncompress: Input data is corrupted"); + return QByteArray(); +} + QByteArray qUncompress(const uchar* data, int nbytes) { if (!data) { @@ -676,53 +690,29 @@ QByteArray qUncompress(const uchar* data, int nbytes) ulong expectedSize = uint((data[0] << 24) | (data[1] << 16) | (data[2] << 8) | (data[3] )); ulong len = qMax(expectedSize, 1ul); - QScopedPointer<QByteArray::Data, QScopedPointerPodDeleter> d; + const ulong maxPossibleSize = MaxAllocSize - sizeof(QByteArray::Data); + if (Q_UNLIKELY(len >= maxPossibleSize)) { + // QByteArray does not support that huge size anyway. + return invalidCompressedData(); + } + QScopedPointer<QByteArray::Data, QByteArrayDataDeleter> d(QByteArray::Data::allocate(expectedSize + 1)); + if (Q_UNLIKELY(d.data() == nullptr)) + return invalidCompressedData(); + + d->size = expectedSize; forever { ulong alloc = len; - if (len >= (1u << 31u) - sizeof(QByteArray::Data)) { - //QByteArray does not support that huge size anyway. - qWarning("qUncompress: Input data is corrupted"); - return QByteArray(); - } - QByteArray::Data *p = static_cast<QByteArray::Data *>(::realloc(d.data(), sizeof(QByteArray::Data) + alloc + 1)); - if (!p) { - // we are not allowed to crash here when compiling with QT_NO_EXCEPTIONS - qWarning("qUncompress: could not allocate enough memory to uncompress data"); - return QByteArray(); - } - d.take(); // realloc was successful - d.reset(p); - d->offset = sizeof(QByteArrayData); - d->size = 0; // Shut up valgrind "uninitialized variable" warning int res = ::uncompress((uchar*)d->data(), &len, data+4, nbytes-4); switch (res) { case Z_OK: - if (len != alloc) { - if (len >= (1u << 31u) - sizeof(QByteArray::Data)) { - //QByteArray does not support that huge size anyway. - qWarning("qUncompress: Input data is corrupted"); - return QByteArray(); - } - QByteArray::Data *p = static_cast<QByteArray::Data *>(::realloc(d.data(), sizeof(QByteArray::Data) + len + 1)); - if (!p) { - // we are not allowed to crash here when compiling with QT_NO_EXCEPTIONS - qWarning("qUncompress: could not allocate enough memory to uncompress data"); - return QByteArray(); - } - d.take(); // realloc was successful - d.reset(p); - } - d->ref.initializeOwned(); + Q_ASSERT(len <= alloc); + Q_UNUSED(alloc); d->size = len; - d->alloc = uint(len) + 1u; - d->capacityReserved = false; - d->offset = sizeof(QByteArrayData); d->data()[len] = 0; - { QByteArrayDataPtr dataPtr = { d.take() }; return QByteArray(dataPtr); @@ -734,6 +724,17 @@ QByteArray qUncompress(const uchar* data, int nbytes) case Z_BUF_ERROR: len *= 2; + if (Q_UNLIKELY(len >= maxPossibleSize)) { + // QByteArray does not support that huge size anyway. + return invalidCompressedData(); + } else { + // grow the block + QByteArray::Data *p = QByteArray::Data::reallocateUnaligned(d.data(), len + 1); + if (Q_UNLIKELY(p == nullptr)) + return invalidCompressedData(); + d.take(); // don't free + d.reset(p); + } continue; case Z_DATA_ERROR: |