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 13ff10bd81a3a6d7e13c43596a2f0efb6be39088
parent 1ffb06efd201e4d754a6295f70aecbe59152f18a
Author: Louis Burda <quent.burda@gmail.com>
Date:   Sat, 12 Nov 2022 01:59:45 +0100

Rename uspt to cachepc events

Diffstat:
Acachepc/events.c | 131+++++++++++++++++++++++++++++++++++++++++++++++++++++++++++++++++++++++++++++++
Acachepc/events.h | 25+++++++++++++++++++++++++
Mcachepc/kvm.c | 36++++++++++++++++++------------------
Mcachepc/mmu.c | 10+++-------
Dcachepc/uspt.c | 138-------------------------------------------------------------------------------
Dcachepc/uspt.h | 26--------------------------
6 files changed, 177 insertions(+), 189 deletions(-)

diff --git a/cachepc/events.c b/cachepc/events.c @@ -0,0 +1,131 @@ +#include "events.h" +#include "sevstep.h" +#include "cachepc.h" +#include "uapi.h" + +#include <linux/kvm.h> +#include <linux/timekeeping.h> +#include <linux/uaccess.h> +#include <linux/types.h> +#include <linux/vmalloc.h> +#include <linux/printk.h> +#include <linux/ratelimit.h> + +#define ARRLEN(x) (sizeof(x)/sizeof((x)[0])) + +void +cachepc_events_reset(void) +{ + write_lock(&cachepc_event_lock); + cachepc_events_init = true; + cachepc_last_event_sent = 1; + cachepc_last_event_acked = 1; + cachepc_event_avail = false; + write_unlock(&cachepc_event_lock); +} + +int +cachepc_send_tracking_event(uint64_t inst_fault_gfn, uint32_t inst_fault_err, + uint64_t data_fault_gfn, uint32_t data_fault_err) +{ + struct cpc_track_event event; + ktime_t deadline; + + read_lock(&cachepc_event_lock); + if (!cachepc_events_init) { + pr_warn("CachePC: events ctx not initialized!\n"); + read_unlock(&cachepc_event_lock); + return 1; + } + read_unlock(&cachepc_event_lock); + + write_lock(&cachepc_event_lock); + if (cachepc_last_event_sent != cachepc_last_event_acked) { + pr_warn("CachePC: event IDs out of sync\n"); + write_unlock(&cachepc_event_lock); + return 1; + } else { + cachepc_last_event_sent++; + } + event.id = cachepc_last_event_sent; + event.inst_fault_gfn = inst_fault_gfn; + event.inst_fault_err = inst_fault_err; + event.data_fault_avail = (data_fault_err != 0); + event.data_fault_gfn = data_fault_gfn; + event.data_fault_err = data_fault_err; + event.timestamp_ns = ktime_get_real_ns(); + event.retinst = cachepc_retinst; + + cachepc_event_avail = true; + cachepc_event = event; + write_unlock(&cachepc_event_lock); + + /* wait for ack with timeout */ + deadline = ktime_get_ns() + 2000000000ULL; /* 2s in ns */ + while (!cachepc_event_is_done(cachepc_event.id)) { + if (ktime_get_ns() > deadline) { + pr_warn("CachePC: Timeout waiting for ack of event %llu\n", + cachepc_event.id); + return 3; + } + } + + return 0; +} + +bool +cachepc_event_is_done(uint64_t id) +{ + bool done; + + read_lock(&cachepc_event_lock); + done = cachepc_last_event_acked >= id; + read_unlock(&cachepc_event_lock); + + return done; +} + +int +cachepc_handle_poll_event_ioctl(struct cpc_track_event __user *event) +{ + int err; + + read_lock(&cachepc_event_lock); + if (!cachepc_event_avail) { + read_unlock(&cachepc_event_lock); + return -EAGAIN; + } + read_unlock(&cachepc_event_lock); + + write_lock(&cachepc_event_lock); + if (cachepc_event_avail) { + err = copy_to_user(event, &cachepc_event, + sizeof(struct cpc_track_event)); + cachepc_event_avail = false; + } else { + err = -EAGAIN; + } + write_unlock(&cachepc_event_lock); + + return err; +} + +int +cachepc_handle_ack_event_ioctl(uint64_t eventid) +{ + int err; + + write_lock(&cachepc_event_lock); + if (eventid == cachepc_last_event_sent) { + err = 0; + cachepc_last_event_acked = cachepc_last_event_sent; + } else { + err = 1; + pr_warn("CachePC: Acked event does not match sent: %llu %llu\n", + cachepc_last_event_sent, eventid); + } + write_unlock(&cachepc_event_lock); + + return err; +} + diff --git a/cachepc/events.h b/cachepc/events.h @@ -0,0 +1,25 @@ +#pragma once + +#include "uapi.h" + +#include <linux/kvm.h> +#include <linux/kvm_host.h> +#include <linux/types.h> + +extern uint64_t cachepc_last_event_sent; +extern uint64_t cachepc_last_event_acked; +extern rwlock_t cachepc_event_lock; + +extern struct cpc_track_event cachepc_event; +extern bool cachepc_event_avail; + +extern bool cachepc_events_init; + +void cachepc_events_reset(void); + +int cachepc_send_tracking_event(uint64_t inst_fault_gfn, uint32_t inst_fault_err, + uint64_t data_fault_gfn, uint32_t data_fault_err); +bool cachepc_event_is_done(uint64_t id); + +int cachepc_handle_poll_event_ioctl(struct cpc_track_event *userpace_mem); +int cachepc_handle_ack_event_ioctl(uint64_t eventid); diff --git a/cachepc/kvm.c b/cachepc/kvm.c @@ -1,5 +1,5 @@ #include "kvm.h" -#include "uspt.h" +#include "events.h" #include "cachepc.h" #include "sevstep.h" #include "uapi.h" @@ -59,20 +59,20 @@ uint64_t cachepc_regs_vm[16]; EXPORT_SYMBOL(cachepc_regs_tmp); EXPORT_SYMBOL(cachepc_regs_vm); -uint64_t last_sent_eventid; -uint64_t last_acked_eventid; -DEFINE_RWLOCK(event_lock); -EXPORT_SYMBOL(last_sent_eventid); -EXPORT_SYMBOL(last_acked_eventid); -EXPORT_SYMBOL(event_lock); +uint64_t cachepc_last_event_sent; +uint64_t cachepc_last_event_acked; +DEFINE_RWLOCK(cachepc_event_lock); +EXPORT_SYMBOL(cachepc_last_event_sent); +EXPORT_SYMBOL(cachepc_last_event_acked); +EXPORT_SYMBOL(cachepc_event_lock); -struct cpc_track_event sent_event; -bool have_event; -EXPORT_SYMBOL(sent_event); -EXPORT_SYMBOL(have_event); +struct cpc_track_event cachepc_event; +bool cachepc_event_avail; +EXPORT_SYMBOL(cachepc_event); +EXPORT_SYMBOL(cachepc_event_avail); -bool uspt_init; -EXPORT_SYMBOL(uspt_init); +bool cachepc_events_init; +EXPORT_SYMBOL(cachepc_events_init); static void cachepc_kvm_prime_probe_test(void *p); static void cachepc_kvm_stream_hwpf_test(void *p); @@ -588,7 +588,7 @@ cachepc_kvm_uspt_reset_ioctl(void __user *arg_user) { struct kvm_vcpu *vcpu; - sevstep_uspt_clear(); + cachepc_events_reset(); vcpu = xa_load(&main_vm->vcpu_array, 0); sevstep_untrack_all(vcpu, KVM_PAGE_TRACK_EXEC); sevstep_untrack_all(vcpu, KVM_PAGE_TRACK_ACCESS); @@ -600,10 +600,10 @@ cachepc_kvm_uspt_reset_ioctl(void __user *arg_user) int cachepc_kvm_poll_event_ioctl(void __user *arg_user) { - if (!sevstep_uspt_is_initialized()) + if (!cachepc_events_init) return -EINVAL; - return sevstep_uspt_handle_poll_event(arg_user); + return cachepc_handle_poll_event_ioctl(arg_user); } int @@ -613,13 +613,13 @@ cachepc_kvm_uscpt_ack_event_ioctl(void __user *arg_user) if (!arg_user) return -EINVAL; - if (!sevstep_uspt_is_initialized()) + if (!cachepc_events_init) return -EINVAL; if (copy_from_user(&eventid, arg_user, sizeof(eventid))) return -EINVAL; - return sevstep_uspt_handle_ack_event_ioctl(eventid); + return cachepc_handle_ack_event_ioctl(eventid); } long diff --git a/cachepc/mmu.c b/cachepc/mmu.c @@ -1,13 +1,11 @@ #include "../cachepc/sevstep.h" #include "../cachepc/cachepc.h" -#include "../cachepc/uspt.h" +#include "../cachepc/events.h" static void sevstep_uspt_page_fault_handle(struct kvm_vcpu *vcpu, struct kvm_page_fault *fault) { - int err; - if (!kvm_slot_page_track_is_active(vcpu->kvm, fault->slot, fault->gfn, KVM_PAGE_TRACK_ACCESS)) return; @@ -52,12 +50,10 @@ sevstep_uspt_page_fault_handle(struct kvm_vcpu *vcpu, } cachepc_inst_fault_gfn = fault->gfn; cachepc_inst_fault_err = fault->error_code; - if (sevstep_uspt_send_and_block(fault->gfn, fault->error_code, 0, 0)) - pr_warn("Sevstep: uspt_send_and_block failed (%d)\n", err); + cachepc_send_tracking_event(fault->gfn, fault->error_code, 0, 0); } else if (cachepc_track_mode == CPC_TRACK_ACCESS) { sevstep_track_single(vcpu, fault->gfn, KVM_PAGE_TRACK_ACCESS); - if (sevstep_uspt_send_and_block(fault->gfn, fault->error_code, 0, 0)) - pr_warn("Sevstep: uspt_send_and_block failed (%d)\n", err); + cachepc_send_tracking_event(fault->gfn, fault->error_code, 0, 0); } } diff --git a/cachepc/uspt.c b/cachepc/uspt.c @@ -1,138 +0,0 @@ -#include "uspt.h" -#include "sevstep.h" -#include "cachepc.h" -#include "uapi.h" - -#include <linux/kvm.h> -#include <linux/timekeeping.h> -#include <linux/uaccess.h> -#include <linux/types.h> -#include <linux/vmalloc.h> -#include <linux/printk.h> -#include <linux/ratelimit.h> - -#define ARRLEN(x) (sizeof(x)/sizeof((x)[0])) - -void -sevstep_uspt_clear(void) -{ - write_lock(&event_lock); - uspt_init = true; - last_sent_eventid = 1; - last_acked_eventid = 1; - have_event = false; - write_unlock(&event_lock); -} - -bool -sevstep_uspt_is_initialized() -{ - return uspt_init; -} - -int -sevstep_uspt_send_and_block(uint64_t inst_fault_gfn, uint32_t inst_fault_err, - uint64_t data_fault_gfn, uint32_t data_fault_err) -{ - struct cpc_track_event event; - ktime_t deadline; - - read_lock(&event_lock); - if (!sevstep_uspt_is_initialized()) { - pr_warn("Sevstep: ctx not initialized!\n"); - read_unlock(&event_lock); - return 1; - } - read_unlock(&event_lock); - - write_lock(&event_lock); - if (last_sent_eventid != last_acked_eventid) { - pr_warn("Sevstep: event id_s out of sync, aborting\n"); - write_unlock(&event_lock); - return 1; - } else { - last_sent_eventid++; - } - event.id = last_sent_eventid; - event.inst_fault_gfn = inst_fault_gfn; - event.inst_fault_err = inst_fault_err; - event.data_fault_avail = (data_fault_err != 0); - event.data_fault_gfn = data_fault_gfn; - event.data_fault_err = data_fault_err; - event.timestamp_ns = ktime_get_real_ns(); - event.retinst = cachepc_retinst; - - have_event = true; - sent_event = event; - write_unlock(&event_lock); - - /* wait for ack with timeout */ - // pr_warn("Sevstep: uspt_send_and_block: Begin wait for event ack"); - deadline = ktime_get_ns() + 2000000000ULL; /* 1s in ns */ - while (!sevstep_uspt_is_event_done(sent_event.id)) { - if (ktime_get_ns() > deadline) { - pr_warn("Sevstep: Timeout waiting for ack of event %llu ", - sent_event.id); - return 3; - } - } - - return 0; -} - -int -sevstep_uspt_is_event_done(uint64_t id) -{ - bool done; - - read_lock(&event_lock); - done = last_acked_eventid >= id; - read_unlock(&event_lock); - - return done; -} - -int -sevstep_uspt_handle_poll_event(struct cpc_track_event __user *event) -{ - int err; - - read_lock(&event_lock); - if (!have_event) { - read_unlock(&event_lock); - return -EAGAIN; - } - read_unlock(&event_lock); - - write_lock(&event_lock); - if (have_event) { - err = copy_to_user(event, &sent_event, - sizeof(struct cpc_track_event)); - have_event = false; - } else { - err = -EAGAIN; - } - write_unlock(&event_lock); - - return err; -} - -int -sevstep_uspt_handle_ack_event_ioctl(uint64_t eventid) -{ - int err; - - write_lock(&event_lock); - if (eventid == last_sent_eventid) { - err = 0; - last_acked_eventid = last_sent_eventid; - } else { - err = 1; - pr_warn("Sevstep: Ackd event does not match sent: %llu %llu\n", - last_sent_eventid, eventid); - } - write_unlock(&event_lock); - - return err; -} - diff --git a/cachepc/uspt.h b/cachepc/uspt.h @@ -1,26 +0,0 @@ -#pragma once - -#include "uapi.h" - -#include <linux/kvm.h> -#include <linux/kvm_host.h> -#include <linux/types.h> - -extern uint64_t last_sent_eventid; -extern uint64_t last_acked_eventid; -extern rwlock_t event_lock; - -extern struct cpc_track_event sent_event; -extern bool have_event; - -extern bool uspt_init; - -bool sevstep_uspt_is_initialized(void); -void sevstep_uspt_clear(void); - -int sevstep_uspt_send_and_block(uint64_t inst_fault_gfn, uint32_t inst_fault_err, - uint64_t data_fault_gfn, uint32_t data_fault_err); -int sevstep_uspt_is_event_done(uint64_t id); - -int sevstep_uspt_handle_poll_event(struct cpc_track_event *userpace_mem); -int sevstep_uspt_handle_ack_event_ioctl(uint64_t eventid);