All of lore.kernel.org
 help / color / mirror / Atom feed
* [PATCH] virtio: validate split queue head count before popping
@ 2026-07-09 10:05 Jia Jia
  2026-07-09 12:43 ` Michael S. Tsirkin
  0 siblings, 1 reply; 5+ messages in thread
From: Jia Jia @ 2026-07-09 10:05 UTC (permalink / raw)
  To: qemu-devel; +Cc: mst, Jia Jia

virtqueue_split_pop() reads the next split avail ring entry after
virtio_queue_empty_rcu() reports work. Call virtqueue_num_heads() before
consuming that entry, so an avail index distance larger than the queue
size is rejected.

virtqueue_num_heads() also keeps the read barrier needed before the avail
ring entry is read.

This prevents an invalid split queue state from being expanded into
repeated device command processing.

Link: https://gitlab.com/qemu-project/qemu/-/issues/3930
Signed-off-by: Jia Jia <physicalmtea@gmail.com>
---
Tested with qemu-system-x86_64 11.0.50, built from origin/master
f893c46c3931 plus this patch, configured with:
  --target-list=x86_64-softmmu --enable-kvm --disable-tcg

The original virtio-iommu live-vring qtest reproducer used for the
report completed successfully on the patched build without host OOM,
confirming that this reproducer is fixed by the patch.

 hw/virtio/virtio.c | 8 +++++---
 1 file changed, 5 insertions(+), 3 deletions(-)

diff --git a/hw/virtio/virtio.c b/hw/virtio/virtio.c
index f4d86a3655..252bff8641 100644
--- a/hw/virtio/virtio.c
+++ b/hw/virtio/virtio.c
@@ -1751,9 +1751,11 @@ static void *virtqueue_split_pop(VirtQueue *vq, size_t sz)
     if (virtio_queue_empty_rcu(vq)) {
         goto done;
     }
-    /* Needed after virtio_queue_empty(), see comment in
-     * virtqueue_num_heads(). */
-    smp_rmb();
+
+    rc = virtqueue_num_heads(vq, vq->last_avail_idx);
+    if (rc <= 0) {
+        goto done;
+    }
 
     /* When we start there are none of either input nor output. */
     out_num = in_num = elem_entries = 0;
-- 
2.34.1


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

* Re: [PATCH] virtio: validate split queue head count before popping
  2026-07-09 10:05 [PATCH] virtio: validate split queue head count before popping Jia Jia
@ 2026-07-09 12:43 ` Michael S. Tsirkin
  2026-07-10  1:34   ` m'te'a physical
  0 siblings, 1 reply; 5+ messages in thread
From: Michael S. Tsirkin @ 2026-07-09 12:43 UTC (permalink / raw)
  To: Jia Jia; +Cc: qemu-devel

On Thu, Jul 09, 2026 at 06:05:29PM +0800, Jia Jia wrote:
> virtqueue_split_pop() reads the next split avail ring entry after
> virtio_queue_empty_rcu() reports work. Call virtqueue_num_heads() before
> consuming that entry, so an avail index distance larger than the queue
> size is rejected.
> 
> virtqueue_num_heads() also keeps the read barrier needed before the avail
> ring entry is read.
> 
> This prevents an invalid split queue state from being expanded into
> repeated device command processing.
> 
> Link: https://gitlab.com/qemu-project/qemu/-/issues/3930
> Signed-off-by: Jia Jia <physicalmtea@gmail.com>
> ---
> Tested with qemu-system-x86_64 11.0.50, built from origin/master
> f893c46c3931 plus this patch, configured with:
>   --target-list=x86_64-softmmu --enable-kvm --disable-tcg
> 
> The original virtio-iommu live-vring qtest reproducer used for the
> report completed successfully on the patched build without host OOM,
> confirming that this reproducer is fixed by the patch.
> 
>  hw/virtio/virtio.c | 8 +++++---
>  1 file changed, 5 insertions(+), 3 deletions(-)
> 
> diff --git a/hw/virtio/virtio.c b/hw/virtio/virtio.c
> index f4d86a3655..252bff8641 100644
> --- a/hw/virtio/virtio.c
> +++ b/hw/virtio/virtio.c
> @@ -1751,9 +1751,11 @@ static void *virtqueue_split_pop(VirtQueue *vq, size_t sz)
>      if (virtio_queue_empty_rcu(vq)) {
>          goto done;
>      }
> -    /* Needed after virtio_queue_empty(), see comment in
> -     * virtqueue_num_heads(). */
> -    smp_rmb();
> +
> +    rc = virtqueue_num_heads(vq, vq->last_avail_idx);
> +    if (rc <= 0) {
> +        goto done;
> +    }


Do we still need virtio_queue_empty_rcu then? Why?

>      /* When we start there are none of either input nor output. */
>      out_num = in_num = elem_entries = 0;
> -- 
> 2.34.1



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

* Re: [PATCH] virtio: validate split queue head count before popping
  2026-07-09 12:43 ` Michael S. Tsirkin
