mirror of https://lore.kernel.org/lkml/
 help / color / mirror / Atom feed
* [PATCH v3 0/3] virtio: fix callback synchronization and avq cleanup on reset
@ 2026-09-11 21:20 Michael S. Tsirkin
  2026-09-11 21:20 ` [PATCH v3 1/3] virtio: synchronize callbacks after device reset Michael S. Tsirkin
                   ` (2 more replies)
  0 siblings, 3 replies; 8+ messages in thread
From: Michael S. Tsirkin @ 2026-09-11 21:20 UTC (permalink / raw)
  To: virtualization
  Cc: jasowangio, eperezma, xuanzhuo, jiri, kmehltretter, sashiko-bot,
	linux-kernel

Two issues with virtio device reset:

1. Karl Mehltretter reported that virtio_reset_device() promises
   callbacks are not in progress after reset, but only PCI transports
   actually synchronize callbacks - other transports leave a window
   where a handler already executing keeps running while the driver
   tears down state.

2. sashiko reported a race in virtio_pci_modern: the avq interrupt
   handler calls virtqueue_get_buf concurrently with
   virtqueue_detach_unused_buf in vp_modern_avq_cleanup, and there
   is no synchronize_irq between reset and cleanup.

Fix 1 by adding virtio_synchronize_cbs in the core after reset,
then dropping the now-redundant per-transport sync calls. Fix 2 by
moving avq cleanup from vp_reset to vp_del_vqs, which runs after
callbacks have been synchronized - and is where buffer teardown
conceptually belongs.

Changes v2->v3:
    patch 1: unchanged
    patch 2: split from v2 patch 2 - legacy part only
    patch 3: new - v2 just dropped the sync from modern vp_reset,
        leaving avq_cleanup there before the removed sync. v3
        moves avq_cleanup out of vp_reset entirely into vp_del_vqs,
        fixing the race. Adds NULL check for admin_vq.info needed
        because find_vqs error paths call vp_del_vqs before it is
        allocated.

Michael S. Tsirkin (3):
  virtio: synchronize callbacks after device reset
  virtio_pci_legacy: drop callback sync on reset
  virtio_pci_modern: move avq cleanup from reset to del_vqs

 drivers/virtio/virtio.c            |  2 ++
 drivers/virtio/virtio_pci_common.c |  2 ++
 drivers/virtio/virtio_pci_common.h |  1 +
 drivers/virtio/virtio_pci_legacy.c |  2 --
 drivers/virtio/virtio_pci_modern.c | 10 ++++------
 5 files changed, 9 insertions(+), 8 deletions(-)

-- 
MST


^ permalink raw reply	[flat|nested] 8+ messages in thread

* [PATCH v3 1/3] virtio: synchronize callbacks after device reset
  2026-09-11 21:20 [PATCH v3 0/3] virtio: fix callback synchronization and avq cleanup on reset Michael S. Tsirkin
@ 2026-09-11 21:20 ` Michael S. Tsirkin
  2026-09-11 22:50   ` Karl Mehltretter
  2026-09-11 21:20 ` [PATCH v3 3/3] virtio_pci_modern: move avq cleanup from reset to del_vqs Michael S. Tsirkin
  2026-09-11 21:20 ` [PATCH v3 2/3] virtio_pci_legacy: drop callback sync on reset Michael S. Tsirkin
  2 siblings, 1 reply; 8+ messages in thread
From: Michael S. Tsirkin @ 2026-09-11 21:20 UTC (permalink / raw)
  To: virtualization
  Cc: jasowangio, eperezma, xuanzhuo, jiri, kmehltretter, sashiko-bot,
	linux-kernel

virtio_reset_device says:
	Note: this guarantees that vq callbacks are not in progress
but in practice, only virtio pci correctly synchronizes the cbs.

On other transports, a callback that is already executing, keeps running
while the driver tears down the state it uses.

Add virtio_synchronize_cbs to virtio_reset_device fixing this for all
transports that correctly implement synchronize_cbs().

Reported-by: Karl Mehltretter <kmehltretter@gmail.com>
Link: https://lore.kernel.org/all/20260818040433.66986-1-kmehltretter@gmail.com/
Fixes: c46eccdaadab ("virtio: document virtio_reset_device")
Signed-off-by: Michael S. Tsirkin <mst@redhat.com>
Assisted-by: LLM
---
Changes v2->v3: unchanged

 drivers/virtio/virtio.c | 2 ++
 1 file changed, 2 insertions(+)

