diff --git a/random_seed.c b/random_seed.c index 3232777..56642da 100644 --- a/random_seed.c +++ b/random_seed.c @@ -26,20 +26,11 @@ static void do_cpuid(int regs[], int h) { - __asm__ __volatile__( -#if defined __x86_64__ - "pushq %%rbx;\n" -#else - "pushl %%ebx;\n" -#endif - "cpuid;\n" -#if defined __x86_64__ - "popq %%rbx;\n" -#else - "popl %%ebx;\n" -#endif - : "=a"(regs[0]), [ebx] "=r"(regs[1]), "=c"(regs[2]), "=d"(regs[3]) + /* clang-format off */ + __asm__ __volatile__("cpuid" + : "=a"(regs[0]), "=b"(regs[1]), "=c"(regs[2]), "=d"(regs[3]) : "a"(h)); + /* clang-format on */ } #elif defined _MSC_VER @@ -53,12 +44,51 @@ static void do_cpuid(int regs[], int h) #if HAS_X86_CPUID -static int has_rdrand() +static int get_rdrand_seed(void); + +/* Valid values are -1 (haven't tested), 0 (no), and 1 (yes). */ +static int _has_rdrand = -1; + +static int has_rdrand(void) { - // CPUID.01H:ECX.RDRAND[bit 30] == 1 - int regs[4]; - do_cpuid(regs, 1); - return (regs[2] & (1 << 30)) != 0; + if (_has_rdrand != -1) + { + return _has_rdrand; + } + + /* CPUID.01H:ECX.RDRAND[bit 30] == 1 */ + int regs[4]; + do_cpuid(regs, 1); + if (!(regs[2] & (1 << 30))) + { + _has_rdrand = 0; + return 0; + } + + /* + * Some CPUs advertise RDRAND in CPUID, but return 0xFFFFFFFF + * unconditionally. To avoid locking up later, test RDRAND here. If over + * 3 trials RDRAND has returned the same value, declare it broken. + * Example CPUs are AMD Ryzen 3000 series + * and much older AMD APUs, such as the E1-1500 + * https://github.com/systemd/systemd/issues/11810 + * https://linuxreviews.org/RDRAND_stops_returning_random_values_on_older_AMD_CPUs_after_suspend + */ + _has_rdrand = 0; + int prev = get_rdrand_seed(); + for (int i = 0; i < 3; i++) + { + int temp = get_rdrand_seed(); + if (temp != prev) + { + _has_rdrand = 1; + break; + } + + prev = temp; + } + + return _has_rdrand; } #endif @@ -69,17 +99,19 @@ static int has_rdrand() #define HAVE_RDRAND 1 -static int get_rdrand_seed() +static int get_rdrand_seed(void) { - DEBUG_SEED("get_rdrand_seed"); - int _eax; - // rdrand eax - __asm__ __volatile__("1: .byte 0x0F\n" - " .byte 0xC7\n" - " .byte 0xF0\n" - " jnc 1b;\n" - : "=a" (_eax)); - return _eax; + DEBUG_SEED("get_rdrand_seed"); + int _eax; + /* rdrand eax */ + /* clang-format off */ + __asm__ __volatile__("1: .byte 0x0F\n" + " .byte 0xC7\n" + " .byte 0xF0\n" + " jnc 1b;\n" + : "=a" (_eax)); + /* clang-format on */ + return _eax; } #endif @@ -109,7 +141,7 @@ static int get_rdrand_seed() DEBUG_SEED("get_rdrand_seed"); int _eax; retry: - // rdrand eax + /* rdrand eax */ __asm _emit 0x0F __asm _emit 0xC7 __asm _emit 0xF0 __asm jnc retry __asm mov _eax, eax @@ -178,8 +210,14 @@ static int get_dev_random_seed() #define HAVE_CRYPTGENRANDOM 1 +/* clang-format off */ #include + +/* Caution: these blank lines must remain so clang-format doesn't reorder + includes to put windows.h after wincrypt.h */ + #include +/* clang-format on */ #ifndef __GNUC__ #pragma comment(lib, "advapi32.lib") #endif