Linux kernel mirror (for testing) git.kernel.org/pub/scm/linux/kernel/git/torvalds/linux.git
kernel os linux

vhost_vdpa: assign irq bypass producer token correctly

We used to call irq_bypass_unregister_producer() in
vhost_vdpa_setup_vq_irq() which is problematic as we don't know if the
token pointer is still valid or not.

Actually, we use the eventfd_ctx as the token so the life cycle of the
token should be bound to the VHOST_SET_VRING_CALL instead of
vhost_vdpa_setup_vq_irq() which could be called by set_status().

Fixing this by setting up irq bypass producer's token when handling
VHOST_SET_VRING_CALL and un-registering the producer before calling
vhost_vring_ioctl() to prevent a possible use after free as eventfd
could have been released in vhost_vring_ioctl(). And such registering
and unregistering will only be done if DRIVER_OK is set.

Reported-by: Dragos Tatulea <dtatulea@nvidia.com>
Tested-by: Dragos Tatulea <dtatulea@nvidia.com>
Reviewed-by: Dragos Tatulea <dtatulea@nvidia.com>
Fixes: 2cf1ba9a4d15 ("vhost_vdpa: implement IRQ offloading in vhost_vdpa")
Signed-off-by: Jason Wang <jasowang@redhat.com>
Message-Id: <20240816031900.18013-1-jasowang@redhat.com>
Signed-off-by: Michael S. Tsirkin <mst@redhat.com>

authored by

Jason Wang and committed by
Michael S. Tsirkin
02e9e936 dc125029

+13 -3
+13 -3
drivers/vhost/vdpa.c
··· 209 209 if (irq < 0) 210 210 return; 211 211 212 - irq_bypass_unregister_producer(&vq->call_ctx.producer); 213 212 if (!vq->call_ctx.ctx) 214 213 return; 215 214 216 - vq->call_ctx.producer.token = vq->call_ctx.ctx; 217 215 vq->call_ctx.producer.irq = irq; 218 216 ret = irq_bypass_register_producer(&vq->call_ctx.producer); 219 217 if (unlikely(ret)) ··· 707 709 vq->last_avail_idx = vq_state.split.avail_index; 708 710 } 709 711 break; 712 + case VHOST_SET_VRING_CALL: 713 + if (vq->call_ctx.ctx) { 714 + if (ops->get_status(vdpa) & 715 + VIRTIO_CONFIG_S_DRIVER_OK) 716 + vhost_vdpa_unsetup_vq_irq(v, idx); 717 + vq->call_ctx.producer.token = NULL; 718 + } 719 + break; 710 720 } 711 721 712 722 r = vhost_vring_ioctl(&v->vdev, cmd, argp); ··· 753 747 cb.callback = vhost_vdpa_virtqueue_cb; 754 748 cb.private = vq; 755 749 cb.trigger = vq->call_ctx.ctx; 750 + vq->call_ctx.producer.token = vq->call_ctx.ctx; 751 + if (ops->get_status(vdpa) & 752 + VIRTIO_CONFIG_S_DRIVER_OK) 753 + vhost_vdpa_setup_vq_irq(v, idx); 756 754 } else { 757 755 cb.callback = NULL; 758 756 cb.private = NULL; 759 757 cb.trigger = NULL; 760 758 } 761 759 ops->set_vq_cb(vdpa, idx, &cb); 762 - vhost_vdpa_setup_vq_irq(v, idx); 763 760 break; 764 761 765 762 case VHOST_SET_VRING_NUM: ··· 1428 1419 for (i = 0; i < nvqs; i++) { 1429 1420 vqs[i] = &v->vqs[i]; 1430 1421 vqs[i]->handle_kick = handle_vq_kick; 1422 + vqs[i]->call_ctx.ctx = NULL; 1431 1423 } 1432 1424 vhost_dev_init(dev, vqs, nvqs, 0, 0, 0, false, 1433 1425 vhost_vdpa_process_iotlb_msg);