aboutsummaryrefslogtreecommitdiffstats
diff options
context:
space:
mode:
authorWanpeng Li <wanpengli@tencent.com>2018-08-29 22:03:30 -0400
committerRadim Krčmář <rkrcmar@redhat.com>2018-09-07 12:38:43 -0400
commitbdf7ffc89922a52a4f08a12f7421ea24bb7626a0 (patch)
tree1442be4f9a512ea3078fb9a8c4804617f5657e71
parentb5861e5cf2fcf83031ea3e26b0a69d887adf7d21 (diff)
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>
-rw-r--r--arch/x86/include/asm/kvm_host.h2
-rw-r--r--arch/x86/kvm/lapic.c27
2 files changed, 21 insertions, 8 deletions
diff --git a/arch/x86/include/asm/kvm_host.h b/arch/x86/include/asm/kvm_host.h
index 3ad10f634d4c..8e90488c3d56 100644
--- a/arch/x86/include/asm/kvm_host.h
+++ b/arch/x86/include/asm/kvm_host.h
@@ -1455,7 +1455,7 @@ void kvm_vcpu_reset(struct kvm_vcpu *vcpu, bool init_event);
1455void kvm_vcpu_reload_apic_access_page(struct kvm_vcpu *vcpu); 1455void kvm_vcpu_reload_apic_access_page(struct kvm_vcpu *vcpu);
1456 1456
1457int kvm_pv_send_ipi(struct kvm *kvm, unsigned long ipi_bitmap_low, 1457int 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,
1459 unsigned long icr, int op_64_bit); 1459 unsigned long icr, int op_64_bit);
1460 1460
1461u64 kvm_get_arch_capabilities(void); 1461u64 kvm_get_arch_capabilities(void);
diff --git a/arch/x86/kvm/lapic.c b/arch/x86/kvm/lapic.c
index 0cefba28c864..17c0472c5b34 100644
--- a/arch/x86/kvm/lapic.c
+++ b/arch/x86/kvm/lapic.c
@@ -548,7 +548,7 @@ int kvm_apic_set_irq(struct kvm_vcpu *vcpu, struct kvm_lapic_irq *irq,
548} 548}
549 549
550int kvm_pv_send_ipi(struct kvm *kvm, unsigned long ipi_bitmap_low, 550int 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,
552 unsigned long icr, int op_64_bit) 552 unsigned long icr, int op_64_bit)
553{ 553{
554 int i; 554 int i;
@@ -571,18 +571,31 @@ int kvm_pv_send_ipi(struct kvm *kvm, unsigned long ipi_bitmap_low,
571 rcu_read_lock(); 571 rcu_read_lock();
572 map = rcu_dereference(kvm->arch.apic_map); 572 map = rcu_dereference(kvm->arch.apic_map);
573 573
574 if (min > map->max_apic_id)
575 goto out;
574 /* Bits above cluster_size are masked in the caller. */ 576 /* Bits above cluster_size are masked in the caller. */
575 for_each_set_bit(i, &ipi_bitmap_low, BITS_PER_LONG) { 577 for_each_set_bit(i, &ipi_bitmap_low,
576 vcpu = map->phys_map[min + i]->vcpu; 578 min((u32)BITS_PER_LONG, (map->max_apic_id - min + 1))) {
577 count += kvm_apic_set_irq(vcpu, &irq, NULL); 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 }
578 } 583 }
579 584
580 min += cluster_size; 585 min += cluster_size;
581 for_each_set_bit(i, &ipi_bitmap_high, BITS_PER_LONG) { 586
582 vcpu = map->phys_map[min + i]->vcpu; 587 if (min > map->max_apic_id)
583 count += kvm_apic_set_irq(vcpu, &irq, NULL); 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 }
584 } 596 }
585 597
598out:
586 rcu_read_unlock(); 599 rcu_read_unlock();
587 return count; 600 return count;
588} 601}