This is a straightforward implementation: for each bit of internal
RNG state, request one bit from KVM_GET_RNG_SEED.  This is done even
if RDSEED/RDRAND worked, since KVM_GET_RNG_SEED is likely to provide
cryptographically secure output even if the CPU's RNG is weak or
compromised.

Signed-off-by: Andy Lutomirski <l...@amacapital.net>
---
 arch/x86/Kconfig                 |  4 ++++
 arch/x86/include/asm/kvm_guest.h |  9 +++++++++
 arch/x86/kernel/archrandom.c     | 22 +++++++++++++++++++++-
 arch/x86/kernel/kvm.c            | 10 ++++++++++
 4 files changed, 44 insertions(+), 1 deletion(-)

diff --git a/arch/x86/Kconfig b/arch/x86/Kconfig
index a8f749e..adfa09c 100644
--- a/arch/x86/Kconfig
+++ b/arch/x86/Kconfig
@@ -593,6 +593,7 @@ config KVM_GUEST
        bool "KVM Guest support (including kvmclock)"
        depends on PARAVIRT
        select PARAVIRT_CLOCK
+       select ARCH_RANDOM
        default y
        ---help---
          This option enables various optimizations for running under the KVM
@@ -1507,6 +1508,9 @@ config ARCH_RANDOM
          If supported, this is a high bandwidth, cryptographically
          secure hardware random number generator.
 
+         This also enables paravirt RNGs such as KVM's if the relevant
+         PV guest support is enabled.
+
 config X86_SMAP
        def_bool y
        prompt "Supervisor Mode Access Prevention" if EXPERT
diff --git a/arch/x86/include/asm/kvm_guest.h b/arch/x86/include/asm/kvm_guest.h
index a92b176..8c4dbd5 100644
--- a/arch/x86/include/asm/kvm_guest.h
+++ b/arch/x86/include/asm/kvm_guest.h
@@ -3,4 +3,13 @@
 
 int kvm_setup_vsyscall_timeinfo(void);
 
+#if defined(CONFIG_KVM_GUEST) && defined(CONFIG_ARCH_RANDOM)
+extern bool kvm_get_rng_seed(u64 *rv);
+#else
+static inline bool kvm_get_rng_seed(u64 *rv)
+{
+       return false;
+}
+#endif
+
 #endif /* _ASM_X86_KVM_GUEST_H */
diff --git a/arch/x86/kernel/archrandom.c b/arch/x86/kernel/archrandom.c
index 5515fc8..3bcfa58 100644
--- a/arch/x86/kernel/archrandom.c
+++ b/arch/x86/kernel/archrandom.c
@@ -15,6 +15,7 @@
  */
 
 #include <asm/archrandom.h>
+#include <asm/kvm_guest.h>
 
 void arch_get_rng_seed(void *ctx,
                       void (*seed)(void *ctx, u32 data),
@@ -22,7 +23,7 @@ void arch_get_rng_seed(void *ctx,
                       const char *log_prefix)
 {
        int i, longs = (bits_per_source + BITS_PER_LONG - 1) / BITS_PER_LONG;
-       int rdseed_bits = 0, rdrand_bits = 0;
+       int rdseed_bits = 0, rdrand_bits = 0, kvm_bits = 0;
        int rdrand_longs_wanted = 0;
        char buf[128] = "";
        char *msgptr = buf;
@@ -74,6 +75,25 @@ void arch_get_rng_seed(void *ctx,
        if (rdrand_bits)
                msgptr += sprintf(msgptr, ", %d bits from RDRAND", rdrand_bits);
 
+       /*
+        * Use KVM_GET_RNG_SEED regardless of whether the CPU RNG worked,
+        * since it incorporates entropy unavailable to the CPU.  We
+        * request enough bits for the entire internal RNG state, because
+        * there's no good reason not to.
+        */
+       for (i = 0; i < (bits_per_source + 63) / 64; i++) {
+               u64 rv;
+
+               if (kvm_get_rng_seed(&rv)) {
+                       seed(ctx, (u32)rv);
+                       seed(ctx, (u32)(rv >> 32));
+                       kvm_bits += 8 * sizeof(rv);
+               }
+       }
+       if (kvm_bits)
+               msgptr += sprintf(msgptr, ", %d bits from KVM_GET_RNG_BITS",
+                                 kvm_bits);
+
        if (buf[0])
                pr_info("%s with %s\n", log_prefix, buf + 2);
 }
diff --git a/arch/x86/kernel/kvm.c b/arch/x86/kernel/kvm.c
index 3dd8e2c..bd8783a 100644
--- a/arch/x86/kernel/kvm.c
+++ b/arch/x86/kernel/kvm.c
@@ -416,6 +416,16 @@ void kvm_disable_steal_time(void)
        wrmsr(MSR_KVM_STEAL_TIME, 0, 0);
 }
 
+bool kvm_get_rng_seed(u64 *v)
+{
+       /*
+        * Allow migration from a hypervisor with the GET_RNG_SEED
+        * feature to a hypervisor without it.
+        */
+       return (kvm_para_has_feature(KVM_FEATURE_GET_RNG_SEED) &&
+               rdmsrl_safe(MSR_KVM_GET_RNG_SEED, v) == 0);
+}
+
 #ifdef CONFIG_SMP
 static void __init kvm_smp_prepare_boot_cpu(void)
 {
-- 
1.9.3

--
To unsubscribe from this list: send the line "unsubscribe kvm" in
the body of a message to majord...@vger.kernel.org
More majordomo info at  http://vger.kernel.org/majordomo-info.html

Reply via email to