diff options
author | Christoffer Dall <christoffer.dall@linaro.org> | 2016-08-02 22:05:42 +0200 |
---|---|---|
committer | Christoffer Dall <christoffer.dall@linaro.org> | 2016-08-10 11:41:54 +0200 |
commit | 2cccbb368a2bf27d98cf36bb424fbbf5572c0fab (patch) | |
tree | 83edb3161029df208d04341330a381b8c6e9f5a1 | |
parent | 99e5e886a0a59df267ff6838f763b789847df982 (diff) | |
download | linux-stable-2cccbb368a2bf27d98cf36bb424fbbf5572c0fab.tar.gz linux-stable-2cccbb368a2bf27d98cf36bb424fbbf5572c0fab.tar.bz2 linux-stable-2cccbb368a2bf27d98cf36bb424fbbf5572c0fab.zip |
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>
-rw-r--r-- | virt/kvm/arm/vgic/vgic.c | 10 |
1 files changed, 5 insertions, 5 deletions
diff --git a/virt/kvm/arm/vgic/vgic.c b/virt/kvm/arm/vgic/vgic.c index e7aeac719e09..e83b7fe4baae 100644 --- a/virt/kvm/arm/vgic/vgic.c +++ b/virt/kvm/arm/vgic/vgic.c @@ -117,17 +117,17 @@ static void vgic_irq_release(struct kref *ref) 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) return; - if (!kref_put(&irq->refcount, vgic_irq_release)) + spin_lock(&dist->lpi_list_lock); + if (!kref_put(&irq->refcount, vgic_irq_release)) { + spin_unlock(&dist->lpi_list_lock); return; + }; - dist = &kvm->arch.vgic; - - spin_lock(&dist->lpi_list_lock); list_del(&irq->lpi_list); dist->lpi_list_count--; spin_unlock(&dist->lpi_list_lock); |