diff --git a/drivers/virtio/virtio.c b/drivers/virtio/virtio.c
index b6c9e927bef5..a8588d7ad109 100644
--- a/drivers/virtio/virtio.c
+++ b/drivers/virtio/virtio.c
@@ -264,6 +264,8 @@ void virtio_reset_device(struct virtio_device *dev)
 #endif
 
 	dev->config->reset(dev);
+	/* Flush pending VQ/configuration callbacks. */
+	virtio_synchronize_cbs(dev);
 }
 EXPORT_SYMBOL_GPL(virtio_reset_device);
 
-- 
MST


^ permalink raw reply	[flat|nested] 8+ messages in thread

* [PATCH v3 3/3] virtio_pci_modern: move avq cleanup from reset to del_vqs
  2026-09-11 21:20 [PATCH v3 0/3] virtio: fix callback synchronization and avq cleanup on reset Michael S. Tsirkin
  2026-09-11 21:20 ` [PATCH v3 1/3] virtio: synchronize callbacks after device reset Michael S. Tsirkin
@ 2026-09-11 21:20 ` Michael S. Tsirkin
  2026-09-11 22:48   ` Karl Mehltretter
  2026-09-11 21:20 ` [PATCH v3 2/3] virtio_pci_legacy: drop callback sync on reset Michael S. Tsirkin
  2 siblings, 1 reply; 8+ messages in thread
From: Michael S. Tsirkin @ 2026-09-11 21:20 UTC (permalink / raw)
  To: virtualization
  Cc: jasowangio, eperezma, xuanzhuo, jiri, kmehltretter, sashiko-bot,
	linux-kernel

vp_modern_avq_cleanup() detaches unused buffers from the admin
virtqueue and completes pending commands with -EIO. Calling it
from vp_reset() is incorrect: virtqueue_get_buf in the avq
interrupt handler can race with virtqueue_detach_unused_buf in
cleanup, and get_buf after detach is not documented as valid.

The root cause is that detaching buffers does not belong in
reset at all - reset quiesces the device, while cleanup belongs
where the virtqueue is about to be destroyed, in del_vqs.

Move the call to vp_del_vqs(), which runs after
virtio_synchronize_cbs() has already guaranteed that no
interrupt handler is in progress, eliminating the race.

Reported-by: sashiko-bot@kernel.org
Closes: https://lore.kernel.org/virtualization/20260911125745.E0A2F1F00899@smtp.kernel.org/
Fixes: 4c3b54af907e ("virtio_pci_modern: use completion instead of busy loop to wait on admin cmd result")
Cc: Jiri Pirko <jiri@resnulli.us>
Signed-off-by: Michael S. Tsirkin <mst@redhat.com>
Assisted-by: LLM
---
New in v3. v2 dropped sync from modern vp_reset in one combined
patch, leaving avq_cleanup in vp_reset. v3 moves avq_cleanup out
of vp_reset entirely into vp_del_vqs, fixing the race. Adds NULL
check for admin_vq.info for find_vqs error paths.

 drivers/virtio/virtio_pci_common.c |  2 ++
 drivers/virtio/virtio_pci_common.h |  1 +
 drivers/virtio/virtio_pci_modern.c | 10 ++++------
 3 files changed, 7 insertions(+), 6 deletions(-)

diff --git a/drivers/virtio/virtio_pci_common.c b/drivers/virtio/virtio_pci_common.c
index b90c174450b2..28b254ee4726 100644
--- a/drivers/virtio/virtio_pci_common.c
+++ b/drivers/virtio/virtio_pci_common.c
@@ -270,6 +270,8 @@ void vp_del_vqs(struct virtio_device *vdev)
 	struct virtqueue *vq, *n;
 	int i;
 
