* [PATCH 1/2] KVM: nSVM: Restore L1's debug registers on nested VM-Exit
@ 2026-08-27 5:35 Hao Zhang
2026-08-27 5:38 ` [PATCH 2/2] selftests: kvm: Add regression test for nSVM debug register state Hao Zhang
` (2 more replies)
0 siblings, 3 replies; 5+ messages in thread
From: Hao Zhang @ 2026-08-27 5:35 UTC (permalink / raw)
To: Sean Christopherson; +Cc: Paolo Bonzini, kvm
From: Hao Zhang <zhanghao1@kylinos.cn>
Restore L1's DR6 and DR7 when emulating a nested VM-Exit from L2 to L1.
KVM loads L2's debug register state from vmcb12 on nested VMRUN, but
currently restores DR7 to its reset value and leaves DR6 untouched when
switching back to vmcb01. As a result, L1 can observe stale L2 DR6 state
and lose its DR7 state after an L2 VM-Exit.
Restore both registers from vmcb01, matching the rest of the L1 processor
state restored on nested VM-Exit.
Fixes: 4995a3685f1b ("KVM: SVM: Use a separate vmcb for the nested L2 guest")
Signed-off-by: Hao Zhang <zhanghao1@kylinos.cn>
---
arch/x86/kvm/svm/nested.c | 3 ++-
1 file changed, 2 insertions(+), 1 deletion(-)
diff --git a/arch/x86/kvm/svm/nested.c b/arch/x86/kvm/svm/nested.c
index 73f37b050d0a..e7975ac7bfca 100644
--- a/arch/x86/kvm/svm/nested.c
+++ b/arch/x86/kvm/svm/nested.c
@@ -1427,7 +1427,8 @@ void nested_svm_vmexit(struct vcpu_svm *svm)
kvm_rsp_write(vcpu, vmcb01->save.rsp);
kvm_rip_write(vcpu, vmcb01->save.rip);
- svm->vcpu.arch.dr7 = DR7_FIXED_1;
+ svm->vcpu.arch.dr6 = vmcb01->save.dr6;
+ svm->vcpu.arch.dr7 = vmcb01->save.dr7;
kvm_update_dr7(&svm->vcpu);
nested_svm_transition_tlb_flush(vcpu);
base-commit: 45c13f3f9e3bb15fd89ff2864c6f627a3b4b4229
--
2.15.0
^ permalink raw reply related [flat|nested] 5+ messages in thread* [PATCH 2/2] selftests: kvm: Add regression test for nSVM debug register state 2026-08-27 5:35 [PATCH 1/2] KVM: nSVM: Restore L1's debug registers on nested VM-Exit Hao Zhang @ 2026-08-27 5:38 ` Hao Zhang 2026-08-27 5:52 ` [PATCH 1/2] KVM: nSVM: Restore L1's debug registers on nested VM-Exit sashiko-bot 2026-08-27 13:43 ` Sean Christopherson 2 siblings, 0 replies; 5+ messages in thread From: Hao Zhang @ 2026-08-27 5:38 UTC (permalink / raw) To: Sean Christopherson; +Cc: Paolo Bonzini, kvm From: Hao Zhang <zhanghao1@kylinos.cn> Add a regression test for debug register state restoration when KVM emulates a nested SVM VM-Exit from L2 to L1. Give L1 and L2 distinct DR6 and DR7 values, run L2 until a VMMCALL VM-Exit, and then verify that KVM saved L2's debug register state to vmcb12 while restoring L1's architectural DR6 and DR7 values. Without the fix, L1 observes stale L2 DR6 state and loses its DR7 state after the nested VM-Exit. Signed-off-by: Hao Zhang <zhanghao1@kylinos.cn> --- tools/testing/selftests/kvm/Makefile.kvm | 1 + .../selftests/kvm/x86/svm_nested_debug_regs_test.c | 104 +++++++++++++++++++++ 2 files changed, 105 insertions(+) create mode 100644 tools/testing/selftests/kvm/x86/svm_nested_debug_regs_test.c diff --git a/tools/testing/selftests/kvm/Makefile.kvm b/tools/testing/selftests/kvm/Makefile.kvm index 1bde5ab306cf..65ad842eecd4 100644 --- a/tools/testing/selftests/kvm/Makefile.kvm +++ b/tools/testing/selftests/kvm/Makefile.kvm @@ -122,6 +122,7 @@ TEST_GEN_PROGS_x86 += x86/svm_nested_clear_efer_svme TEST_GEN_PROGS_x86 += x86/svm_nested_shutdown_test TEST_GEN_PROGS_x86 += x86/svm_nested_soft_inject_test TEST_GEN_PROGS_x86 += x86/svm_nested_vmcb12_gpa +TEST_GEN_PROGS_x86 += x86/svm_nested_debug_regs_test TEST_GEN_PROGS_x86 += x86/svm_nested_pat_test TEST_GEN_PROGS_x86 += x86/svm_lbr_nested_state TEST_GEN_PROGS_x86 += x86/svm_pmu_host_guest_test diff --git a/tools/testing/selftests/kvm/x86/svm_nested_debug_regs_test.c b/tools/testing/selftests/kvm/x86/svm_nested_debug_regs_test.c new file mode 100644 index 000000000000..6ef29cf0a819 --- /dev/null +++ b/tools/testing/selftests/kvm/x86/svm_nested_debug_regs_test.c @@ -0,0 +1,104 @@ +// SPDX-License-Identifier: GPL-2.0-only +/* + * Nested SVM debug register state test. + */ +#include "test_util.h" +#include "kvm_util.h" +#include "processor.h" +#include "svm_util.h" + +#define DR6_ACTIVE_LOW 0xffff0ff0 +#define DR6_B0 BIT(0) +#define DR6_BS BIT(14) +#define DR7_FIXED_1 0x400 +#define DR7_GE BIT(9) + +#define L1_DR6 (DR6_ACTIVE_LOW | DR6_BS) +#define L2_DR6 (DR6_ACTIVE_LOW | DR6_B0) +#define L1_DR7 (DR7_FIXED_1 | DR7_GE) +#define L2_DR7 (DR7_FIXED_1) + +static inline u64 get_dr6(void) +{ + u64 val; + + asm volatile("mov %%dr6, %0" : "=r"(val) : : "memory"); + return val; +} + +static inline u64 get_dr7(void) +{ + u64 val; + + asm volatile("mov %%dr7, %0" : "=r"(val) : : "memory"); + return val; +} + +static inline void set_dr6(u64 val) +{ + asm volatile("mov %0, %%dr6" : : "r"(val) : "memory"); +} + +static inline void set_dr7(u64 val) +{ + asm volatile("mov %0, %%dr7" : : "r"(val) : "memory"); +} + +static void l2_guest_code(void) +{ + GUEST_ASSERT_EQ(get_dr6(), L2_DR6); + GUEST_ASSERT_EQ(get_dr7(), L2_DR7); + vmmcall(); +} + +static void l1_guest_code(struct svm_test_data *svm) +{ + struct vmcb *vmcb = svm->vmcb; + + set_dr6(L1_DR6); + set_dr7(L1_DR7); + + generic_svm_setup(svm, l2_guest_code); + vmcb->save.dr6 = L2_DR6; + vmcb->save.dr7 = L2_DR7; + + run_guest(vmcb, svm->vmcb_gpa); + GUEST_ASSERT_EQ(vmcb->control.exit_code, SVM_EXIT_VMMCALL); + + GUEST_ASSERT_EQ(vmcb->save.dr6, L2_DR6); + GUEST_ASSERT_EQ(vmcb->save.dr7, L2_DR7); + GUEST_ASSERT_EQ(get_dr6(), L1_DR6); + GUEST_ASSERT_EQ(get_dr7(), L1_DR7); + + GUEST_DONE(); +} + +int main(int argc, char *argv[]) +{ + struct kvm_vcpu *vcpu; + struct kvm_vm *vm; + struct ucall uc; + gva_t svm_gva; + + TEST_REQUIRE(kvm_cpu_has(X86_FEATURE_SVM)); + + vm = vm_create_with_one_vcpu(&vcpu, l1_guest_code); + vcpu_alloc_svm(vm, &svm_gva); + vcpu_args_set(vcpu, 1, svm_gva); + + vcpu_run(vcpu); + TEST_ASSERT_KVM_EXIT_REASON(vcpu, KVM_EXIT_IO); + + switch (get_ucall(vcpu, &uc)) { + case UCALL_ABORT: + REPORT_GUEST_ASSERT(uc); + break; + case UCALL_DONE: + break; + default: + TEST_FAIL("Unknown ucall %lu", uc.cmd); + } + + kvm_vm_free(vm); + return 0; +} -- 2.15.0 ^ permalink raw reply related [flat|nested] 5+ messages in thread
* Re: [PATCH 1/2] KVM: nSVM: Restore L1's debug registers on nested VM-Exit 2026-08-27 5:35 [PATCH 1/2] KVM: nSVM: Restore L1's debug registers on nested VM-Exit Hao Zhang 2026-08-27 5:38 ` [PATCH 2/2] selftests: kvm: Add regression test for nSVM debug register state Hao Zhang @ 2026-08-27 5:52 ` sashiko-bot 2026-08-27 13:43 ` Sean Christopherson 2 siblings, 0 replies; 5+ messages in thread From: sashiko-bot @ 2026-08-27 5:52 UTC (permalink / raw) To: Hao Zhang; +Cc: kvm Thank you for your contribution! Sashiko AI review found 1 potential issue(s) to consider: - [High] Restoring L1's architectural DR6 and DR7 from the hardware `vmcb01->save` state on nested VM-Exit corrupts L1's debug state and leaks host debugging information. -- commit cba4fc9e5c8bde0943c94c7835fb0e3712e3df8e Author: Hao Zhang <zhanghao1@kylinos.cn> KVM: nSVM: Restore L1's debug registers on nested VM-Exit This commit restores L1's DR6 and DR7 when emulating a nested VM-Exit from L2 to L1. It copies the debug states from vmcb01 to prevent L1 from observing stale L2 state or losing its DR7 state. > diff --git a/arch/x86/kvm/svm/nested.c b/arch/x86/kvm/svm/nested.c > index 73f37b050d0a0..e7975ac7bfca2 100644 > --- a/arch/x86/kvm/svm/nested.c > +++ b/arch/x86/kvm/svm/nested.c > @@ -1427,7 +1427,8 @@ void nested_svm_vmexit(struct vcpu_svm *svm) > kvm_rsp_write(vcpu, vmcb01->save.rsp); > kvm_rip_write(vcpu, vmcb01->save.rip); > > - svm->vcpu.arch.dr7 = DR7_FIXED_1; > + svm->vcpu.arch.dr6 = vmcb01->save.dr6; > + svm->vcpu.arch.dr7 = vmcb01->save.dr7; [Severity: High] Does copying the hardware state from vmcb01->save back into the software structures here in nested_svm_vmexit() corrupt L1's architectural debug state? If KVM intercepts debug registers (which is the default) or if the host is actively debugging the guest, vmcb01->save contains host-controlled values like DR6_ACTIVE_LOW or vcpu->arch.guest_debug_dr7. Overwriting L1's software-tracked DR6 destroys pending L1 debug exceptions, while pulling the host-polluted DR7 permanently leaks host-level breakpoints to the unprivileged L1 guest. > kvm_update_dr7(&svm->vcpu); > > nested_svm_transition_tlb_flush(vcpu); -- Sashiko AI review · https://sashiko.dev/#/patchset/ao_Ms5alWwKa30U8@192.168.1.215?part=1 ^ permalink raw reply [flat|nested] 5+ messages in thread
* Re: [PATCH 1/2] KVM: nSVM: Restore L1's debug registers on nested VM-Exit 2026-08-27 5:35 [PATCH 1/2] KVM: nSVM: Restore L1's debug registers on nested VM-Exit Hao Zhang 2026-08-27 5:38 ` [PATCH 2/2] selftests: kvm: Add regression test for nSVM debug register state Hao Zhang 2026-08-27 5:52 ` [PATCH 1/2] KVM: nSVM: Restore L1's debug registers on nested VM-Exit sashiko-bot @ 2026-08-27 13:43 ` Sean Christopherson 2026-08-28 1:11 ` Hao Zhang 2 siblings, 1 reply; 5+ messages in thread From: Sean Christopherson @ 2026-08-27 13:43 UTC (permalink / raw) To: Hao Zhang; +Cc: Paolo Bonzini, kvm On Thu, Aug 27, 2026, Hao Zhang wrote: > From: Hao Zhang <zhanghao1@kylinos.cn> > > Restore L1's DR6 and DR7 when emulating a nested VM-Exit from L2 to L1. > KVM loads L2's debug register state from vmcb12 on nested VMRUN, but > currently restores DR7 to its reset value and leaves DR6 untouched when > switching back to vmcb01. As a result, L1 can observe stale L2 DR6 state > and lose its DR7 state after an L2 VM-Exit. Well, yeah, because the APM says so. The APM very clearly says: Disables all breakpoints in the host DR7 register. In typical APM fashion, it says nothing about DR6, so it's reasonable to assume it's preserved as-is across #VMEXIT, i.e. is L1's responsibility to ignore since there's no #DB. > Restore both registers from vmcb01, matching the rest of the L1 processor > state restored on nested VM-Exit. What happens to other state is irrelevant, what matters is what the architecture says happens. ^ permalink raw reply [flat|nested] 5+ messages in thread
* Re: [PATCH 1/2] KVM: nSVM: Restore L1's debug registers on nested VM-Exit 2026-08-27 13:43 ` Sean Christopherson @ 2026-08-28 1:11 ` Hao Zhang 0 siblings, 0 replies; 5+ messages in thread From: Hao Zhang @ 2026-08-28 1:11 UTC (permalink / raw) To: Sean Christopherson; +Cc: Hao Zhang, Paolo Bonzini, kvm On Thu, Aug 27, 2026, Sean Christopherson wrote: > On Thu, Aug 27, 2026, Hao Zhang wrote: > > From: Hao Zhang <zhanghao1@kylinos.cn> > > > > Restore L1's DR6 and DR7 when emulating a nested VM-Exit from L2 to L1. > > KVM loads L2's debug register state from vmcb12 on nested VMRUN, but > > currently restores DR7 to its reset value and leaves DR6 untouched when > > switching back to vmcb01. As a result, L1 can observe stale L2 DR6 state > > and lose its DR7 state after an L2 VM-Exit. > > Well, yeah, because the APM says so. The APM very clearly says: > > Disables all breakpoints in the host DR7 register. > > In typical APM fashion, it says nothing about DR6, so it's reasonable to assume > it's preserved as-is across #VMEXIT, i.e. is L1's responsibility to ignore since > there's no #DB. > Thanks. I checked the APM which requires #VMEXIT disables all breakpoints in the host DR7 register, which means KVM's DR7_FIXED_1 restore on nested VM-Exit is intentional architectural behavior for L1-as-host, not a lost-state bug. I'll drop this patch as-is. Thanks, Hao > > Restore both registers from vmcb01, matching the rest of the L1 processor > > state restored on nested VM-Exit. > > What happens to other state is irrelevant, what matters is what the architecture > says happens. ^ permalink raw reply [flat|nested] 5+ messages in thread
end of thread, other threads:[~2026-08-28 1:11 UTC | newest] Thread overview: 5+ messages (download: mbox.gz follow: Atom feed -- links below jump to the message on this page -- 2026-08-27 5:35 [PATCH 1/2] KVM: nSVM: Restore L1's debug registers on nested VM-Exit Hao Zhang 2026-08-27 5:38 ` [PATCH 2/2] selftests: kvm: Add regression test for nSVM debug register state Hao Zhang 2026-08-27 5:52 ` [PATCH 1/2] KVM: nSVM: Restore L1's debug registers on nested VM-Exit sashiko-bot 2026-08-27 13:43 ` Sean Christopherson 2026-08-28 1:11 ` Hao Zhang
This is a public inbox, see mirroring instructions for how to clone and mirror all data and code used for this inbox