random-util: eat up bad RDRAND values seen on AMD CPUs
authorMichael Biebl <biebl@debian.org>
Tue, 14 May 2019 11:12:35 +0000 (13:12 +0200)
committerMichael Biebl <biebl@debian.org>
Wed, 16 Oct 2019 13:24:54 +0000 (14:24 +0100)
An ugly, ugly work-around for #11810. And no, we shouldn't have to do
this. This is something for AMD, the firmware or the kernel to
fix/work-around, not us. But nonetheless, this should do it for now.

Fixes: #11810
(cherry picked from commit 1c53d4a070edbec8ad2d384ba0014d0eb6bae077)

Gbp-Pq: Name random-util-eat-up-bad-RDRAND-values-seen-on-AMD-CPUs.patch

src/basic/random-util.c

index f7decf60b6189a7206577dc44ce650b2b148273e..38f8180b406af94ab988fc17b66caa2bdb525249 100644 (file)
@@ -37,6 +37,7 @@ int rdrand(unsigned long *ret) {
 
 #if defined(__i386__) || defined(__x86_64__)
         static int have_rdrand = -1;
+        unsigned long v;
         unsigned char err;
 
         if (have_rdrand < 0) {
@@ -56,7 +57,7 @@ int rdrand(unsigned long *ret) {
 
         asm volatile("rdrand %0;"
                      "setc %1"
-                     : "=r" (*ret),
+                     : "=r" (v),
                        "=qm" (err));
 
 #if HAS_FEATURE_MEMORY_SANITIZER
@@ -66,6 +67,18 @@ int rdrand(unsigned long *ret) {
         if (!err)
                 return -EAGAIN;
 
+        /* Apparently on some AMD CPUs RDRAND will sometimes (after a suspend/resume cycle?) report success
+         * via the carry flag but nonetheless return the same fixed value -1 in all cases. This appears to be
+         * a bad bug in the CPU or firmware. Let's deal with that and work-around this by explicitly checking
+         * for this special value (and also 0, just to be sure) and filtering it out. This is a work-around
+         * only however and something AMD really should fix properly. The Linux kernel should probably work
+         * around this issue by turning off RDRAND altogether on those CPUs. See:
+         * https://github.com/systemd/systemd/issues/11810 */
+        if (v == 0 || v == ULONG_MAX)
+                return log_debug_errno(SYNTHETIC_ERRNO(EUCLEAN),
+                                       "RDRAND returned suspicious value %lx, assuming bad hardware RNG, not using value.", v);
+
+        *ret = v;
         return 0;
 #else
         return -EOPNOTSUPP;