summaryrefslogtreecommitdiffstats
path: root/src/corelib/global/qrandom.cpp
diff options
context:
space:
mode:
Diffstat (limited to 'src/corelib/global/qrandom.cpp')
-rw-r--r--src/corelib/global/qrandom.cpp160
1 files changed, 77 insertions, 83 deletions
diff --git a/src/corelib/global/qrandom.cpp b/src/corelib/global/qrandom.cpp
index eb7acc5edf..fd742898f8 100644
--- a/src/corelib/global/qrandom.cpp
+++ b/src/corelib/global/qrandom.cpp
@@ -1,61 +1,27 @@
-/****************************************************************************
-**
-** Copyright (C) 2020 Intel Corporation.
-** Copyright (C) 2021 The Qt Company Ltd.
-** Contact: https://www.qt.io/licensing/
-**
-** This file is part of the QtCore module of the Qt Toolkit.
-**
-** $QT_BEGIN_LICENSE:LGPL$
-** Commercial License Usage
-** Licensees holding valid commercial Qt licenses may use this file in
-** accordance with the commercial license agreement provided with the
-** Software or, alternatively, in accordance with the terms contained in
-** a written agreement between you and The Qt Company. For licensing terms
-** and conditions see https://www.qt.io/terms-conditions. For further
-** information use the contact form at https://www.qt.io/contact-us.
-**
-** GNU Lesser General Public License Usage
-** Alternatively, this file may be used under the terms of the GNU Lesser
-** General Public License version 3 as published by the Free Software
-** Foundation and appearing in the file LICENSE.LGPL3 included in the
-** packaging of this file. Please review the following information to
-** ensure the GNU Lesser General Public License version 3 requirements
-** will be met: https://www.gnu.org/licenses/lgpl-3.0.html.
-**
-** GNU General Public License Usage
-** Alternatively, this file may be used under the terms of the GNU
-** General Public License version 2.0 or (at your option) the GNU General
-** Public license version 3 or any later version approved by the KDE Free
-** Qt Foundation. The licenses are as published by the Free Software
-** Foundation and appearing in the file LICENSE.GPL2 and LICENSE.GPL3
-** included in the packaging of this file. Please review the following
-** information to ensure the GNU General Public License requirements will
-** be met: https://www.gnu.org/licenses/gpl-2.0.html and
-** https://www.gnu.org/licenses/gpl-3.0.html.
-**
-** $QT_END_LICENSE$
-**
-****************************************************************************/
+// Copyright (C) 2021 Intel Corporation.
+// Copyright (C) 2021 The Qt Company Ltd.
+// SPDX-License-Identifier: LicenseRef-Qt-Commercial OR LGPL-3.0-only OR GPL-2.0-only OR GPL-3.0-only
// for rand_s
#define _CRT_RAND_S
#include "qrandom.h"
#include "qrandom_p.h"
-#include <qobjectdefs.h>
+#include <qendian.h>
#include <qmutex.h>
-#include <qthreadstorage.h>
+#include <qobjectdefs.h>
#include <errno.h>
-#if !QT_CONFIG(getentropy) && (!defined(Q_OS_BSD4) || defined(__GLIBC__)) && !defined(Q_OS_WIN)
+#if QT_CONFIG(getauxval)
+# include <sys/auxv.h>
+#endif
+
+#if QT_CONFIG(getentropy) && __has_include(<sys/random.h>)
+# include <sys/random.h>
+#elif !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
@@ -92,6 +58,10 @@ enum {
FillBufferNoexcept = true
};
+#if defined(QT_BUILD_INTERNAL)
+QBasicAtomicInteger<uint> qt_randomdevice_control = Q_BASIC_ATOMIC_INITIALIZER(0U);
+#endif
+
struct QRandomGenerator::SystemGenerator
{
#if QT_CONFIG(getentropy)
@@ -165,7 +135,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;
@@ -223,10 +193,10 @@ static void fallback_fill(quint32 *ptr, qsizetype left) noexcept
arc4random_buf(ptr, left * sizeof(*ptr));
}
#else
-static QBasicAtomicInteger<unsigned> seed = Q_BASIC_ATOMIC_INITIALIZER(0U);
+Q_CONSTINIT static QBasicAtomicInteger<unsigned> seed = Q_BASIC_ATOMIC_INITIALIZER(0U);
static void fallback_update_seed(unsigned value)
{
- // Update the seed to be used for the fallback mechansim, if we need to.
+ // Update the seed to be used for the fallback mechanism, if we need to.
// We can't use QtPrivate::QHashCombine here because that is not an atomic
// operation. A simple XOR will have to do then.
seed.fetchAndXorRelaxed(value);
@@ -347,16 +317,17 @@ struct QRandomGenerator::SystemAndGlobalGenerators
// the state in case another thread tries to lock the mutex. It's not
// a common scenario, but since sizeof(QRandomGenerator) >= 2560, the
// overhead is actually acceptable.
- // 2) We use both alignas and std::aligned_storage<..., 64> because
- // some implementations of std::aligned_storage can't align to more
- // than a primitive type's alignment.
+ // 2) We use both alignas(T) and alignas(64) because some implementations
+ // can't align to more than a primitive type's alignment.
// 3) We don't store the entire system QRandomGenerator, only the space
// used by the QRandomGenerator::type member. This is fine because we
// (ab)use the common initial sequence exclusion to aliasing rules.
QBasicMutex globalPRNGMutex;
struct ShortenedSystem { uint type; } system_;
SystemGenerator sys;
- alignas(64) std::aligned_storage<sizeof(QRandomGenerator64), 64>::type global_;
+ alignas(64) struct {
+ alignas(QRandomGenerator64) uchar data[sizeof(QRandomGenerator64)];
+ } global_;
constexpr SystemAndGlobalGenerators()
: globalPRNGMutex{}, system_{0}, sys{}, global_{}
@@ -364,22 +335,16 @@ struct QRandomGenerator::SystemAndGlobalGenerators
void confirmLiteral()
{
-#if !defined(Q_CC_MSVC) && !defined(Q_OS_INTEGRITY)
- // Currently fails to compile with MSVC 2017, saying QBasicMutex is not
- // a literal type. Disassembly with MSVC 2013 and 2015 shows it is
- // actually a literal; MSVC 2017 has a bug relating to this, so we're
- // withhold judgement for now. Integrity's compiler is unable to
- // guarantee g's alignment for some reason.
-
+#if !defined(Q_OS_INTEGRITY)
+ // Integrity's compiler is unable to guarantee g's alignment for some reason.
constexpr SystemAndGlobalGenerators g = {};
Q_UNUSED(g);
- static_assert(std::is_literal_type<SystemAndGlobalGenerators>::value);
#endif
}
static SystemAndGlobalGenerators *self()
{
- static SystemAndGlobalGenerators g;
+ Q_CONSTINIT static SystemAndGlobalGenerators g;
static_assert(sizeof(g) > sizeof(QRandomGenerator64));
return &g;
}
@@ -412,8 +377,9 @@ struct QRandomGenerator::SystemAndGlobalGenerators
struct PRNGLocker
{
+ Q_DISABLE_COPY_MOVE(PRNGLocker)
const bool locked;
- PRNGLocker(const QRandomGenerator *that)
+ Q_NODISCARD_CTOR explicit PRNGLocker(const QRandomGenerator *that)
: locked(that == globalNoInit())
{
if (locked)
@@ -672,7 +638,7 @@ inline QRandomGenerator::SystemGenerator &QRandomGenerator::SystemGenerator::sel
position in the deterministic sequence as the \a other object was. Both
objects will generate the same sequence from this point on.
- For that reason, it is not adviseable to create a copy of
+ For that reason, it is not advisable to create a copy of
QRandomGenerator::global(). If one needs an exclusive deterministic
generator, consider instead using securelySeeded() to obtain a new object
that shares no relationship with the QRandomGenerator::global().
@@ -682,7 +648,7 @@ inline QRandomGenerator::SystemGenerator &QRandomGenerator::SystemGenerator::sel
\fn bool operator==(const QRandomGenerator &rng1, const QRandomGenerator &rng2)
\relates QRandomGenerator
- Returns true if the two the two engines \a rng1 and \a rng2 are at the same
+ Returns true if the two engines \a rng1 and \a rng2 are at the same
state or if they are both reading from the operating system facilities,
false otherwise.
*/
@@ -690,7 +656,7 @@ inline QRandomGenerator::SystemGenerator &QRandomGenerator::SystemGenerator::sel
/*!
\fn bool QRandomGenerator::operator!=(const QRandomGenerator &rng1, const QRandomGenerator &rng2)
- Returns \c true if the two the two engines \a rng1 and \a rng2 are at
+ Returns \c true if the two engines \a rng1 and \a rng2 are at
different states or if one of them is reading from the operating system
facilities and the other is not, \c false otherwise.
*/
@@ -800,7 +766,7 @@ inline QRandomGenerator::SystemGenerator &QRandomGenerator::SystemGenerator::sel
*/
/*!
- \fn template <typename UInt> void QRandomGenerator::fillRange(UInt *buffer, qsizetype count)
+ \fn template <typename UInt, QRandomGenerator::IfValidUInt<UInt> = true> void QRandomGenerator::fillRange(UInt *buffer, qsizetype count)
Generates \a count 32- or 64-bit quantities (depending on the type \c UInt)
and stores them in the buffer pointed by \a buffer. This is the most
@@ -816,7 +782,7 @@ inline QRandomGenerator::SystemGenerator &QRandomGenerator::SystemGenerator::sel
*/
/*!
- \fn template <typename UInt, size_t N> void QRandomGenerator::fillRange(UInt (&buffer)[N])
+ \fn template <typename UInt, size_t N, QRandomGenerator::IfValidUInt<UInt> = true> void QRandomGenerator::fillRange(UInt (&buffer)[N])
Generates \c N 32- or 64-bit quantities (depending on the type \c UInt) and
stores them in the \a buffer array. This is the most efficient way to
@@ -1295,26 +1261,54 @@ quint64 QRandomGenerator::_fillRange(void *buffer, qptrdiff count)
return begin[0] | (quint64(begin[1]) << 32);
}
-namespace {
-struct QRandEngine
+// helper function to call fillBuffer, since we need something to be
+// argument-dependent
+template <typename Generator, typename FillBufferType, typename T>
+static qsizetype callFillBuffer(FillBufferType f, T *v)
{
- std::minstd_rand engine;
- QRandEngine() : engine(1) {}
+ 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));
+ }
+}
- int generate()
- {
- std::minstd_rand::result_type v = engine();
- if (std::numeric_limits<int>::max() != RAND_MAX)
- v %= uint(RAND_MAX) + 1;
+/*!
+ \internal
- return int(v);
- }
+ 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.
- void seed(std::minstd_rand::result_type q)
- {
- engine.seed(q);
+ Note: on some systems, this functionn may rerturn the same value every time
+ it is called.
+ */
+QRandomGenerator::InitialRandomData qt_initial_random_value() noexcept
+{
+#if QT_CONFIG(getauxval) && defined(AT_RANDOM)
+ auto at_random_ptr = reinterpret_cast<size_t *>(getauxval(AT_RANDOM));
+ if (at_random_ptr)
+ return qFromUnaligned<QRandomGenerator::InitialRandomData>(at_random_ptr);
+#endif
+
+ // bypass the hardware RNG, which would mean initializing qsimd.cpp
+
+ QRandomGenerator::InitialRandomData v;
+ for (int attempts = 16; attempts; --attempts) {
+ using Generator = QRandomGenerator::SystemGenerator;
+ auto fillBuffer = &Generator::fillBuffer;
+ if (callFillBuffer<Generator>(fillBuffer, &v) != sizeof(v))
+ continue;
+
+ return v;
}
-};
+
+ quint32 data[sizeof(v) / sizeof(quint32)];
+ fallback_fill(data, std::size(data));
+ memcpy(v.data, data, sizeof(v.data));
+ return v;
}
QT_END_NAMESPACE