+	vp_modern_avq_cleanup(vdev);
+
 	list_for_each_entry_safe(vq, n, &vdev->vqs, list) {
 		info = vp_is_avq(vdev, vq->index) ? vp_dev->admin_vq.info :
 						    vp_dev->vqs[vq->index];
diff --git a/drivers/virtio/virtio_pci_common.h b/drivers/virtio/virtio_pci_common.h
index 8cd01de27baf..a4ff6ec903a3 100644
--- a/drivers/virtio/virtio_pci_common.h
+++ b/drivers/virtio/virtio_pci_common.h
@@ -194,6 +194,7 @@ struct virtio_device *virtio_pci_vf_get_pf_dev(struct pci_dev *pdev);
 #endif
 
 bool vp_is_avq(struct virtio_device *vdev, unsigned int index);
+void vp_modern_avq_cleanup(struct virtio_device *vdev);
 void vp_modern_avq_done(struct virtqueue *vq);
 int vp_modern_admin_cmd_exec(struct virtio_device *vdev,
 			     struct virtio_admin_cmd *cmd);
diff --git a/drivers/virtio/virtio_pci_modern.c b/drivers/virtio/virtio_pci_modern.c
index 6d8ae2a6a8ca..ef76f35c6b2c 100644
--- a/drivers/virtio/virtio_pci_modern.c
+++ b/drivers/virtio/virtio_pci_modern.c
@@ -345,7 +345,7 @@ static void vp_modern_avq_activate(struct virtio_device *vdev)
 	virtio_pci_admin_cmd_cap_init(vdev);
 }
 
-static void vp_modern_avq_cleanup(struct virtio_device *vdev)
+void vp_modern_avq_cleanup(struct virtio_device *vdev)
 {
 	struct virtio_pci_device *vp_dev = to_vp_device(vdev);
 	struct virtio_admin_cmd *cmd;
@@ -354,6 +354,9 @@ static void vp_modern_avq_cleanup(struct virtio_device *vdev)
 	if (!virtio_has_feature(vdev, VIRTIO_F_ADMIN_VQ))
 		return;
 
+	if (!vp_dev->admin_vq.info)
+		return;
+
 	vq = vp_dev->admin_vq.info->vq;
 	if (!vq)
 		return;
@@ -557,11 +560,6 @@ static void vp_reset(struct virtio_device *vdev)
 	 */
 	while (vp_modern_get_status(mdev))
 		msleep(1);
-
-	vp_modern_avq_cleanup(vdev);
-
-	/* Flush pending VQ/configuration callbacks. */
-	vp_synchronize_vectors(vdev);
 }
 
 static int vp_active_vq(struct virtqueue *vq, u16 msix_vec)
-- 
MST


^ permalink raw reply	[flat|nested] 8+ messages in thread

* [PATCH v3 2/3] virtio_pci_legacy: drop callback sync on reset
  2026-09-11 21:20 [PATCH v3 0/3] virtio: fix callback synchronization and avq cleanup on reset Michael S. Tsirkin
  2026-09-11 21:20 ` [PATCH v3 1/3] virtio: synchronize callbacks after device reset Michael S. Tsirkin
  2026-09-11 21:20 ` [PATCH v3 3/3] virtio_pci_modern: move avq cleanup from reset to del_vqs Michael S. Tsirkin
@ 2026-09-11 21:20 ` Michael S. Tsirkin
  2026-09-11 22:51   ` Karl Mehltretter
  2 siblings, 1 reply; 8+ messages in thread
From: Michael S. Tsirkin @ 2026-09-11 21:20 UTC (permalink / raw)
  To: virtualization
  Cc: jasowangio, eperezma, xuanzhuo, jiri, kmehltretter, sashiko-bot,
	linux-kernel

The virtio core now synchronizes callbacks after reset,
so the legacy PCI transport no longer needs to do it itself.

Assisted-by: LLM
Signed-off-by: Michael S. Tsirkin <mst@redhat.com>
---
Changes v2->v3: split from v2 patch 2 (legacy part only)

 drivers/virtio/virtio_pci_legacy.c | 2 --
 1 file changed, 2 deletions(-)

diff --git a/drivers/virtio/virtio_pci_legacy.c b/drivers/virtio/virtio_pci_legacy.c
index d9cbb02b35a1..8115aa39e01e 100644
--- a/drivers/virtio/virtio_pci_legacy.c
+++ b/drivers/virtio/virtio_pci_legacy.c
@@ -98,8 +98,6 @@ static void vp_reset(struct virtio_device *vdev)
 	/* Flush out the status write, and flush in device writes,
 	 * including MSi-X interrupts, if any. */
 	vp_legacy_get_status(&vp_dev->ldev);
