> Am 06.10.2026 um 18:37 schrieb Sammy Al Hashemi <[email protected]>:
>
> From: Sammyalhashe <[email protected]>
>
> 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.
Is that a good failure mode upon libstdc++ upgrade ? IIRC the kernel will stop
advertising the feature on buggy CPUs, so if we can statically detect this then
we can use a fallback implementation based on a static var?
> 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
>