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 afe49b52f6b2ac52efe8e610e64ee07c7c2efef4
parent 10d3c8f52dc2c0f498e81946bef8292424fcd1ce
Author: Louis Burda <quent.burda@gmail.com>
Date:   Wed, 16 Nov 2022 16:03:04 +0100

APIC single stepping working

Diffstat:
Mcachepc/cachepc.h | 2+-
Mcachepc/mmu.c | 10+++++++---
Mnotes | 13+++++++------
Mtest/sevstep.c | 22+++++++++++-----------
4 files changed, 26 insertions(+), 21 deletions(-)

diff --git a/cachepc/cachepc.h b/cachepc/cachepc.h @@ -290,6 +290,6 @@ 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_128); + native_apic_mem_write(APIC_TDCR, APIC_TDR_DIV_2); native_apic_mem_write(APIC_TMICT, interval); } diff --git a/cachepc/mmu.c b/cachepc/mmu.c @@ -35,20 +35,22 @@ cachepc_page_fault_handle(struct kvm_vcpu *vcpu, cachepc_data_fault_avail = false; cachepc_single_step = true; - cachepc_apic_timer = 10; + cachepc_apic_timer = 390; cachepc_track_state_next = CPC_TRACK_AWAIT_DATA_FAULT; } else if (cachepc_track_state == CPC_TRACK_AWAIT_DATA_FAULT) { /* second fault from data access */ pr_warn("CachePC: Got data fault gfn:%llu err:%u\n", fault->gfn, fault->error_code); + if (!cachepc_inst_fault_avail) + pr_err("CachePC: Waiting for data fault without inst\n"); cachepc_data_fault_gfn = fault->gfn; cachepc_data_fault_err = fault->error_code; cachepc_data_fault_avail = true; cachepc_single_step = true; - cachepc_apic_timer = 10; + cachepc_apic_timer = 390; cachepc_track_state_next = CPC_TRACK_AWAIT_STEP_INTR; } else if (cachepc_track_state == CPC_TRACK_AWAIT_STEP_INTR) { @@ -80,7 +82,9 @@ cachepc_page_fault_handle(struct kvm_vcpu *vcpu, cachepc_track_state_next = CPC_TRACK_AWAIT_INST_FAULT; } } else if (cachepc_track_mode == CPC_TRACK_EXEC_PAGES) { - /* TODO: skip if not instruction decode fault */ + /* untrack pages that are not code (could be a problem for WX */ + if (!inst_fetch) return; + /* TODO: calculate retired instructions (save and subtract global counter) */ if (cachepc_inst_fault_avail) { /* track previous faulted page, current stays untracked */ diff --git a/notes b/notes @@ -6,9 +6,6 @@ Questions: - test/sevstep: why does it seem to work with event race but not without? => it doesnt (anymore) -Tried: -- try invalidating tlbs (no effect, prob was already) - Next steps: - enable single-steping with multiple page faults (only one ends up in last_fault_gfn and others stay untracked) @@ -17,11 +14,15 @@ Next steps: such that we see the relevant page faults and the gfn increment - test/sevstep: implement counter.. read program memory to see how many instructions were executed on apic timer -- add warning for high values in baseline Meeting questions: - Why is decrypted rip sometimes off-by-one? => -- VM gets interrupted independent of our APIC (by the scheduler) is this a problem? +- VM gets interrupted independent of our APIC (by the scheduler?) was this a problem for you? + => +- Is supplementing apic with waiting for next fault viable? (seems to work well actually) + => +- How does single stepping work with fixed cpu frequency when we always step less than one instruction? => -- + + diff --git a/test/sevstep.c b/test/sevstep.c @@ -480,6 +480,7 @@ runonce(struct kvm *kvm) int monitor(struct kvm *kvm, bool baseline) { + static uint64_t rip_prev = 1; struct cpc_track_event event; cpc_msrmt_t counts[64]; uint64_t rip; @@ -488,20 +489,17 @@ monitor(struct kvm *kvm, bool baseline) /* Get page fault info */ ret = ioctl(kvm_dev, KVM_CPC_POLL_EVENT, &event); if (!ret) { - if (!baseline) { - rip = sev_dbg_rip(kvm->vmfd); - printf("Event: inst:%llu data:%llu retired:%llu rip:%lu\n", - event.inst_fault_gfn, event.data_fault_gfn, - event.retinst, rip); - } - faultcnt++; - ret = ioctl(kvm_dev, KVM_CPC_READ_COUNTS, counts); if (ret == -1) err(1, "ioctl READ_COUNTS"); - if (!baseline) { + rip = sev_dbg_rip(kvm->vmfd); + if (!baseline && rip != rip_prev) { + printf("Event: inst:%llu data:%llu retired:%llu rip:%lu\n", + event.inst_fault_gfn, event.data_fault_gfn, + event.retinst, rip); print_counts(counts); printf("\n"); + rip_prev = rip; } for (i = 0; i < 64; i++) { @@ -513,6 +511,8 @@ monitor(struct kvm *kvm, bool baseline) ret = ioctl(kvm_dev, KVM_CPC_ACK_EVENT, &event.id); if (ret == -1) err(1, "ioctl ACK_EVENT"); + + faultcnt++; } else if (errno != EAGAIN) { perror("ioctl POLL_EVENT"); return 1; @@ -589,7 +589,7 @@ main(int argc, const char **argv) if (ret == -1) err(1, "ioctl MEASURE_BASELINE"); faultcnt = 0; - while (faultcnt < 30) { + while (faultcnt < 50) { if (monitor(&kvm_with_access, true)) break; } @@ -626,7 +626,7 @@ main(int argc, const char **argv) if (ret == -1) err(1, "ioctl ACK_EVENT"); faultcnt = 0; - while (faultcnt < 30) { + while (faultcnt < 50) { if (monitor(&kvm_with_access, false)) break; }