Yes I took inspiration from your work :) I hope that it is okay! Primarily my work here has been a big learning exercise for me. During this patch I learned how to properly test with qemu, learned how to properly cross-compile, and learned more of the gcc contributing standard and code. I should have given you credit in the patch comments I apologize. But once I figured out how to properly cross compile here and ensure that this workaround was indeed used, I pushed it up for the community to discuss. I apologize Jonathan for my lack of foresight here ;(.
Best, Sammy On Tue, Oct 6, 2026, at 4:06 PM, Jonathan Wakely wrote: > 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 > > > >
