From be73ca7eb1cebcc15064666e647bc337b5c2baa2 Mon Sep 17 00:00:00 2001 From: =?UTF-8?q?=C3=98ystein=20Heskestad?= Date: Wed, 3 Mar 2021 12:23:18 +0100 Subject: Make qdecompresshelper archive bomb check only trigger for large files MIME-Version: 1.0 Content-Type: text/plain; charset=UTF-8 Content-Transfer-Encoding: 8bit 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 Pick-to: 6.0 6.1 Change-Id: I32258cb7c957f2a23a05157ba4ed5c0af2ba585e Reviewed-by: MÃ¥rten Nordheim Reviewed-by: Edward Welbourne --- src/network/access/qdecompresshelper.cpp | 8 +++++ src/network/access/qdecompresshelper_p.h | 2 ++ tests/auto/network/access/qdecompresshelper/10K.gz | Bin 0 -> 55 bytes .../qdecompresshelper/tst_qdecompresshelper.cpp | 40 +++++++++++++++++++++ 4 files changed, 50 insertions(+) create mode 100644 tests/auto/network/access/qdecompresshelper/10K.gz diff --git a/src/network/access/qdecompresshelper.cpp b/src/network/access/qdecompresshelper.cpp index 2e44a58cf4..d0e75ef0dc 100644 --- a/src/network/access/qdecompresshelper.cpp +++ b/src/network/access/qdecompresshelper.cpp @@ -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); diff --git a/src/network/access/qdecompresshelper_p.h b/src/network/access/qdecompresshelper_p.h index 4e66581022..6a77775790 100644 --- a/src/network/access/qdecompresshelper_p.h +++ b/src/network/access/qdecompresshelper_p.h @@ -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; diff --git a/tests/auto/network/access/qdecompresshelper/10K.gz b/tests/auto/network/access/qdecompresshelper/10K.gz new file mode 100644 index 0000000000..c5c4959763 Binary files /dev/null and b/tests/auto/network/access/qdecompresshelper/10K.gz differ diff --git a/tests/auto/network/access/qdecompresshelper/tst_qdecompresshelper.cpp b/tests/auto/network/access/qdecompresshelper/tst_qdecompresshelper.cpp index c31ab294cc..cfeff188f4 100644 --- a/tests/auto/network/access/qdecompresshelper/tst_qdecompresshelper.cpp +++ b/tests/auto/network/access/qdecompresshelper/tst_qdecompresshelper.cpp @@ -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("encoding"); + QTest::addColumn("path"); + QTest::addColumn("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() { -- cgit v1.2.3