]> granicus.if.org Git - json-c/commitdiff
Detect broken RDRAND during initialization
authorTudor Brindus <me@tbrindus.ca>
Sat, 2 May 2020 01:09:22 +0000 (21:09 -0400)
committerTudor Brindus <me@tbrindus.ca>
Sun, 3 May 2020 19:15:24 +0000 (15:15 -0400)
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.

Fixes #588.

random_seed.c

index c459f0f92ff46b7b35a8a75a303a5c981f0e2390..4ddcb07d16b1293fba6a70006e621480820052b9 100644 (file)
@@ -43,12 +43,41 @@ 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
-       int regs[4];
-       do_cpuid(regs, 1);
-       return (regs[2] & (1 << 30)) != 0;
+       if (_has_rdrand == -1)
+       {
+               // CPUID.01H:ECX.RDRAND[bit 30] == 1
+               int regs[4];
+               do_cpuid(regs, 1);
+               if (!(regs[2] & (1 << 30)))
+               {
+                       _has_rdrand = 0;
+               } else
+               {
+                       // Some CPUs advertise RDRAND in CPUID, but return 0xFFFFFFFF
+                       // unconditionally. To avoid locking up later, test RDRAND here. If over
+                       // 10 trials RDRAND has returned the same value, declare it broken.
+                       _has_rdrand = 0;
+                       int prev = get_rdrand_seed();
+                       for (int i = 0; i < 10; i++) {
+                               int temp = get_rdrand_seed();
+                               if (temp != prev) {
+                                       _has_rdrand = 1;
+                                       break;
+                               }
+
+                               prev = temp;
+                       }
+               }
+       }
+
+       return _has_rdrand;
 }
 
 #endif