When L2's LMSW exits to L0 but not to L1, emulate it through handle_set_cr0(), as is done for MOV to CR0. kvm_lmsw() ignores vmcs12's CR0 guest/host mask, so L2 could change CR0 bits that L1 owns, e.g. clear TS, and read their effective values instead of the read shadow. Fixes: eeadf9e7558c ("KVM: nVMX: Handling of CR0 and CR4 modifying instructions") Signed-off-by: Mahmoud Abumandour --- arch/x86/kvm/vmx/vmx.c | 15 +++++++++++++++ 1 file changed, 15 insertions(+) diff --git a/arch/x86/kvm/vmx/vmx.c b/arch/x86/kvm/vmx/vmx.c index b341af4c60..3633496299 100644 --- a/arch/x86/kvm/vmx/vmx.c +++ b/arch/x86/kvm/vmx/vmx.c @@ -5748,6 +5748,21 @@ static int handle_cr(struct kvm_vcpu *vcpu) case 3: /* lmsw */ val = (exit_qualification >> LMSW_SOURCE_DATA_SHIFT) & 0x0f; trace_kvm_cr_write(0, (kvm_read_cr0_bits(vcpu, ~0xful) | val)); + if (is_guest_mode(vcpu)) { + struct vmcs12 *vmcs12 = get_vmcs12(vcpu); + unsigned long mask = vmcs12->cr0_guest_host_mask; + unsigned long cr0; + + /* + * LMSW leaves the bits L1 owns unmodified, as does a MOV + * to CR0 that writes their read shadow values. Emulate + * it as that MOV: L2's view of CR0 with LMSW's bits. + */ + cr0 = (kvm_read_cr0(vcpu) & ~mask) | + (vmcs12->cr0_read_shadow & mask); + err = handle_set_cr0(vcpu, (cr0 & ~0x0eul) | val); + return kvm_complete_insn_gp(vcpu, err); + } kvm_lmsw(vcpu, val); return kvm_skip_emulated_instruction(vcpu); -- 2.25.1