KVM: arm64: vgic-its: Plug race in vgic_put_irq

Right now the following sequence of events can happen:

  1. Thread X calls vgic_put_irq
  2. Thread Y calls vgic_add_lpi
  3. Thread Y gets lpi_list_lock
  4. Thread X drops the ref count to 0 and blocks on lpi_list_lock
  5. Thread Y finds the irq via the lpi_list_lock, raises the ref
     count to 1, and release the lpi_list_lock.
  6. Thread X proceeds and frees the irq.

Avoid this by holding the spinlock around the kref_put.

Reviewed-by: Andre Przywara <andre.przywara@arm.com>
Signed-off-by: Christoffer Dall <christoffer.dall@linaro.org>
This commit is contained in:
Christoffer Dall 2016-08-02 22:05:42 +02:00
parent 99e5e886a0
commit 2cccbb368a

View File

@ -117,17 +117,17 @@ static void vgic_irq_release(struct kref *ref)
void vgic_put_irq(struct kvm *kvm, struct vgic_irq *irq) void vgic_put_irq(struct kvm *kvm, struct vgic_irq *irq)
{ {
struct vgic_dist *dist; struct vgic_dist *dist = &kvm->arch.vgic;
if (irq->intid < VGIC_MIN_LPI) if (irq->intid < VGIC_MIN_LPI)
return; return;
if (!kref_put(&irq->refcount, vgic_irq_release))
return;
dist = &kvm->arch.vgic;
spin_lock(&dist->lpi_list_lock); spin_lock(&dist->lpi_list_lock);
if (!kref_put(&irq->refcount, vgic_irq_release)) {
spin_unlock(&dist->lpi_list_lock);
return;
};
list_del(&irq->lpi_list); list_del(&irq->lpi_list);
dist->lpi_list_count--; dist->lpi_list_count--;
spin_unlock(&dist->lpi_list_lock); spin_unlock(&dist->lpi_list_lock);