[PATCH for-11.1] virtio: avoid packed vring virtio_queue_empty() infinite loops (CVE-2026-16457)

Stefan Hajnoczi posted 1 patch 3 days, 19 hours ago
Failed in applying to current master (apply log)
Maintainers: "Michael S. Tsirkin" <mst@redhat.com>
hw/virtio/virtio.c | 4 ++++
1 file changed, 4 insertions(+)
[PATCH for-11.1] virtio: avoid packed vring virtio_queue_empty() infinite loops (CVE-2026-16457)
Posted by Stefan Hajnoczi 3 days, 19 hours ago
Virtqueue handler functions in device emulation code often look
something like this:

  while (!virtio_queue_empty(vq)) {
      ...pop and process virtqueue element...
  }

virtio-blk, virtio-scsi, virtio-crypto, and vhost-shadow-virtqueue use
this pattern.

The device may break (i.e. hit an error that requires device reset)
during the loop. virtio_queue_empty() returns 1 for broken split vrings
but not for broken packed vrings, leading to an infinite loop.

Adjust the packed vring behavior to match split vrings and avoid
infinite loops.

Fixes: CVE-2026-16457
Resolves: https://gitlab.com/qemu-project/qemu/-/work_items/3968
Reported-by: Anatol Belski <anbelski@linux.microsoft.com>
Signed-off-by: Stefan Hajnoczi <stefanha@redhat.com>
---
 hw/virtio/virtio.c | 4 ++++
 1 file changed, 4 insertions(+)

diff --git a/hw/virtio/virtio.c b/hw/virtio/virtio.c
index f4d86a36553..3795e3c8c68 100644
--- a/hw/virtio/virtio.c
+++ b/hw/virtio/virtio.c
@@ -763,6 +763,10 @@ static int virtio_queue_packed_empty_rcu(VirtQueue *vq)
     struct VRingPackedDesc desc;
     VRingMemoryRegionCaches *cache;
 
+    if (virtio_device_disabled(vq->vdev)) {
+        return 1;
+    }
+
     if (unlikely(!vq->vring.desc)) {
         return 1;
     }
-- 
2.55.0
Re: [PATCH for-11.1] virtio: avoid packed vring virtio_queue_empty() infinite loops (CVE-2026-16457)
Posted by Philippe Mathieu-Daudé 3 days, 18 hours ago
On 21/7/26 15:44, Stefan Hajnoczi wrote:
> Virtqueue handler functions in device emulation code often look
> something like this:
> 
>    while (!virtio_queue_empty(vq)) {
>        ...pop and process virtqueue element...
>    }
> 
> virtio-blk, virtio-scsi, virtio-crypto, and vhost-shadow-virtqueue use
> this pattern.
> 
> The device may break (i.e. hit an error that requires device reset)
> during the loop. virtio_queue_empty() returns 1 for broken split vrings
> but not for broken packed vrings, leading to an infinite loop.
> 
> Adjust the packed vring behavior to match split vrings and avoid
> infinite loops.
> 
> Fixes: CVE-2026-16457
> Resolves: https://gitlab.com/qemu-project/qemu/-/work_items/3968
> Reported-by: Anatol Belski <anbelski@linux.microsoft.com>
> Signed-off-by: Stefan Hajnoczi <stefanha@redhat.com>
> ---
>   hw/virtio/virtio.c | 4 ++++
>   1 file changed, 4 insertions(+)
> 
> diff --git a/hw/virtio/virtio.c b/hw/virtio/virtio.c
> index f4d86a36553..3795e3c8c68 100644
> --- a/hw/virtio/virtio.c
> +++ b/hw/virtio/virtio.c
> @@ -763,6 +763,10 @@ static int virtio_queue_packed_empty_rcu(VirtQueue *vq)
>       struct VRingPackedDesc desc;
>       VRingMemoryRegionCaches *cache;
>   
> +    if (virtio_device_disabled(vq->vdev)) {
> +        return 1;
> +    }

Correct, so:
Reviewed-by: Philippe Mathieu-Daudé <philmd@oss.qualcomm.com>

But why not run this check in virtio_queue_empty() entry?

>       if (unlikely(!vq->vring.desc)) {
>           return 1;
>       }


Re: [PATCH for-11.1] virtio: avoid packed vring virtio_queue_empty() infinite loops (CVE-2026-16457)
Posted by Philippe Mathieu-Daudé 3 days, 18 hours ago
On 21/7/26 16:26, Philippe Mathieu-Daudé wrote:
> On 21/7/26 15:44, Stefan Hajnoczi wrote:
>> Virtqueue handler functions in device emulation code often look
>> something like this:
>>
>>    while (!virtio_queue_empty(vq)) {
>>        ...pop and process virtqueue element...
>>    }
>>
>> virtio-blk, virtio-scsi, virtio-crypto, and vhost-shadow-virtqueue use
>> this pattern.
>>
>> The device may break (i.e. hit an error that requires device reset)
>> during the loop. virtio_queue_empty() returns 1 for broken split vrings
>> but not for broken packed vrings, leading to an infinite loop.
>>
>> Adjust the packed vring behavior to match split vrings and avoid
>> infinite loops.
>>
>> Fixes: CVE-2026-16457
>> Resolves: https://gitlab.com/qemu-project/qemu/-/work_items/3968
>> Reported-by: Anatol Belski <anbelski@linux.microsoft.com>
>> Signed-off-by: Stefan Hajnoczi <stefanha@redhat.com>
>> ---
>>   hw/virtio/virtio.c | 4 ++++
>>   1 file changed, 4 insertions(+)
>>
>> diff --git a/hw/virtio/virtio.c b/hw/virtio/virtio.c
>> index f4d86a36553..3795e3c8c68 100644
>> --- a/hw/virtio/virtio.c
>> +++ b/hw/virtio/virtio.c
>> @@ -763,6 +763,10 @@ static int 
>> virtio_queue_packed_empty_rcu(VirtQueue *vq)
>>       struct VRingPackedDesc desc;
>>       VRingMemoryRegionCaches *cache;
>> +    if (virtio_device_disabled(vq->vdev)) {
>> +        return 1;
>> +    }
> 
> Correct, so:
> Reviewed-by: Philippe Mathieu-Daudé <philmd@oss.qualcomm.com>
> 
> But why not run this check in virtio_queue_empty() entry?

