Commit fb7333df authored by Paolo Bonzini's avatar Paolo Bonzini
Browse files

KVM: SVM: fix calls to is_intercept



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: default avatarQian Cai <cai@lca.pw>
Signed-off-by: default avatarPaolo Bonzini <pbonzini@redhat.com>
parent 75ad6e80
Loading
Loading
Loading
Loading
+1 −1
Original line number Diff line number Diff line
@@ -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
+3 −1
Original line number Diff line number Diff line
@@ -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);