Skip to content

Commit 90db104

Browse files
davidhildenbrandbonzini
authored andcommitted
KVM: kvm_io_bus_unregister_dev() should never fail
No caller currently checks the return value of kvm_io_bus_unregister_dev(). This is evil, as all callers silently go on freeing their device. A stale reference will remain in the io_bus, getting at least used again, when the iobus gets teared down on kvm_destroy_vm() - leading to use after free errors. There is nothing the callers could do, except retrying over and over again. So let's simply remove the bus altogether, print an error and make sure no one can access this broken bus again (returning -ENOMEM on any attempt to access it). Fixes: e93f8a0 ("KVM: convert io_bus to SRCU") Cc: stable@vger.kernel.org # 3.4+ Reported-by: Dmitry Vyukov <dvyukov@google.com> Reviewed-by: Cornelia Huck <cornelia.huck@de.ibm.com> Signed-off-by: David Hildenbrand <david@redhat.com> Signed-off-by: Paolo Bonzini <pbonzini@redhat.com>
1 parent 08d839c commit 90db104

File tree

3 files changed

+29
-20
lines changed

3 files changed

+29
-20
lines changed

include/linux/kvm_host.h

Lines changed: 2 additions & 2 deletions
Original file line numberDiff line numberDiff line change
@@ -162,8 +162,8 @@ int kvm_io_bus_read(struct kvm_vcpu *vcpu, enum kvm_bus bus_idx, gpa_t addr,
162162
int len, void *val);
163163
int kvm_io_bus_register_dev(struct kvm *kvm, enum kvm_bus bus_idx, gpa_t addr,
164164
int len, struct kvm_io_device *dev);
165-
int kvm_io_bus_unregister_dev(struct kvm *kvm, enum kvm_bus bus_idx,
166-
struct kvm_io_device *dev);
165+
void kvm_io_bus_unregister_dev(struct kvm *kvm, enum kvm_bus bus_idx,
166+
struct kvm_io_device *dev);
167167
struct kvm_io_device *kvm_io_bus_get_dev(struct kvm *kvm, enum kvm_bus bus_idx,
168168
gpa_t addr);
169169

virt/kvm/eventfd.c

Lines changed: 2 additions & 1 deletion
Original file line numberDiff line numberDiff line change
@@ -870,7 +870,8 @@ kvm_deassign_ioeventfd_idx(struct kvm *kvm, enum kvm_bus bus_idx,
870870
continue;
871871

872872
kvm_io_bus_unregister_dev(kvm, bus_idx, &p->dev);
873-
kvm->buses[bus_idx]->ioeventfd_count--;
873+
if (kvm->buses[bus_idx])
874+
kvm->buses[bus_idx]->ioeventfd_count--;
874875
ioeventfd_release(p);
875876
ret = 0;
876877
break;

virt/kvm/kvm_main.c

Lines changed: 25 additions & 17 deletions
Original file line numberDiff line numberDiff line change
@@ -728,7 +728,8 @@ static void kvm_destroy_vm(struct kvm *kvm)
728728
spin_unlock(&kvm_lock);
729729
kvm_free_irq_routing(kvm);
730730
for (i = 0; i < KVM_NR_BUSES; i++) {
731-
kvm_io_bus_destroy(kvm->buses[i]);
731+
if (kvm->buses[i])
732+
kvm_io_bus_destroy(kvm->buses[i]);
732733
kvm->buses[i] = NULL;
733734
}
734735
kvm_coalesced_mmio_free(kvm);
@@ -3476,6 +3477,8 @@ int kvm_io_bus_write(struct kvm_vcpu *vcpu, enum kvm_bus bus_idx, gpa_t addr,
34763477
};
34773478

34783479
bus = srcu_dereference(vcpu->kvm->buses[bus_idx], &vcpu->kvm->srcu);
3480+
if (!bus)
3481+
return -ENOMEM;
34793482
r = __kvm_io_bus_write(vcpu, bus, &range, val);
34803483
return r < 0 ? r : 0;
34813484
}
@@ -3493,6 +3496,8 @@ int kvm_io_bus_write_cookie(struct kvm_vcpu *vcpu, enum kvm_bus bus_idx,
34933496
};
34943497

34953498
bus = srcu_dereference(vcpu->kvm->buses[bus_idx], &vcpu->kvm->srcu);
3499+
if (!bus)
3500+
return -ENOMEM;
34963501

