Qt
Internal/Contributor docs for the Qt SDK. <b>Note:</b> These are NOT official API docs; those are found <a href='https://doc.qt.io/'>here</a>.
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)
62QBasicAtomicInteger<uint> qt_randomdevice_control = Q_BASIC_ATOMIC_INITIALIZER(0U);
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
196Q_CONSTINIT static QBasicAtomicInteger<unsigned> seed = Q_BASIC_ATOMIC_INITIALIZER(0U);
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 Q_DISABLE_COPY_MOVE(PRNGLocker)
381 const bool locked;
383 : locked(that == globalNoInit())
384 {
385 if (locked)
386 self()->globalPRNGMutex.lock();
387 }
389 {
390 if (locked)
391 self()->globalPRNGMutex.unlock();
392 }
393 };
394};
395
400
1116constexpr QRandomGenerator::Storage::Storage()
1117 : dummy(0)
1118{
1119 // nothing
1120}
1121
1124{
1125}
1126
1128{
1130 Q_ASSERT(self->type == SystemRNG);
1131 return self;
1132}
1133
1135{
1137
1138 // Yes, this is a double-checked lock.
1139 // We can return even if the type is not completely initialized yet:
1140 // any thread trying to actually use the contents of the random engine
1141 // will necessarily wait on the lock.
1142 if (Q_LIKELY(self->type != SystemRNG))
1143 return self;
1144
1145 SystemAndGlobalGenerators::PRNGLocker locker(self);
1146 if (self->type == SystemRNG)
1148
1149 return self;
1150}
1151
1158
1163 : type(SystemRNG)
1164{
1165 // don't touch storage
1166}
1167
1169 : type(other.type)
1170{
1171 Q_ASSERT(this != system());
1173
1174 if (type != SystemRNG) {
1176 storage.engine() = other.storage.engine();
1177 }
1178}
1179
1181{
1183 qFatal("Attempted to overwrite a QRandomGenerator to system() or global().");
1184
1185 if ((type = other.type) != SystemRNG) {
1187 storage.engine() = other.storage.engine();
1188 }
1189 return *this;
1190}
1191
1194{
1195 Q_ASSERT(this != system());
1196 Q_ASSERT(this != SystemAndGlobalGenerators::globalNoInit());
1197
1198 new (&storage.engine()) RandomEngine(sseq);
1199}
1200
1203{
1204 Q_ASSERT(this != system());
1206
1207 std::seed_seq s(begin, end);
1208 new (&storage.engine()) RandomEngine(s);
1209}
1210
1211void QRandomGenerator::discard(unsigned long long z)
1212{
1213 if (Q_UNLIKELY(type == SystemRNG))
1214 return;
1215
1217 storage.engine().discard(z);
1218}
1219
1220bool operator==(const QRandomGenerator &rng1, const QRandomGenerator &rng2)
1221{
1222 if (rng1.type != rng2.type)
1223 return false;
1224 if (rng1.type == SystemRNG)
1225 return true;
1226
1227 // Lock global() if either is it (otherwise this locking is a no-op)
1229 PRNGLocker locker(&rng1 == QRandomGenerator::global() ? &rng1 : &rng2);
1230 return rng1.storage.engine() == rng2.storage.engine();
1231}
1232
1241quint64 QRandomGenerator::_fillRange(void *buffer, qptrdiff count)
1242{
1243 // Verify that the pointers are properly aligned for 32-bit
1244 Q_ASSERT(quintptr(buffer) % sizeof(quint32) == 0);
1245 Q_ASSERT(count >= 0);
1246 Q_ASSERT(buffer || count <= 2);
1247
1248 quint64 dummy;
1249 quint32 *begin = static_cast<quint32 *>(buffer ? buffer : &dummy);
1250 quint32 *end = begin + count;
1251
1253 SystemGenerator::self().generate(begin, end);
1254 } else {
1255 SystemAndGlobalGenerators::PRNGLocker lock(this);
1256 std::generate(begin, end, [this]() { return storage.engine()(); });
1257 }
1258
1259 if (end - begin == 1)
1260 return *begin;
1261 return begin[0] | (quint64(begin[1]) << 32);
1262}
1263
1264// helper function to call fillBuffer, since we need something to be
1265// argument-dependent
1266template <typename Generator, typename FillBufferType, typename T>
1267static qsizetype callFillBuffer(FillBufferType f, T *v)
1268{
1269 if constexpr (std::is_member_function_pointer_v<FillBufferType>) {
1270 // member function, need an object
1271 return (Generator::self().*f)(v, sizeof(*v));
1272 } else {
1273 // static, call directly
1274 return f(v, sizeof(*v));
1275 }
1276}
1277
1288QRandomGenerator::InitialRandomData qt_initial_random_value() noexcept
1289{
1290#if QT_CONFIG(getauxval) && defined(AT_RANDOM)
1291 auto at_random_ptr = reinterpret_cast<size_t *>(getauxval(AT_RANDOM));
1292 if (at_random_ptr)
1293 return qFromUnaligned<QRandomGenerator::InitialRandomData>(at_random_ptr);
1294#endif
1295
1296 // bypass the hardware RNG, which would mean initializing qsimd.cpp
1297
1298 QRandomGenerator::InitialRandomData v;
1299 for (int attempts = 16; attempts; --attempts) {
1301 auto fillBuffer = &Generator::fillBuffer;
1302 if (callFillBuffer<Generator>(fillBuffer, &v) != sizeof(v))
1303 continue;
1304
1305 return v;
1306 }
1307
1308 quint32 data[sizeof(v) / sizeof(quint32)];
1309 fallback_fill(data, std::size(data));
1310 memcpy(v.data, data, sizeof(v.data));
1311 return v;
1312}
1313
struct capHdr __attribute__
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.
\inmodule QtCore
Definition qmutex.h:281
\inmodule QtCore
Definition qrandom.h:209
static Q_CORE_EXPORT QRandomGenerator64 securelySeeded()
Definition qrandom.cpp:1152
static Q_DECL_CONST_FUNCTION Q_CORE_EXPORT QRandomGenerator64 * global()
Definition qrandom.cpp:1134
static Q_DECL_CONST_FUNCTION Q_CORE_EXPORT QRandomGenerator64 * system()
Definition qrandom.cpp:1127
\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:1180
Q_CORE_EXPORT void discard(unsigned long long z)
Discards the next z entries from the sequence.
Definition qrandom.cpp:1211
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_NODISCARD_CTOR
#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:168
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
GLint GLsizei GLsizei GLenum GLenum GLsizei void * data
GLfloat GLfloat f
GLenum GLuint buffer
GLint left
GLenum type
GLboolean GLboolean g
GLuint64 GLenum GLint fd
GLfloat n
GLdouble s
[6]
Definition qopenglext.h:235
GLuint64EXT * result
[6]
static qsizetype callFillBuffer(FillBufferType f, T *v)
Definition qrandom.cpp:1267
@ FillBufferNoexcept
Definition qrandom.cpp:58
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:1220
DECLSPEC_IMPORT BOOLEAN WINAPI SystemFunction036(PVOID RandomBuffer, ULONG RandomBufferLength)
#define Q_ASSERT(cond)
Definition qrandom.cpp:47
QRandomGenerator::InitialRandomData qt_initial_random_value() noexcept
Definition qrandom.cpp:1288
@ MersenneTwister
Definition qrandom_p.h:36
@ SystemRNG
Definition qrandom_p.h:35
static const struct @9 qt_randomdevice_control
@ 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
QtPrivate::QRegularExpressionMatchIteratorRangeBasedForIterator begin(const QRegularExpressionMatchIterator &iterator)
#define Q_UNUSED(x)
unsigned int quint32
Definition qtypes.h:50
unsigned char uchar
Definition qtypes.h:32
size_t quintptr
Definition qtypes.h:167
unsigned long ulong
Definition qtypes.h:35
ptrdiff_t qptrdiff
Definition qtypes.h:164
unsigned long long quint64
Definition qtypes.h:61
ptrdiff_t qsizetype
Definition qtypes.h:165
unsigned int uint
Definition qtypes.h:34
long long qint64
Definition qtypes.h:60
static const uint base
Definition qurlidna.cpp:20
ReturnedValue read(const char *data)
#define explicit
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::@12 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:396
void generate(T *begin, T *end)
Definition qrandom.cpp:152