summaryrefslogtreecommitdiffstats
path: root/src/corelib/global/qrandom.cpp
diff options
context:
space:
mode:
authorThiago Macieira <thiago.macieira@intel.com>2021-04-02 01:18:32 -0700
committerThiago Macieira <thiago.macieira@intel.com>2021-05-23 12:08:43 -0700
commit928ce402a41c291e6f746cac903a4a44702c4689 (patch)
tree69a91d922965d59e577897af602b9d73ff6a0bf3 /src/corelib/global/qrandom.cpp
parent4ef99b8fbb3ba32df5591ac306fe8a7baa31c92c (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.cpp81
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