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> (cherry picked from commit 5839714d986f28412c9f9ed4801d1bf9378f7b51) Reviewed-by: Thiago Macieira <thiago.macieira@intel.com>
This commit is contained in:
parent
4a9292f169
commit
d1646b37b1
@ -90,42 +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;
|
||||
|
||||
while (ptr + sizeof(qregisteruint)/sizeof(*ptr) <= end) {
|
||||
if (_rdrandXX_step(reinterpret_cast<qregisteruint *>(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<unsigned *>(buffer);
|
||||
}
|
||||
#else
|
||||
static qsizetype qt_random_cpu(void *, qsizetype)
|
||||
{
|
||||
return 0;
|
||||
}
|
||||
#endif
|
||||
|
||||
enum {
|
||||
// may be "overridden" by a member enum
|
||||
FillBufferNoexcept = true
|
||||
@ -366,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 =
|
||||
|
@ -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
|
||||
|
@ -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;
|
||||
}
|
||||
|
||||
@ -589,4 +621,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
|
||||
|
@ -346,6 +346,15 @@ extern Q_CORE_EXPORT QBasicAtomicInteger<unsigned> qt_cpu_features[2];
|
||||
#endif
|
||||
Q_CORE_EXPORT void 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();
|
||||
@ -366,6 +375,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)
|
||||
|
||||
|
@ -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
|
||||
|
Loading…
x
Reference in New Issue
Block a user