KVM: x86: Fix handling of APIC LVT updates when userspace changes MCG_CAP
authorSean Christopherson <seanjc@google.com>
Fri, 8 Jul 2022 22:48:10 +0000 (15:48 -0700)
committerSean Christopherson <seanjc@google.com>
Fri, 8 Jul 2022 22:58:16 +0000 (15:58 -0700)
Add a helper to update KVM's in-kernel local APIC in response to MCG_CAP
being changed by userspace to fix multiple bugs.  First and foremost,
KVM needs to check that there's an in-kernel APIC prior to dereferencing
vcpu->arch.apic.  Beyond that, any "new" LVT entries need to be masked,
and the APIC version register needs to be updated as it reports out the
number of LVT entries.

Fixes: 4b903561ec49 ("KVM: x86: Add Corrected Machine Check Interrupt (CMCI) emulation to lapic.")
Reported-by: syzbot+8cdad6430c24f396f158@syzkaller.appspotmail.com
Cc: Siddh Raman Pant <code@siddh.me>
Cc: Jue Wang <juew@google.com>
Signed-off-by: Sean Christopherson <seanjc@google.com>
arch/x86/kvm/lapic.c
arch/x86/kvm/lapic.h
arch/x86/kvm/x86.c

index 1540d01..50354c7 100644 (file)
@@ -433,6 +433,25 @@ void kvm_apic_set_version(struct kvm_vcpu *vcpu)
        kvm_lapic_set_reg(apic, APIC_LVR, v);
 }
 
+void kvm_apic_after_set_mcg_cap(struct kvm_vcpu *vcpu)
+{
+       int nr_lvt_entries = kvm_apic_calc_nr_lvt_entries(vcpu);
+       struct kvm_lapic *apic = vcpu->arch.apic;
+       int i;
+
+       if (!lapic_in_kernel(vcpu) || nr_lvt_entries == apic->nr_lvt_entries)
+               return;
+
+       /* Initialize/mask any "new" LVT entries. */
+       for (i = apic->nr_lvt_entries; i < nr_lvt_entries; i++)
+               kvm_lapic_set_reg(apic, APIC_LVTx(i), APIC_LVT_MASKED);
+
+       apic->nr_lvt_entries = nr_lvt_entries;
+
+       /* The number of LVT entries is reflected in the version register. */
+       kvm_apic_set_version(vcpu);
+}
+
 static const unsigned int apic_lvt_mask[KVM_APIC_MAX_NR_LVT_ENTRIES] = {
        [LVT_TIMER] = LVT_MASK,      /* timer mode mask added at runtime */
        [LVT_THERMAL_MONITOR] = LVT_MASK | APIC_MODE_MASK,
index 762bf61..117a46d 100644 (file)
@@ -99,6 +99,7 @@ void kvm_lapic_set_base(struct kvm_vcpu *vcpu, u64 value);
 u64 kvm_lapic_get_base(struct kvm_vcpu *vcpu);
 void kvm_recalculate_apic_map(struct kvm *kvm);
 void kvm_apic_set_version(struct kvm_vcpu *vcpu);
+void kvm_apic_after_set_mcg_cap(struct kvm_vcpu *vcpu);
 bool kvm_apic_match_dest(struct kvm_vcpu *vcpu, struct kvm_lapic *source,
                           int shorthand, unsigned int dest, int dest_mode);
 int kvm_apic_compare_prio(struct kvm_vcpu *vcpu1, struct kvm_vcpu *vcpu2);
index fb37d11..801c3cf 100644 (file)
@@ -4893,8 +4893,8 @@ static int kvm_vcpu_ioctl_x86_setup_mce(struct kvm_vcpu *vcpu,
                if (mcg_cap & MCG_CMCI_P)
                        vcpu->arch.mci_ctl2_banks[bank] = 0;
        }
-       vcpu->arch.apic->nr_lvt_entries =
-               KVM_APIC_MAX_NR_LVT_ENTRIES - !(mcg_cap & MCG_CMCI_P);
+
+       kvm_apic_after_set_mcg_cap(vcpu);
 
        static_call(kvm_x86_setup_mce)(vcpu);
 out: