Kernel KVM virtualization development
 help / color / mirror / Atom feed
* [RFC PATCH] vhost-scsi: Serialize completion notification with callfd updates
@ 2026-07-20  8:53 Jia Jia
  0 siblings, 0 replies; only message in thread
From: Jia Jia @ 2026-07-20  8:53 UTC (permalink / raw)
  To: mst, jasowangio, michael.christie
  Cc: pbonzini, stefanha, eperezma, virtualization, kvm

During userspace stress testing on a KASAN-enabled host, a host-side
callfd replacement concurrent with ordinary SCSI completions exposed an
eventfd_ctx lifetime race. The test requires access to /dev/vhost-scsi and
a host process that can issue VHOST_SET_VRING_CALL; on the default device
permissions this normally means root or an explicitly delegated service.

The test used TUR completions while closing the old callfd and binding a
new one. The unbind form tends to produce a NULL pointer, while replacing
the callfd after closing the old file makes the old eventfd_ctx eligible
for release.

vhost_scsi_complete_cmd_work() drops vq->mutex before calling
vhost_signal(). VHOST_SET_VRING_CALL updates call_ctx under the same mutex
and puts the old eventfd_ctx before returning. vhost_signal() does not take
the mutex or hold a reference to call_ctx.ctx:

	if (vq->call_ctx.ctx && vhost_notify(dev, vq))
		eventfd_signal(vq->call_ctx.ctx);

	CPU 0 (ioctl / callfd replace)		CPU 1 (vhost worker)

						vhost_scsi_complete_cmd_work()
						mutex_unlock(&vq->mutex)
						vhost_signal()
						  /* load old ctx */
						  vhost_notify() ...

	VHOST_SET_VRING_CALL
	mutex_lock(&vq->mutex)
	swap(vq->call_ctx.ctx, new)
	eventfd_ctx_put(old)	/* free */
	mutex_unlock(&vq->mutex)

						eventfd_signal(old)
						  /* use-after-free */

On the reproducing run, the worker ran on CPU 1 and the callfd ioctl path
ran on CPU 0. KASAN reported:

	BUG: KASAN: slab-use-after-free in eventfd_signal_mask+0x6c/0x110

The use stack was:

	eventfd_signal_mask
	vhost_signal
	vhost_scsi_complete_cmd_work
	vhost_run_work_list
	vhost_task_fn

The freeing stack was:

	eventfd_ctx_put
	vhost_vring_ioctl
	vhost_scsi_ioctl
	__x64_sys_ioctl

Hold vq->mutex across vhost_signal() so SET_VRING_CALL cannot swap and
release the context while the completion worker is notifying the guest.
This gives the completion and callfd update a clear ordering without
changing the vhost API or adding a new lock. Other vhost-scsi response
paths already signal while holding the virtqueue mutex.

A private eventfd reference taken under the mutex would allow signalling
after unlock, but it needs additional reference-count plumbing. An RCU
design would require broader vhost-core changes for all call_ctx readers
and additional eventfd lifetime rules. Since callfd changes are a
control-plane operation, extending the existing per-virtqueue critical
section is the smaller complete fix.

This is an RFC because keeping vhost_notify() and eventfd_signal() inside
the virtqueue critical section extends the lock hold time slightly. I'd
appreciate feedback on whether this is acceptable or whether a
reference-pinning approach, taking the reference under the mutex and
releasing it after unlock, would be preferable.

Signed-off-by: Jia Jia <physicalmtea@gmail.com>
---
 drivers/vhost/scsi.c | 2 +-
 1 file changed, 1 insertion(+), 1 deletion(-)

diff --git a/drivers/vhost/scsi.c b/drivers/vhost/scsi.c
index 9a1253b9d8c5..7f46bc0de3c2 100644
--- a/drivers/vhost/scsi.c
+++ b/drivers/vhost/scsi.c
@@ -735,10 +735,9 @@ static void vhost_scsi_complete_cmd_work(struct vhost_work *work)
 		vhost_scsi_release_cmd_res(se_cmd);
 	}
 
-	mutex_unlock(&svq->vq.mutex);
-
 	if (signal)
 		vhost_signal(&svq->vs->dev, &svq->vq);
+	mutex_unlock(&svq->vq.mutex);
 }
 
 static struct vhost_scsi_cmd *
-- 
2.34.1

^ permalink raw reply related	[flat|nested] only message in thread

only message in thread, other threads:[~2026-07-20  8:53 UTC | newest]

Thread overview: (only message) (download: mbox.gz follow: Atom feed
-- links below jump to the message on this page --
2026-07-20  8:53 [RFC PATCH] vhost-scsi: Serialize completion notification with callfd updates Jia Jia

This is a public inbox, see mirroring instructions
for how to clone and mirror all data and code used for this inbox