chiark / gitweb /
base/dispatch.c, etc.: Check that `rdrand' works.
authorMark Wooding <mdw@distorted.org.uk>
Wed, 30 Oct 2019 00:45:11 +0000 (00:45 +0000)
committerMark Wooding <mdw@distorted.org.uk>
Mon, 16 Dec 2019 16:25:17 +0000 (16:25 +0000)
When probing for `rdrand', check to make sure that it doesn't just
return the same thing every time, and that it can reasonably well make
progress.  We check that up to five 32-bit samples are not all the same,
which will mistakenly mark a working CPU as defective with probability
2^-128.

It seems that some processors will return a constant value from `rdrand'
but set the carry flag to indicate that it's properly random anyway.
See

https://arstechnica.com/gadgets/2019/10/how-a-months-old-amd-microcode-bug-destroyed-my-weekend/

as an example.

base/dispatch.c

index b281ec3d4ca90f084f3c20ab1795492f5e2119ce..42c64ee526b17896a73b61f35aeaf2ed9556a8f4 100644 (file)
@@ -198,6 +198,59 @@ static int xmm_registers_available_p(void)
 #endif
 }
 
+/* --- @rdrand_works_p@ --- *
+ *
+ *
+ * Arguments:  ---
+ *
+ * Returns:    Nonzero if the `rdrand' instruction actually works.  Assumes
+ *             that it's already been verified to be safe to issue.
+ */
+
+#ifdef __GNUC__
+static int rdrand(unsigned *x)
+{
+  int i, rc;
+  unsigned _t;
+
+  i = 16;
+  __asm__ ("" : "=g" (_t));
+  __asm__ ("0: rdrand %2; jc 1f; decl %1; jnz 0b\n"
+          "mov $-1, %0; jmp 9f\n"
+          "1: movl %2, (%3); xorl %0, %0\n"
+          "9:"
+          : "=r" (rc), "+r" (i), "+r" (_t)
+          : "r" (x)
+          : "cc");
+  return (rc);
+}
+#endif
+
+static int rdrand_works_p(void)
+{
+  unsigned ref, x, i;
+
+  /* Check that it doesn't always give the same answer.  Try four times: this
+   * will fail with probability %$2^{-128}$% with a truly random generator,
+   * which seems fair enough.
+   */
+  if (rdrand(&ref)) goto fail;
+  for (i = 0; i < 4; i++) {
+    if (rdrand(&x)) goto fail;
+    if (x != ref) goto not_stuck;
+  }
+  dispatch_debug("RDRAND always returns 0x%08x!", ref);
+  return (0);
+
+not_stuck:
+  dispatch_debug("RDRAND instruction looks plausible");
+  return (1);
+
+fail:
+  dispatch_debug("RDRAND instruction fails too often");
+  return (0);
+}
+
 #endif
 
 /*----- General feature probing using auxiliary vectors -------------------*/
@@ -544,7 +597,7 @@ int cpu_feature_p(int feat)
                 cpuid_features_p(CPUID1D_SSE2, CPUID1C_AESNI) &&
                 xmm_registers_available_p());
     CASE_CPUFEAT(X86_RDRAND, "x86:rdrand",
-                cpuid_features_p(0, CPUID1C_RDRAND));
+                cpuid_features_p(0, CPUID1C_RDRAND) && rdrand_works_p());
 #endif
 #ifdef CAPMAP
 #  define FEATP__CASE(feat, tok)                                       \