Protect guest RAMBlocks from host-side speculative execution attacks by
locking and unlocking the guest memory protection key before and after
`KVM_RUN`.

Signed-off-by: Jacky Li <[email protected]>
---
 accel/kvm/kvm-all.c       |  7 ++++++-
 include/exec/cpu-common.h |  2 ++
 system/physmem.c          |  6 +++++-
 util/pkey.c               | 45 +++++++++++++++++++++++++++++++++++++++++++++
 4 files changed, 58 insertions(+), 2 deletions(-)

diff --git a/accel/kvm/kvm-all.c b/accel/kvm/kvm-all.c
index 83cbd120a8..ae395cd374 100644
--- a/accel/kvm/kvm-all.c
+++ b/accel/kvm/kvm-all.c
@@ -19,6 +19,7 @@
 
 #include <linux/kvm.h>
 
+#include "exec/cpu-common.h"
 #include "qemu/atomic.h"
 #include "qemu/option.h"
 #include "qemu/config-file.h"
@@ -3666,7 +3667,11 @@ int kvm_vcpu_ioctl(CPUState *cpu, unsigned long type, 
...)
 
     trace_kvm_vcpu_ioctl(cpu->cpu_index, type, arg);
     accel_cpu_ioctl_begin(cpu);
-    ret = ioctl(cpu->kvm_fd, type, arg);
+    if (type == KVM_RUN) {
+        ret = qemu_pkey_kvm_run(cpu->kvm_fd, arg);
+    } else {
+        ret = ioctl(cpu->kvm_fd, type, arg);
+    }
     accel_cpu_ioctl_end(cpu);
     if (ret == -1) {
         ret = -errno;
diff --git a/include/exec/cpu-common.h b/include/exec/cpu-common.h
index 28399088f6..90047128de 100644
--- a/include/exec/cpu-common.h
+++ b/include/exec/cpu-common.h
@@ -114,4 +114,6 @@ static inline CPUState *env_cpu(CPUArchState *env)
 
 void qemu_init_guest_memory_pkey(void);
 int qemu_pkey_mprotect_guest_memory(void *addr, size_t len, int prot);
+void qemu_reset_pkey_with_ibpb(void);
+int qemu_pkey_kvm_run(int fd, void *arg);
 #endif /* CPU_COMMON_H */
diff --git a/system/physmem.c b/system/physmem.c
index 9f5a0f194c..c7101f6841 100644
--- a/system/physmem.c
+++ b/system/physmem.c
@@ -2755,7 +2755,11 @@ static void *qemu_ram_ptr_length(RAMBlock *block, 
ram_addr_t addr,
                                     1, lock, is_write);
     }
 
-    return ramblock_ptr(block, addr);
+    void *ptr = ramblock_ptr(block, addr);
+    if (ptr) {
+        qemu_reset_pkey_with_ibpb();
+    }
+    return ptr;
 }
 
 /*
diff --git a/util/pkey.c b/util/pkey.c
index 0151714f32..2e42da7d9c 100644
--- a/util/pkey.c
+++ b/util/pkey.c
@@ -117,17 +117,62 @@ __attribute__((target("pku"))) int 
qemu_pkey_mprotect_guest_memory(void *addr,
     return pkey_mprotect(addr, len, prot, pkey);
 }
 
+__attribute__((target("pku"))) void qemu_reset_pkey_with_ibpb(void)
+{
+    int pkey = guest_memory_pkey;
+    if (pkey == -1) {
+        return;
+    }
+    if (inline_pkey_get(pkey) == 0) {
+        return;
+    }
+
+    /* IBPB */
+    prctl(PR_SET_SPECULATION_CTRL, PR_SPEC_INDIRECT_BRANCH, PR_SPEC_DISABLE, 0,
+                0);
+
+    inline_pkey_set(pkey, 0);
+}
+
+__attribute__((target("pku"))) int qemu_pkey_kvm_run(int fd, void *arg)
+{
+    int pkey = guest_memory_pkey;
+    if (pkey == -1) {
+        return ioctl(fd, KVM_RUN, arg);
+    }
+
+    assert(pkey >= 0 && pkey < KEY_COUNT);
+
+    inline_pkey_set(pkey, 0);
+
+    intptr_t ret = local_syscall3(__NR_ioctl, fd, KVM_RUN, (intptr_t)arg);
+
+    inline_pkey_set(pkey, PKEY_DISABLE_ACCESS);
+
+    if (ret < 0) {
+        errno = -ret;
+        ret = -1;
+    }
+    return (int)ret;
+}
+
 #else
 /* Dummy implementations for all other configurations (non-x86_64 Linux, */
 /* Windows, macOS, etc.) */
 #if defined(CONFIG_LINUX)
 #include <linux/kvm.h>
 #include <sys/ioctl.h>
+
+int qemu_pkey_kvm_run(int fd, void *arg)
+{ return ioctl(fd, KVM_RUN, arg); }
 #endif
 
 void qemu_init_guest_memory_pkey(void)
 {}
 
+void qemu_reset_pkey_with_ibpb(void)
+{}
+
 int qemu_pkey_mprotect_guest_memory(void *addr, size_t len, int prot)
 {
     return 0;

-- 
2.55.0.737.g08866a6d13-goog


Reply via email to