]> git.ipfire.org Git - thirdparty/json-c.git/commitdiff
Detect broken RDRAND during initialization. 624/head
authorBjörn Esser <besser82@fedoraproject.org>
Mon, 25 May 2020 11:03:31 +0000 (13:03 +0200)
committerBjörn Esser <besser82@fedoraproject.org>
Mon, 25 May 2020 11:06:21 +0000 (13:06 +0200)
Some CPUs advertise RDRAND in CPUID, but return
0xFFFFFFFF unconditionally.  To avoid locking up
later, test RDRAND during initialization, and if
it returns 0xFFFFFFFF, mark it as nonexistent.

Also fix a possible segmentation fault in CPUID check.

This commit is a squashed backport of the following
commits on the master branch:

  * 0e5bbcaa162ac7850eb4fcd8f91391837d0efb50
  * 4d36b0287d3ab0912ba8a4790340ca099960b2b0
  * 80863140263be5f2dc630938ed8f0066f8a1ab43

random_seed.c

index fc19e26d132be0e60adcaa39eb968177fb9ac7db..b5f8a0795e96b2b0d7018501175562260db09921 100644 (file)
 static void do_cpuid(int regs[], int h)
 {
        /* clang-format off */
-    __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])
+    __asm__ __volatile__("cpuid"
+                         : "=a"(regs[0]), "=b"(regs[1]), "=c"(regs[2]), "=d"(regs[3])
                          : "a"(h));
        /* clang-format on */
 }
@@ -54,12 +43,51 @@ static void do_cpuid(int regs[], int h)
 
 #if HAS_X86_CPUID
 
+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
+       if (_has_rdrand != -1)
+       {
+               return _has_rdrand;
+       }
+
+       /* CPUID.01H:ECX.RDRAND[bit 30] == 1 */
        int regs[4];
        do_cpuid(regs, 1);
-       return (regs[2] & (1 << 30)) != 0;
+       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
@@ -74,7 +102,7 @@ static int get_rdrand_seed(void)
 {
        DEBUG_SEED("get_rdrand_seed");
        int _eax;
-       // rdrand eax
+       /* rdrand eax */
        /* clang-format off */
        __asm__ __volatile__("1: .byte 0x0F\n"
                             "   .byte 0xC7\n"
@@ -114,7 +142,7 @@ static int get_rdrand_seed(void)
        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
@@ -188,6 +216,10 @@ static int get_dev_random_seed(void)
 
 /* clang-format off */
 #include <windows.h>
+
+/* Caution: these blank lines must remain so clang-format doesn't reorder
+   includes to put windows.h after wincrypt.h */
+
 #include <wincrypt.h>
 /* clang-format on */
 #ifndef __GNUC__