-	/* Flush pending VQ/configuration callbacks. */
-	vp_synchronize_vectors(vdev);
 }
 
 static u16 vp_config_vector(struct virtio_pci_device *vp_dev, u16 vector)
-- 
MST


^ permalink raw reply	[flat|nested] 8+ messages in thread

* Re: [PATCH v3 3/3] virtio_pci_modern: move avq cleanup from reset to del_vqs
  2026-09-11 21:20 ` [PATCH v3 3/3] virtio_pci_modern: move avq cleanup from reset to del_vqs Michael S. Tsirkin
@ 2026-09-11 22:48   ` Karl Mehltretter
  2026-09-11 22:51     ` Michael S. Tsirkin
  0 siblings, 1 reply; 8+ messages in thread
From: Karl Mehltretter @ 2026-09-11 22:48 UTC (permalink / raw)
  To: Michael S. Tsirkin
  Cc: virtualization, jasowangio, eperezma, xuanzhuo, jiri,
	sashiko-bot, linux-kernel

On Fri, Sep 11, 2026 at 05:20:15PM +0100, Michael S. Tsirkin wrote:
> New in v3. v2 dropped sync from modern vp_reset in one combined
> patch, leaving avq_cleanup in vp_reset. v3 moves avq_cleanup out
> of vp_reset entirely into vp_del_vqs, fixing the race. Adds NULL
> check for admin_vq.info for find_vqs error paths.
> 

I think patch 3 introduces a use-after-free in the MSI-X setup error
path.

With VP_VQ_VECTOR_POLICY_EACH, vp_setup_vq() stores the admin queue's
info pointer before request_irq(). If request_irq() fails, vp_del_vq()
frees info but leaves admin_vq.info set.

vp_find_vqs_msix() then calls vp_del_vqs(). vp_del_vqs() now calls
vp_modern_avq_cleanup() first and dereferences the freed pointer:

    vq = vp_dev->admin_vq.info->vq;

No interrupt needs to fire.

I reproduced this on v3 with KASAN by returning -ENOMEM only for the
admin queue's request_irq():

    BUG: KASAN: slab-use-after-free in vp_modern_avq_cleanup+0xf4/0x110

KASAN shows the allocation in vp_setup_vq(), the free in
vp_find_one_vq_msix(), and the access in vp_modern_avq_cleanup(). Adding
*p_info = NULL after vp_del_vq() made the same test fall back and boot
without a KASAN report.

Thanks,
Karl

^ permalink raw reply	[flat|nested] 8+ messages in thread

* Re: [PATCH v3 1/3] virtio: synchronize callbacks after device reset
  2026-09-11 21:20 ` [PATCH v3 1/3] virtio: synchronize callbacks after device reset Michael S. Tsirkin
@ 2026-09-11 22:50   ` Karl Mehltretter
  0 siblings, 0 replies; 8+ messages in thread
From: Karl Mehltretter @ 2026-09-11 22:50 UTC (permalink / raw)
  To: Michael S. Tsirkin
  Cc: virtualization, jasowangio, eperezma, xuanzhuo, jiri,
	sashiko-bot, linux-kernel

On Fri, Sep 11, 2026 at 05:20:09PM +0100, Michael S. Tsirkin wrote:
> Add virtio_synchronize_cbs to virtio_reset_device fixing this for all
> transports that correctly implement synchronize_cbs().
> 

I ran 1,200 instrumented virtio-input reset cycles on x86-64 PCI and
arm64 virtio-mmio under KASAN, KCSAN and lockdep. All 173 callback/reset
overlaps completed before reset returned.

Tested-by: Karl Mehltretter <kmehltretter@gmail.com>
Acked-by: Karl Mehltretter <kmehltretter@gmail.com>

Thanks,
Karl

^ permalink raw reply	[flat|nested] 8+ messages in thread

* Re: [PATCH v3 3/3] virtio_pci_modern: move avq cleanup from reset to del_vqs
  2026-09-11 22:48   ` Karl Mehltretter
