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
