QHash: make it so a zero seed selects a constant algorithm

That's the only value for which we will guarantee a stable result across
Qt versions and across invocations of the same application on different
architectures is zero. For any other value, we reserve the right to
change the algorithm. We'll now print a warning when we detect that.

Task-number: QTBUG-47566
Change-Id: I27b55fdf514247549455fffd14b1135e10d24ab4
Reviewed-by: Lars Knoll <lars.knoll@qt.io>
Reviewed-by: David Faure <david.faure@kdab.com>
This commit is contained in:
Thiago Macieira 2017-03-31 14:04:06 -07:00
parent 9763b324fd
commit 4ba740b3ba
2 changed files with 27 additions and 13 deletions

View File

@ -203,7 +203,7 @@ static inline uint hash(const uchar *p, size_t len, uint seed) Q_DECL_NOTHROW
{
uint h = seed;
if (hasFastCrc32())
if (seed && hasFastCrc32())
return crc32(p, len, h);
for (size_t i = 0; i < len; ++i)
@ -221,7 +221,7 @@ static inline uint hash(const QChar *p, size_t len, uint seed) Q_DECL_NOTHROW
{
uint h = seed;
if (hasFastCrc32())
if (seed && hasFastCrc32())
return crc32(p, len, h);
for (size_t i = 0; i < len; ++i)
@ -288,8 +288,15 @@ static uint qt_create_qhash_seed()
#ifndef QT_BOOTSTRAPPED
QByteArray envSeed = qgetenv("QT_HASH_SEED");
if (!envSeed.isNull())
return envSeed.toUInt();
if (!envSeed.isNull()) {
uint seed = envSeed.toUInt();
if (seed) {
// can't use qWarning here (reentrancy)
fprintf(stderr, "QT_HASH_SEED: forced seed value is not 0, cannot guarantee that the "
"hashing functions will produce a stable value.");
}
return seed;
}
#ifdef Q_OS_UNIX
int randomfd = qt_safe_open("/dev/urandom", O_RDONLY);
@ -379,14 +386,17 @@ int qGlobalQHashSeed()
is needed. We discourage to do it in production code as it can make your
application susceptible to \l{algorithmic complexity attacks}.
From Qt 5.10 and onwards, the only allowed values are 0 and -1. Passing the
value -1 will reinitialize the global QHash seed to a random value, while
the value of 0 is used to request a stable algorithm for C++ primitive
types types (like \c int) and string types (QString, QByteArray).
The seed is set in any newly created QHash. See \l{qHash} about how this seed
is being used by QHash.
If the environment variable \c QT_HASH_SEED is set, calling this function will
result in a no-op.
Passing the value -1 will reinitialize the global QHash seed to a random value.
\sa qGlobalQHashSeed
*/
void qSetGlobalQHashSeed(int newSeed)
@ -397,6 +407,11 @@ void qSetGlobalQHashSeed(int newSeed)
int x(qt_create_qhash_seed() & INT_MAX);
qt_qhash_seed.store(x);
} else {
if (newSeed) {
// can't use qWarning here (reentrancy)
fprintf(stderr, "qSetGlobalQHashSeed: forced seed value is not 0, cannot guarantee that the "
"hashing functions will produce a stable value.");
}
qt_qhash_seed.store(newSeed & INT_MAX);
}
}
@ -1254,9 +1269,8 @@ uint qHash(long double key, uint seed) Q_DECL_NOTHROW
should never depend on a particular QHash ordering, there may be situations
where you temporarily need deterministic behavior, for example for debugging or
regression testing. To disable the randomization, define the environment
variable \c QT_HASH_SEED. The contents of that variable, interpreted as a
decimal value, will be used as the seed for qHash(). Alternatively, you can
call the qSetGlobalQHashSeed() function.
variable \c QT_HASH_SEED to have the value 0. Alternatively, you can call
the qSetGlobalQHashSeed() function with the value 0.
\sa QHashIterator, QMutableHashIterator, QMap, QSet
*/

View File

@ -284,17 +284,17 @@ void tst_QHashFunctions::rangeCommutative()
void tst_QHashFunctions::setGlobalQHashSeed()
{
// Setter works as advertised
qSetGlobalQHashSeed(0x10101010);
QCOMPARE(qGlobalQHashSeed(), 0x10101010);
qSetGlobalQHashSeed(0);
QCOMPARE(qGlobalQHashSeed(), 0);
// Creating a new QHash doesn't reset the seed
QHash<QString, int> someHash;
someHash.insert("foo", 42);
QCOMPARE(qGlobalQHashSeed(), 0x10101010);
QCOMPARE(qGlobalQHashSeed(), 0);
// Reset works as advertised
qSetGlobalQHashSeed(-1);
QVERIFY(qGlobalQHashSeed() != -1);
QVERIFY(qGlobalQHashSeed() > 0);
}
QTEST_APPLESS_MAIN(tst_QHashFunctions)