@ 2026-09-11 22:51     ` Michael S. Tsirkin
  0 siblings, 0 replies; 8+ messages in thread
From: Michael S. Tsirkin @ 2026-09-11 22:51 UTC (permalink / raw)
  To: Karl Mehltretter
  Cc: virtualization, jasowangio, eperezma, xuanzhuo, jiri,
	sashiko-bot, linux-kernel

On Sat, Sep 12, 2026 at 12:48:54AM +0200, Karl Mehltretter wrote:
> On Fri, Sep 11, 2026 at 05:20:15PM +0100, Michael S. Tsirkin wrote:
> > New in v3. v2 dropped sync from modern vp_reset in one combined
> > patch, leaving avq_cleanup in vp_reset. v3 moves avq_cleanup out
> > of vp_reset entirely into vp_del_vqs, fixing the race. Adds NULL
> > check for admin_vq.info for find_vqs error paths.
> > 
> 
> I think patch 3 introduces a use-after-free in the MSI-X setup error
> path.
> 
> With VP_VQ_VECTOR_POLICY_EACH, vp_setup_vq() stores the admin queue's
> info pointer before request_irq(). If request_irq() fails, vp_del_vq()
> frees info but leaves admin_vq.info set.
> 
> vp_find_vqs_msix() then calls vp_del_vqs(). vp_del_vqs() now calls
> vp_modern_avq_cleanup() first and dereferences the freed pointer:
> 
>     vq = vp_dev->admin_vq.info->vq;
> 
> No interrupt needs to fire.
> 
> I reproduced this on v3 with KASAN by returning -ENOMEM only for the
> admin queue's request_irq():
> 
>     BUG: KASAN: slab-use-after-free in vp_modern_avq_cleanup+0xf4/0x110
> 
> KASAN shows the allocation in vp_setup_vq(), the free in
> vp_find_one_vq_msix(), and the access in vp_modern_avq_cleanup(). Adding
> *p_info = NULL after vp_del_vq() made the same test fall back and boot
> without a KASAN report.
> 
> Thanks,
> Karl

Right. Besides, poking at modern from common is ugly. I'll rework this,
thanks a lot for the testing!

-- 
MST


^ permalink raw reply	[flat|nested] 8+ messages in thread

* Re: [PATCH v3 2/3] virtio_pci_legacy: drop callback sync on reset
  2026-09-11 21:20 ` [PATCH v3 2/3] virtio_pci_legacy: drop callback sync on reset Michael S. Tsirkin
@ 2026-09-11 22:51   ` Karl Mehltretter
  0 siblings, 0 replies; 8+ messages in thread
From: Karl Mehltretter @ 2026-09-11 22:51 UTC (permalink / raw)
  To: Michael S. Tsirkin
  Cc: virtualization, jasowangio, eperezma, xuanzhuo, jiri,
	sashiko-bot, linux-kernel

On Fri, Sep 11, 2026 at 05:20:18PM +0100, Michael S. Tsirkin wrote:
> The virtio core now synchronizes callbacks after reset,
> so the legacy PCI transport no longer needs to do it itself.
> 

I ran 200 instrumented virtio-input reset cycles over legacy INTx under
KASAN and lockdep. All 19 callback/reset overlaps completed before reset
returned.

Tested-by: Karl Mehltretter <kmehltretter@gmail.com>
Acked-by: Karl Mehltretter <kmehltretter@gmail.com>

Thanks,
Karl

^ permalink raw reply	[flat|nested] 8+ messages in thread

end of thread, other threads:[~2026-09-11 22:52 UTC | newest]

Thread overview: 8+ messages (download: mbox.gz / follow: Atom feed)
-- links below jump to the message on this page --
2026-09-11 21:20 [PATCH v3 0/3] virtio: fix callback synchronization and avq cleanup on reset Michael S. Tsirkin
2026-09-11 21:20 ` [PATCH v3 1/3] virtio: synchronize callbacks after device reset Michael S. Tsirkin
2026-09-11 22:50   ` Karl Mehltretter
2026-09-11 21:20 ` [PATCH v3 3/3] virtio_pci_modern: move avq cleanup from reset to del_vqs Michael S. Tsirkin
2026-09-11 22:48   ` Karl Mehltretter
2026-09-11 22:51     ` Michael S. Tsirkin
2026-09-11 21:20 ` [PATCH v3 2/3] virtio_pci_legacy: drop callback sync on reset Michael S. Tsirkin
2026-09-11 22:51   ` Karl Mehltretter

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

all inboxes | Powered by JetHome®