To be clear:

-- >8 --
diff --git a/hw/virtio/virtio.c b/hw/virtio/virtio.c
index f4d86a36553..019c9c9e293 100644
--- a/hw/virtio/virtio.c
+++ b/hw/virtio/virtio.c
@@ -786,6 +786,9 @@ static int virtio_queue_packed_empty(VirtQueue *vq)

  int virtio_queue_empty(VirtQueue *vq)
  {
+    if (virtio_device_disabled(vq->vdev)) {
+        return 1;
+    }
      if (virtio_vdev_has_feature(vq->vdev, VIRTIO_F_RING_PACKED)) {
          return virtio_queue_packed_empty(vq);
      } else {
---

Having:

static inline bool virtio_device_disabled(VirtIODevice *vdev)
{
     return unlikely(vdev->disabled || vdev->broken);
}

that would avoid the RCU_READ_LOCK access.

Re: [PATCH for-11.1] virtio: avoid packed vring virtio_queue_empty() infinite loops (CVE-2026-16457)
Posted by Stefan Hajnoczi 3 days, 17 hours ago
On Tue, Jul 21, 2026 at 10:33 AM Philippe Mathieu-Daudé
<philmd@oss.qualcomm.com> wrote:
>
> On 21/7/26 16:26, Philippe Mathieu-Daudé wrote:
> > On 21/7/26 15:44, Stefan Hajnoczi wrote:
> >> Virtqueue handler functions in device emulation code often look
> >> something like this:
> >>
> >>    while (!virtio_queue_empty(vq)) {
> >>        ...pop and process virtqueue element...
> >>    }
> >>
> >> virtio-blk, virtio-scsi, virtio-crypto, and vhost-shadow-virtqueue use
> >> this pattern.
> >>
> >> The device may break (i.e. hit an error that requires device reset)
> >> during the loop. virtio_queue_empty() returns 1 for broken split vrings
> >> but not for broken packed vrings, leading to an infinite loop.
> >>
> >> Adjust the packed vring behavior to match split vrings and avoid
> >> infinite loops.
> >>
> >> Fixes: CVE-2026-16457
> >> Resolves: https://gitlab.com/qemu-project/qemu/-/work_items/3968
> >> Reported-by: Anatol Belski <anbelski@linux.microsoft.com>
> >> Signed-off-by: Stefan Hajnoczi <stefanha@redhat.com>
> >> ---
> >>   hw/virtio/virtio.c | 4 ++++
> >>   1 file changed, 4 insertions(+)
> >>
> >> diff --git a/hw/virtio/virtio.c b/hw/virtio/virtio.c
> >> index f4d86a36553..3795e3c8c68 100644
> >> --- a/hw/virtio/virtio.c
> >> +++ b/hw/virtio/virtio.c
> >> @@ -763,6 +763,10 @@ static int
> >> virtio_queue_packed_empty_rcu(VirtQueue *vq)
> >>       struct VRingPackedDesc desc;
> >>       VRingMemoryRegionCaches *cache;
> >> +    if (virtio_device_disabled(vq->vdev)) {
> >> +        return 1;
> >> +    }
> >
> > Correct, so:
> > Reviewed-by: Philippe Mathieu-Daudé <philmd@oss.qualcomm.com>
> >
> > But why not run this check in virtio_queue_empty() entry?
>
> To be clear:
>
> -- >8 --
> diff --git a/hw/virtio/virtio.c b/hw/virtio/virtio.c
> index f4d86a36553..019c9c9e293 100644
> --- a/hw/virtio/virtio.c
> +++ b/hw/virtio/virtio.c
> @@ -786,6 +786,9 @@ static int virtio_queue_packed_empty(VirtQueue *vq)
>
>   int virtio_queue_empty(VirtQueue *vq)
>   {
> +    if (virtio_device_disabled(vq->vdev)) {
> +        return 1;
> +    }

The _rcu() version of the functions still needs to check this as well,
so I don't think it can be extracted cleanly.

Stefan

>       if (virtio_vdev_has_feature(vq->vdev, VIRTIO_F_RING_PACKED)) {
>           return virtio_queue_packed_empty(vq);
>       } else {
> ---
>
> Having:
>
> static inline bool virtio_device_disabled(VirtIODevice *vdev)
> {
>      return unlikely(vdev->disabled || vdev->broken);
> }
>
> that would avoid the RCU_READ_LOCK access.
>