From 0c355e78f0cf51d4ad6cfb2eb0f289979cba843a Mon Sep 17 00:00:00 2001 From: Thiago Macieira Date: Thu, 13 Apr 2017 15:45:30 -0700 Subject: QRandomGenerator: add support for hardware-based random generators This commit adds support for the x86 RDRAND instruction for QRandomGenerator. This is the same that libstdc++-v3 uses for std::random_device() by default. If it fails because the hardware does not have enough entropy collected, we fall back to the operating system generator, which often has more entropy collected from other sources. Change-Id: Icd0e0d4b27cb4e5eb892fffd14b5167214e1ea3f Reviewed-by: Lars Knoll --- src/corelib/global/qrandom.cpp | 48 ++++++++++++++++++++++++++++++++++++++++-- 1 file changed, 46 insertions(+), 2 deletions(-) (limited to 'src/corelib') diff --git a/src/corelib/global/qrandom.cpp b/src/corelib/global/qrandom.cpp index 99a91c2221..954e879887 100644 --- a/src/corelib/global/qrandom.cpp +++ b/src/corelib/global/qrandom.cpp @@ -44,6 +44,7 @@ #include "qrandom_p.h" #include #include +#include #if QT_HAS_INCLUDE() # include @@ -70,6 +71,37 @@ DECLSPEC_IMPORT BOOLEAN WINAPI SystemFunction036(PVOID RandomBuffer, ULONG Rando QT_BEGIN_NAMESPACE +#if defined(Q_PROCESSOR_X86) && QT_COMPILER_SUPPORTS_HERE(RDRND) +static qssize_t qt_random_cpu(void *buffer, qssize_t count); + +# ifdef Q_PROCESSOR_X86_64 +# define _rdrandXX_step _rdrand64_step +# else +# define _rdrandXX_step _rdrand32_step +# endif + +static QT_FUNCTION_TARGET(RDRND) qssize_t qt_random_cpu(void *buffer, qssize_t count) +{ + unsigned *ptr = reinterpret_cast(buffer); + unsigned *end = ptr + count; + + while (ptr + sizeof(qregisteruint)/sizeof(*ptr) <= end) { + if (_rdrandXX_step(reinterpret_cast(ptr)) == 0) + goto out; + ptr += sizeof(qregisteruint)/sizeof(*ptr); + } + + if (sizeof(*ptr) != sizeof(qregisteruint) && ptr != end) { + if (_rdrand32_step(ptr)) + goto out; + ++ptr; + } + +out: + return ptr - reinterpret_cast(buffer); +} +#endif + namespace { #ifdef Q_OS_UNIX class SystemRandom @@ -196,6 +228,18 @@ static Q_NORETURN void fallback_fill(quint32 *, qssize_t) } #endif +static qssize_t fill_cpu(quint32 *buffer, qssize_t count) +{ +#if defined(Q_PROCESSOR_X86) && QT_COMPILER_SUPPORTS_HERE(RDRND) + if (qCpuHasFeature(RDRND) && (uint(qt_randomdevice_control) & SkipHWRNG) == 0) + return qt_random_cpu(buffer, count); +#else + Q_UNUSED(buffer); + Q_UNUSED(count); +#endif + return 0; +} + static void fill_internal(quint32 *buffer, qssize_t count) { if (Q_UNLIKELY(uint(qt_randomdevice_control) & SetRandomData)) { @@ -204,8 +248,8 @@ static void fill_internal(quint32 *buffer, qssize_t count) return; } - qssize_t filled = 0; - if (uint(qt_randomdevice_control) & SkipSystemRNG) == 0) { + qssize_t filled = fill_cpu(buffer, count); + if (filled != count && (uint(qt_randomdevice_control) & SkipSystemRNG) == 0) { qssize_t bytesFilled = SystemRandom::fillBuffer(buffer + filled, (count - filled) * qssize_t(sizeof(*buffer))); filled += bytesFilled / qssize_t(sizeof(*buffer)); -- cgit v1.2.3