* [PATCH] vhost-vdpa: drop the parent's vq callback before the call fd is released
@ 2026-08-06 15:03 Yu Zhang
0 siblings, 0 replies; only message in thread
From: Yu Zhang @ 2026-08-06 15:03 UTC (permalink / raw)
To: Michael S . Tsirkin, Jason Wang, Eugenio Pérez
Cc: virtualization, kvm, netdev, linux-kernel, Yu Zhang
VHOST_SET_VRING_CALL releases the previous call eventfd inside
vhost_vring_ioctl() -- it swaps the new context into vq->call_ctx.ctx
and then eventfd_ctx_put()s the old one, which is a synchronous
kfree(). The parent vdpa device is only told about the change
afterwards, when vhost_vdpa_vring_ioctl() reaches ops->set_vq_cb().
Parent drivers cache the pointer handed to them in
vdpa_callback::trigger and do not take a reference on it, so
throughout that window the parent holds a dangling eventfd_ctx and may
signal it. The documentation added with the field describes what
signalling it means but says nothing about how long it stays valid.
This is the same hazard that "vhost_vdpa: assign irq bypass producer
token correctly" addressed for the irq bypass producer token, by
moving vhost_vdpa_unsetup_vq_irq() ahead of the vhost_vring_ioctl()
call. The producer token was only one of the two consumers of that
pointer; the one the parent keeps via ->set_vq_cb() was left behind
the free.
With VDUSE the window is directly reachable from userspace, because
the device emulation daemon can inject an interrupt at any time from a
different fd, and neither side shares a lock with the other: VDUSE
takes vq->irq_lock, vhost takes vhost_dev.mutex + vq->mutex.
BUG: KASAN: slab-use-after-free in _raw_spin_lock_irqsave+0x76/0xe0
Write of size 4 at addr ffff8881084e8788 by task vduse_uaf/2987
_raw_spin_lock_irqsave+0x76/0xe0
eventfd_signal_mask+0x69/0x120
vduse_dev_ioctl+0x337/0x1a60 <- vduse_vq_signal_irqfd(), inlined
__x64_sys_ioctl+0x120/0x170 <- VDUSE_VQ_INJECT_IRQ
Allocated by task 2986:
do_eventfd+0x50/0x200
__x64_sys_eventfd2+0x2e/0x40
kmalloc-64, freed 64-byte region [ffff8881084e8780, ffff8881084e87c0)
One thread loops VHOST_SET_VRING_CALL on /dev/vhost-vdpa-N with a
fresh eventfd and then unbinds it, while another loops
VDUSE_VQ_INJECT_IRQ on /dev/vduse/<name>. This reproduces in 5 out of
5 ten-second runs on v7.1.6 and 3 out of 3 on v7.2-rc6. With the patch
there are no reports in 3 out of 3 runs on either, while the same
workload still gets ~30000 interrupts per run delivered into live
eventfds, so the path is still being exercised.
Tell the parent to drop the callback before vhost_vring_ioctl() can
free the eventfd, mirroring what is already done for the bypass
producer, and restore it if the ioctl fails -- on failure the swap
never happened, the old context is still installed, and leaving the
parent without a callback would silently drop that vq's interrupts.
Fixes: 5e68470f4e80 ("vdpa: Add eventfd for the vdpa callback")
Signed-off-by: Yu Zhang <yuz08559@gmail.com>
---
drivers/vhost/vdpa.c | 23 ++++++++++++++++++++++-
1 file changed, 22 insertions(+), 1 deletion(-)
diff --git a/drivers/vhost/vdpa.c b/drivers/vhost/vdpa.c
index ac55275..b7833bd 100644
--- a/drivers/vhost/vdpa.c
+++ b/drivers/vhost/vdpa.c
@@ -714,13 +714,34 @@ static long vhost_vdpa_vring_ioctl(struct vhost_vdpa *v, unsigned int cmd,
if (ops->get_status(vdpa) &
VIRTIO_CONFIG_S_DRIVER_OK)
vhost_vdpa_unsetup_vq_irq(v, idx);
+ /*
+ * The parent caches call_ctx.ctx in cb.trigger without
+ * holding a reference, so it has to stop using it
+ * before vhost_vring_ioctl() drops the last one.
+ */
+ cb.callback = NULL;
+ cb.private = NULL;
+ cb.trigger = NULL;
+ ops->set_vq_cb(vdpa, idx, &cb);
}
break;
}
r = vhost_vring_ioctl(&v->vdev, cmd, argp);
- if (r)
+ if (r) {
+ /*
+ * A failure here means the swap never happened and the old
+ * context is still installed, so give the parent back the
+ * callback that was torn down above.
+ */
+ if (cmd == VHOST_SET_VRING_CALL && vq->call_ctx.ctx) {
+ cb.callback = vhost_vdpa_virtqueue_cb;
+ cb.private = vq;
+ cb.trigger = vq->call_ctx.ctx;
+ ops->set_vq_cb(vdpa, idx, &cb);
+ }
return r;
+ }
switch (cmd) {
case VHOST_SET_VRING_ADDR:
--
2.43.0
^ permalink raw reply related [flat|nested] only message in thread
only message in thread, other threads:[~2026-08-06 15:03 UTC | newest]
Thread overview: (only message) (download: mbox.gz follow: Atom feed
-- links below jump to the message on this page --
2026-08-06 15:03 [PATCH] vhost-vdpa: drop the parent's vq callback before the call fd is released Yu Zhang
This is a public inbox, see mirroring instructions
for how to clone and mirror all data and code used for this inbox;
as well as URLs for NNTP newsgroup(s).