From a14fadfb640103ecea76c3966cf23668bb7f448b Mon Sep 17 00:00:00 2001 From: Gabriel de Dietrich Date: Mon, 21 Sep 2015 15:53:49 +0200 Subject: Add getter and setter for qt_qhash_seed In some cases it's not possible to use QT_HASH_SEED, specially when we need to set the environment variable from inside the application, as dynamically loaded libraries or plugins may create static QHash instances. That would set qt_qhash_seed to a value different from -1 and skip the env var value. For those cases, and when we still want to set qt_qhash_seed, we provide a way to enforce its value. Auto-tests accessing qt_qhash_seed directly have been updated accordingly. Usage in qdoc, uic and rcc has been left as is for the time being. Change-Id: I3b35b4fa0223c83b1348a6508641905a2a63266f Reviewed-by: Lars Knoll --- src/corelib/tools/qhash.cpp | 50 +++++++++++++++++++++- src/corelib/tools/qhash.h | 3 ++ .../tools/qhashfunctions/tst_qhashfunctions.cpp | 18 ++++++++ tests/auto/corelib/tools/qset/tst_qset.cpp | 24 ++--------- .../dbus/qdbusxmlparser/tst_qdbusxmlparser.cpp | 7 +-- tests/auto/other/lancelot/tst_lancelot.cpp | 6 +-- 6 files changed, 75 insertions(+), 33 deletions(-) diff --git a/src/corelib/tools/qhash.cpp b/src/corelib/tools/qhash.cpp index 1f3ea36121..b334a697a9 100644 --- a/src/corelib/tools/qhash.cpp +++ b/src/corelib/tools/qhash.cpp @@ -290,6 +290,53 @@ static void qt_initialize_qhash_seed() } } +/*! \relates QHash + \since 5.6 + + Returns the current global QHash seed. + + The seed is set in any newly created QHash. See \l{qHash} about how this seed + is being used by QHash. + + \sa qSetGlobalQHashSeed + */ +int qGlobalQHashSeed() +{ + return qt_qhash_seed.load(); +} + +/*! \relates QHash + \since 5.6 + + Sets the global QHash seed. + + Manually setting the global QHash seed value should be done only for testing + and debugging purposes, when deterministic and reproducible behavior on a QHash + is needed. We discourage to do it in production code as it can make your + application susceptible to \l{algorithmic complexity attacks}. + + 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) +{ + if (qEnvironmentVariableIsSet("QT_HASH_SEED")) + return; + if (newSeed == -1) { + int x(qt_create_qhash_seed() & INT_MAX); + qt_qhash_seed.store(x); + } else { + qt_qhash_seed.store(newSeed & INT_MAX); + } +} + /*! \internal @@ -1132,7 +1179,8 @@ uint qHash(long double key, uint seed) Q_DECL_NOTHROW 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(). + decimal value, will be used as the seed for qHash(). Alternatively, you can + call the qSetGlobalQHashSeed() function. \sa QHashIterator, QMutableHashIterator, QMap, QSet */ diff --git a/src/corelib/tools/qhash.h b/src/corelib/tools/qhash.h index e367cc0068..8d65a018ae 100644 --- a/src/corelib/tools/qhash.h +++ b/src/corelib/tools/qhash.h @@ -1037,6 +1037,9 @@ Q_INLINE_TEMPLATE int QMultiHash::count(const Key &key, const T &value) return n; } +Q_CORE_EXPORT int qGlobalQHashSeed(); +Q_CORE_EXPORT void qSetGlobalQHashSeed(int newSeed); + Q_DECLARE_ASSOCIATIVE_ITERATOR(Hash) Q_DECLARE_MUTABLE_ASSOCIATIVE_ITERATOR(Hash) diff --git a/tests/auto/corelib/tools/qhashfunctions/tst_qhashfunctions.cpp b/tests/auto/corelib/tools/qhashfunctions/tst_qhashfunctions.cpp index 6961426f59..bde9433a24 100644 --- a/tests/auto/corelib/tools/qhashfunctions/tst_qhashfunctions.cpp +++ b/tests/auto/corelib/tools/qhashfunctions/tst_qhashfunctions.cpp @@ -50,6 +50,8 @@ private Q_SLOTS: void qthash(); void range(); void rangeCommutative(); + + void setGlobalQHashSeed(); }; void tst_QHashFunctions::qhash() @@ -207,5 +209,21 @@ void tst_QHashFunctions::rangeCommutative() (void)qHashRangeCommutative(hashables, hashables + numHashables); } +void tst_QHashFunctions::setGlobalQHashSeed() +{ + // Setter works as advertised + qSetGlobalQHashSeed(0x10101010); + QCOMPARE(qGlobalQHashSeed(), 0x10101010); + + // Creating a new QHash doesn't reset the seed + QHash someHash; + someHash.insert("foo", 42); + QCOMPARE(qGlobalQHashSeed(), 0x10101010); + + // Reset works as advertised + qSetGlobalQHashSeed(-1); + QVERIFY(qGlobalQHashSeed() != -1); +} + QTEST_APPLESS_MAIN(tst_QHashFunctions) #include "tst_qhashfunctions.moc" diff --git a/tests/auto/corelib/tools/qset/tst_qset.cpp b/tests/auto/corelib/tools/qset/tst_qset.cpp index ef0ebabd66..134d257d64 100644 --- a/tests/auto/corelib/tools/qset/tst_qset.cpp +++ b/tests/auto/corelib/tools/qset/tst_qset.cpp @@ -970,24 +970,6 @@ void tst_QSet::initializerList() #endif } -QT_BEGIN_NAMESPACE -extern Q_CORE_EXPORT QBasicAtomicInt qt_qhash_seed; // from qhash.cpp -QT_END_NAMESPACE - -class QtQHashSeedSaver { - int oldSeed, newSeed; -public: - explicit QtQHashSeedSaver(int seed) - : oldSeed(qt_qhash_seed.fetchAndStoreRelaxed(seed)), - newSeed(seed) - {} - ~QtQHashSeedSaver() - { - // only restore when no-one else changed the seed in the meantime: - qt_qhash_seed.testAndSetRelaxed(newSeed, oldSeed); - } -}; - void tst_QSet::qhash() { // @@ -995,14 +977,14 @@ void tst_QSet::qhash() // { // create some deterministic initial state: - const QtQHashSeedSaver seed1(0); + qSetGlobalQHashSeed(0); QSet s1; s1.reserve(4); s1 << 400 << 300 << 200 << 100; // also change the seed: - const QtQHashSeedSaver seed2(0x10101010); + qSetGlobalQHashSeed(0x10101010); QSet s2; s2.reserve(100); // provoke different bucket counts @@ -1049,7 +1031,7 @@ void tst_QSet::intersects() s1 << 200; QVERIFY(s1.intersects(s2)); - const QtQHashSeedSaver seedSaver(0x10101010); + qSetGlobalQHashSeed(0x10101010); QSet s3; s3 << 500; QVERIFY(!s1.intersects(s3)); diff --git a/tests/auto/dbus/qdbusxmlparser/tst_qdbusxmlparser.cpp b/tests/auto/dbus/qdbusxmlparser/tst_qdbusxmlparser.cpp index 4dbe0aae7d..c57292b7db 100644 --- a/tests/auto/dbus/qdbusxmlparser/tst_qdbusxmlparser.cpp +++ b/tests/auto/dbus/qdbusxmlparser/tst_qdbusxmlparser.cpp @@ -64,15 +64,10 @@ private slots: void properties(); }; -QT_BEGIN_NAMESPACE -// Avoid QHash randomization so that the order of the XML attributes is stable -extern Q_CORE_EXPORT QBasicAtomicInt qt_qhash_seed; // from qhash.cpp -QT_END_NAMESPACE - void tst_QDBusXmlParser::initTestCase() { // Always initialize the hash seed to 0 to get reliable test results - qt_qhash_seed.store(0); + qSetGlobalQHashSeed(0); } void tst_QDBusXmlParser::parsing_data() diff --git a/tests/auto/other/lancelot/tst_lancelot.cpp b/tests/auto/other/lancelot/tst_lancelot.cpp index 6da3d06ba8..3022114403 100644 --- a/tests/auto/other/lancelot/tst_lancelot.cpp +++ b/tests/auto/other/lancelot/tst_lancelot.cpp @@ -259,13 +259,9 @@ void tst_Lancelot::paint(QPaintDevice *device, GraphicsEngine engine, const QStr QTEST_MAIN(tst_Lancelot) #undef main -QT_BEGIN_NAMESPACE -extern Q_DECL_IMPORT QBasicAtomicInt qt_qhash_seed; // from qhash.cpp -QT_END_NAMESPACE - int main(int argc, char *argv[]) { - qt_qhash_seed.store(0); // Avoid rendering variations caused by QHash randomization + qSetGlobalQHashSeed(0); // Avoid rendering variations caused by QHash randomization QBaselineTest::handleCmdLineArgs(&argc, &argv); return _realmain(argc, argv); -- cgit v1.2.3