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
>

Reply via email to