QHash: Initialize the hash seed as soon as QtCore loads

Instead of lazily initializing (with some platform exceptions, see the
equivalent qsimd.cpp commit). The rationale is the same as for
qCpuFeatures(): this the qHashBits() function is hot and the current
code generation needs to save a lot of state because of the possible
call to the C++ runtime functions to enable the thread-safe
initialization of the hash seed.

[ChangeLog][Important Behavior Changes] QtCore now initializes the QHash
global seed before the main() function is run, so it is no longer
possible to use qputenv() to affect the seed value for the current
process. Disabling the random global seed for the current process should
be done programmatically with by calling either the 6.2 function
QHashSeed::setDeterministicGlobalSeed() or, if compatibility with Qt 5
is required, by calling qSetGlobalQHashSeed() with value 0.

Change-Id: I54f205f6b7314351b078fffd16cf7eae93f9e27e
Reviewed-by: Lars Knoll <lars.knoll@qt.io>
This commit is contained in:
Thiago Macieira 2022-01-31 15:27:57 -08:00
parent 41b2c4204b
commit cc5cc3225d
2 changed files with 34 additions and 13 deletions

View File

@ -35,8 +35,10 @@ qt_internal_add_module(Core
NO_GENERATE_METATYPES # metatypes are extracted manually below NO_GENERATE_METATYPES # metatypes are extracted manually below
EXCEPTIONS EXCEPTIONS
SOURCES SOURCES
# Keep qsimd.cpp first so its global static runs first # Keep these .cpp files in the first and in the order they are so their
# static initialization order is retained
global/qsimd.cpp global/qsimd.h global/qsimd_p.h global/qsimd.cpp global/qsimd.h global/qsimd_p.h
tools/qhash.cpp tools/qhash.h
# Keep the rest alphabetical # Keep the rest alphabetical
compat/removed_api.cpp compat/removed_api.cpp
@ -226,7 +228,6 @@ qt_internal_add_module(Core
tools/qduplicatetracker_p.h tools/qduplicatetracker_p.h
tools/qflatmap_p.h tools/qflatmap_p.h
tools/qfreelist.cpp tools/qfreelist_p.h tools/qfreelist.cpp tools/qfreelist_p.h
tools/qhash.cpp tools/qhash.h
tools/qhashfunctions.h tools/qhashfunctions.h
tools/qiterator.h tools/qiterator.h
tools/qline.cpp tools/qline.h tools/qline.cpp tools/qline.h

View File

@ -66,11 +66,17 @@
#ifndef QT_BOOTSTRAPPED #ifndef QT_BOOTSTRAPPED
#include <qcoreapplication.h> #include <qcoreapplication.h>
#include <qrandom.h> #include <qrandom.h>
#include <private/qlocale_tools_p.h>
#endif // QT_BOOTSTRAPPED #endif // QT_BOOTSTRAPPED
#include <array> #include <array>
#include <limits.h> #include <limits.h>
#if defined(QT_NO_DEBUG) && !defined(NDEBUG)
# define NDEBUG
#endif
#include <assert.h>
#ifdef Q_CC_GNU #ifdef Q_CC_GNU
# define Q_DECL_HOT_FUNCTION __attribute__((hot)) # define Q_DECL_HOT_FUNCTION __attribute__((hot))
#else #else
@ -89,7 +95,11 @@ struct HashSeedStorage
static constexpr int SeedCount = 2; static constexpr int SeedCount = 2;
QBasicAtomicInteger<quintptr> seeds[SeedCount] = { Q_BASIC_ATOMIC_INITIALIZER(0), Q_BASIC_ATOMIC_INITIALIZER(0) }; QBasicAtomicInteger<quintptr> seeds[SeedCount] = { Q_BASIC_ATOMIC_INITIALIZER(0), Q_BASIC_ATOMIC_INITIALIZER(0) };
#if !QT_SUPPORTS_INIT_PRIORITY || defined(QT_BOOTSTRAPPED)
constexpr HashSeedStorage() = default; constexpr HashSeedStorage() = default;
#else
HashSeedStorage() { initialize(0); }
#endif
enum State { enum State {
OverriddenByEnvironment = -1, OverriddenByEnvironment = -1,
@ -126,19 +136,24 @@ struct HashSeedStorage
private: private:
Q_DECL_COLD_FUNCTION Q_NEVER_INLINE StateResult initialize(int which) noexcept; Q_DECL_COLD_FUNCTION Q_NEVER_INLINE StateResult initialize(int which) noexcept;
[[maybe_unused]] static void ensureConstexprConstructibility()
{
static_assert(std::is_trivially_destructible_v<HashSeedStorage>);
static constexpr HashSeedStorage dummy {};
Q_UNUSED(dummy);
}
}; };
[[maybe_unused]] HashSeedStorage::StateResult HashSeedStorage::initialize(int which) noexcept [[maybe_unused]] HashSeedStorage::StateResult HashSeedStorage::initialize(int which) noexcept
{ {
StateResult result = { 0, OverriddenByEnvironment }; StateResult result = { 0, OverriddenByEnvironment };
bool ok; #ifdef QT_BOOTSTRAPPED
int seed = qEnvironmentVariableIntValue("QT_HASH_SEED", &ok); Q_UNUSED(which);
Q_UNREACHABLE();
#else
// can't use qEnvironmentVariableIntValue (reentrancy)
const char *seedstr = getenv("QT_HASH_SEED");
const char *endptr = nullptr;
bool ok = false;
int seed;
if (seedstr)
seed = qstrntoll(seedstr, strlen(seedstr), &endptr, 10, &ok);
if (ok && endptr != seedstr + strlen(seedstr))
ok = false;
if (ok) { if (ok) {
if (seed) { if (seed) {
// can't use qWarning here (reentrancy) // can't use qWarning here (reentrancy)
@ -158,6 +173,7 @@ private:
result.requestedSeed = x.data[i]; result.requestedSeed = x.data[i];
} }
result.state = JustInitialized; result.state = JustInitialized;
#endif
return result; return result;
} }
@ -166,14 +182,15 @@ inline HashSeedStorage::StateResult HashSeedStorage::state(int which)
constexpr quintptr BadSeed = quintptr(Q_UINT64_C(0x5555'5555'5555'5555)); constexpr quintptr BadSeed = quintptr(Q_UINT64_C(0x5555'5555'5555'5555));
StateResult result = { BadSeed, AlreadyInitialized }; StateResult result = { BadSeed, AlreadyInitialized };
#ifndef QT_BOOTSTRAPPED #if defined(QT_BOOTSTRAPPED)
result = { 0, OverriddenByEnvironment };
#elif !QT_SUPPORTS_INIT_PRIORITY
// dynamic initialization
static auto once = [&]() { static auto once = [&]() {
result = initialize(which); result = initialize(which);
return true; return true;
}(); }();
Q_UNUSED(once); Q_UNUSED(once);
#else
result = { 0, OverriddenByEnvironment };
#endif #endif
if (result.state == AlreadyInitialized && which >= 0) if (result.state == AlreadyInitialized && which >= 0)
@ -185,6 +202,9 @@ inline HashSeedStorage::StateResult HashSeedStorage::state(int which)
/* /*
The QHash seed itself. The QHash seed itself.
*/ */
#ifdef Q_DECL_INIT_PRIORITY
Q_DECL_INIT_PRIORITY(05)
#endif
static HashSeedStorage qt_qhash_seed; static HashSeedStorage qt_qhash_seed;
/* /*