All of lore.kernel.org
 help / color / mirror / Atom feed
* [PATCH] KVM: nVMX: fix guest CR4 loading when emulating L2 to L1 exit
@ 2017-10-10  7:01 Haozhong Zhang
  2017-10-10  8:19 ` Paolo Bonzini
  0 siblings, 1 reply; 3+ messages in thread
From: Haozhong Zhang @ 2017-10-10  7:01 UTC (permalink / raw)
  To: kvm; +Cc: Paolo Bonzini, rkrcmar, Haozhong Zhang

When KVM emulates an exit from L2 to L1, it loads L1 CR4 into the
guest CR4. Before this CR4 loading, the guest CR4 refers to L2
CR4. Because these two CR4's are in different levels of guest, we
should vmx_set_cr4() rather than kvm_set_cr4() here. The latter, which
is used to handle guest writes to its CR4, checks the guest change to
CR4 and may fail if the change is invalid.

The failure may cause trouble. Consider we start
  a L1 guest with non-zero L1 PCID in use,
     (i.e. L1 CR4.PCIDE == 1 && L1 CR3.PCID != 0)
and
  a L2 guest with L2 PCID disabled,
     (i.e. L2 CR4.PCIDE == 0)
and following events may happen:

1. If kvm_set_cr4() is used in load_vmcs12_host_state() to load L1 CR4
   into guest CR4 (in VMCS01) for L2 to L1 exit, it will fail because
   of PCID check. As a result, the guest CR4 recorded in L0 KVM (i.e.
   vcpu->arch.cr4) is left to the value of L2 CR4.

2. Later, if L1 attempts to change its CR4, e.g., clearing VMXE bit,
   kvm_set_cr4() in L0 KVM will think L1 also wants to enable PCID,
   because the wrong L2 CR4 is used by L0 KVM as L1 CR4. As L1
   CR3.PCID != 0, L0 KVM will inject GP to L1 guest.

Fixes: 4704d0befb072 ("KVM: nVMX: Exiting from L2 to L1")
Signed-off-by: Haozhong Zhang <haozhong.zhang@intel.com>
---
 arch/x86/kvm/vmx.c | 2 +-
 1 file changed, 1 insertion(+), 1 deletion(-)

diff --git a/arch/x86/kvm/vmx.c b/arch/x86/kvm/vmx.c
index b9d2140eb212..3fe639e0e1f6 100644
--- a/arch/x86/kvm/vmx.c
+++ b/arch/x86/kvm/vmx.c
@@ -11297,7 +11297,7 @@ static void load_vmcs12_host_state(struct kvm_vcpu *vcpu,
 
 	/* Same as above - no reason to call set_cr4_guest_host_mask().  */
 	vcpu->arch.cr4_guest_owned_bits = ~vmcs_readl(CR4_GUEST_HOST_MASK);
-	kvm_set_cr4(vcpu, vmcs12->host_cr4);
+	vmx_set_cr4(vcpu, vmcs12->host_cr4);
 
 	nested_ept_uninit_mmu_context(vcpu);
 
-- 
2.11.0

^ permalink raw reply related	[flat|nested] 3+ messages in thread

* Re: [PATCH] KVM: nVMX: fix guest CR4 loading when emulating L2 to L1 exit
  2017-10-10  7:01 [PATCH] KVM: nVMX: fix guest CR4 loading when emulating L2 to L1 exit Haozhong Zhang
@ 2017-10-10  8:19 ` Paolo Bonzini
  2017-10-10  9:12   ` Haozhong Zhang
  0 siblings, 1 reply; 3+ messages in thread
From: Paolo Bonzini @ 2017-10-10  8:19 UTC (permalink / raw)
  To: Haozhong Zhang, kvm; +Cc: rkrcmar

On 10/10/2017 09:01, Haozhong Zhang wrote:
> When KVM emulates an exit from L2 to L1, it loads L1 CR4 into the
> guest CR4. Before this CR4 loading, the guest CR4 refers to L2
> CR4. Because these two CR4's are in different levels of guest, we
> should vmx_set_cr4() rather than kvm_set_cr4() here. The latter, which
> is used to handle guest writes to its CR4, checks the guest change to
> CR4 and may fail if the change is invalid.
> 
> The failure may cause trouble. Consider we start
>   a L1 guest with non-zero L1 PCID in use,
>      (i.e. L1 CR4.PCIDE == 1 && L1 CR3.PCID != 0)
> and
>   a L2 guest with L2 PCID disabled,
>      (i.e. L2 CR4.PCIDE == 0)
> and following events may happen:
> 
> 1. If kvm_set_cr4() is used in load_vmcs12_host_state() to load L1 CR4
>    into guest CR4 (in VMCS01) for L2 to L1 exit, it will fail because
>    of PCID check. As a result, the guest CR4 recorded in L0 KVM (i.e.
>    vcpu->arch.cr4) is left to the value of L2 CR4.
> 
> 2. Later, if L1 attempts to change its CR4, e.g., clearing VMXE bit,
>    kvm_set_cr4() in L0 KVM will think L1 also wants to enable PCID,
>    because the wrong L2 CR4 is used by L0 KVM as L1 CR4. As L1
>    CR3.PCID != 0, L0 KVM will inject GP to L1 guest.
> 
> Fixes: 4704d0befb072 ("KVM: nVMX: Exiting from L2 to L1")
> Signed-off-by: Haozhong Zhang <haozhong.zhang@intel.com>

Makes sense, and none of the other effects of kvm_set_cr4 matter:

	// MMU is already reset on L2->L1 exit
        if (((cr4 ^ old_cr4) & pdptr_bits) ||
            (!(cr4 & X86_CR4_PCIDE) && (old_cr4 & X86_CR4_PCIDE)))
                kvm_mmu_reset_context(vcpu);

	// L2 CPUID always exits to L1
        if ((cr4 ^ old_cr4) & (X86_CR4_OSXSAVE | X86_CR4_PKE))
                kvm_update_cpuid(vcpu);

Would you please write a testcase too?

Paolo

> ---
>  arch/x86/kvm/vmx.c | 2 +-
>  1 file changed, 1 insertion(+), 1 deletion(-)
> 
> diff --git a/arch/x86/kvm/vmx.c b/arch/x86/kvm/vmx.c
> index b9d2140eb212..3fe639e0e1f6 100644
> --- a/arch/x86/kvm/vmx.c
> +++ b/arch/x86/kvm/vmx.c
> @@ -11297,7 +11297,7 @@ static void load_vmcs12_host_state(struct kvm_vcpu *vcpu,
>  
>  	/* Same as above - no reason to call set_cr4_guest_host_mask().  */
>  	vcpu->arch.cr4_guest_owned_bits = ~vmcs_readl(CR4_GUEST_HOST_MASK);
> -	kvm_set_cr4(vcpu, vmcs12->host_cr4);
> +	vmx_set_cr4(vcpu, vmcs12->host_cr4);
>  
>  	nested_ept_uninit_mmu_context(vcpu);
>  
> 

^ permalink raw reply	[flat|nested] 3+ messages in thread

* Re: [PATCH] KVM: nVMX: fix guest CR4 loading when emulating L2 to L1 exit
  2017-10-10  8:19 ` Paolo Bonzini
