QRandomGenerator: mix the Linux AT_RANDOM random bytes in the fallback

The Linux kernel gives us 16 bytes of random data and sets a pointer to
it in the ELF AuxV (the same one that allows us to get HWCAP on ARM
systems). So if we end up in the fallback generator, at leat we'll get a
good amount of entropy to seed the Mersenne Twister.

This could happen if the application is run in a chroot(2) or container
without /dev/random or /dev/urandom. That is probably an installation
mistake, so we don't optimize this case for performance.

With this commit, we have now good, high-quality fallbacks for Windows
(rand_s), for BSDs (arc4random) and for Linux. The only missing,
supported OS without a good entropy source is QNX.

Change-Id: Ia3e896da908f42939148fffd14c5b1084051f1a8
Reviewed-by: Lars Knoll <lars.knoll@qt.io>
This commit is contained in:
Thiago Macieira 2017-06-06 17:58:20 -07:00
parent 629f3c0277
commit d57bf5e8aa

View File

@ -52,6 +52,10 @@
# include "qhashfunctions.h"
#endif
#if QT_HAS_INCLUDE(<sys/auxv.h>)
# include <sys/auxv.h>
#endif
#ifdef Q_OS_UNIX
# include <fcntl.h>
# include <private/qcore_unix_p.h>
@ -247,12 +251,31 @@ static void fallback_fill(quint32 *ptr, qssize_t left) Q_DECL_NOTHROW
{
Q_ASSERT(left);
// ELF's auxv AT_RANDOM has 16 random bytes
using RandomBytes = quint32[16 / sizeof(quint32)];
RandomBytes scratch = {};
RandomBytes *auxvSeed = nullptr;
#if QT_HAS_INCLUDE(<sys/auxv.h>) && defined(AT_RANDOM)
// works on Linux -- all modern libc have getauxval
// other ELF-based systems don't seem to have AT_RANDOM
auxvSeed = reinterpret_cast<RandomBytes *>(getauxval(AT_RANDOM));
if (auxvSeed)
memcpy(scratch, auxvSeed, sizeof(RandomBytes));
#endif
quint32 v = seed.load();
if (Q_UNLIKELY(v == 0))
emergency_seed(v);
if (Q_LIKELY(v)) {
// mix the stored seed value
// (using the fact that qHash(unsigned) is idempotent)
scratch[0] = QtPrivate::QHashCombine()(scratch[0], v);
} else if (Q_UNLIKELY(auxvSeed == nullptr)) {
emergency_seed(scratch[0]);
}
// this is highly inefficient, we should save the generator across calls...
std::mt19937 generator(v);
std::seed_seq sseq(std::begin(scratch), std::end(scratch));
std::mt19937 generator(sseq);
std::generate(ptr, ptr + left, generator);
fallback_update_seed(*ptr);