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 501c6bcf4513d40a53022dc93d4156402d43670b
parent 1fe8249bbc782d28185e0e893504e8ac3a1fcaec
Author: Louis Burda <quent.burda@gmail.com>
Date:   Tue, 24 Jan 2023 01:43:18 +0100

Page track support (without speculation)

Diffstat:
Mcachepc/cachepc.h | 10+++++++---
Mcachepc/kvm.c | 28++++++++++++----------------
Mtest/kvm-pagestep.c | 118+++++++++++++++++++++++++++++--------------------------------------------------
Mtest/kvm.c | 3---
4 files changed, 62 insertions(+), 97 deletions(-)

diff --git a/cachepc/cachepc.h b/cachepc/cachepc.h @@ -65,6 +65,12 @@ struct cpc_fault { struct list_head list; }; +struct cpc_track_exec { + bool cur_avail; + uint64_t cur_gfn; + uint64_t retinst; +}; + static_assert(sizeof(struct cacheline) == L1_LINESIZE, "Bad cacheline struct"); static_assert(CPC_CL_NEXT_OFFSET == offsetof(struct cacheline, next)); static_assert(CPC_CL_PREV_OFFSET == offsetof(struct cacheline, prev)); @@ -128,9 +134,7 @@ extern uint64_t cachepc_rip; extern uint64_t cachepc_rip_prev; extern bool cachepc_rip_prev_set; -extern uint64_t cachepc_inst_fault_gfn; -extern uint32_t cachepc_inst_fault_err; -extern uint64_t cachepc_inst_fault_retinst; +extern struct cpc_track_exec cachepc_track_exec; extern struct list_head cachepc_faults; diff --git a/cachepc/kvm.c b/cachepc/kvm.c @@ -71,12 +71,8 @@ EXPORT_SYMBOL(cachepc_track_end_gfn); LIST_HEAD(cachepc_faults); EXPORT_SYMBOL(cachepc_faults); -uint64_t cachepc_inst_fault_gfn = 0; -uint32_t cachepc_inst_fault_err = 0; -uint64_t cachepc_inst_fault_retinst = 0; -EXPORT_SYMBOL(cachepc_inst_fault_gfn); -EXPORT_SYMBOL(cachepc_inst_fault_err); -EXPORT_SYMBOL(cachepc_inst_fault_retinst); +struct cpc_track_exec cachepc_track_exec; +EXPORT_SYMBOL(cachepc_track_exec); cache_ctx *cachepc_ctx = NULL; cacheline *cachepc_ds = NULL; @@ -290,6 +286,8 @@ cachepc_kvm_reset_ioctl(void __user *arg_user) put_cpu(); + cachepc_events_reset(); + cachepc_kvm_reset_tracking_ioctl(NULL); cachepc_kvm_reset_baseline_ioctl(NULL); @@ -477,12 +475,11 @@ cachepc_kvm_reset_tracking_ioctl(void __user *arg_user) cachepc_untrack_all(vcpu, KVM_PAGE_TRACK_ACCESS); cachepc_untrack_all(vcpu, KVM_PAGE_TRACK_WRITE); - cachepc_inst_fault_gfn = 0; - cachepc_inst_fault_err = 0; - cachepc_track_start_gfn = 0; cachepc_track_end_gfn = 0; + memset(&cachepc_track_exec, 0, sizeof(cachepc_track_exec)); + cachepc_singlestep = false; cachepc_singlestep_reset = false; @@ -526,22 +523,22 @@ cachepc_kvm_track_mode_ioctl(void __user *arg_user) case CPC_TRACK_FULL: cachepc_track_all(vcpu, KVM_PAGE_TRACK_ACCESS); cachepc_singlestep_reset = true; - cachepc_track_mode = CPC_TRACK_FULL; break; case CPC_TRACK_EXEC: cachepc_track_all(vcpu, KVM_PAGE_TRACK_EXEC); cachepc_singlestep_reset = true; - cachepc_track_mode = CPC_TRACK_EXEC; break; case CPC_TRACK_FAULT_NO_RUN: cachepc_track_all(vcpu, KVM_PAGE_TRACK_ACCESS); - cachepc_track_mode = CPC_TRACK_FAULT_NO_RUN; break; - default: - cachepc_track_mode = CPC_TRACK_NONE; + case CPC_TRACK_NONE: break; + default: + return -EINVAL; } + cachepc_track_mode = mode; + return 0; } @@ -740,8 +737,7 @@ cachepc_kvm_init(void) cachepc_apic_oneshot = false; cachepc_apic_timer = 0; - cachepc_inst_fault_gfn = 0; - cachepc_inst_fault_err = 0; + memset(&cachepc_track_exec, 0, sizeof(cachepc_track_exec)); INIT_LIST_HEAD(&cachepc_faults); diff --git a/test/kvm-pagestep.c b/test/kvm-pagestep.c @@ -5,8 +5,8 @@ #include <sys/ioctl.h> #include <sys/mman.h> -#include <unistd.h> #include <signal.h> +#include <unistd.h> #include <fcntl.h> #include <errno.h> #include <err.h> @@ -21,11 +21,12 @@ extern uint8_t guest_start[]; extern uint8_t guest_stop[]; +static int child; + uint64_t monitor(struct kvm *kvm, bool baseline) { struct cpc_event event; - uint8_t counts[64]; int ret; /* Get page fault info */ @@ -33,17 +34,12 @@ monitor(struct kvm *kvm, bool baseline) if (ret && errno == EAGAIN) return 0; if (ret) err(1, "ioctl KVM_CPC_POLL_EVENT"); - if (event.type != CPC_EVENT_TRACK_STEP) + if (event.type != CPC_EVENT_TRACK_PAGE) errx(1, "unexpected event type %i", event.type); - ret = ioctl(kvm_dev, KVM_CPC_READ_COUNTS, counts); - if (ret) err(1, "ioctl KVM_CPC_READ_COUNTS"); - - printf("Event: rip:%llu cnt:%llu inst:%llu data:%llu ret:%llu\n", - vm_get_rip(kvm), event.step.fault_count, - event.step.fault_gfns[0], event.step.fault_gfns[1], - event.step.retinst); - print_counts(counts); + printf("Event: rip:%08llx prev:%llu next:%llu ret:%llu\n", + vm_get_rip(kvm), event.page.inst_gfn_prev, + event.page.inst_gfn, event.page.retinst); printf("\n"); ret = ioctl(kvm_dev, KVM_CPC_ACK_EVENT, &event.id); @@ -52,36 +48,48 @@ monitor(struct kvm *kvm, bool baseline) return 1; } +void +kill_child(void) +{ + kill(child, SIGKILL); +} + int main(int argc, const char **argv) { + struct ipc *ipc; struct kvm kvm; - uint8_t baseline[L1_SETS]; - struct cpc_event event; uint64_t eventcnt; - pid_t ppid, pid; uint32_t arg; int ret; - parse_vmtype(argc, argv); + vmtype = "kvm"; + if (argc > 1) vmtype = argv[1]; + if (strcmp(vmtype, "kvm") && strcmp(vmtype, "sev") + && strcmp(vmtype, "sev-es") + && strcmp(vmtype, "sev-snp")) + errx(1, "invalid vm mode: %s", vmtype); setvbuf(stdout, NULL, _IONBF, 0); - pin_process(0, TARGET_CORE, true); - kvm_setup_init(); - vm_init(&kvm, guest_start, guest_stop); + ipc = ipc_alloc(); + + child = fork(); + if (child < 0) err(1, "fork"); - /* reset kernel module state */ - ret = ioctl(kvm_dev, KVM_CPC_RESET, NULL); - if (ret < 0) err(1, "ioctl KVM_CPC_RESET"); + if (child == 0) { + pin_process(0, TARGET_CORE, true); - ppid = getpid(); - if ((pid = fork())) { - if (pid < 0) err(1, "fork"); + vm_init(&kvm, guest_start, guest_stop); - sleep(1); /* give time for child to pin other core */ + /* reset kernel module state */ + ret = ioctl(kvm_dev, KVM_CPC_RESET, NULL); + if (ret < 0) err(1, "ioctl KVM_CPC_RESET"); + + ipc_signal_parent(ipc); + ipc_wait_parent(ipc); printf("VM start\n"); @@ -94,72 +102,32 @@ main(int argc, const char **argv) } while (kvm.run->exit_reason == KVM_EXIT_HLT); printf("VM exit\n"); + + vm_deinit(&kvm); } else { pin_process(0, SECONDARY_CORE, true); - /* capture baseline by just letting it fault over and over */ - arg = CPC_TRACK_EXEC; - ret = ioctl(kvm_dev, KVM_CPC_TRACK_MODE, &arg); - if (ret) err(1, "ioctl KVM_CPC_TRACK_MODE"); - - printf("Monitor ready\n"); - - /* run vm while baseline is calculated */ - eventcnt = 0; - while (eventcnt < 50) { - eventcnt += monitor(&kvm, true); - } - - ret = ioctl(kvm_dev, KVM_CPC_VM_REQ_PAUSE); - if (ret) err(1, "ioctl KVM_CPC_VM_REQ_PAUSE"); + atexit(kill_child); - while (1) { - ret = ioctl(kvm_dev, KVM_CPC_POLL_EVENT, &event); - if (ret && errno == EAGAIN) continue; - if (ret) err(1, "ioctl KVM_CPC_POLL_EVENT"); + ipc_wait_child(ipc); - if (event.type == CPC_EVENT_PAUSE) break; + printf("Monitor start\n"); - printf("Skipping non-pause event..\n"); - ret = ioctl(kvm_dev, KVM_CPC_ACK_EVENT, &event.id); - if (ret) err(1, "ioctl KVM_CPC_ACK_EVENT"); - } - - arg = false; - ret = ioctl(kvm_dev, KVM_CPC_CALC_BASELINE, &arg); - if (ret) err(1, "ioctl KVM_CPC_CALC_BASELINE"); - - ret = ioctl(kvm_dev, KVM_CPC_READ_BASELINE, baseline); - if (ret) err(1, "ioctl KVM_CPC_READ_BASELINE"); - - printf("\nBaseline:\n"); - print_counts(baseline); - printf("\n"); - print_counts_raw(baseline); - printf("\n\n"); - - arg = true; - ret = ioctl(kvm_dev, KVM_CPC_APPLY_BASELINE, &arg); - if (ret) err(1, "ioctl KMV_CPC_APPLY_BASELINE"); - - /* single step and log all accessed pages */ - arg = CPC_TRACK_FULL; + arg = CPC_TRACK_EXEC; ret = ioctl(kvm_dev, KVM_CPC_TRACK_MODE, &arg); if (ret) err(1, "ioctl KVM_CPC_TRACK_MODE"); - ret = ioctl(kvm_dev, KVM_CPC_ACK_EVENT, &event.id); - if (ret) err(1, "ioctl KVM_CPC_ACK_EVENT"); + ipc_signal_child(ipc); eventcnt = 0; while (eventcnt < 50) { - eventcnt += monitor(&kvm, false); + eventcnt += monitor(&kvm, true); } - kill(ppid, SIGINT); - exit(0); + printf("Monitor exit\n"); } - vm_deinit(&kvm); + ipc_free(ipc); kvm_setup_deinit(); } diff --git a/test/kvm.c b/test/kvm.c @@ -226,9 +226,6 @@ kvm_init_memory(struct kvm *kvm, size_t ramsize, assert(code_stop - code_start <= kvm->memsize); memcpy(kvm->mem, code_start, code_stop - code_start); - printf("KVM Memory:\n"); - hexdump(code_start, code_stop - code_start); - memset(&region, 0, sizeof(region)); region.slot = 0; region.memory_size = kvm->memsize;