Skip to content

Commit 5d3c4c7

Browse files
sean-jcbonzini
authored andcommitted
KVM: Stop looking for coalesced MMIO zones if the bus is destroyed
Abort the walk of coalesced MMIO zones if kvm_io_bus_unregister_dev() fails to allocate memory for the new instance of the bus. If it can't instantiate a new bus, unregister_dev() destroys all devices _except_ the target device. But, it doesn't tell the caller that it obliterated the bus and invoked the destructor for all devices that were on the bus. In the coalesced MMIO case, this can result in a deleted list entry dereference due to attempting to continue iterating on coalesced_zones after future entries (in the walk) have been deleted. Opportunistically add curly braces to the for-loop, which encompasses many lines but sneaks by without braces due to the guts being a single if statement. Fixes: f658866 ("KVM: fix memory leak in kvm_io_bus_unregister_dev()") Cc: [email protected] Reported-by: Hao Sun <[email protected]> Signed-off-by: Sean Christopherson <[email protected]> Message-Id: <[email protected]> Signed-off-by: Paolo Bonzini <[email protected]>
1 parent 2ee3757 commit 5d3c4c7

File tree

3 files changed

+24
-9
lines changed

3 files changed

+24
-9
lines changed

include/linux/kvm_host.h

Lines changed: 2 additions & 2 deletions
Original file line numberDiff line numberDiff line change
@@ -192,8 +192,8 @@ int kvm_io_bus_read(struct kvm_vcpu *vcpu, enum kvm_bus bus_idx, gpa_t addr,
192192
int len, void *val);
193193
int kvm_io_bus_register_dev(struct kvm *kvm, enum kvm_bus bus_idx, gpa_t addr,
194194
int len, struct kvm_io_device *dev);
195-
void kvm_io_bus_unregister_dev(struct kvm *kvm, enum kvm_bus bus_idx,
196-
struct kvm_io_device *dev);
195+
int kvm_io_bus_unregister_dev(struct kvm *kvm, enum kvm_bus bus_idx,
196+
struct kvm_io_device *dev);
197197
struct kvm_io_device *kvm_io_bus_get_dev(struct kvm *kvm, enum kvm_bus bus_idx,
198198
gpa_t addr);
199199

virt/kvm/coalesced_mmio.c

Lines changed: 17 additions & 2 deletions
Original file line numberDiff line numberDiff line change
@@ -174,21 +174,36 @@ int kvm_vm_ioctl_unregister_coalesced_mmio(struct kvm *kvm,
174174
struct kvm_coalesced_mmio_zone *zone)
175175
{
176176
struct kvm_coalesced_mmio_dev *dev, *tmp;
177+
int r;
177178

178179
if (zone->pio != 1 && zone->pio != 0)
179180
return -EINVAL;
180181

181182
mutex_lock(&kvm->slots_lock);
182183

183-
list_for_each_entry_safe(dev, tmp, &kvm->coalesced_zones, list)
184+
list_for_each_entry_safe(dev, tmp, &kvm->coalesced_zones, list) {
184185
if (zone->pio == dev->zone.pio &&
185186
coalesced_mmio_in_range(dev, zone->addr, zone->size)) {
186-
kvm_io_bus_unregister_dev(kvm,
187+
r = kvm_io_bus_unregister_dev(kvm,
187188
zone->pio ? KVM_PIO_BUS : KVM_MMIO_BUS, &dev->dev);
188189
kvm_iodevice_destructor(&dev->dev);
190+
191+
/*
192+
* On failure, unregister destroys all devices on the
193+
* bus _except_ the target device, i.e. coalesced_zones
194+
* has been modified. No need to restart the walk as
195+
* there aren't any zones left.
196+
*/
197+
if (r)
198+
break;
189199
}
200+
}
190201

191202
mutex_unlock(&kvm->slots_lock);
192203

204+
/*
205+
* Ignore the result of kvm_io_bus_unregister_dev(), from userspace's
206+
* perspective, the coalesced MMIO is most definitely unregistered.
207+
*/
193208
return 0;
194209
}

virt/kvm/kvm_main.c

Lines changed: 5 additions & 5 deletions
Original file line numberDiff line numberDiff line change
@@ -4621,23 +4621,23 @@ int kvm_io_bus_register_dev(struct kvm *kvm, enum kvm_bus bus_idx, gpa_t addr,
46214621
}
46224622

46234623
/* Caller must hold slots_lock. */
4624-
void kvm_io_bus_unregister_dev(struct kvm *kvm, enum kvm_bus bus_idx,
4625-
struct kvm_io_device *dev)
4624+
int kvm_io_bus_unregister_dev(struct kvm *kvm, enum kvm_bus bus_idx,
4625+
struct kvm_io_device *dev)
46264626
{
46274627
int i, j;
46284628
struct kvm_io_bus *new_bus, *bus;
46294629

46304630
bus = kvm_get_bus(kvm, bus_idx);
46314631
if (!bus)
4632-
return;
4632+
return 0;
46334633

46344634
for (i = 0; i < bus->dev_count; i++)
46354635
if (bus->range[i].dev == dev) {
46364636
break;
46374637
}
46384638

46394639
if (i == bus->dev_count)
4640-
return;
4640+
return 0;
46414641

46424642
new_bus = kmalloc(struct_size(bus, range, bus->dev_count - 1),
46434643
GFP_KERNEL_ACCOUNT);
@@ -4662,7 +4662,7 @@ void kvm_io_bus_unregister_dev(struct kvm *kvm, enum kvm_bus bus_idx,
46624662
}
46634663

46644664
kfree(bus);
4665-
return;
4665+
return new_bus ? 0 : -ENOMEM;
46664666
}
46674667

46684668
struct kvm_io_device *kvm_io_bus_get_dev(struct kvm *kvm, enum kvm_bus bus_idx,

0 commit comments

Comments
 (0)