diff options
author | Thiago Macieira <thiago.macieira@intel.com> | 2021-04-02 01:18:32 -0700 |
---|---|---|
committer | Thiago Macieira <thiago.macieira@intel.com> | 2021-05-23 12:08:43 -0700 |
commit | 928ce402a41c291e6f746cac903a4a44702c4689 (patch) | |
tree | 69a91d922965d59e577897af602b9d73ff6a0bf3 /src/corelib/global/qrandom.cpp | |
parent | 4ef99b8fbb3ba32df5591ac306fe8a7baa31c92c (diff) |
QHash & QRandomGenerator: cooperate to provide a simpler initial seed
Instead of initializing the whole QRandomGenerator::system(), which in
turn gets to checking CPUID and whether the HWRNG works, trust the
operating system functions for an initial value. On Linux, we'll use 4
or 8 of the 16 bytes of random data that the kernel populates for us on
AT_RANDOM.
This should make Qt applications not stall on an early system launch
without an RNG daemon, if compiled without getentropy() support. And
avoids silly mistakes causing recursion, like QTBUG-78007 found.
Additionally, qt_random_initial_value() will most likely not throw
either. It's marked noexcept, even though SystemGenerator::fillBuffer
could throw on Linux, if the current thread is canceled, but Linux also
has AT_RANDOM. That leaves the other Unix systems without getentropy()
(read: macOS, since the BSDs have getentropy()).
Fixes: QTBUG-69555
Change-Id: Id2983978ad544ff79911fffd1671fca1a9f9044d
Reviewed-by: Lars Knoll <lars.knoll@qt.io>
Diffstat (limited to 'src/corelib/global/qrandom.cpp')
-rw-r--r-- | src/corelib/global/qrandom.cpp | 81 |
1 files changed, 74 insertions, 7 deletions
diff --git a/src/corelib/global/qrandom.cpp b/src/corelib/global/qrandom.cpp index 230310f20f..cbc7551b6f 100644 --- a/src/corelib/global/qrandom.cpp +++ b/src/corelib/global/qrandom.cpp @@ -1,6 +1,6 @@ /**************************************************************************** ** -** Copyright (C) 2020 Intel Corporation. +** Copyright (C) 2021 Intel Corporation. ** Copyright (C) 2021 The Qt Company Ltd. ** Contact: https://www.qt.io/licensing/ ** @@ -43,19 +43,20 @@ #include "qrandom.h" #include "qrandom_p.h" -#include <qobjectdefs.h> +#include <qendian.h> #include <qmutex.h> +#include <qobjectdefs.h> #include <qthreadstorage.h> #include <errno.h> +#if QT_CONFIG(getauxval) +# include <sys/auxv.h> +#endif + #if !QT_CONFIG(getentropy) && (!defined(Q_OS_BSD4) || defined(__GLIBC__)) && !defined(Q_OS_WIN) # include "qdeadlinetimer.h" # include "qhashfunctions.h" - -# if QT_CONFIG(getauxval) -# include <sys/auxv.h> -# endif #endif // !QT_CONFIG(getentropy) #ifdef Q_OS_UNIX @@ -169,7 +170,7 @@ struct QRandomGenerator::SystemGenerator } #elif defined(Q_OS_WIN) - qsizetype fillBuffer(void *buffer, qsizetype count) noexcept + static qsizetype fillBuffer(void *buffer, qsizetype count) noexcept { auto RtlGenRandom = SystemFunction036; return RtlGenRandom(buffer, ULONG(count)) ? count: 0; @@ -1293,4 +1294,70 @@ quint64 QRandomGenerator::_fillRange(void *buffer, qptrdiff count) return begin[0] | (quint64(begin[1]) << 32); } +// helper function to call fillBuffer, since we need something to be +// argument-dependent +template <typename Generator, typename FillBufferType> +static qsizetype callFillBuffer(FillBufferType f, quintptr *v) +{ + if constexpr (std::is_member_function_pointer_v<FillBufferType>) { + // member function, need an object + return (Generator::self().*f)(v, sizeof(*v)); + } else { + // static, call directly + return f(v, sizeof(*v)); + } +} + +/*! + \internal + + Returns an initial random value (useful for QHash's global seed). This + function attempts to use OS-provided random values to avoid initializing + QRandomGenerator::system() and qsimd.cpp. + + Note: on some systems, this functionn may rerturn the same value every time + it is called. + */ +quintptr qt_initial_random_value() noexcept +{ + auto acceptableSeed = [](size_t v) { + // two values are reserved: 0 to indicate uninitialized and 1 to + // indicate deterministic seed + return Q_LIKELY(v > 1); + }; + quintptr v = 0; + +#if QT_CONFIG(getauxval) && defined(AT_RANDOM) + // We actually have 16 bytes, but this will do + auto at_random_ptr = reinterpret_cast<size_t *>(getauxval(AT_RANDOM)); + if (at_random_ptr) { + v = qFromUnaligned<quintptr>(at_random_ptr); + if (acceptableSeed(v)) + return v; + } +#endif + + // bypass the hardware RNG, which would mean initializing qsimd.cpp + + for (int attempts = 16; attempts; --attempts) { + using Generator = QRandomGenerator::SystemGenerator; + auto fillBuffer = &Generator::fillBuffer; + if (callFillBuffer<Generator>(fillBuffer, &v) != sizeof(v)) + continue; + + // check if it is static + if (acceptableSeed(v)) + return v; + } + + quint32 u32[2] = {}; + forever { + fallback_fill(u32, sizeof(v) / sizeof(quint32)); + v = u32[0] | (quint64(u32[1]) << 32); + if (acceptableSeed(v)) + break; + } + return v; +} + QT_END_NAMESPACE |