aboutsummaryrefslogtreecommitdiffstats
diff options
context:
space:
mode:
authorMarc Zyngier <marc.zyngier@arm.com>2017-01-12 04:21:56 -0500
committerGreg Kroah-Hartman <gregkh@linuxfoundation.org>2017-01-26 02:24:39 -0500
commit26c4d513b6af730941bb4ff4c237789a4d190c27 (patch)
tree499c1c93d3ddd682cd8bf72cf93776a0e7c6e5f4
parente0641f201114700dceac729babc89991ebb4b3ef (diff)
KVM: arm/arm64: vgic: Fix deadlock on error handling
commit 1193e6aeecb36c74c48c7cd0f641acbbed9ddeef upstream. Dmitry Vyukov reported that the syzkaller fuzzer triggered a deadlock in the vgic setup code when an error was detected, as the cleanup code tries to take a lock that is already held by the setup code. The fix is to avoid retaking the lock when cleaning up, by telling the cleanup function that we already hold it. Reported-by: Dmitry Vyukov <dvyukov@google.com> Reviewed-by: Christoffer Dall <christoffer.dall@linaro.org> Reviewed-by: Eric Auger <eric.auger@redhat.com> Signed-off-by: Marc Zyngier <marc.zyngier@arm.com> Signed-off-by: Greg Kroah-Hartman <gregkh@linuxfoundation.org>
-rw-r--r--virt/kvm/arm/vgic/vgic-init.c18
-rw-r--r--virt/kvm/arm/vgic/vgic-v2.c2
-rw-r--r--virt/kvm/arm/vgic/vgic-v3.c2
3 files changed, 13 insertions, 9 deletions
diff --git a/virt/kvm/arm/vgic/vgic-init.c b/virt/kvm/arm/vgic/vgic-init.c
index 8cebfbc19e90..539d3f5cb619 100644
--- a/virt/kvm/arm/vgic/vgic-init.c
+++ b/virt/kvm/arm/vgic/vgic-init.c
@@ -268,15 +268,11 @@ static void kvm_vgic_dist_destroy(struct kvm *kvm)
268{ 268{
269 struct vgic_dist *dist = &kvm->arch.vgic; 269 struct vgic_dist *dist = &kvm->arch.vgic;
270 270
271 mutex_lock(&kvm->lock);
272
273 dist->ready = false; 271 dist->ready = false;
274 dist->initialized = false; 272 dist->initialized = false;
275 273
276 kfree(dist->spis); 274 kfree(dist->spis);
277 dist->nr_spis = 0; 275 dist->nr_spis = 0;
278
279 mutex_unlock(&kvm->lock);
280} 276}
281 277
282void kvm_vgic_vcpu_destroy(struct kvm_vcpu *vcpu) 278void kvm_vgic_vcpu_destroy(struct kvm_vcpu *vcpu)
@@ -286,7 +282,8 @@ void kvm_vgic_vcpu_destroy(struct kvm_vcpu *vcpu)
286 INIT_LIST_HEAD(&vgic_cpu->ap_list_head); 282 INIT_LIST_HEAD(&vgic_cpu->ap_list_head);
287} 283}
288 284
289void kvm_vgic_destroy(struct kvm *kvm) 285/* To be called with kvm->lock held */
286static void __kvm_vgic_destroy(struct kvm *kvm)
290{ 287{
291 struct kvm_vcpu *vcpu; 288 struct kvm_vcpu *vcpu;
292 int i; 289 int i;
@@ -297,6 +294,13 @@ void kvm_vgic_destroy(struct kvm *kvm)
297 kvm_vgic_vcpu_destroy(vcpu); 294 kvm_vgic_vcpu_destroy(vcpu);
298} 295}
299 296
297void kvm_vgic_destroy(struct kvm *kvm)
298{
299 mutex_lock(&kvm->lock);
300 __kvm_vgic_destroy(kvm);
301 mutex_unlock(&kvm->lock);
302}
303
300/** 304/**
301 * vgic_lazy_init: Lazy init is only allowed if the GIC exposed to the guest 305 * vgic_lazy_init: Lazy init is only allowed if the GIC exposed to the guest
302 * is a GICv2. A GICv3 must be explicitly initialized by the guest using the 306 * is a GICv2. A GICv3 must be explicitly initialized by the guest using the
@@ -348,6 +352,10 @@ int kvm_vgic_map_resources(struct kvm *kvm)
348 ret = vgic_v2_map_resources(kvm); 352 ret = vgic_v2_map_resources(kvm);
349 else 353 else
350 ret = vgic_v3_map_resources(kvm); 354 ret = vgic_v3_map_resources(kvm);
355
356 if (ret)
357 __kvm_vgic_destroy(kvm);
358
351out: 359out:
352 mutex_unlock(&kvm->lock); 360 mutex_unlock(&kvm->lock);
353 return ret; 361 return ret;
diff --git a/virt/kvm/arm/vgic/vgic-v2.c b/virt/kvm/arm/vgic/vgic-v2.c
index 9bab86757fa4..834137e7b83f 100644
--- a/virt/kvm/arm/vgic/vgic-v2.c
+++ b/virt/kvm/arm/vgic/vgic-v2.c
@@ -293,8 +293,6 @@ int vgic_v2_map_resources(struct kvm *kvm)
293 dist->ready = true; 293 dist->ready = true;
294 294
295out: 295out:
296 if (ret)
297 kvm_vgic_destroy(kvm);
298 return ret; 296 return ret;
299} 297}
300 298
diff --git a/virt/kvm/arm/vgic/vgic-v3.c b/virt/kvm/arm/vgic/vgic-v3.c
index 5c9f9745e6ca..e6b03fd8c374 100644
--- a/virt/kvm/arm/vgic/vgic-v3.c
+++ b/virt/kvm/arm/vgic/vgic-v3.c
@@ -302,8 +302,6 @@ int vgic_v3_map_resources(struct kvm *kvm)
302 dist->ready = true; 302 dist->ready = true;
303 303
304out: 304out:
305 if (ret)
306 kvm_vgic_destroy(kvm);
307 return ret; 305 return ret;
308} 306}
309 307