On Tue, 6 Oct 2026 at 17:35, Sammy Al Hashemi <[email protected]> wrote: > > From: Sammyalhashe <[email protected]>
This patch looks remarkably similar to my own patch posted to bugzilla exactly five years ago: https://gcc.gnu.org/bugzilla/show_bug.cgi?id=100444#c14 Even the wording of the comments is identical. > > Patching libstdc++ random so that it checks if rdrand on amd is broken > before returning a value. > > AMD 3000 CPUs seem to have a bug where the RDRAND instruction always > emits all 1s (aka 4294967295). > > This commit provides a workaround (upon detecting an AMD cpu) - to try > the random function a number of times. If it continues to return the > same broken consistent value, we confirm the random instruction is buggy > on this cpu and emit a runtime error. > > libstdc++-v3/ChangeLog: > > PR libstdc++/100444 > * src/c++11/random.cc (__x86_cpu_sig_ebx): New function. > (__x86_cpu_supports_rdrand, __x86_cpu_supports_rdseed): New > functions. > (__x86_amd_workaround): New function. > (which_source): Look through __x86_amd_workaround to the > wrapped function. > (random_device::_M_init(const string&)): Use new helpers. Wrap > RDRAND and RDSEED with __x86_amd_workaround on AMD CPUs. > (random_device::_M_fini): Do nothing for __x86_amd_workaround. > > Signed-off-by: Sammy Al Hashemi <[email protected]> > --- > I don't have access to an affected Ryzen 3000 system, so this has not > been tested on the hardware that exhibits the bug. > > Tested on x86_64-linux, cross-built and run under `qemu -cpu > EPYC-Rome`. The all-ones failure path was verified with a temporary > fault-injection patch (not included). > > If anyone has an affected machine with a pre-fix BIOS, testing would > be appreciated. > > libstdc++-v3/src/c++11/random.cc | 139 +++++++++++++++++++++++-------- > 1 file changed, 102 insertions(+), 37 deletions(-) > > diff --git a/libstdc++-v3/src/c++11/random.cc > b/libstdc++-v3/src/c++11/random.cc > index 502ba8f44c7..a7888a83c62 100644 > --- a/libstdc++-v3/src/c++11/random.cc > +++ b/libstdc++-v3/src/c++11/random.cc > @@ -146,6 +146,57 @@ namespace std _GLIBCXX_VISIBILITY(default) > #endif > #endif > > +#if USE_RDRAND || USE_RDSEED > + // Check availability of cpuid and if supported, return ebx register. > + // The ebx register can be used to identify the cpu manufacturer. > + inline unsigned int > + __x86_cpu_sig_ebx() noexcept > + { > + unsigned int ebx; > + if (__get_cpuid_max(0, &ebx) > 0) > + return ebx; > + return 0; > + } > + > + inline bool > + __x86_cpu_supports_rdrand() noexcept > + { > +#ifdef USE_RDRAND > + unsigned int eax, ebx, ecx, edx; > + // CPUID.01H:ECX.RDRAND[bit 30] > + __cpuid(1, eax, ebx, ecx, edx); > + return ecx & bit_RDRND; > +#else > + return false; > +#endif > + } > + > + inline bool > + __x86_cpu_supports_rdseed() noexcept > + { > + unsigned int eax, ebx, ecx, edx; > + // CPUID.(EAX=07H, ECX=0H):EBX.RDSEED[bit 18] > + __cpuid_count(7, 0, eax, ebx, ecx, edx); > + return ebx & bit_RDSEED; > + } > + > + // Workaround AMD Ryzen 3000 bug, see PR libstdc++/100444 > + unsigned int > + __x86_amd_workaround(void* fx) > + { > + auto f = reinterpret_cast<unsigned int(*)(void*)>(fx); > + const unsigned int ALL_ONES = 0xffffffff; > + auto n = f(nullptr); > + int retries = 10; > + while (--retries && n == ALL_ONES) > + n = f(nullptr); > + if (n == ALL_ONES) > + std::__throw_runtime_error(__N("random_device: AMD Ryzen 3000 bug " > + "detected, BIOS update is required.")); > + return n; > + } > +#endif > + > #ifdef USE_DARN > unsigned int > __attribute__((target("cpu=power9"))) > @@ -279,6 +330,11 @@ namespace std _GLIBCXX_VISIBILITY(default) > return darn; > #endif > > +#if defined USE_RDRAND || defined USE_RDSEED > + if (func == &__x86_amd_workaround) > + return which_source(reinterpret_cast<decltype(func)>(file), nullptr); > +#endif > + > #ifdef _GLIBCXX_USE_DEV_RANDOM > if (file != nullptr) > return device_file; > @@ -412,31 +468,33 @@ namespace std _GLIBCXX_VISIBILITY(default) > #ifdef USE_RDSEED > if (which & rdseed) > { > - unsigned int eax, ebx, ecx, edx; > - // Check availability of cpuid and, for now at least, also the > - // CPU signature for Intel, AMD and Hygon. > - if (__get_cpuid_max(0, &ebx) > 0 > - && (ebx == signature_INTEL_ebx > - || ebx == signature_AMD_ebx > - || ebx == signature_HYGON_ebx)) > + const unsigned int sig_ebx = __x86_cpu_sig_ebx(); > + // For now, we only use RDSEED for Intel, AMD, and Hygon > + // processors. > + if (sig_ebx == signature_INTEL_ebx > + || sig_ebx == signature_AMD_ebx > + || sig_ebx == signature_HYGON_ebx) > + { > + if (__x86_cpu_supports_rdseed()) > { > - // CPUID.(EAX=07H, ECX=0H):EBX.RDSEED[bit 18] > - __cpuid_count(7, 0, eax, ebx, ecx, edx); > - if (ebx & bit_RDSEED) > - { > -#ifdef USE_RDRAND > - // CPUID.01H:ECX.RDRAND[bit 30] > - __cpuid(1, eax, ebx, ecx, edx); > - if (ecx & bit_RDRND) > - { > - _M_func = &__x86_rdseed_rdrand; > - return; > - } > -#endif > - _M_func = &__x86_rdseed; > - return; > - } > + if (__x86_cpu_supports_rdrand()) > + { > + _M_func = &__x86_rdseed_rdrand; > + } > + else > + { > + _M_func = &__x86_rdseed; > + } > + > + // AMD 3000 Workaround > + if (sig_ebx == signature_AMD_ebx) > + { > + _M_file = reinterpret_cast<void*>(_M_func); > + _M_func = &__x86_amd_workaround; > + } > + return; > } > + } > err = unsupported; > } > #endif // USE_RDSEED > @@ -444,22 +502,24 @@ namespace std _GLIBCXX_VISIBILITY(default) > #ifdef USE_RDRAND > if (which & rdrand) > { > - unsigned int eax, ebx, ecx, edx; > - // Check availability of cpuid and, for now at least, also the > - // CPU signature for Intel, AMD and Hygon. > - if (__get_cpuid_max(0, &ebx) > 0 > - && (ebx == signature_INTEL_ebx > - || ebx == signature_AMD_ebx > - || ebx == signature_HYGON_ebx)) > + const unsigned int sig_ebx = __x86_cpu_sig_ebx(); > + // For now, we only use RDRAND for Intel, AMD, and Hygon > + // processors. > + if (sig_ebx == signature_INTEL_ebx > + || sig_ebx == signature_AMD_ebx > + || sig_ebx == signature_HYGON_ebx) > + { > + if (__x86_cpu_supports_rdrand()) > { > - // CPUID.01H:ECX.RDRAND[bit 30] > - __cpuid(1, eax, ebx, ecx, edx); > - if (ecx & bit_RDRND) > - { > - _M_func = &__x86_rdrand; > - return; > - } > + _M_func = &__x86_rdrand; > + if (sig_ebx == signature_AMD_ebx) > + { > + _M_file = reinterpret_cast<void*>(_M_func); > + _M_func = &__x86_amd_workaround; > + } > + return; > } > + } > err = unsupported; > } > #endif // USE_RDRAND > @@ -584,6 +644,11 @@ namespace std _GLIBCXX_VISIBILITY(default) > if (!_M_file) > return; > > +#if USE_RDRAND || USE_RDSEED > + if (_M_func == &__x86_amd_workaround) > + return; > +#endif > + > #if USE_LCG > if (_M_func == &__lcg) > { > -- > 2.55.0 >
