aboutsummaryrefslogtreecommitdiffstats
path: root/virt/kvm
diff options
context:
space:
mode:
authorAndre Przywara <andre.przywara@arm.com>2016-11-01 14:00:08 -0400
committerMarc Zyngier <marc.zyngier@arm.com>2016-11-04 13:56:54 -0400
commit112b0b8f8f6e18d4695d21457961c0e1b322a1d7 (patch)
tree0145a35a2e65e9cbc713ec2d56dd04c3b9ea6216 /virt/kvm
parent94d0e5980d6791b9f98a9b6c586c1f7cb76b2178 (diff)
KVM: arm/arm64: vgic: Prevent access to invalid SPIs
In our VGIC implementation we limit the number of SPIs to a number that the userland application told us. Accordingly we limit the allocation of memory for virtual IRQs to that number. However in our MMIO dispatcher we didn't check if we ever access an IRQ beyond that limit, leading to out-of-bound accesses. Add a test against the number of allocated SPIs in check_region(). Adjust the VGIC_ADDR_TO_INT macro to avoid an actual division, which is not implemented on ARM(32). [maz: cleaned-up original patch] Cc: stable@vger.kernel.org Reviewed-by: Christoffer Dall <christoffer.dall@linaro.org> Signed-off-by: Andre Przywara <andre.przywara@arm.com> Signed-off-by: Marc Zyngier <marc.zyngier@arm.com>
Diffstat (limited to 'virt/kvm')
-rw-r--r--virt/kvm/arm/vgic/vgic-mmio.c41
-rw-r--r--virt/kvm/arm/vgic/vgic-mmio.h14
2 files changed, 34 insertions, 21 deletions
diff --git a/virt/kvm/arm/vgic/vgic-mmio.c b/virt/kvm/arm/vgic/vgic-mmio.c
index e18b30ddcdce..ebe1b9fa3c4d 100644
--- a/virt/kvm/arm/vgic/vgic-mmio.c
+++ b/virt/kvm/arm/vgic/vgic-mmio.c
@@ -453,17 +453,33 @@ struct vgic_io_device *kvm_to_vgic_iodev(const struct kvm_io_device *dev)
453 return container_of(dev, struct vgic_io_device, dev); 453 return container_of(dev, struct vgic_io_device, dev);
454} 454}
455 455
456static bool check_region(const struct vgic_register_region *region, 456static bool check_region(const struct kvm *kvm,
457 const struct vgic_register_region *region,
457 gpa_t addr, int len) 458 gpa_t addr, int len)
458{ 459{
459 if ((region->access_flags & VGIC_ACCESS_8bit) && len == 1) 460 int flags, nr_irqs = kvm->arch.vgic.nr_spis + VGIC_NR_PRIVATE_IRQS;
460 return true; 461
461 if ((region->access_flags & VGIC_ACCESS_32bit) && 462 switch (len) {
462 len == sizeof(u32) && !(addr & 3)) 463 case sizeof(u8):
463 return true; 464 flags = VGIC_ACCESS_8bit;
464 if ((region->access_flags & VGIC_ACCESS_64bit) && 465 break;
465 len == sizeof(u64) && !(addr & 7)) 466 case sizeof(u32):
466 return true; 467 flags = VGIC_ACCESS_32bit;
468 break;
469 case sizeof(u64):
470 flags = VGIC_ACCESS_64bit;
471 break;
472 default:
473 return false;
474 }
475
476 if ((region->access_flags & flags) && IS_ALIGNED(addr, len)) {
477 if (!region->bits_per_irq)
478 return true;
479
480 /* Do we access a non-allocated IRQ? */
481 return VGIC_ADDR_TO_INTID(addr, region->bits_per_irq) < nr_irqs;
482 }
467 483
468 return false; 484 return false;
469} 485}
@@ -477,7 +493,7 @@ static int dispatch_mmio_read(struct kvm_vcpu *vcpu, struct kvm_io_device *dev,
477 493
478 region = vgic_find_mmio_region(iodev->regions, iodev->nr_regions, 494 region = vgic_find_mmio_region(iodev->regions, iodev->nr_regions,
479 addr - iodev->base_addr); 495 addr - iodev->base_addr);
480 if (!region || !check_region(region, addr, len)) { 496 if (!region || !check_region(vcpu->kvm, region, addr, len)) {
481 memset(val, 0, len); 497 memset(val, 0, len);
482 return 0; 498 return 0;
483 } 499 }
@@ -510,10 +526,7 @@ static int dispatch_mmio_write(struct kvm_vcpu *vcpu, struct kvm_io_device *dev,
510 526
511 region = vgic_find_mmio_region(iodev->regions, iodev->nr_regions, 527 region = vgic_find_mmio_region(iodev->regions, iodev->nr_regions,
512 addr - iodev->base_addr); 528 addr - iodev->base_addr);
513 if (!region) 529 if (!region || !check_region(vcpu->kvm, region, addr, len))
514 return 0;
515
516 if (!check_region(region, addr, len))
517 return 0; 530 return 0;
518 531
519 switch (iodev->iodev_type) { 532 switch (iodev->iodev_type) {
diff --git a/virt/kvm/arm/vgic/vgic-mmio.h b/virt/kvm/arm/vgic/vgic-mmio.h
index 4c34d39d44a0..84961b4e4422 100644
--- a/virt/kvm/arm/vgic/vgic-mmio.h
+++ b/virt/kvm/arm/vgic/vgic-mmio.h
@@ -50,15 +50,15 @@ extern struct kvm_io_device_ops kvm_io_gic_ops;
50#define VGIC_ADDR_IRQ_MASK(bits) (((bits) * 1024 / 8) - 1) 50#define VGIC_ADDR_IRQ_MASK(bits) (((bits) * 1024 / 8) - 1)
51 51
52/* 52/*
53 * (addr & mask) gives us the byte offset for the INT ID, so we want to 53 * (addr & mask) gives us the _byte_ offset for the INT ID.
54 * divide this with 'bytes per irq' to get the INT ID, which is given 54 * We multiply this by 8 the get the _bit_ offset, then divide this by
55 * by '(bits) / 8'. But we do this with fixed-point-arithmetic and 55 * the number of bits to learn the actual INT ID.
56 * take advantage of the fact that division by a fraction equals 56 * But instead of a division (which requires a "long long div" implementation),
57 * multiplication with the inverted fraction, and scale up both the 57 * we shift by the binary logarithm of <bits>.
58 * numerator and denominator with 8 to support at most 64 bits per IRQ: 58 * This assumes that <bits> is a power of two.
59 */ 59 */
60#define VGIC_ADDR_TO_INTID(addr, bits) (((addr) & VGIC_ADDR_IRQ_MASK(bits)) * \ 60#define VGIC_ADDR_TO_INTID(addr, bits) (((addr) & VGIC_ADDR_IRQ_MASK(bits)) * \
61 64 / (bits) / 8) 61 8 >> ilog2(bits))
62 62
63/* 63/*
64 * Some VGIC registers store per-IRQ information, with a different number 64 * Some VGIC registers store per-IRQ information, with a different number