KVM: SVM: fix calls to is_intercept
authorPaolo Bonzini <pbonzini@redhat.com>
Mon, 8 Jun 2020 11:11:47 +0000 (07:11 -0400)
committerPaolo Bonzini <pbonzini@redhat.com>
Mon, 8 Jun 2020 12:00:57 +0000 (08:00 -0400)
is_intercept takes an INTERCEPT_* constant, not SVM_EXIT_*; because
of this, the compiler was removing the body of the conditionals,
as if is_intercept returned 0.

This unveils a latent bug: when clearing the VINTR intercept,
int_ctl must also be changed in the L1 VMCB (svm->nested.hsave),
just like the intercept itself is also changed in the L1 VMCB.
Otherwise V_IRQ remains set and, due to the VINTR intercept being clear,
we get a spurious injection of a vector 0 interrupt on the next
L2->L1 vmexit.

Reported-by: Qian Cai <cai@lca.pw>
Signed-off-by: Paolo Bonzini <pbonzini@redhat.com>
arch/x86/kvm/svm/nested.c
arch/x86/kvm/svm/svm.c

index 8a6db11..6bceafb 100644 (file)
@@ -258,7 +258,7 @@ void sync_nested_vmcb_control(struct vcpu_svm *svm)
        /* Only a few fields of int_ctl are written by the processor.  */
        mask = V_IRQ_MASK | V_TPR_MASK;
        if (!(svm->nested.ctl.int_ctl & V_INTR_MASKING_MASK) &&
-           is_intercept(svm, SVM_EXIT_VINTR)) {
+           is_intercept(svm, INTERCEPT_VINTR)) {
                /*
                 * In order to request an interrupt window, L0 is usurping
                 * svm->vmcb->control.int_ctl and possibly setting V_IRQ
index 9e333b9..c8f5e87 100644 (file)
@@ -1378,6 +1378,8 @@ static void svm_clear_vintr(struct vcpu_svm *svm)
        /* Drop int_ctl fields related to VINTR injection.  */
        svm->vmcb->control.int_ctl &= mask;
        if (is_guest_mode(&svm->vcpu)) {
+               svm->nested.hsave->control.int_ctl &= mask;
+
                WARN_ON((svm->vmcb->control.int_ctl & V_TPR_MASK) !=
                        (svm->nested.ctl.int_ctl & V_TPR_MASK));
                svm->vmcb->control.int_ctl |= svm->nested.ctl.int_ctl & ~mask;
@@ -1999,7 +2001,7 @@ void svm_set_gif(struct vcpu_svm *svm, bool value)
                 */
                if (vgif_enabled(svm))
                        clr_intercept(svm, INTERCEPT_STGI);
-               if (is_intercept(svm, SVM_EXIT_VINTR))
+               if (is_intercept(svm, INTERCEPT_VINTR))
                        svm_clear_vintr(svm);
 
                enable_gif(svm);