• Liran Alon's avatar
    KVM: nVMX: Fix races when sending nested PI while dest enters/leaves L2 · 6b697711
    Liran Alon authored
    Consider the following scenario:
    1. CPU A calls vmx_deliver_nested_posted_interrupt() to send an IPI
    to CPU B via virtual posted-interrupt mechanism.
    2. CPU B is currently executing L2 guest.
    3. vmx_deliver_nested_posted_interrupt() calls
    kvm_vcpu_trigger_posted_interrupt() which will note that
    vcpu->mode == IN_GUEST_MODE.
    4. Assume that before CPU A sends the physical POSTED_INTR_NESTED_VECTOR
    IPI, CPU B exits from L2 to L0 during event-delivery
    (valid IDT-vectoring-info).
    5. CPU A now sends the physical IPI. The IPI is received in host and
    it's handler (smp_kvm_posted_intr_nested_ipi()) does nothing.
    6. Assume that before CPU A sets pi_pending=true and KVM_REQ_EVENT,
    CPU B continues to run in L0 and reach vcpu_enter_guest(). As
    KVM_REQ_EVENT is not set yet, vcpu_enter_guest() will continue and resume
    L2 guest.
    7. At this point, CPU A sets pi_pending=true and KVM_REQ_EVENT but
    it's too late! CPU B already entered L2 and KVM_REQ_EVENT will only be
    consumed at next L2 entry!
    
    Another scenario to consider:
    1. CPU A calls vmx_deliver_nested_posted_interrupt() to send an IPI
    to CPU B via virtual posted-interrupt mechanism.
    2. Assume that before CPU A calls kvm_vcpu_trigger_posted_interrupt(),
    CPU B is at L0 and is about to resume into L2. Further assume that it is
    in vcpu_enter_guest() after check for KVM_REQ_EVENT.
    3. At this point, CPU A calls kvm_vcpu_trigger_posted_interrupt() which
    will note that vcpu->mode != IN_GUEST_MODE. Therefore, do nothing and
    return false. Then, will set pi_pending=true and KVM_REQ_EVENT.
    4. Now CPU B continue and resumes into L2 guest without processing
    the posted-interrupt until next L2 entry!
    
    To fix both issues, we just need to change
    vmx_deliver_nested_posted_interrupt() to set pi_pending=true and
    KVM_REQ_EVENT before calling kvm_vcpu_trigger_posted_interrupt().
    
    It will fix the first scenario by chaging step (6) to note that
    KVM_REQ_EVENT and pi_pending=true and therefore process
    nested posted-interrupt.
    
    It will fix the second scenario by two possible ways:
    1. If kvm_vcpu_trigger_posted_interrupt() is called while CPU B has changed
    vcpu->mode to IN_GUEST_MODE, physical IPI will be sent and will be received
    when CPU resumes into L2.
    2. If kvm_vcpu_trigger_posted_interrupt() is called while CPU B hasn't yet
    changed vcpu->mode to IN_GUEST_MODE, then after CPU B will change
    vcpu->mode it will call kvm_request_pending() which will return true and
    therefore force another round of vcpu_enter_guest() which will note that
    KVM_REQ_EVENT and pi_pending=true and therefore process nested
    posted-interrupt.
    
    Cc: stable@vger.kernel.org
    Fixes: 705699a1 ("KVM: nVMX: Enable nested posted interrupt processing")
    Signed-off-by: default avatarLiran Alon <liran.alon@oracle.com>
    Reviewed-by: default avatarNikita Leshenko <nikita.leshchenko@oracle.com>
    Reviewed-by: default avatarKrish Sadhukhan <krish.sadhukhan@oracle.com>
    [Add kvm_vcpu_kick to also handle the case where L1 doesn't intercept L2 HLT
     and L2 executes HLT instruction. - Paolo]
    Signed-off-by: default avatarPaolo Bonzini <pbonzini@redhat.com>
    Signed-off-by: default avatarRadim Krčmář <rkrcmar@redhat.com>
    6b697711
vmx.c 346 KB