From d57bf5e8aa6c575c4e3b9d80793e9becf1469d82 Mon Sep 17 00:00:00 2001 From: Thiago Macieira Date: Tue, 6 Jun 2017 17:58:20 -0700 Subject: [PATCH] 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 --- src/corelib/global/qrandom.cpp | 29 ++++++++++++++++++++++++++--- 1 file changed, 26 insertions(+), 3 deletions(-) diff --git a/src/corelib/global/qrandom.cpp b/src/corelib/global/qrandom.cpp index eb9eaa68de..0601dbdb30 100644 --- a/src/corelib/global/qrandom.cpp +++ b/src/corelib/global/qrandom.cpp @@ -52,6 +52,10 @@ # include "qhashfunctions.h" #endif +#if QT_HAS_INCLUDE() +# include +#endif + #ifdef Q_OS_UNIX # include # include @@ -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() && 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(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);