]> git.ipfire.org Git - thirdparty/kernel/stable.git/commitdiff
vhost_vdpa: assign irq bypass producer token correctly
authorJason Wang <jasowang@redhat.com>
Fri, 16 Aug 2024 03:19:00 +0000 (11:19 +0800)
committerGreg Kroah-Hartman <gregkh@linuxfoundation.org>
Thu, 17 Oct 2024 13:07:57 +0000 (15:07 +0200)
[ Upstream commit 02e9e9366fefe461719da5d173385b6685f70319 ]

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>
Signed-off-by: Sasha Levin <sashal@kernel.org>
drivers/vhost/vdpa.c

index fdd175730d327ef933969ab7b847d60b79ec96c8..61d11e7317acdc063d6a3102a513bafbb9d5f71d 100644 (file)
@@ -100,11 +100,9 @@ static void vhost_vdpa_setup_vq_irq(struct vhost_vdpa *v, u16 qid)
        if (irq < 0)
                return;
 
-       irq_bypass_unregister_producer(&vq->call_ctx.producer);
        if (!vq->call_ctx.ctx)
                return;
 
-       vq->call_ctx.producer.token = vq->call_ctx.ctx;
        vq->call_ctx.producer.irq = irq;
        ret = irq_bypass_register_producer(&vq->call_ctx.producer);
        if (unlikely(ret))
@@ -397,6 +395,14 @@ static long vhost_vdpa_vring_ioctl(struct vhost_vdpa *v, unsigned int cmd,
 
                vq->last_avail_idx = vq_state.avail_index;
                break;
+       case VHOST_SET_VRING_CALL:
+               if (vq->call_ctx.ctx) {
+                       if (ops->get_status(vdpa) &
+                           VIRTIO_CONFIG_S_DRIVER_OK)
+                               vhost_vdpa_unsetup_vq_irq(v, idx);
+                       vq->call_ctx.producer.token = NULL;
+               }
+               break;
        }
 
        r = vhost_vring_ioctl(&v->vdev, cmd, argp);
@@ -423,13 +429,16 @@ static long vhost_vdpa_vring_ioctl(struct vhost_vdpa *v, unsigned int cmd,
                        cb.callback = vhost_vdpa_virtqueue_cb;
                        cb.private = vq;
                        cb.trigger = vq->call_ctx.ctx;
+                       vq->call_ctx.producer.token = vq->call_ctx.ctx;
+                       if (ops->get_status(vdpa) &
+                           VIRTIO_CONFIG_S_DRIVER_OK)
+                               vhost_vdpa_setup_vq_irq(v, idx);
                } else {
                        cb.callback = NULL;
                        cb.private = NULL;
                        cb.trigger = NULL;
                }
                ops->set_vq_cb(vdpa, idx, &cb);
-               vhost_vdpa_setup_vq_irq(v, idx);
                break;
 
        case VHOST_SET_VRING_NUM:
@@ -890,6 +899,7 @@ static int vhost_vdpa_open(struct inode *inode, struct file *filep)
        for (i = 0; i < nvqs; i++) {
                vqs[i] = &v->vqs[i];
                vqs[i]->handle_kick = handle_vq_kick;
+               vqs[i]->call_ctx.ctx = NULL;
        }
        vhost_dev_init(dev, vqs, nvqs, 0, 0, 0, false,
                       vhost_vdpa_process_iotlb_msg);