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>
This commit is contained in:
parent
42f974f56b
commit
39a2eed039
@ -662,6 +662,20 @@ QByteArray qCompress(const uchar* data, int nbytes, int compressionLevel)
|
|||||||
*/
|
*/
|
||||||
|
|
||||||
#ifndef QT_NO_COMPRESS
|
#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)
|
QByteArray qUncompress(const uchar* data, int nbytes)
|
||||||
{
|
{
|
||||||
if (!data) {
|
if (!data) {
|
||||||
@ -676,53 +690,29 @@ QByteArray qUncompress(const uchar* data, int nbytes)
|
|||||||
ulong expectedSize = uint((data[0] << 24) | (data[1] << 16) |
|
ulong expectedSize = uint((data[0] << 24) | (data[1] << 16) |
|
||||||
(data[2] << 8) | (data[3] ));
|
(data[2] << 8) | (data[3] ));
|
||||||
ulong len = qMax(expectedSize, 1ul);
|
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 {
|
forever {
|
||||||
ulong alloc = len;
|
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,
|
int res = ::uncompress((uchar*)d->data(), &len,
|
||||||
data+4, nbytes-4);
|
data+4, nbytes-4);
|
||||||
|
|
||||||
switch (res) {
|
switch (res) {
|
||||||
case Z_OK:
|
case Z_OK:
|
||||||
if (len != alloc) {
|
Q_ASSERT(len <= alloc);
|
||||||
if (len >= (1u << 31u) - sizeof(QByteArray::Data)) {
|
Q_UNUSED(alloc);
|
||||||
//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();
|
|
||||||
d->size = len;
|
d->size = len;
|
||||||
d->alloc = uint(len) + 1u;
|
|
||||||
d->capacityReserved = false;
|
|
||||||
d->offset = sizeof(QByteArrayData);
|
|
||||||
d->data()[len] = 0;
|
d->data()[len] = 0;
|
||||||
|
|
||||||
{
|
{
|
||||||
QByteArrayDataPtr dataPtr = { d.take() };
|
QByteArrayDataPtr dataPtr = { d.take() };
|
||||||
return QByteArray(dataPtr);
|
return QByteArray(dataPtr);
|
||||||
@ -734,6 +724,17 @@ QByteArray qUncompress(const uchar* data, int nbytes)
|
|||||||
|
|
||||||
case Z_BUF_ERROR:
|
case Z_BUF_ERROR:
|
||||||
len *= 2;
|
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;
|
continue;
|
||||||
|
|
||||||
case Z_DATA_ERROR:
|
case Z_DATA_ERROR:
|
||||||
|
Loading…
x
Reference in New Issue
Block a user