• Wanpeng Li's avatar
    KVM: nVMX: Fix loss of L2's NMI blocking state · 2d6144e3
    Wanpeng Li authored
    Run kvm-unit-tests/eventinj.flat in L1 w/ ept=0 on both L0 and L1:
    
    Before NMI IRET test
    Sending NMI to self
    NMI isr running stack 0x461000
    Sending nested NMI to self
    After nested NMI to self
    Nested NMI isr running rip=40038e
    After iret
    After NMI to self
    FAIL: NMI
    
    Commit 4c4a6f79 (KVM: nVMX: track NMI blocking state separately
    for each VMCS) tracks NMI blocking state separately for vmcs01 and
    vmcs02. However it is not enough:
    
     - The L2 (kvm-unit-tests/eventinj.flat) generates NMI that will fault
       on IRET, so the L2 can generate #PF which can be intercepted by L0.
     - L0 walks L1's guest page table and sees the mapping is invalid, it
       resumes the L1 guest and injects the #PF into L1.  At this point the
       vmcs02 has nmi_known_unmasked=true.
     - L1 sets set bit 3 (blocking by NMI) in the interruptibility-state field
       of vmcs12 (and fixes the shadow page table) before resuming L2 guest.
     - L1 executes VMRESUME to resume L2, causing a vmexit to L0
     - during VMRESUME emulation, prepare_vmcs02 sets bit 3 in the
       interruptibility-state field of vmcs02, but nmi_known_unmasked is
       still true.
     - L2 immediately exits to L0 with another page fault, because L0 still has
       not updated the NGVA->HPA page tables.  However, nmi_known_unmasked is
       true so vmx_recover_nmi_blocking does not do anything.
    
    The fix is to update nmi_known_unmasked when preparing vmcs02 from vmcs12.
    
    Cc: Paolo Bonzini <pbonzini@redhat.com>
    Cc: Radim Krčmář <rkrcmar@redhat.com>
    Signed-off-by: default avatarWanpeng Li <wanpeng.li@hotmail.com>
    Signed-off-by: default avatarPaolo Bonzini <pbonzini@redhat.com>
    2d6144e3
vmx.c 332 KB