kvm_io_bus_get_dev() returns a device that is only matched by the address, and nothing else. This can cause a lifetime issue if the matched device is not the expected type, as by the time the caller can introspect the object, it might be gone (the srcu lock having been dropped). Add an kvm_io_device_ops pointer to the list of things that this helper must check before dropping the lock and returning the pointer, and update the sole user to pass its own ops. Reported-by: Will Deacon Signed-off-by: Marc Zyngier Fixes: 8a39d00670f07 ("KVM: kvm_io_bus: Add kvm_io_bus_get_dev() call") Cc: stable@vger.kernel.org --- arch/arm64/kvm/vgic/vgic-its.c | 5 +---- include/linux/kvm_host.h | 1 + virt/kvm/kvm_main.c | 5 ++++- 3 files changed, 6 insertions(+), 5 deletions(-) diff --git a/arch/arm64/kvm/vgic/vgic-its.c b/arch/arm64/kvm/vgic/vgic-its.c index 4477f870c7b36..1c1bdd420c40d 100644 --- a/arch/arm64/kvm/vgic/vgic-its.c +++ b/arch/arm64/kvm/vgic/vgic-its.c @@ -508,13 +508,10 @@ static struct vgic_its *__vgic_doorbell_to_its(struct kvm *kvm, gpa_t db) struct kvm_io_device *kvm_io_dev; struct vgic_io_device *iodev; - kvm_io_dev = kvm_io_bus_get_dev(kvm, KVM_MMIO_BUS, db); + kvm_io_dev = kvm_io_bus_get_dev(kvm, KVM_MMIO_BUS, &kvm_io_gic_ops, db); if (!kvm_io_dev) return ERR_PTR(-EINVAL); - if (kvm_io_dev->ops != &kvm_io_gic_ops) - return ERR_PTR(-EINVAL); - iodev = container_of(kvm_io_dev, struct vgic_io_device, dev); if (iodev->iodev_type != IODEV_ITS) return ERR_PTR(-EINVAL); diff --git a/include/linux/kvm_host.h b/include/linux/kvm_host.h index 4c14aee1fb063..4c195a8723c40 100644 --- a/include/linux/kvm_host.h +++ b/include/linux/kvm_host.h @@ -231,6 +231,7 @@ int kvm_io_bus_register_dev(struct kvm *kvm, enum kvm_bus bus_idx, gpa_t addr, int kvm_io_bus_unregister_dev(struct kvm *kvm, enum kvm_bus bus_idx, struct kvm_io_device *dev); struct kvm_io_device *kvm_io_bus_get_dev(struct kvm *kvm, enum kvm_bus bus_idx, + const struct kvm_io_device_ops *ops, gpa_t addr); #ifdef CONFIG_KVM_ASYNC_PF diff --git a/virt/kvm/kvm_main.c b/virt/kvm/kvm_main.c index 881f92d7a469e..26c34d500ea18 100644 --- a/virt/kvm/kvm_main.c +++ b/virt/kvm/kvm_main.c @@ -6066,6 +6066,7 @@ int kvm_io_bus_unregister_dev(struct kvm *kvm, enum kvm_bus bus_idx, } struct kvm_io_device *kvm_io_bus_get_dev(struct kvm *kvm, enum kvm_bus bus_idx, + const struct kvm_io_device_ops *ops, gpa_t addr) { struct kvm_io_bus *bus; @@ -6082,7 +6083,9 @@ struct kvm_io_device *kvm_io_bus_get_dev(struct kvm *kvm, enum kvm_bus bus_idx, if (dev_idx < 0) goto out_unlock; - iodev = bus->range[dev_idx].dev; + if (bus->range[dev_idx].dev && + bus->range[dev_idx].dev->ops == ops) + iodev = bus->range[dev_idx].dev; out_unlock: srcu_read_unlock(&kvm->srcu, srcu_idx); -- 2.47.3