aboutsummaryrefslogtreecommitdiff
diff options
context:
space:
mode:
authorJonah Palmer <jonah.palmer@oracle.com>2025-07-21 15:02:08 +0000
committerMichael S. Tsirkin <mst@redhat.com>2025-08-01 08:32:36 -0400
commit6fcf5ebafad65adc19a616260ca7dc90005785d1 (patch)
treec72c4ce798e93b8a65d57247b5749b8f26d95f3b
parent4e06566dbd1b1251c2788af26a30bd148d4eb6c1 (diff)
downloadqemu-6fcf5ebafad65adc19a616260ca7dc90005785d1.zip
qemu-6fcf5ebafad65adc19a616260ca7dc90005785d1.tar.gz
qemu-6fcf5ebafad65adc19a616260ca7dc90005785d1.tar.bz2
virtio: fix off-by-one and invalid access in virtqueue_ordered_fill
Commit b44135daa372 introduced virtqueue_ordered_fill for VIRTIO_F_IN_ORDER support but had a few issues: * Conditional while loop used 'steps <= max_steps' but should've been 'steps < max_steps' since reaching steps == max_steps would indicate that we didn't find an element, which is an error. Without this change, the code would attempt to read invalid data at an index outside of our search range. * Incremented 'steps' using the next chain's ndescs instead of the current one. This patch corrects the loop bounds and synchronizes 'steps' and index increments. We also add a defensive sanity check against malicious or invalid descriptor counts to avoid a potential infinite loop and DoS. Fixes: b44135daa372 ("virtio: virtqueue_ordered_fill - VIRTIO_F_IN_ORDER support") Reported-by: terrynini <terrynini38514@gmail.com> Signed-off-by: Jonah Palmer <jonah.palmer@oracle.com> Message-Id: <20250721150208.2409779-1-jonah.palmer@oracle.com> Reviewed-by: Si-Wei Liu <si-wei.liu@oracle.com> Acked-by: Jason Wang <jasowang@redhat.com> Reviewed-by: Michael S. Tsirkin <mst@redhat.com> Signed-off-by: Michael S. Tsirkin <mst@redhat.com>
-rw-r--r--hw/virtio/virtio.c22
1 files changed, 16 insertions, 6 deletions
diff --git a/hw/virtio/virtio.c b/hw/virtio/virtio.c
index 2ab1d20..9a81ad9 100644
--- a/hw/virtio/virtio.c
+++ b/hw/virtio/virtio.c
@@ -938,18 +938,18 @@ static void virtqueue_packed_fill(VirtQueue *vq, const VirtQueueElement *elem,
static void virtqueue_ordered_fill(VirtQueue *vq, const VirtQueueElement *elem,
unsigned int len)
{
- unsigned int i, steps, max_steps;
+ unsigned int i, steps, max_steps, ndescs;
i = vq->used_idx % vq->vring.num;
steps = 0;
/*
- * We shouldn't need to increase 'i' by more than the distance
- * between used_idx and last_avail_idx.
+ * We shouldn't need to increase 'i' by more than or equal to
+ * the distance between used_idx and last_avail_idx (max_steps).
*/
max_steps = (vq->last_avail_idx - vq->used_idx) % vq->vring.num;
/* Search for element in vq->used_elems */
- while (steps <= max_steps) {
+ while (steps < max_steps) {
/* Found element, set length and mark as filled */
if (vq->used_elems[i].index == elem->index) {
vq->used_elems[i].len = len;
@@ -957,8 +957,18 @@ static void virtqueue_ordered_fill(VirtQueue *vq, const VirtQueueElement *elem,
break;
}
- i += vq->used_elems[i].ndescs;
- steps += vq->used_elems[i].ndescs;
+ ndescs = vq->used_elems[i].ndescs;
+
+ /* Defensive sanity check */
+ if (unlikely(ndescs == 0 || ndescs > vq->vring.num)) {
+ qemu_log_mask(LOG_GUEST_ERROR,
+ "%s: %s invalid ndescs %u at position %u\n",
+ __func__, vq->vdev->name, ndescs, i);
+ return;
+ }
+
+ i += ndescs;
+ steps += ndescs;
if (i >= vq->vring.num) {
i -= vq->vring.num;