]> git.ipfire.org Git - thirdparty/json-c.git/commitdiff
Detect broken RDRAND during initialization. 625/head
authorBjörn Esser <besser82@fedoraproject.org>
Mon, 25 May 2020 11:01:18 +0000 (13:01 +0200)
committerBjörn Esser <besser82@fedoraproject.org>
Mon, 1 Jun 2020 14:45:45 +0000 (16:45 +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 and slightly modified backport
of the following commits on the master branch:

  * 0e5bbcaa162ac7850eb4fcd8f91391837d0efb50
  * 4d36b0287d3ab0912ba8a4790340ca099960b2b0
  * 80863140263be5f2dc630938ed8f0066f8a1ab43

random_seed.c

index 32327774addbf81b7c3ec83316ca895b07476299..56642da245c321470b6d3c2e825fe5e012834843 100644 (file)
 
 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 <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__
 #pragma comment(lib, "advapi32.lib")
 #endif