Make qdecompresshelper archive bomb check only trigger for large files

This is to avoid false positives.
By default files are large if uncompressed size > 10 MB. Only configurable internally.
Also add auto tests.

Task-number: QTBUG-91392
Change-Id: I32258cb7c957f2a23a05157ba4ed5c0af2ba585e
Reviewed-by: Mårten Nordheim <marten.nordheim@qt.io>
Reviewed-by: Edward Welbourne <edward.welbourne@qt.io>
(cherry picked from commit be73ca7eb1cebcc15064666e647bc337b5c2baa2)
Reviewed-by: Qt Cherry-pick Bot <cherrypick_bot@qt-project.org>
This commit is contained in:
Øystein Heskestad 2021-03-03 12:23:18 +01:00 committed by Qt Cherry-pick Bot
parent 218b7c8a5b
commit 03332d8a00
4 changed files with 50 additions and 0 deletions

View File

@ -405,6 +405,11 @@ void QDecompressHelper::setArchiveBombDetectionEnabled(bool enable)
countHelper->setArchiveBombDetectionEnabled(enable);
}
void QDecompressHelper::setMinimumArchiveBombSize(qint64 threshold)
{
minimumArchiveBombSize = threshold;
}
bool QDecompressHelper::isPotentialArchiveBomb() const
{
if (!archiveBombDetectionEnabled)
@ -413,6 +418,9 @@ bool QDecompressHelper::isPotentialArchiveBomb() const
if (totalCompressedBytes == 0)
return false;
if (totalUncompressedBytes <= minimumArchiveBombSize)
return false;
// Some protection against malicious or corrupted compressed files that expand far more than
// is reasonable.
double ratio = double(totalUncompressedBytes) / double(totalCompressedBytes);

View File

@ -92,6 +92,7 @@ public:
void clear();
void setArchiveBombDetectionEnabled(bool enable);
void setMinimumArchiveBombSize(qint64 threshold);
static bool isSupportedEncoding(const QByteArray &encoding);
static QByteArrayList acceptedEncoding();
@ -119,6 +120,7 @@ private:
// Used for calculating the ratio
bool archiveBombDetectionEnabled = true;
qint64 minimumArchiveBombSize = 10 * 1024 * 1024;
qint64 totalUncompressedBytes = 0;
qint64 totalCompressedBytes = 0;

Binary file not shown.

View File

@ -64,6 +64,9 @@ private Q_SLOTS:
void decompressBigData_data();
void decompressBigData();
void archiveBomb_data();
void archiveBomb();
#if QT_POINTER_SIZE >= 8
void bigZlib();
#endif
@ -392,6 +395,43 @@ void tst_QDecompressHelper::decompressBigData()
QTEST(totalSize, "size");
}
void tst_QDecompressHelper::archiveBomb_data()
{
QTest::addColumn<QByteArray>("encoding");
QTest::addColumn<QString>("path");
QTest::addColumn<bool>("shouldFail");
QTest::newRow("gzip-10K") << QByteArray("gzip") << (srcDir + "/10K.gz") << false;
QTest::newRow("gzip-4G") << QByteArray("gzip") << QString(":/4G.gz") << true;
}
void tst_QDecompressHelper::archiveBomb()
{
QFETCH(bool, shouldFail);
QFETCH(QString, path);
QFile file(path);
QVERIFY(file.open(QIODevice::ReadOnly));
QDecompressHelper helper;
QFETCH(QByteArray, encoding);
helper.setEncoding(encoding);
QVERIFY(helper.isValid());
constexpr qint64 SafeSizeLimit = 10 * 1024 * 1024;
constexpr qint64 RatioLimit = 40;
qint64 bytesToRead = std::min(SafeSizeLimit / RatioLimit, file.bytesAvailable());
QByteArray output(1 + bytesToRead * RatioLimit, Qt::Uninitialized);
helper.feed(file.read(bytesToRead));
qsizetype bytesRead = helper.read(output.data(), output.size());
QVERIFY(bytesRead <= output.size());
QVERIFY(helper.isValid());
if (shouldFail)
QCOMPARE(bytesRead, -1);
else
QVERIFY(bytesRead > 0);
}
#if QT_POINTER_SIZE >= 8
void tst_QDecompressHelper::bigZlib()
{