QCryptographicHash: don't present the same data over and over again

Need to decrement 'remaining' (check), but also increment data (meep).

Testing is a bit complicated, as most algorithms are just too slow to
fit into the 5min QTestLib timeout.  Picked the fast ones and Sha512
(which completes here in < 17s, with threads), at least.

Amends e12577b56396cca0df05f88f8787706a3a12c82d.

[ChangeLog][QtCore][QCryptographicHash] Fixed a bug where presenting
more than 4GiB in a single addData() call would calculate the wrong
result().

Change-Id: Ic72916ebc33ba087d58225af6d8240e46e41f434
Reviewed-by: Lars Knoll <lars.knoll@qt.io>
(cherry picked from commit 35453446a511366e1250858b249e36c80b6ad044)
Reviewed-by: Mårten Nordheim <marten.nordheim@qt.io>
This commit is contained in:
Marc Mutz 2021-07-04 23:44:01 +02:00
parent b26f39b946
commit 3fa9c19481
2 changed files with 85 additions and 3 deletions

View File

@ -363,10 +363,8 @@ void QCryptographicHash::addData(const char *data, qsizetype length)
// feed the data UINT_MAX bytes at a time, as some of the methods below
// take a uint (of course, feeding more than 4G of data into the hashing
// functions will be pretty slow anyway)
qsizetype remaining = length;
while (remaining) {
for (auto remaining = length; remaining; remaining -= length, data += length) {
length = qMin(qsizetype(std::numeric_limits<uint>::max()), remaining);
remaining -= length;
#else
{
#endif

View File

@ -29,9 +29,14 @@
#include <QtCore/QCoreApplication>
#include <QTest>
#include <QScopeGuard>
#include <QCryptographicHash>
#include <QtCore/QMetaEnum>
#if QT_CONFIG(cxx11_future)
# include <thread>
#endif
Q_DECLARE_METATYPE(QCryptographicHash::Algorithm)
class tst_QCryptographicHash : public QObject
@ -51,6 +56,11 @@ private slots:
void files();
void hashLength_data();
void hashLength();
// keep last
void moreThan4GiBOfData_data();
void moreThan4GiBOfData();
private:
std::vector<char> large;
};
void tst_QCryptographicHash::repeated_result_data()
@ -419,5 +429,79 @@ void tst_QCryptographicHash::hashLength()
QCOMPARE(QCryptographicHash::hashLength(algorithm), output.length());
}
void tst_QCryptographicHash::moreThan4GiBOfData_data()
{
#if QT_POINTER_SIZE > 4
QElapsedTimer timer;
timer.start();
const size_t GiB = 1024 * 1024 * 1024;
try {
large.resize(4 * GiB + 1, '\0');
} catch (const std::bad_alloc &) {
QSKIP("Could not allocate 4GiB plus one byte of RAM.");
}
QCOMPARE(large.size(), 4 * GiB + 1);
large.back() = '\1';
qDebug("created dataset in %lld ms", timer.elapsed());
QTest::addColumn<QCryptographicHash::Algorithm>("algorithm");
auto me = QMetaEnum::fromType<QCryptographicHash::Algorithm>();
auto row = [me] (QCryptographicHash::Algorithm algo) {
QTest::addRow("%s", me.valueToKey(int(algo))) << algo;
};
// these are reasonably fast (O(secs))
row(QCryptographicHash::Md4);
row(QCryptographicHash::Md5);
row(QCryptographicHash::Sha1);
// this is already significantly slower, but important (O(min)
row(QCryptographicHash::Sha512);
// the rest is just too slow
#else
QSKIP("This test is 64-bit only.");
#endif
}
void tst_QCryptographicHash::moreThan4GiBOfData()
{
QFETCH(const QCryptographicHash::Algorithm, algorithm);
# if QT_CONFIG(cxx11_future)
using MaybeThread = std::thread;
# else
struct MaybeThread {
std::function<void()> func;
void join() { func(); }
};
# endif
QElapsedTimer timer;
timer.start();
const auto sg = qScopeGuard([&] {
qDebug() << algorithm << "test finished in" << timer.restart() << "ms";
});
const auto begin = large.data();
const auto mid = begin + large.size() / 2;
const auto end = begin + large.size();
QByteArray single;
QByteArray chunked;
auto t = MaybeThread{[&] {
QCryptographicHash h(algorithm);
h.addData(begin, end - begin);
single = h.result();
}};
{
QCryptographicHash h(algorithm);
h.addData(begin, mid - begin);
h.addData(mid, end - mid);
chunked = h.result();
}
t.join();
QCOMPARE(single, chunked);
}
QTEST_MAIN(tst_QCryptographicHash)
#include "tst_qcryptographichash.moc"