diff mbox series

[v2,10/10] KVM: x86: WARN on non-zero CRs at RESET to detect improper initalization

Message ID 20210921000303.400537-11-seanjc@google.com (mailing list archive)
State New, archived
Headers show
Series KVM: x86: Clean up RESET "emulation" | expand

Commit Message

Sean Christopherson Sept. 21, 2021, 12:03 a.m. UTC
WARN if CR0, CR3, or CR4 are non-zero at RESET, which given the current
KVM implementation, really means WARN if they're not zeroed at vCPU
creation.  VMX in particular has several ->set_*() flows that read other
registers to handle side effects, and because those flows are common to
RESET and INIT, KVM subtly relies on emulated/virtualized registers to be
zeroed at vCPU creation in order to do the right thing at RESET.

Use CRs as a sentinel because they are most likely to be written as side
effects, and because KVM specifically needs CR0.PG and CR0.PE to be '0'
to correctly reflect the state of the vCPU's MMU.  CRs are also loaded
and stored from/to the VMCS, and so adds some level of coverage to verify
that KVM doesn't conflate zero-allocating the VMCS with properly
initializing the VMCS with VMWRITEs.

Note, '0' is somewhat arbitrary, vCPU creation can technically stuff any
value for a register so long as it's coherent with respect to the current
vCPU state.  In practice, '0' works for all registers and is convenient.

Suggested-by: Vitaly Kuznetsov <vkuznets@redhat.com>
Signed-off-by: Sean Christopherson <seanjc@google.com>
---
 arch/x86/kvm/x86.c | 10 ++++++++++
 1 file changed, 10 insertions(+)

Comments

Vitaly Kuznetsov Sept. 21, 2021, 1:59 p.m. UTC | #1
Sean Christopherson <seanjc@google.com> writes:

> WARN if CR0, CR3, or CR4 are non-zero at RESET, which given the current
> KVM implementation, really means WARN if they're not zeroed at vCPU
> creation.  VMX in particular has several ->set_*() flows that read other
> registers to handle side effects, and because those flows are common to
> RESET and INIT, KVM subtly relies on emulated/virtualized registers to be
> zeroed at vCPU creation in order to do the right thing at RESET.
>
> Use CRs as a sentinel because they are most likely to be written as side
> effects, and because KVM specifically needs CR0.PG and CR0.PE to be '0'
> to correctly reflect the state of the vCPU's MMU.  CRs are also loaded
> and stored from/to the VMCS, and so adds some level of coverage to verify
> that KVM doesn't conflate zero-allocating the VMCS with properly
> initializing the VMCS with VMWRITEs.
>
> Note, '0' is somewhat arbitrary, vCPU creation can technically stuff any
> value for a register so long as it's coherent with respect to the current
> vCPU state.  In practice, '0' works for all registers and is convenient.
>
> Suggested-by: Vitaly Kuznetsov <vkuznets@redhat.com>
> Signed-off-by: Sean Christopherson <seanjc@google.com>
> ---
>  arch/x86/kvm/x86.c | 10 ++++++++++
>  1 file changed, 10 insertions(+)
>
> diff --git a/arch/x86/kvm/x86.c b/arch/x86/kvm/x86.c
> index ec61b90d9b73..4e25baac3977 100644
> --- a/arch/x86/kvm/x86.c
> +++ b/arch/x86/kvm/x86.c
> @@ -10800,6 +10800,16 @@ void kvm_vcpu_reset(struct kvm_vcpu *vcpu, bool init_event)
>  	unsigned long new_cr0;
>  	u32 eax, dummy;
>  
> +	/*
> +	 * Several of the "set" flows, e.g. ->set_cr0(), read other registers
> +	 * to handle side effects.  RESET emulation hits those flows and relies
> +	 * on emulated/virtualized registers, including those that are loaded
> +	 * into hardware, to be zeroed at vCPU creation.  Use CRs as a sentinel
> +	 * to detect improper or missing initialization.
> +	 */
> +	WARN_ON_ONCE(!init_event &&
> +		     (old_cr0 || kvm_read_cr3(vcpu) || kvm_read_cr4(vcpu)));
> +
>  	kvm_lapic_reset(vcpu, init_event);
>  
>  	vcpu->arch.hflags = 0;

Reviewed-by: Vitaly Kuznetsov <vkuznets@redhat.com>
diff mbox series

Patch

diff --git a/arch/x86/kvm/x86.c b/arch/x86/kvm/x86.c
index ec61b90d9b73..4e25baac3977 100644
--- a/arch/x86/kvm/x86.c
+++ b/arch/x86/kvm/x86.c
@@ -10800,6 +10800,16 @@  void kvm_vcpu_reset(struct kvm_vcpu *vcpu, bool init_event)
 	unsigned long new_cr0;
 	u32 eax, dummy;
 
+	/*
+	 * Several of the "set" flows, e.g. ->set_cr0(), read other registers
+	 * to handle side effects.  RESET emulation hits those flows and relies
+	 * on emulated/virtualized registers, including those that are loaded
+	 * into hardware, to be zeroed at vCPU creation.  Use CRs as a sentinel
+	 * to detect improper or missing initialization.
+	 */
+	WARN_ON_ONCE(!init_event &&
+		     (old_cr0 || kvm_read_cr3(vcpu) || kvm_read_cr4(vcpu)));
+
 	kvm_lapic_reset(vcpu, init_event);
 
 	vcpu->arch.hflags = 0;