@ 2026-07-10  1:34   ` m'te'a physical
  2026-07-10  6:21     ` Michael S. Tsirkin
  0 siblings, 1 reply; 5+ messages in thread
From: m'te'a physical @ 2026-07-10  1:34 UTC (permalink / raw)
  To: Michael S. Tsirkin; +Cc: qemu-devel

[-- Attachment #1: Type: text/plain, Size: 3396 bytes --]

I think virtio_queue_empty_rcu() is no longer needed after this change.

For this caller, the pieces it covered are either already handled elsewhere
or
kept explicitly in the split pop path.

The disabled-device check is already done by virtqueue_pop() before it calls
virtqueue_split_pop().

The empty-queue check is covered by virtqueue_num_heads(): rc == 0 is the
normal empty case, and rc < 0 is the invalid avail index distance case that
this
patch wants to reject.

The read barrier is also covered by virtqueue_num_heads(), since it does
smp_rmb() when it returns a positive head count, before the caller reads the
avail ring entry with virtqueue_get_head().

The only old check that I think should stay explicit in
virtqueue_split_pop() is
!vq->vring.avail. virtqueue_num_heads() does not check that, and the old
behavior for an unconfigured split queue was to treat it as empty. So v2
keeps
that guard and replaces the rest of virtio_queue_empty_rcu() with
virtqueue_num_heads():

    RCU_READ_LOCK_GUARD();
    if (unlikely(!vq->vring.avail)) {
        goto done;
    }

    rc = virtqueue_num_heads(vq, vq->last_avail_idx);
    if (rc <= 0) {
        goto done;
    }

With that, virtio_queue_empty_rcu() has no remaining users and can be
removed.

If this looks right, I can send a v2 patch with this change.

Michael S. Tsirkin <mst@redhat.com> 于2026年7月9日周四 20:43写道:

> On Thu, Jul 09, 2026 at 06:05:29PM +0800, Jia Jia wrote:
> > virtqueue_split_pop() reads the next split avail ring entry after
> > virtio_queue_empty_rcu() reports work. Call virtqueue_num_heads() before
> > consuming that entry, so an avail index distance larger than the queue
> > size is rejected.
> >
> > virtqueue_num_heads() also keeps the read barrier needed before the avail
> > ring entry is read.
> >
> > This prevents an invalid split queue state from being expanded into
> > repeated device command processing.
> >
> > Link: https://gitlab.com/qemu-project/qemu/-/issues/3930
> > Signed-off-by: Jia Jia <physicalmtea@gmail.com>
> > ---
> > Tested with qemu-system-x86_64 11.0.50, built from origin/master
> > f893c46c3931 plus this patch, configured with:
> >   --target-list=x86_64-softmmu --enable-kvm --disable-tcg
> >
> > The original virtio-iommu live-vring qtest reproducer used for the
> > report completed successfully on the patched build without host OOM,
> > confirming that this reproducer is fixed by the patch.
> >
> >  hw/virtio/virtio.c | 8 +++++---
> >  1 file changed, 5 insertions(+), 3 deletions(-)
> >
> > diff --git a/hw/virtio/virtio.c b/hw/virtio/virtio.c
> > index f4d86a3655..252bff8641 100644
> > --- a/hw/virtio/virtio.c
> > +++ b/hw/virtio/virtio.c
> > @@ -1751,9 +1751,11 @@ static void *virtqueue_split_pop(VirtQueue *vq,
> size_t sz)
> >      if (virtio_queue_empty_rcu(vq)) {
> >          goto done;
> >      }
> > -    /* Needed after virtio_queue_empty(), see comment in
> > -     * virtqueue_num_heads(). */
> > -    smp_rmb();
> > +
> > +    rc = virtqueue_num_heads(vq, vq->last_avail_idx);
> > +    if (rc <= 0) {
> > +        goto done;
> > +    }
>
>
> Do we still need virtio_queue_empty_rcu then? Why?
>
> >      /* When we start there are none of either input nor output. */
> >      out_num = in_num = elem_entries = 0;
> > --
> > 2.34.1
>
>

[-- Attachment #2: Type: text/html, Size: 4236 bytes --]

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

* Re: [PATCH] virtio: validate split queue head count before popping
  2026-07-10  1:34   ` m'te'a physical
@ 2026-07-10  6:21     ` Michael S. Tsirkin
  2026-07-10  7:35       ` [PATCH v2] " Jia Jia
  0 siblings, 1 reply; 5+ messages in thread
From: Michael S. Tsirkin @ 2026-07-10  6:21 UTC (permalink / raw)
  To: m'te'a physical; +Cc: qemu-devel

On Fri, Jul 10, 2026 at 09:34:54AM +0800, m'te'a physical wrote:
> I think virtio_queue_empty_rcu() is no longer needed after this change.
> 
> For this caller, the pieces it covered are either already handled elsewhere or
> kept explicitly in the split pop path.
> 
> The disabled-device check is already done by virtqueue_pop() before it calls
> virtqueue_split_pop().
> 
> The empty-queue check is covered by virtqueue_num_heads(): rc == 0 is the
> normal empty case, and rc < 0 is the invalid avail index distance case that
> this
> patch wants to reject.
> 
> The read barrier is also covered by virtqueue_num_heads(), since it does
> smp_rmb() when it returns a positive head count, before the caller reads the
> avail ring entry with virtqueue_get_head().
> 
> The only old check that I think should stay explicit in virtqueue_split_pop()
> is
> !vq->vring.avail. virtqueue_num_heads() does not check that, and the old
> behavior for an unconfigured split queue was to treat it as empty. So v2 keeps
> that guard and replaces the rest of virtio_queue_empty_rcu() with
> virtqueue_num_heads():

Yea this is our hacky way to check the mr cache is valid.


>     RCU_READ_LOCK_GUARD();
>     if (unlikely(!vq->vring.avail)) {
>         goto done;
>     }
> 
>     rc = virtqueue_num_heads(vq, vq->last_avail_idx);
>     if (rc <= 0) {
>         goto done;
>     }
> 
> With that, virtio_queue_empty_rcu() has no remaining users and can be removed.
> 
> If this looks right, I can send a v2 patch with this change.
> 
> Michael S. Tsirkin <mst@redhat.com> 于2026年7月9日周四 20:43写道:
> 
>     On Thu, Jul 09, 2026 at 06:05:29PM +0800, Jia Jia wrote:
>     > virtqueue_split_pop() reads the next split avail ring entry after
>     > virtio_queue_empty_rcu() reports work. Call virtqueue_num_heads() before
>     > consuming that entry, so an avail index distance larger than the queue
>     > size is rejected.
>     >
>     > virtqueue_num_heads() also keeps the read barrier needed before the avail
>     > ring entry is read.
>     >
>     > This prevents an invalid split queue state from being expanded into
>     > repeated device command processing.
>     >
>     > Link: https://gitlab.com/qemu-project/qemu/-/issues/3930
>     > Signed-off-by: Jia Jia <physicalmtea@gmail.com>
>     > ---
>     > Tested with qemu-system-x86_64 11.0.50, built from origin/master
>     > f893c46c3931 plus this patch, configured with:
>     >   --target-list=x86_64-softmmu --enable-kvm --disable-tcg
>     >
>     > The original virtio-iommu live-vring qtest reproducer used for the
>     > report completed successfully on the patched build without host OOM,
>     > confirming that this reproducer is fixed by the patch.
>     >
>     >  hw/virtio/virtio.c | 8 +++++---
>     >  1 file changed, 5 insertions(+), 3 deletions(-)
>     >
>     > diff --git a/hw/virtio/virtio.c b/hw/virtio/virtio.c
>     > index f4d86a3655..252bff8641 100644
>     > --- a/hw/virtio/virtio.c
>     > +++ b/hw/virtio/virtio.c
>     > @@ -1751,9 +1751,11 @@ static void *virtqueue_split_pop(VirtQueue *vq,
>     size_t sz)
>     >      if (virtio_queue_empty_rcu(vq)) {
>     >          goto done;
>     >      }
>     > -    /* Needed after virtio_queue_empty(), see comment in
>     > -     * virtqueue_num_heads(). */
>     > -    smp_rmb();
>     > +
>     > +    rc = virtqueue_num_heads(vq, vq->last_avail_idx);
>     > +    if (rc <= 0) {
>     > +        goto done;
>     > +    }
> 
> 
>     Do we still need virtio_queue_empty_rcu then? Why?
> 
>     >      /* When we start there are none of either input nor output. */
>     >      out_num = in_num = elem_entries = 0;
>     > --
>     > 2.34.1
> 
> 



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

* [PATCH v2] virtio: validate split queue head count before popping
  2026-07-10  6:21     ` Michael S. Tsirkin
@ 2026-07-10  7:35       ` Jia Jia
  0 siblings, 0 replies; 5+ messages in thread
From: Jia Jia @ 2026-07-10  7:35 UTC (permalink / raw)
  To: qemu-devel; +Cc: mst

virtqueue_split_pop() reads the next split avail ring entry after
checking that the ring is configured. Call virtqueue_num_heads() before
consuming that entry, so an avail index distance larger than the queue
size is rejected.

virtqueue_num_heads() also reports an empty queue and keeps the read
barrier needed before the avail ring entry is read. Drop the now unused
virtio_queue_empty_rcu() helper.

This prevents an invalid split queue state from being expanded into
repeated device command processing.

Link: https://gitlab.com/qemu-project/qemu/-/issues/3930
Signed-off-by: Jia Jia <physicalmtea@gmail.com>
---
v2:
- Drop virtio_queue_empty_rcu() from the split pop path.
- Keep only the existing !vq->vring.avail early exit before
  virtqueue_num_heads().
- Remove the now unused virtio_queue_empty_rcu() helper.

Tested with qemu-system-x86_64 11.0.50, built from origin/master
f893c46c3931 plus this patch, configured with:
  --target-list=x86_64-softmmu --enable-kvm --disable-tcg

The original virtio-iommu live-vring qtest reproducer used for the
report completed successfully on the patched build without host OOM,
confirming that this reproducer is fixed by the patch.

 hw/virtio/virtio.c | 30 ++++++------------------------
 1 file changed, 6 insertions(+), 24 deletions(-)

diff --git a/hw/virtio/virtio.c b/hw/virtio/virtio.c
index f4d86a3655..68ec3f0751 100644
--- a/hw/virtio/virtio.c
+++ b/hw/virtio/virtio.c
@@ -716,26 +716,6 @@ static inline bool is_desc_avail(uint16_t flags, bool wrap_counter)
     return (avail != used) && (avail == wrap_counter);
 }
 
-/* Fetch avail_idx from VQ memory only when we really need to know if
- * guest has added some buffers.
- * Called within rcu_read_lock().  */
-static int virtio_queue_empty_rcu(VirtQueue *vq)
-{
-    if (virtio_device_disabled(vq->vdev)) {
-        return 1;
-    }
-
-    if (unlikely(!vq->vring.avail)) {
-        return 1;
-    }
-
-    if (vq->shadow_avail_idx != vq->last_avail_idx) {
-        return 0;
-    }
-
-    return vring_avail_idx(vq) == vq->last_avail_idx;
-}
-
 static int virtio_queue_split_empty(VirtQueue *vq)
 {
     bool empty;
@@ -1748,12 +1728,14 @@ static void *virtqueue_split_pop(VirtQueue *vq, size_t sz)
     address_space_cache_init_empty(&indirect_desc_cache);
 
     RCU_READ_LOCK_GUARD();
-    if (virtio_queue_empty_rcu(vq)) {
+    if (unlikely(!vq->vring.avail)) {
+        goto done;
+    }
+
+    rc = virtqueue_num_heads(vq, vq->last_avail_idx);
+    if (rc <= 0) {
         goto done;
     }
-    /* Needed after virtio_queue_empty(), see comment in
-     * virtqueue_num_heads(). */
-    smp_rmb();
 
     /* When we start there are none of either input nor output. */
     out_num = in_num = elem_entries = 0;
-- 
2.34.1


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

end of thread, other threads:[~2026-07-10  7:36 UTC | newest]

Thread overview: 5+ messages (download: mbox.gz follow: Atom feed
-- links below jump to the message on this page --
2026-07-09 10:05 [PATCH] virtio: validate split queue head count before popping Jia Jia
2026-07-09 12:43 ` Michael S. Tsirkin
2026-07-10  1:34   ` m'te'a physical
2026-07-10  6:21     ` Michael S. Tsirkin
2026-07-10  7:35       ` [PATCH v2] " Jia Jia

This is an external index of several public inboxes,
see mirroring instructions on how to clone and mirror
all data and code used by this external index.