QRandomGenerator: improve the fallback seeding in emergencies

If we've never read any samples from hardware RNG or from /dev/urandom,
then there are no stored samples for us to seed the Mersenne Twister. In
that case, attempt to obtain an emergency sample that consists of some
random bits from the variable addresses (ASLR should help) and from the
clock (using nanosecond quality, instead of seconds).

There's still the possibility that we'll get poor entropy: very close to
boot, if the kernel entropy pool is empty, ASLR could be poor and the
monotonic clock value could be consistent from boot to boot. There's
nothing we can do about that.

Change-Id: Ia3e896da908f42939148fffd14c5b0c7b608371b
Reviewed-by: Lars Knoll <lars.knoll@qt.io>
bb10
Thiago Macieira 2017-06-06 17:53:42 -07:00
parent ec63fcb314
commit 629f3c0277
1 changed files with 31 additions and 1 deletions

View File

@ -48,6 +48,8 @@
#if QT_HAS_INCLUDE(<random>)
# include <random>
# include "qdeadlinetimer.h"
# include "qhashfunctions.h"
#endif
#ifdef Q_OS_UNIX
@ -217,12 +219,40 @@ static void fallback_update_seed(unsigned value)
seed.fetchAndXorRelaxed(value);
}
static void emergency_seed(quint32 &v) Q_DECL_NOTHROW
{
// we don't have any seed at all, so let's use the some bits from the
// seed variable's address (hoping to get entropy from the system ASLR)
// and the current monotonic time in nanoseconds
// (wall-clock time can be predicted)
v = quint32(quintptr(&seed)) >> 12; // PAGE_SHIFT
if (sizeof(quintptr) == sizeof(quint32)) {
// mask off highest 4 bits, since they're likely to be constant
v &= ~0xf00000000U;
}
#ifndef QT_BUILD_QMAKE
// see QtPrivate::QHashCombine for the algorithm
quint32 nsecs = quint32(QDeadlineTimer::current(Qt::PreciseTimer).deadline());
v = QtPrivate::QHashCombine()(v, nsecs);
#endif
}
Q_NEVER_INLINE
#ifdef Q_CC_GNU
__attribute__((cold)) // this function is pretty big, so optimize for size
#endif
static void fallback_fill(quint32 *ptr, qssize_t left) Q_DECL_NOTHROW
{
Q_ASSERT(left);
quint32 v = seed.load();
if (Q_UNLIKELY(v == 0))
emergency_seed(v);
// this is highly inefficient, we should save the generator across calls...
std::mt19937 generator(seed.load());
std::mt19937 generator(v);
std::generate(ptr, ptr + left, generator);
fallback_update_seed(*ptr);