summaryrefslogtreecommitdiffstats
diff options
context:
space:
mode:
authorDmitry Kazakov <dimula73@gmail.com>2019-09-05 10:23:08 +0300
committerDmitry Kazakov <dimula73@gmail.com>2019-09-26 19:52:50 +0300
commit5839714d986f28412c9f9ed4801d1bf9378f7b51 (patch)
tree72f48ba65dbb68ceec5acc65658903bd7eff30ab
parentd5abda313dab0f83873d34b7c4ddbe8d8a06dacf (diff)
Fix QRandomGenerator initialization on AMD CPUs
Some AMD CPUs (e.g. AMD A4-6250J and AMD Ryzen 3000-series) have a failing random generation instruction, which always returns 0xffffffff, even when generation was "successful". This code checks if hardware random generator generates four consecutive equal numbers. If it does, then we probably have a failing one and should disable it completely. Change-Id: I38c87920ca2e8cce4143afbff5e453ce3845d11a Fixes: QTBUG-69423 Reviewed-by: Edward Welbourne <edward.welbourne@qt.io>
-rw-r--r--src/corelib/global/qrandom.cpp45
-rw-r--r--src/corelib/global/qrandom_p.h8
-rw-r--r--src/corelib/tools/qsimd.cpp68
-rw-r--r--src/corelib/tools/qsimd_p.h18
-rw-r--r--tests/auto/corelib/global/qrandomgenerator/tst_qrandomgenerator.cpp6
5 files changed, 91 insertions, 54 deletions
diff --git a/src/corelib/global/qrandom.cpp b/src/corelib/global/qrandom.cpp
index fa26d54afa..b150a29b88 100644
--- a/src/corelib/global/qrandom.cpp
+++ b/src/corelib/global/qrandom.cpp
@@ -90,47 +90,6 @@ DECLSPEC_IMPORT BOOLEAN WINAPI SystemFunction036(PVOID RandomBuffer, ULONG Rando
QT_BEGIN_NAMESPACE
-#if defined(Q_PROCESSOR_X86) && QT_COMPILER_SUPPORTS_HERE(RDRND)
-static qsizetype qt_random_cpu(void *buffer, qsizetype count) Q_DECL_NOTHROW;
-
-# ifdef Q_PROCESSOR_X86_64
-# define _rdrandXX_step _rdrand64_step
-# else
-# define _rdrandXX_step _rdrand32_step
-# endif
-
-static QT_FUNCTION_TARGET(RDRND) qsizetype qt_random_cpu(void *buffer, qsizetype count) Q_DECL_NOTHROW
-{
- unsigned *ptr = reinterpret_cast<unsigned *>(buffer);
- unsigned *end = ptr + count;
- int retries = 10;
-
- while (ptr + sizeof(qregisteruint)/sizeof(*ptr) <= end) {
- if (_rdrandXX_step(reinterpret_cast<qregisteruint *>(ptr)))
- ptr += sizeof(qregisteruint)/sizeof(*ptr);
- else if (--retries == 0)
- goto out;
- }
-
- while (sizeof(*ptr) != sizeof(qregisteruint) && ptr != end) {
- bool ok = _rdrand32_step(ptr);
- if (!ok && --retries)
- continue;
- if (ok)
- ++ptr;
- break;
- }
-
-out:
- return ptr - reinterpret_cast<unsigned *>(buffer);
-}
-#else
-static qsizetype qt_random_cpu(void *, qsizetype)
-{
- return 0;
-}
-#endif
-
enum {
// may be "overridden" by a member enum
FillBufferNoexcept = true
@@ -371,8 +330,8 @@ Q_NEVER_INLINE void QRandomGenerator::SystemGenerator::generate(quint32 *begin,
}
qsizetype filled = 0;
- if (qt_has_hwrng() && (uint(qt_randomdevice_control) & SkipHWRNG) == 0)
- filled += qt_random_cpu(buffer, count);
+ if (qHasHwrng() && (uint(qt_randomdevice_control) & SkipHWRNG) == 0)
+ filled += qRandomCpu(buffer, count);
if (filled != count && (uint(qt_randomdevice_control) & SkipSystemRNG) == 0) {
qsizetype bytesFilled =
diff --git a/src/corelib/global/qrandom_p.h b/src/corelib/global/qrandom_p.h
index 917a91098e..4a0adff51c 100644
--- a/src/corelib/global/qrandom_p.h
+++ b/src/corelib/global/qrandom_p.h
@@ -79,14 +79,6 @@ extern Q_CORE_EXPORT QBasicAtomicInteger<uint> qt_randomdevice_control;
enum { qt_randomdevice_control = 0 };
#endif
-inline bool qt_has_hwrng()
-{
-#if defined(Q_PROCESSOR_X86) && QT_COMPILER_SUPPORTS_HERE(RDRND)
- return qCpuHasFeature(RDRND);
-#else
- return false;
-#endif
-}
QT_END_NAMESPACE
diff --git a/src/corelib/tools/qsimd.cpp b/src/corelib/tools/qsimd.cpp
index ddd715f745..09c276c4e0 100644
--- a/src/corelib/tools/qsimd.cpp
+++ b/src/corelib/tools/qsimd.cpp
@@ -376,6 +376,38 @@ static quint64 detectProcessorFeatures()
features &= ~AllAVX512;
}
+#if defined(Q_PROCESSOR_X86) && QT_COMPILER_SUPPORTS_HERE(RDRND)
+ /**
+ * Some AMD CPUs (e.g. AMD A4-6250J and AMD Ryzen 3000-series) have a
+ * failing random generation instruction, which always returns
+ * 0xffffffff, even when generation was "successful".
+ *
+ * This code checks if hardware random generator generates four consecutive
+ * equal numbers. If it does, then we probably have a failing one and
+ * should disable it completely.
+ *
+ * https://bugreports.qt.io/browse/QTBUG-69423
+ */
+ if (features & CpuFeatureRDRND) {
+ const qsizetype testBufferSize = 4;
+ unsigned testBuffer[4] = {};
+
+ const qsizetype generated = qRandomCpu(testBuffer, testBufferSize);
+
+ if (Q_UNLIKELY(generated == testBufferSize &&
+ testBuffer[0] == testBuffer[1] &&
+ testBuffer[1] == testBuffer[2] &&
+ testBuffer[2] == testBuffer[3])) {
+
+ fprintf(stderr, "WARNING: CPU random generator seem to be failing, disable hardware random number generation\n");
+ fprintf(stderr, "WARNING: RDRND generated: 0x%x 0x%x 0x%x 0x%x\n",
+ testBuffer[0], testBuffer[1], testBuffer[2], testBuffer[3]);
+
+ features &= ~CpuFeatureRDRND;
+ }
+ }
+#endif
+
return features;
}
@@ -590,4 +622,40 @@ void qDumpCPUFeatures()
puts("");
}
+#if defined(Q_PROCESSOR_X86) && QT_COMPILER_SUPPORTS_HERE(RDRND)
+
+# ifdef Q_PROCESSOR_X86_64
+# define _rdrandXX_step _rdrand64_step
+# else
+# define _rdrandXX_step _rdrand32_step
+# endif
+
+QT_FUNCTION_TARGET(RDRND) qsizetype qRandomCpu(void *buffer, qsizetype count) Q_DECL_NOTHROW
+{
+ unsigned *ptr = reinterpret_cast<unsigned *>(buffer);
+ unsigned *end = ptr + count;
+ int retries = 10;
+
+ while (ptr + sizeof(qregisteruint)/sizeof(*ptr) <= end) {
+ if (_rdrandXX_step(reinterpret_cast<qregisteruint *>(ptr)))
+ ptr += sizeof(qregisteruint)/sizeof(*ptr);
+ else if (--retries == 0)
+ goto out;
+ }
+
+ while (sizeof(*ptr) != sizeof(qregisteruint) && ptr != end) {
+ bool ok = _rdrand32_step(ptr);
+ if (!ok && --retries)
+ continue;
+ if (ok)
+ ++ptr;
+ break;
+ }
+
+out:
+ return ptr - reinterpret_cast<unsigned *>(buffer);
+}
+#endif
+
+
QT_END_NAMESPACE
diff --git a/src/corelib/tools/qsimd_p.h b/src/corelib/tools/qsimd_p.h
index c36e1e484f..d603631a24 100644
--- a/src/corelib/tools/qsimd_p.h
+++ b/src/corelib/tools/qsimd_p.h
@@ -346,6 +346,15 @@ extern Q_CORE_EXPORT QBasicAtomicInteger<unsigned> qt_cpu_features[2];
#endif
Q_CORE_EXPORT quint64 qDetectCpuFeatures();
+#if defined(Q_PROCESSOR_X86) && QT_COMPILER_SUPPORTS_HERE(RDRND)
+Q_CORE_EXPORT qsizetype qRandomCpu(void *, qsizetype) Q_DECL_NOTHROW;
+#else
+static inline qsizetype qRandomCpu(void *, qsizetype) Q_DECL_NOTHROW
+{
+ return 0;
+}
+#endif
+
static inline quint64 qCpuFeatures()
{
quint64 features = qt_cpu_features[0].load();
@@ -362,6 +371,15 @@ static inline quint64 qCpuFeatures()
#define qCpuHasFeature(feature) (((qCompilerCpuFeatures & CpuFeature ## feature) == CpuFeature ## feature) \
|| ((qCpuFeatures() & CpuFeature ## feature) == CpuFeature ## feature))
+inline bool qHasHwrng()
+{
+#if defined(Q_PROCESSOR_X86) && QT_COMPILER_SUPPORTS_HERE(RDRND)
+ return qCpuHasFeature(RDRND);
+#else
+ return false;
+#endif
+}
+
#define ALIGNMENT_PROLOGUE_16BYTES(ptr, i, length) \
for (; i < static_cast<int>(qMin(static_cast<quintptr>(length), ((4 - ((reinterpret_cast<quintptr>(ptr) >> 2) & 0x3)) & 0x3))); ++i)
diff --git a/tests/auto/corelib/global/qrandomgenerator/tst_qrandomgenerator.cpp b/tests/auto/corelib/global/qrandomgenerator/tst_qrandomgenerator.cpp
index 7c04611823..dca49e9f7e 100644
--- a/tests/auto/corelib/global/qrandomgenerator/tst_qrandomgenerator.cpp
+++ b/tests/auto/corelib/global/qrandomgenerator/tst_qrandomgenerator.cpp
@@ -320,7 +320,7 @@ void tst_QRandomGenerator::generate32_data()
QTest::newRow("fixed") << (RandomValue32 & RandomDataMask);
QTest::newRow("global") << 0U;
#ifdef QT_BUILD_INTERNAL
- if (qt_has_hwrng())
+ if (qHasHwrng())
QTest::newRow("hwrng") << uint(UseSystemRNG);
QTest::newRow("system") << uint(UseSystemRNG | SkipHWRNG);
# ifdef HAVE_FALLBACK_ENGINE
@@ -755,7 +755,7 @@ void tst_QRandomGenerator::stdUniformIntDistribution_data()
auto newRow = [&](quint32 max) {
#ifdef QT_BUILD_INTERNAL
- if (qt_has_hwrng())
+ if (qHasHwrng())
QTest::addRow("hwrng:%u", max) << uint(UseSystemRNG) << max;
QTest::addRow("system:%u", max) << uint(UseSystemRNG | SkipHWRNG) << max;
# ifdef HAVE_FALLBACK_ENGINE
@@ -868,7 +868,7 @@ void tst_QRandomGenerator::stdUniformRealDistribution_data()
auto newRow = [&](double min, double sup) {
#ifdef QT_BUILD_INTERNAL
- if (qt_has_hwrng())
+ if (qHasHwrng())
QTest::addRow("hwrng:%g-%g", min, sup) << uint(UseSystemRNG) << min << sup;
QTest::addRow("system:%g-%g", min, sup) << uint(UseSystemRNG | SkipHWRNG) << min << sup;
# ifdef HAVE_FALLBACK_ENGINE