34973502
/* First try the device referenced by cookie. */
34983503
if ((cookie >= 0) && (cookie < bus->dev_count) &&
@@ -3543,6 +3548,8 @@ int kvm_io_bus_read(struct kvm_vcpu *vcpu, enum kvm_bus bus_idx, gpa_t addr,
35433548
};
35443549

35453550
bus = srcu_dereference(vcpu->kvm->buses[bus_idx], &vcpu->kvm->srcu);
3551+
if (!bus)
3552+
return -ENOMEM;
35463553
r = __kvm_io_bus_read(vcpu, bus, &range, val);
35473554
return r < 0 ? r : 0;
35483555
}
@@ -3555,6 +3562,9 @@ int kvm_io_bus_register_dev(struct kvm *kvm, enum kvm_bus bus_idx, gpa_t addr,
35553562
struct kvm_io_bus *new_bus, *bus;
35563563

35573564
bus = kvm->buses[bus_idx];
3565+
if (!bus)
3566+
return -ENOMEM;
3567+
35583568
/* exclude ioeventfd which is limited by maximum fd */
35593569
if (bus->dev_count - bus->ioeventfd_count > NR_IOBUS_DEVS - 1)
35603570
return -ENOSPC;
@@ -3574,45 +3584,41 @@ int kvm_io_bus_register_dev(struct kvm *kvm, enum kvm_bus bus_idx, gpa_t addr,
35743584
}
35753585

35763586
/* Caller must hold slots_lock. */
3577-
int kvm_io_bus_unregister_dev(struct kvm *kvm, enum kvm_bus bus_idx,
3578-
struct kvm_io_device *dev)
3587+
void kvm_io_bus_unregister_dev(struct kvm *kvm, enum kvm_bus bus_idx,
3588+
struct kvm_io_device *dev)
35793589
{
3580-
int i, r;
3590+
int i;
35813591
struct kvm_io_bus *new_bus, *bus;
35823592

35833593
bus = kvm->buses[bus_idx];
3584-
3585-
/*
3586-
* It's possible the bus being released before hand. If so,
3587-
* we're done here.
3588-
*/
35893594
if (!bus)
3590-
return 0;
3595+
return;
35913596

3592-
r = -ENOENT;
35933597
for (i = 0; i < bus->dev_count; i++)
35943598
if (bus->range[i].dev == dev) {
3595-
r = 0;
35963599
break;
35973600
}
35983601

3599-
if (r)
3600-
return r;
3602+
if (i == bus->dev_count)
3603+
return;
36013604

36023605
new_bus = kmalloc(sizeof(*bus) + ((bus->dev_count - 1) *
36033606
sizeof(struct kvm_io_range)), GFP_KERNEL);
3604-
if (!new_bus)
3605-
return -ENOMEM;
3607+
if (!new_bus) {
3608+
pr_err("kvm: failed to shrink bus, removing it completely\n");
3609+
goto broken;
3610+
}
36063611

36073612
memcpy(new_bus, bus, sizeof(*bus) + i * sizeof(struct kvm_io_range));
36083613
new_bus->dev_count--;
36093614
memcpy(new_bus->range + i, bus->range + i + 1,
36103615
(new_bus->dev_count - i) * sizeof(struct kvm_io_range));
36113616

3617+
broken:
36123618
rcu_assign_pointer(kvm->buses[bus_idx], new_bus);
36133619
synchronize_srcu_expedited(&kvm->srcu);
36143620
kfree(bus);
3615-
return r;
3621+
return;
36163622
}
36173623

36183624
struct kvm_io_device *kvm_io_bus_get_dev(struct kvm *kvm, enum kvm_bus bus_idx,
@@ -3625,6 +3631,8 @@ struct kvm_io_device *kvm_io_bus_get_dev(struct kvm *kvm, enum kvm_bus bus_idx,
36253631
srcu_idx = srcu_read_lock(&kvm->srcu);
36263632

36273633
bus = srcu_dereference(kvm->buses[bus_idx], &kvm->srcu);
3634+
if (!bus)
3635+
goto out_unlock;
36283636

36293637
dev_idx = kvm_io_bus_get_first_dev(bus, addr, 1);
36303638
if (dev_idx < 0)

0 commit comments

Comments
 (0)