Commit d30be112 authored by Jason Wang's avatar Jason Wang Committed by Liu Chuang
Browse files

vhost_vdpa: assign irq bypass producer token correctly

stable inclusion
from stable-v5.10.227
commit 0c170b1e918b9afac25e2bbd01eaa2bfc0ece8c0
category: bugfix
bugzilla: https://gitee.com/src-openeuler/kernel/issues/IAYQSA
CVE: CVE-2024-47748

Reference: https://git.kernel.org/pub/scm/linux/kernel/git/torvalds/linux.git/commit/?id=0c170b1e918b9afac25e2bbd01eaa2bfc0ece8c0



--------------------------------

[ 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: default avatarDragos Tatulea <dtatulea@nvidia.com>
Tested-by: default avatarDragos Tatulea <dtatulea@nvidia.com>
Reviewed-by: default avatarDragos Tatulea <dtatulea@nvidia.com>
Fixes: 2cf1ba9a ("vhost_vdpa: implement IRQ offloading in vhost_vdpa")
Signed-off-by: default avatarJason Wang <jasowang@redhat.com>
Message-Id: <20240816031900.18013-1-jasowang@redhat.com>
Signed-off-by: default avatarMichael S. Tsirkin <mst@redhat.com>
Signed-off-by: default avatarSasha Levin <sashal@kernel.org>
Conflicts:
	drivers/vhost/vdpa.c
[Context difference.]
Signed-off-by: default avatarLiu Chuang <liuchuang40@huawei.com>
parent 0c901c96
Loading
Loading
Loading
Loading
+13 −3
Original line number Diff line number Diff line
@@ -193,11 +193,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))
@@ -750,6 +748,14 @@ static long vhost_vdpa_vring_ioctl(struct vhost_vdpa *v, unsigned int cmd,
			vq->last_avail_idx = vq_state.split.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);
@@ -781,12 +787,15 @@ static long vhost_vdpa_vring_ioctl(struct vhost_vdpa *v, unsigned int cmd,
		if (vq->call_ctx.ctx) {
			cb.callback = vhost_vdpa_virtqueue_cb;
			cb.private = vq;
			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;
		}
		ops->set_vq_cb(vdpa, idx, &cb);
		vhost_vdpa_setup_vq_irq(v, idx);
		break;

	case VHOST_SET_VRING_NUM:
@@ -1510,6 +1519,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);