Donate to e Foundation | Murena handsets with /e/OS | Own a part of Murena! Learn more

Commit 840e124f authored by Rustam Kovhaev's avatar Rustam Kovhaev Committed by Greg Kroah-Hartman
Browse files

KVM: fix memory leak in kvm_io_bus_unregister_dev()



[ Upstream commit f65886606c2d3b562716de030706dfe1bea4ed5e ]

when kmalloc() fails in kvm_io_bus_unregister_dev(), before removing
the bus, we should iterate over all other devices linked to it and call
kvm_iodevice_destructor() for them

Fixes: 90db10434b16 ("KVM: kvm_io_bus_unregister_dev() should never fail")
Cc: stable@vger.kernel.org
Reported-and-tested-by: default avatar <syzbot+f196caa45793d6374707@syzkaller.appspotmail.com>
Link: https://syzkaller.appspot.com/bug?extid=f196caa45793d6374707


Signed-off-by: default avatarRustam Kovhaev <rkovhaev@gmail.com>
Reviewed-by: default avatarVitaly Kuznetsov <vkuznets@redhat.com>
Message-Id: <20200907185535.233114-1-rkovhaev@gmail.com>
Signed-off-by: default avatarPaolo Bonzini <pbonzini@redhat.com>
Signed-off-by: default avatarSasha Levin <sashal@kernel.org>
parent 31c59173
Loading
Loading
Loading
Loading
+12 −9
Original line number Diff line number Diff line
@@ -3639,7 +3639,7 @@ int kvm_io_bus_register_dev(struct kvm *kvm, enum kvm_bus bus_idx, gpa_t addr,
void kvm_io_bus_unregister_dev(struct kvm *kvm, enum kvm_bus bus_idx,
			       struct kvm_io_device *dev)
{
	int i;
	int i, j;
	struct kvm_io_bus *new_bus, *bus;

	bus = kvm->buses[bus_idx];
@@ -3656,17 +3656,20 @@ void kvm_io_bus_unregister_dev(struct kvm *kvm, enum kvm_bus bus_idx,

	new_bus = kmalloc(sizeof(*bus) + ((bus->dev_count - 1) *
			  sizeof(struct kvm_io_range)), GFP_KERNEL);
	if (!new_bus)  {
		pr_err("kvm: failed to shrink bus, removing it completely\n");
		goto broken;
	}

	if (new_bus) {
		memcpy(new_bus, bus, sizeof(*bus) + i * sizeof(struct kvm_io_range));
		new_bus->dev_count--;
		memcpy(new_bus->range + i, bus->range + i + 1,
		       (new_bus->dev_count - i) * sizeof(struct kvm_io_range));
	} else {
		pr_err("kvm: failed to shrink bus, removing it completely\n");
		for (j = 0; j < bus->dev_count; j++) {
			if (j == i)
				continue;
			kvm_iodevice_destructor(bus->range[j].dev);
		}
	}

broken:
	rcu_assign_pointer(kvm->buses[bus_idx], new_bus);
	synchronize_srcu_expedited(&kvm->srcu);
	kfree(bus);