@ 2017-10-10  9:12   ` Haozhong Zhang
  0 siblings, 0 replies; 3+ messages in thread
From: Haozhong Zhang @ 2017-10-10  9:12 UTC (permalink / raw)
  To: Paolo Bonzini; +Cc: kvm, rkrcmar

On 10/10/17 10:19 +0200, Paolo Bonzini wrote:
> On 10/10/2017 09:01, Haozhong Zhang wrote:
> > When KVM emulates an exit from L2 to L1, it loads L1 CR4 into the
> > guest CR4. Before this CR4 loading, the guest CR4 refers to L2
> > CR4. Because these two CR4's are in different levels of guest, we
> > should vmx_set_cr4() rather than kvm_set_cr4() here. The latter, which
> > is used to handle guest writes to its CR4, checks the guest change to
> > CR4 and may fail if the change is invalid.
> > 
> > The failure may cause trouble. Consider we start
> >   a L1 guest with non-zero L1 PCID in use,
> >      (i.e. L1 CR4.PCIDE == 1 && L1 CR3.PCID != 0)
> > and
> >   a L2 guest with L2 PCID disabled,
> >      (i.e. L2 CR4.PCIDE == 0)
> > and following events may happen:
> > 
> > 1. If kvm_set_cr4() is used in load_vmcs12_host_state() to load L1 CR4
> >    into guest CR4 (in VMCS01) for L2 to L1 exit, it will fail because
> >    of PCID check. As a result, the guest CR4 recorded in L0 KVM (i.e.
> >    vcpu->arch.cr4) is left to the value of L2 CR4.
> > 
> > 2. Later, if L1 attempts to change its CR4, e.g., clearing VMXE bit,
> >    kvm_set_cr4() in L0 KVM will think L1 also wants to enable PCID,
> >    because the wrong L2 CR4 is used by L0 KVM as L1 CR4. As L1
> >    CR3.PCID != 0, L0 KVM will inject GP to L1 guest.
> > 
> > Fixes: 4704d0befb072 ("KVM: nVMX: Exiting from L2 to L1")
> > Signed-off-by: Haozhong Zhang <haozhong.zhang@intel.com>
> 
> Makes sense, and none of the other effects of kvm_set_cr4 matter:
> 
> 	// MMU is already reset on L2->L1 exit
>         if (((cr4 ^ old_cr4) & pdptr_bits) ||
>             (!(cr4 & X86_CR4_PCIDE) && (old_cr4 & X86_CR4_PCIDE)))
>                 kvm_mmu_reset_context(vcpu);
> 
> 	// L2 CPUID always exits to L1
>         if ((cr4 ^ old_cr4) & (X86_CR4_OSXSAVE | X86_CR4_PKE))
>                 kvm_update_cpuid(vcpu);
> 
> Would you please write a testcase too?
> 

Sure, I'll send it later.

Haozhong

^ permalink raw reply	[flat|nested] 3+ messages in thread

end of thread, other threads:[~2017-10-10  9:13 UTC | newest]

Thread overview: 3+ messages (download: mbox.gz / follow: Atom feed)
-- links below jump to the message on this page --
2017-10-10  7:01 [PATCH] KVM: nVMX: fix guest CR4 loading when emulating L2 to L1 exit Haozhong Zhang
2017-10-10  8:19 ` Paolo Bonzini
2017-10-10  9:12   ` Haozhong Zhang

This is an external index of several public inboxes,
see mirroring instructions on how to clone and mirror
all data and code used by this external index.