KVM: nVMX: Fix races when sending nested PI while dest enters/leaves L2

"Linux Kernel Mailing List" <[email protected]> Sat, 10 Feb 2018 22:00:21 +0000 (UTC)
Newsgroups gmane.linux.kernel.commits.head
Message-ID <[email protected]>
Web:        https://git.kernel.org/torvalds/c/6b6977117f50d60455ace86b2d256f6fb4f3de05
Commit:     6b6977117f50d60455ace86b2d256f6fb4f3de05
Parent:     851c1a18c5412fd321e387cfe60739387cdbf37d
Refname:    refs/heads/master
Author:     Liran Alon <[email protected]>
AuthorDate: Thu Nov 9 20:27:20 2017 +0200
Committer:  Radim Krčmář <[email protected]>
CommitDate: Tue Jan 16 16:40:09 2018 +0100

    KVM: nVMX: Fix races when sending nested PI while dest enters/leaves L2
    
    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: [email protected]
    Fixes: 705699a13994 ("KVM: nVMX: Enable nested posted interrupt processing")
    Signed-off-by: Liran Alon <[email protected]>
    Reviewed-by: Nikita Leshenko <[email protected]>
    Reviewed-by: Krish Sadhukhan <[email protected]>
    [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: Paolo Bonzini <[email protected]>
    Signed-off-by: Radim Krčmář <[email protected]>
---
 arch/x86/kvm/vmx.c | 5 +++--
 1 file changed, 3 insertions(+), 2 deletions(-)

diff --git a/arch/x86/kvm/vmx.c b/arch/x86/kvm/vmx.c
index 2a96b1abe77c..2c8749ee3221 100644
--- a/arch/x86/kvm/vmx.c
+++ b/arch/x86/kvm/vmx.c
@@ -5146,14 +5146,15 @@ static int vmx_deliver_nested_posted_interrupt(struct kvm_vcpu *vcpu,
 
 	if (is_guest_mode(vcpu) &&
 	    vector == vmx->nested.posted_intr_nv) {
-		/* the PIR and ON have been set by L1. */
-		kvm_vcpu_trigger_posted_interrupt(vcpu, true);
 		/*
 		 * If a posted intr is not recognized by hardware,
 		 * we will accomplish it in the next vmentry.
 		 */
 		vmx->nested.pi_pending = true;
 		kvm_make_request(KVM_REQ_EVENT, vcpu);
+		/* the PIR and ON have been set by L1. */
+		if (!kvm_vcpu_trigger_posted_interrupt(vcpu, true))
+			kvm_vcpu_kick(vcpu);
 		return 0;
 	}
 	return -1;
--
To unsubscribe from this list: send the line "unsubscribe git-commits-head" in
the body of a message to [email protected]
More majordomo info at  http://vger.kernel.org/majordomo-info.html