Skip to content

Commit bdf7ffc

Browse files
Wanpeng Lirkrcmar
authored andcommitted
KVM: LAPIC: Fix pv ipis out-of-bounds access
Dan Carpenter reported that the untrusted data returns from kvm_register_read() results in the following static checker warning: arch/x86/kvm/lapic.c:576 kvm_pv_send_ipi() error: buffer underflow 'map->phys_map' 's32min-s32max' KVM guest can easily trigger this by executing the following assembly sequence in Ring0: mov $10, %rax mov $0xFFFFFFFF, %rbx mov $0xFFFFFFFF, %rdx mov $0, %rsi vmcall As this will cause KVM to execute the following code-path: vmx_handle_exit() -> handle_vmcall() -> kvm_emulate_hypercall() -> kvm_pv_send_ipi() which will reach out-of-bounds access. This patch fixes it by adding a check to kvm_pv_send_ipi() against map->max_apic_id, ignoring destinations that are not present and delivering the rest. We also check whether or not map->phys_map[min + i] is NULL since the max_apic_id is set to the max apic id, some phys_map maybe NULL when apic id is sparse, especially kvm unconditionally set max_apic_id to 255 to reserve enough space for any xAPIC ID. Reported-by: Dan Carpenter <dan.carpenter@oracle.com> Reviewed-by: Liran Alon <liran.alon@oracle.com> Cc: Paolo Bonzini <pbonzini@redhat.com> Cc: Radim Krčmář <rkrcmar@redhat.com> Cc: Liran Alon <liran.alon@oracle.com> Cc: Dan Carpenter <dan.carpenter@oracle.com> Signed-off-by: Wanpeng Li <wanpengli@tencent.com> [Add second "if (min > map->max_apic_id)" to complete the fix. -Radim] Signed-off-by: Radim Krčmář <rkrcmar@redhat.com>
1 parent b5861e5 commit bdf7ffc

File tree

2 files changed

+21
-8
lines changed

2 files changed

+21
-8
lines changed

arch/x86/include/asm/kvm_host.h

Lines changed: 1 addition & 1 deletion
Original file line numberDiff line numberDiff line change
@@ -1455,7 +1455,7 @@ void kvm_vcpu_reset(struct kvm_vcpu *vcpu, bool init_event);
14551455
void kvm_vcpu_reload_apic_access_page(struct kvm_vcpu *vcpu);
14561456

14571457
int kvm_pv_send_ipi(struct kvm *kvm, unsigned long ipi_bitmap_low,
1458-
unsigned long ipi_bitmap_high, int min,
1458+
unsigned long ipi_bitmap_high, u32 min,
14591459
unsigned long icr, int op_64_bit);
14601460

14611461
u64 kvm_get_arch_capabilities(void);

arch/x86/kvm/lapic.c

Lines changed: 20 additions & 7 deletions
Original file line numberDiff line numberDiff line change
@@ -548,7 +548,7 @@ int kvm_apic_set_irq(struct kvm_vcpu *vcpu, struct kvm_lapic_irq *irq,
548548
}
549549

550550
int kvm_pv_send_ipi(struct kvm *kvm, unsigned long ipi_bitmap_low,
551-
unsigned long ipi_bitmap_high, int min,
551+
unsigned long ipi_bitmap_high, u32 min,
552552
unsigned long icr, int op_64_bit)
553553
{
554554
int i;
@@ -571,18 +571,31 @@ int kvm_pv_send_ipi(struct kvm *kvm, unsigned long ipi_bitmap_low,
571571
rcu_read_lock();
572572
map = rcu_dereference(kvm->arch.apic_map);
573573

574+
if (min > map->max_apic_id)
575+
goto out;
574576
/* Bits above cluster_size are masked in the caller. */
575-
for_each_set_bit(i, &ipi_bitmap_low, BITS_PER_LONG) {
576-
vcpu = map->phys_map[min + i]->vcpu;
577-
count += kvm_apic_set_irq(vcpu, &irq, NULL);
577+
for_each_set_bit(i, &ipi_bitmap_low,
578+
min((u32)BITS_PER_LONG, (map->max_apic_id - min + 1))) {
579+
if (map->phys_map[min + i]) {
580+
vcpu = map->phys_map[min + i]->vcpu;
581+
count += kvm_apic_set_irq(vcpu, &irq, NULL);
582+
}
578583
}
579584

580585
min += cluster_size;
581-
for_each_set_bit(i, &ipi_bitmap_high, BITS_PER_LONG) {
582-
vcpu = map->phys_map[min + i]->vcpu;
583-
count += kvm_apic_set_irq(vcpu, &irq, NULL);
586+
587+
if (min > map->max_apic_id)
588+
goto out;
589+
590+
for_each_set_bit(i, &ipi_bitmap_high,
591+
min((u32)BITS_PER_LONG, (map->max_apic_id - min + 1))) {
592+
if (map->phys_map[min + i]) {
593+
vcpu = map->phys_map[min + i]->vcpu;
594+
count += kvm_apic_set_irq(vcpu, &irq, NULL);
595+
}
584596
}
585597

598+
out:
586599
rcu_read_unlock();
587600
return count;
588601
}

0 commit comments

Comments
 (0)