qUncompress(): introduce more functions a la invalidCompressedData()

... and use them consistently instead of naked qWarning()s.

As a drive-by, fix the warnings we generate on failures, which
all-too-often was "invalid data".

Code for minimal size by DRYing what the compiler can't, and using
Q_DECL_COLD_FUNCTION to avoid excessive inlining.

Left the one qWarning() in qCompress(), for now, because that would
have changed the return value from an empty to a null byte array.

Move invalidCompressedData() to where the others are defined, too,
because it's required by a follow-up commit.

Pick-to: 6.4 6.3 6.2
Task-number: QTBUG-104972
Change-Id: Ia43aa315960b06530e98c1d7525ccf0e7f71bb5c
Reviewed-by: Thiago Macieira <thiago.macieira@intel.com>
Reviewed-by: Edward Welbourne <edward.welbourne@qt.io>
Reviewed-by: Mårten Nordheim <marten.nordheim@qt.io>
This commit is contained in:
Marc Mutz 2022-09-15 15:29:34 +02:00
parent ce4c26c8d1
commit 6472616e6c

View File

@ -532,16 +532,53 @@ quint16 qChecksum(QByteArrayView data, Qt::ChecksumType standard)
#ifndef QT_NO_COMPRESS #ifndef QT_NO_COMPRESS
using CompressSizeHint_t = quint32; // 32-bit BE, historically using CompressSizeHint_t = quint32; // 32-bit BE, historically
enum class ZLibOp : bool { Compression, Decompression };
Q_DECL_COLD_FUNCTION
static const char *zlibOpAsString(ZLibOp op)
{
switch (op) {
case ZLibOp::Compression: return "qCompress";
case ZLibOp::Decompression: return "qUncompress";
}
Q_UNREACHABLE();
return nullptr;
}
Q_DECL_COLD_FUNCTION
static QByteArray zlibError(ZLibOp op, const char *what)
{
qWarning("%s: %s", zlibOpAsString(op), what);
return QByteArray();
}
Q_DECL_COLD_FUNCTION
static QByteArray dataIsNull(ZLibOp op)
{
return zlibError(op, "Data is null");
}
Q_DECL_COLD_FUNCTION
static QByteArray tooMuchData(ZLibOp op)
{
return zlibError(op, "Not enough memory");
}
Q_DECL_COLD_FUNCTION
static QByteArray invalidCompressedData()
{
return zlibError(ZLibOp::Decompression, "Input data is corrupted");
}
QByteArray qCompress(const uchar* data, qsizetype nbytes, int compressionLevel) QByteArray qCompress(const uchar* data, qsizetype nbytes, int compressionLevel)
{ {
constexpr qsizetype HeaderSize = sizeof(CompressSizeHint_t); constexpr qsizetype HeaderSize = sizeof(CompressSizeHint_t);
if (nbytes == 0) { if (nbytes == 0) {
return QByteArray(HeaderSize, '\0'); return QByteArray(HeaderSize, '\0');
} }
if (!data) { if (!data)
qWarning("qCompress: Data is null"); return dataIsNull(ZLibOp::Compression);
return QByteArray();
}
if (compressionLevel < -1 || compressionLevel > 9) if (compressionLevel < -1 || compressionLevel > 9)
compressionLevel = -1; compressionLevel = -1;
@ -606,13 +643,6 @@ QByteArray qCompress(const uchar* data, qsizetype nbytes, int compressionLevel)
*/ */
#ifndef QT_NO_COMPRESS #ifndef QT_NO_COMPRESS
Q_DECL_COLD_FUNCTION
static QByteArray invalidCompressedData()
{
qWarning("qUncompress: Input data is corrupted");
return QByteArray();
}
/*! \relates QByteArray /*! \relates QByteArray
\overload \overload
@ -624,10 +654,8 @@ static QByteArray invalidCompressedData()
*/ */
QByteArray qUncompress(const uchar* data, qsizetype nbytes) QByteArray qUncompress(const uchar* data, qsizetype nbytes)
{ {
if (!data) { if (!data)
qWarning("qUncompress: Data is null"); return dataIsNull(ZLibOp::Decompression);
return QByteArray();
}
constexpr qsizetype HeaderSize = sizeof(CompressSizeHint_t); constexpr qsizetype HeaderSize = sizeof(CompressSizeHint_t);
if (nbytes < HeaderSize) if (nbytes < HeaderSize)
@ -643,12 +671,12 @@ QByteArray qUncompress(const uchar* data, qsizetype nbytes)
constexpr size_t MaxZLibSize = (std::numeric_limits<uLong>::max)(); constexpr size_t MaxZLibSize = (std::numeric_limits<uLong>::max)();
constexpr size_t MaxDecompressedSize = (std::min)(size_t(MaxByteArraySize), MaxZLibSize); constexpr size_t MaxDecompressedSize = (std::min)(size_t(MaxByteArraySize), MaxZLibSize);
if (len > MaxDecompressedSize) if (len > MaxDecompressedSize)
return invalidCompressedData(); return tooMuchData(ZLibOp::Decompression);
Q_ASSERT(len <= size_t((std::numeric_limits<qsizetype>::max)())); Q_ASSERT(len <= size_t((std::numeric_limits<qsizetype>::max)()));
QByteArray::DataPointer d(QByteArray::Data::allocate(qsizetype(len))); QByteArray::DataPointer d(QByteArray::Data::allocate(qsizetype(len)));
if (d.data() == nullptr) // allocation failed if (d.data() == nullptr) // allocation failed
return invalidCompressedData(); return tooMuchData(ZLibOp::Decompression);
forever { forever {
const auto alloc = len; const auto alloc = len;
@ -665,23 +693,21 @@ QByteArray qUncompress(const uchar* data, qsizetype nbytes)
} }
case Z_MEM_ERROR: case Z_MEM_ERROR:
qWarning("qUncompress: Z_MEM_ERROR: Not enough memory"); return tooMuchData(ZLibOp::Decompression);
return QByteArray();
case Z_BUF_ERROR: case Z_BUF_ERROR:
if (len == MaxDecompressedSize) // can't grow further if (len == MaxDecompressedSize) // can't grow further
return invalidCompressedData(); return tooMuchData(ZLibOp::Decompression);
if (qMulOverflow<2>(len, &len)) if (qMulOverflow<2>(len, &len))
len = MaxDecompressedSize; len = MaxDecompressedSize;
d->reallocate(qsizetype(len), QArrayData::Grow); // cannot overflow! d->reallocate(qsizetype(len), QArrayData::Grow); // cannot overflow!
if (d.data() == nullptr) // reallocation failed if (d.data() == nullptr) // reallocation failed
return invalidCompressedData(); return tooMuchData(ZLibOp::Decompression);
continue; continue;
case Z_DATA_ERROR: case Z_DATA_ERROR:
qWarning("qUncompress: Z_DATA_ERROR: Input data is corrupted"); return invalidCompressedData();
return QByteArray();
} }
} }
} }