> 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
> 

Reply via email to