Kernel KVM virtualization development
 help / color / mirror / Atom feed
* [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