Qt 6.x
The Qt SDK
Loading...
Searching...
No Matches
qrandom.cpp
Go to the documentation of this file.
1// Copyright (C) 2021 Intel Corporation.
2// Copyright (C) 2021 The Qt Company Ltd.
3// SPDX-License-Identifier: LicenseRef-Qt-Commercial OR LGPL-3.0-only OR GPL-2.0-only OR GPL-3.0-only
4
5// for rand_s
6#define _CRT_RAND_S
7
8#include "qrandom.h"
9#include "qrandom_p.h"
10#include <qendian.h>
11#include <qmutex.h>
12#include <qobjectdefs.h>
13
14#include <errno.h>
15
16#if QT_CONFIG(getauxval)
17# include <sys/auxv.h>
18#endif
19
20#if QT_CONFIG(getentropy) && __has_include(<sys/random.h>)
21# include <sys/random.h>
22#elif !QT_CONFIG(getentropy) && (!defined(Q_OS_BSD4) || defined(__GLIBC__)) && !defined(Q_OS_WIN)
23# include "qdeadlinetimer.h"
24# include "qhashfunctions.h"
25#endif // !QT_CONFIG(getentropy)
26
27#ifdef Q_OS_UNIX
28# include <fcntl.h>
29# include <private/qcore_unix_p.h>
30#else
31# include <qt_windows.h>
32
33// RtlGenRandom is not exported by its name in advapi32.dll, but as SystemFunction036
34// See https://msdn.microsoft.com/en-us/library/windows/desktop/aa387694(v=vs.85).aspx
35// Implementation inspired on https://hg.mozilla.org/mozilla-central/file/722fdbff1efc/security/nss/lib/freebl/win_rand.c#l146
36// Argument why this is safe to use: https://bugzilla.mozilla.org/show_bug.cgi?id=504270
37extern "C" {
38DECLSPEC_IMPORT BOOLEAN WINAPI SystemFunction036(PVOID RandomBuffer, ULONG RandomBufferLength);
39}
40#endif
41
42// This file is too low-level for regular Q_ASSERT (the logging framework may
43// recurse back), so use regular assert()
44#undef NDEBUG
45#undef Q_ASSERT_X
46#undef Q_ASSERT
47#define Q_ASSERT(cond) assert(cond)
48#define Q_ASSERT_X(cond, x, msg) assert(cond && msg)
49#if defined(QT_NO_DEBUG) && !defined(QT_FORCE_ASSERTS)
50# define NDEBUG 1
51#endif
52#include <assert.h>
53
55
56enum {
57 // may be "overridden" by a member enum
59};
60
61#if defined(QT_BUILD_INTERNAL)
63#endif
64
66{
67#if QT_CONFIG(getentropy)
68 static qsizetype fillBuffer(void *buffer, qsizetype count) noexcept
69 {
70 // getentropy can read at most 256 bytes, so break the reading
71 qsizetype read = 0;
72 while (count - read > 256) {
73 // getentropy can't fail under normal circumstances
74 int ret = getentropy(reinterpret_cast<uchar *>(buffer) + read, 256);
75 Q_ASSERT(ret == 0);
77 read += 256;
78 }
79
80 int ret = getentropy(reinterpret_cast<uchar *>(buffer) + read, count - read);
81 Q_ASSERT(ret == 0);
83 return count;
84 }
85
86#elif defined(Q_OS_UNIX)
87 enum { FillBufferNoexcept = false };
88
89 QBasicAtomicInt fdp1; // "file descriptor plus 1"
90 int openDevice()
91 {
92 int fd = fdp1.loadAcquire() - 1;
93 if (fd != -1)
94 return fd;
95
96 fd = qt_safe_open("/dev/urandom", O_RDONLY);
97 if (fd == -1)
98 fd = qt_safe_open("/dev/random", O_RDONLY | O_NONBLOCK);
99 if (fd == -1) {
100 // failed on both, set to -2 so we won't try again
101 fd = -2;
102 }
103
104 int opened_fdp1;
105 if (fdp1.testAndSetOrdered(0, fd + 1, opened_fdp1))
106 return fd;
107
108 // failed, another thread has opened the file descriptor
109 if (fd >= 0)
111 return opened_fdp1 - 1;
112 }
113
114#ifdef Q_CC_GNU
115 // If it's not GCC or GCC-like, then we'll leak the file descriptor
116 __attribute__((destructor))
117#endif
118 static void closeDevice()
119 {
120 int fd = self().fdp1.loadRelaxed() - 1;
121 if (fd >= 0)
123 }
124
125 constexpr SystemGenerator() : fdp1 Q_BASIC_ATOMIC_INITIALIZER(0) {}
126
127 qsizetype fillBuffer(void *buffer, qsizetype count)
128 {
129 int fd = openDevice();
130 if (Q_UNLIKELY(fd < 0))
131 return 0;
132
134 return qMax<qsizetype>(n, 0); // ignore any errors
135 }
136
137#elif defined(Q_OS_WIN)
138 static qsizetype fillBuffer(void *buffer, qsizetype count) noexcept
139 {
140 auto RtlGenRandom = SystemFunction036;
141 return RtlGenRandom(buffer, ULONG(count)) ? count: 0;
142 }
143#endif // Q_OS_WIN
144
145 static SystemGenerator &self();
148
149 // For std::mersenne_twister_engine implementations that use something
150 // other than quint32 (unsigned int) to fill their buffers.
151 template<typename T>
152 void generate(T *begin, T *end)
153 {
154 static_assert(sizeof(T) >= sizeof(quint32));
155 if (sizeof(T) == sizeof(quint32)) {
156 // Microsoft Visual Studio uses unsigned long, but that's still 32-bit
157 generate(reinterpret_cast<quint32 *>(begin), reinterpret_cast<quint32 *>(end));
158 } else {
159 // Slow path. Fix your C++ library.
160 std::generate(begin, end, [this]() {
161 quint32 datum;
162 generate(&datum, &datum + 1);
163 return datum;
164 });
165 }
166 }
167};
168
169#if defined(Q_OS_WIN)
170static void fallback_update_seed(unsigned) {}
171static void fallback_fill(quint32 *ptr, qsizetype left) noexcept
172{
173 // on Windows, rand_s is a high-quality random number generator
174 // and it requires no seeding
175 std::generate(ptr, ptr + left, []() {
176 unsigned value;
177 rand_s(&value);
178 return value;
179 });
180}
181#elif QT_CONFIG(getentropy)
182static void fallback_update_seed(unsigned) {}
183static void fallback_fill(quint32 *, qsizetype) noexcept
184{
185 // no fallback necessary, getentropy cannot fail under normal circumstances
186 Q_UNREACHABLE();
188#elif defined(Q_OS_BSD4) && !defined(__GLIBC__)
189static void fallback_update_seed(unsigned) {}
190static void fallback_fill(quint32 *ptr, qsizetype left) noexcept
191{
192 // BSDs have arc4random(4) and these work even in chroot(2)
193 arc4random_buf(ptr, left * sizeof(*ptr));
194}
195#else
197static void fallback_update_seed(unsigned value)
198{
199 // Update the seed to be used for the fallback mechanism, if we need to.
200 // We can't use QtPrivate::QHashCombine here because that is not an atomic
201 // operation. A simple XOR will have to do then.
202 seed.fetchAndXorRelaxed(value);
203}
204
206#ifdef Q_CC_GNU
207__attribute__((cold)) // this function is pretty big, so optimize for size
208#endif
209static void fallback_fill(quint32 *ptr, qsizetype left) noexcept
210{
211 quint32 scratch[12]; // see element count below
212 quint32 *end = scratch;
213
214 auto foldPointer = [](quintptr v) {
215 if (sizeof(quintptr) == sizeof(quint32)) {
216 // For 32-bit systems, we simply return the pointer.
217 return quint32(v);
218 } else {
219 // For 64-bit systems, we try to return the variable part of the
220 // pointer. On current x86-64 and AArch64, the top 17 bits are
221 // architecturally required to be the same, but in reality the top
222 // 24 bits on Linux are likely to be the same for all processes.
223 return quint32(v >> (32 - 24));
224 }
225 };
226
227 Q_ASSERT(left);
228
229 *end++ = foldPointer(quintptr(&seed)); // 1: variable in this library/executable's .data
230 *end++ = foldPointer(quintptr(&scratch)); // 2: variable in the stack
231 *end++ = foldPointer(quintptr(&errno)); // 3: veriable either in libc or thread-specific
232 *end++ = foldPointer(quintptr(reinterpret_cast<void*>(strerror))); // 4: function in libc (and unlikely to be a macro)
233
234#ifndef QT_BOOTSTRAPPED
236 *end++ = quint32(nsecs); // 5
237#endif
238
239 if (quint32 v = seed.loadRelaxed())
240 *end++ = v; // 6
241
242#if QT_CONFIG(getauxval)
243 // works on Linux -- all modern libc have getauxval
244# ifdef AT_RANDOM
245 // ELF's auxv AT_RANDOM has 16 random bytes
246 // (other ELF-based systems don't seem to have AT_RANDOM)
247 ulong auxvSeed = getauxval(AT_RANDOM);
248 if (auxvSeed) {
249 memcpy(end, reinterpret_cast<void *>(auxvSeed), 16);
250 end += 4; // 7 to 10
251 }
252# endif
253
254 // Both AT_BASE and AT_SYSINFO_EHDR have some randomness in them due to the
255 // system's ASLR, even if many bits are the same. They also have randomness
256 // between them.
257# ifdef AT_BASE
258 // present at least on the BSDs too, indicates the address of the loader
259 ulong base = getauxval(AT_BASE);
260 if (base)
261 *end++ = foldPointer(base); // 11
262# endif
263# ifdef AT_SYSINFO_EHDR
264 // seems to be Linux-only, indicates the global page of the sysinfo
265 ulong sysinfo_ehdr = getauxval(AT_SYSINFO_EHDR);
266 if (sysinfo_ehdr)
267 *end++ = foldPointer(sysinfo_ehdr); // 12
268# endif
269#endif
270
271 Q_ASSERT(end <= std::end(scratch));
272
273 // this is highly inefficient, we should save the generator across calls...
274 std::seed_seq sseq(scratch, end);
275 std::mt19937 generator(sseq);
276 std::generate(ptr, ptr + left, generator);
277
279}
280#endif
281
283 noexcept(FillBufferNoexcept)
284{
287
290 std::fill_n(buffer, count, value);
291 return;
292 }
293
294 qsizetype filled = 0;
295 if (qHasHwrng() && (uint(qt_randomdevice_control.loadAcquire()) & SkipHWRNG) == 0)
296 filled += qRandomCpu(buffer, count);
297
298 if (filled != count && (uint(qt_randomdevice_control.loadAcquire()) & SkipSystemRNG) == 0) {
299 qsizetype bytesFilled =
300 fillBuffer(buffer + filled, (count - filled) * qsizetype(sizeof(*buffer)));
301 filled += bytesFilled / qsizetype(sizeof(*buffer));
302 }
303 if (filled)
305
306 if (Q_UNLIKELY(filled != count)) {
307 // failed to fill the entire buffer, try the faillback mechanism
308 fallback_fill(buffer + filled, count - filled);
309 }
310}
311
313{
314 // Construction notes:
315 // 1) The global PRNG state is in a different cacheline compared to the
316 // mutex that protects it. This avoids any false cacheline sharing of
317 // the state in case another thread tries to lock the mutex. It's not
318 // a common scenario, but since sizeof(QRandomGenerator) >= 2560, the
319 // overhead is actually acceptable.
320 // 2) We use both alignas(T) and alignas(64) because some implementations
321 // can't align to more than a primitive type's alignment.
322 // 3) We don't store the entire system QRandomGenerator, only the space
323 // used by the QRandomGenerator::type member. This is fine because we
324 // (ab)use the common initial sequence exclusion to aliasing rules.
328 alignas(64) struct {
331
333 : globalPRNGMutex{}, system_{0}, sys{}, global_{}
334 {}
335
337 {
338#if !defined(Q_OS_INTEGRITY)
339 // Integrity's compiler is unable to guarantee g's alignment for some reason.
340 constexpr SystemAndGlobalGenerators g = {};
341 Q_UNUSED(g);
342#endif
343 }
344
346 {
347 Q_CONSTINIT static SystemAndGlobalGenerators g;
348 static_assert(sizeof(g) > sizeof(QRandomGenerator64));
349 return &g;
350 }
351
353 {
354 // Though we never call the constructor, the system QRandomGenerator is
355 // properly initialized by the zero initialization performed in self().
356 // Though QRandomGenerator is has non-vacuous initialization, we
357 // consider it initialized because of the common initial sequence.
358 return reinterpret_cast<QRandomGenerator64 *>(&self()->system_);
359 }
360
362 {
363 // This function returns the pointer to the global QRandomGenerator,
364 // but does not initialize it. Only call it directly if you meant to do
365 // a pointer comparison.
366 return reinterpret_cast<QRandomGenerator64 *>(&self()->global_);
367 }
368
370 {
371 // force reconstruction, just to be pedantic
372 new (rng) QRandomGenerator{System{}};
373
374 rng->type = MersenneTwister;
375 new (&rng->storage.engine()) RandomEngine(self()->sys);
376 }
377
379 {
380 const bool locked;
382 : locked(that == globalNoInit())
383 {
384 if (locked)
386 }
388 {
389 if (locked)
391 }
392 };
393};
394
396{
398}
399
1115constexpr QRandomGenerator::Storage::Storage()
1116 : dummy(0)
1117{
1118 // nothing
1119}
1120
1123{
1124}
1125
1127{
1129 Q_ASSERT(self->type == SystemRNG);
1130 return self;
1131}
1132
1134{
1136
1137 // Yes, this is a double-checked lock.
1138 // We can return even if the type is not completely initialized yet:
1139 // any thread trying to actually use the contents of the random engine
1140 // will necessarily wait on the lock.
1141 if (Q_LIKELY(self->type != SystemRNG))
1142 return self;
1143
1144 SystemAndGlobalGenerators::PRNGLocker locker(self);
1145 if (self->type == SystemRNG)
1147
1148 return self;
1149}
1150
1152{
1155 return result;
1156}
1157
1162 : type(SystemRNG)
1163{
1164 // don't touch storage
1165}
1166
1168 : type(other.type)
1169{
1170 Q_ASSERT(this != system());
1172
1173 if (type != SystemRNG) {
1175 storage.engine() = other.storage.engine();
1176 }
1177}
1178
1180{
1182 qFatal("Attempted to overwrite a QRandomGenerator to system() or global().");
1183
1184 if ((type = other.type) != SystemRNG) {
1186 storage.engine() = other.storage.engine();
1187 }
1188 return *this;
1189}
1190
1193{
1194 Q_ASSERT(this != system());
1195 Q_ASSERT(this != SystemAndGlobalGenerators::globalNoInit());
1196
1197 new (&storage.engine()) RandomEngine(sseq);
1198}
1199
1202{
1203 Q_ASSERT(this != system());
1205
1206 std::seed_seq s(begin, end);
1207 new (&storage.engine()) RandomEngine(s);
1208}
1209
1210void QRandomGenerator::discard(unsigned long long z)
1211{
1212 if (Q_UNLIKELY(type == SystemRNG))
1213 return;
1214
1216 storage.engine().discard(z);
1217}
1218
1219bool operator==(const QRandomGenerator &rng1, const QRandomGenerator &rng2)
1220{
1221 if (rng1.type != rng2.type)
1222 return false;
1223 if (rng1.type == SystemRNG)
1224 return true;
1225
1226 // Lock global() if either is it (otherwise this locking is a no-op)
1228 PRNGLocker locker(&rng1 == QRandomGenerator::global() ? &rng1 : &rng2);
1229 return rng1.storage.engine() == rng2.storage.engine();
1230}
1231
1240quint64 QRandomGenerator::_fillRange(void *buffer, qptrdiff count)
1241{
1242 // Verify that the pointers are properly aligned for 32-bit
1243 Q_ASSERT(quintptr(buffer) % sizeof(quint32) == 0);
1244 Q_ASSERT(count >= 0);
1245 Q_ASSERT(buffer || count <= 2);
1246
1247 quint64 dummy;
1248 quint32 *begin = static_cast<quint32 *>(buffer ? buffer : &dummy);
1249 quint32 *end = begin + count;
1250
1253 } else {
1254 SystemAndGlobalGenerators::PRNGLocker lock(this);
1255 std::generate(begin, end, [this]() { return storage.engine()(); });
1256 }
1257
1258 if (end - begin == 1)
1259 return *begin;
1260 return begin[0] | (quint64(begin[1]) << 32);
1261}
1262
1263// helper function to call fillBuffer, since we need something to be
1264// argument-dependent
1265template <typename Generator, typename FillBufferType, typename T>
1266static qsizetype callFillBuffer(FillBufferType f, T *v)
1267{
1268 if constexpr (std::is_member_function_pointer_v<FillBufferType>) {
1269 // member function, need an object
1270 return (Generator::self().*f)(v, sizeof(*v));
1271 } else {
1272 // static, call directly
1273 return f(v, sizeof(*v));
1274 }
1275}
1276
1287QRandomGenerator::InitialRandomData qt_initial_random_value() noexcept
1288{
1289#if QT_CONFIG(getauxval) && defined(AT_RANDOM)
1290 auto at_random_ptr = reinterpret_cast<size_t *>(getauxval(AT_RANDOM));
1291 if (at_random_ptr)
1292 return qFromUnaligned<QRandomGenerator::InitialRandomData>(at_random_ptr);
1293#endif
1294
1295 // bypass the hardware RNG, which would mean initializing qsimd.cpp
1296
1297 QRandomGenerator::InitialRandomData v;
1298 for (int attempts = 16; attempts; --attempts) {
1300 auto fillBuffer = &Generator::fillBuffer;
1301 if (callFillBuffer<Generator>(fillBuffer, &v) != sizeof(v))
1302 continue;
1303
1304 return v;
1305 }
1306
1307 quint32 data[sizeof(v) / sizeof(quint32)];
1308 fallback_fill(data, std::size(data));
1309 memcpy(v.data, data, sizeof(v.data));
1310 return v;
1311}
1312
bool testAndSetOrdered(T expectedValue, T newValue) noexcept
T loadAcquire() const noexcept
static QDeadlineTimer current(Qt::TimerType timerType=Qt::CoarseTimer) noexcept
Returns a QDeadlineTimer that is expired but is guaranteed to contain the current time.
qint64 deadline() const noexcept Q_DECL_PURE_FUNCTION
Returns the absolute time point for the deadline stored in QDeadlineTimer object, calculated in milli...
\inmodule QtCore
Definition qmutex.h:285
void unlock() noexcept
Unlocks the mutex.
Definition qmutex.h:293
void lock() noexcept
Locks the mutex.
Definition qmutex.h:290
\inmodule QtCore
Definition qrandom.h:209
static Q_CORE_EXPORT QRandomGenerator64 securelySeeded()
Definition qrandom.cpp:1151
static Q_DECL_CONST_FUNCTION Q_CORE_EXPORT QRandomGenerator64 * global()
Definition qrandom.cpp:1133
static Q_DECL_CONST_FUNCTION Q_CORE_EXPORT QRandomGenerator64 * system()
Definition qrandom.cpp:1126
\inmodule QtCore \reentrant
Definition qrandom.h:21
friend class QRandomGenerator64
Definition qrandom.h:188
static Q_DECL_CONST_FUNCTION QRandomGenerator * system()
\threadsafe
Definition qrandom.h:270
Q_CORE_EXPORT QRandomGenerator & operator=(const QRandomGenerator &other)
Definition qrandom.cpp:1179
Q_CORE_EXPORT void discard(unsigned long long z)
Discards the next z entries from the sequence.
Definition qrandom.cpp:1210
QRandomGenerator(quint32 seedValue=1)
Initializes this QRandomGenerator object with the value seedValue as the seed.
Definition qrandom.h:26
quint32 generate()
Generates a 32-bit random quantity and returns it.
Definition qrandom.h:48
static Q_DECL_CONST_FUNCTION QRandomGenerator * global()
\threadsafe
Definition qrandom.h:275
Combined button and popup list for selecting options.
@ PreciseTimer
#define Q_BASIC_ATOMIC_INITIALIZER(a)
#define Q_UNLIKELY(x)
#define Q_NEVER_INLINE
#define Q_LIKELY(x)
static int qt_safe_open(const char *pathname, int flags, mode_t mode=0777)
static qint64 qt_safe_read(int fd, void *data, qint64 maxlen)
static int qt_safe_close(int fd)
EGLOutputLayerEXT EGLint EGLAttrib value
[5]
#define qFatal
Definition qlogging.h:164
return ret
static ControlElement< T > * ptr(QWidget *widget)
GLsizei const GLfloat * v
[13]
GLuint GLfloat GLfloat GLfloat GLfloat GLfloat z
GLuint GLuint end
GLenum GLenum GLsizei count
GLfloat GLfloat f
GLenum GLuint buffer
GLint left
GLenum type
GLint GLsizei GLsizei GLenum GLenum GLsizei void * data
GLboolean GLboolean g
GLuint64 GLenum GLint fd
GLfloat n
GLuint64EXT * result
[6]
GLdouble s
[6]
Definition qopenglext.h:235
static qsizetype callFillBuffer(FillBufferType f, T *v)
Definition qrandom.cpp:1266
static Q_NEVER_INLINE void fallback_fill(quint32 *ptr, qsizetype left) noexcept
Definition qrandom.cpp:209
static Q_CONSTINIT QBasicAtomicInteger< unsigned > seed
Definition qrandom.cpp:196
static void fallback_update_seed(unsigned value)
Definition qrandom.cpp:197
bool operator==(const QRandomGenerator &rng1, const QRandomGenerator &rng2)
Definition qrandom.cpp:1219
DECLSPEC_IMPORT BOOLEAN WINAPI SystemFunction036(PVOID RandomBuffer, ULONG RandomBufferLength)
@ FillBufferNoexcept
Definition qrandom.cpp:58
#define Q_ASSERT(cond)
Definition qrandom.cpp:47
QRandomGenerator::InitialRandomData qt_initial_random_value() noexcept
Definition qrandom.cpp:1287
@ MersenneTwister
Definition qrandom_p.h:36
@ SystemRNG
Definition qrandom_p.h:35
@ UseSystemRNG
Definition qrandom_p.h:25
@ SkipSystemRNG
Definition qrandom_p.h:26
@ SetRandomData
Definition qrandom_p.h:28
@ SkipHWRNG
Definition qrandom_p.h:27
@ RandomDataMask
Definition qrandom_p.h:31
static const struct @7 qt_randomdevice_control
QtPrivate::QRegularExpressionMatchIteratorRangeBasedForIterator begin(const QRegularExpressionMatchIterator &iterator)
#define Q_UNUSED(x)
unsigned int quint32
Definition qtypes.h:45
unsigned char uchar
Definition qtypes.h:27
size_t quintptr
Definition qtypes.h:72
unsigned long ulong
Definition qtypes.h:30
ptrdiff_t qptrdiff
Definition qtypes.h:69
unsigned long long quint64
Definition qtypes.h:56
ptrdiff_t qsizetype
Definition qtypes.h:70
unsigned int uint
Definition qtypes.h:29
long long qint64
Definition qtypes.h:55
ReturnedValue read(const char *data)
std::seed_seq sseq(seedBuffer, seedBuffer+len)
[3]
QRandomGenerator generator(sseq)
QStorageInfo storage
[1]
QReadWriteLock lock
[0]
QSharedPointer< T > other(t)
[5]
struct QRandomGenerator::SystemAndGlobalGenerators::@19 global_
static QRandomGenerator64 * system()
Definition qrandom.cpp:352
static void securelySeed(QRandomGenerator *rng)
Definition qrandom.cpp:369
static SystemAndGlobalGenerators * self()
Definition qrandom.cpp:345
static QRandomGenerator64 * globalNoInit()
Definition qrandom.cpp:361
struct QRandomGenerator::SystemAndGlobalGenerators::ShortenedSystem system_
void generate(quint32 *begin, quint32 *end) noexcept(FillBufferNoexcept)
Definition qrandom.cpp:282
static SystemGenerator & self()
Definition qrandom.cpp:395
void generate(T *begin, T *end)
Definition qrandom.cpp:152