A guest could race and craft shorter descriptors than what the vhost
library previously evaluated as necessary.
This would then break the rest of the descriptor filling for the packet
data.

Prepare the vhost net header in a temporary buffer, then copy to the
descriptors ensuring we are still under the nr_vec limit.

Additionally, we can't expect the iova address of the descriptors to be
contiguous.

CVE-2026-86566
Bugzilla ID: 2020

Cc: [email protected]

Acked-by: Maxime Coquelin <[email protected]>
Signed-off-by: David Marchand <[email protected]>
---
 lib/vhost/virtio_net.c | 83 ++++++++++++++++++++++--------------------
 1 file changed, 43 insertions(+), 40 deletions(-)

diff --git a/lib/vhost/virtio_net.c b/lib/vhost/virtio_net.c
index 0658b81de5..ff4613df68 100644
--- a/lib/vhost/virtio_net.c
+++ b/lib/vhost/virtio_net.c
@@ -1014,21 +1014,24 @@ fill_vec_buf_packed(struct virtio_net *dev, struct 
vhost_virtqueue *vq,
        return 0;
 }
 
-static __rte_noinline void
+static __rte_noinline int
 copy_vnet_hdr_to_desc(struct virtio_net *dev, struct vhost_virtqueue *vq,
-               struct buf_vector *buf_vec,
-               struct virtio_net_hdr_mrg_rxbuf *hdr)
+               struct buf_vector *buf_vec, uint16_t nr_vec,
+               struct virtio_net_hdr_mrg_rxbuf *hdr,
+               uint32_t *vec_idx, uint32_t *buf_offset)
        __rte_requires_shared_capability(&vq->iotlb_lock)
 {
-       uint64_t len;
+       uint64_t len = 0;
        uint64_t remain = dev->vhost_hlen;
-       uint64_t src = (uint64_t)(uintptr_t)hdr, dst;
-       uint64_t iova = buf_vec->buf_iova;
+       uint64_t src = (uint64_t)(uintptr_t)hdr;
+       uint32_t idx = 0;
 
        while (remain) {
+               uint64_t iova = buf_vec->buf_iova;
+               uint64_t dst = buf_vec->buf_addr;
+
                len = RTE_MIN(remain,
                                buf_vec->buf_len);
-               dst = buf_vec->buf_addr;
                rte_memcpy((void *)(uintptr_t)dst,
                                (void *)(uintptr_t)src,
                                len);
@@ -1039,10 +1042,21 @@ copy_vnet_hdr_to_desc(struct virtio_net *dev, struct 
vhost_virtqueue *vq,
                                iova, len);
 
                remain -= len;
-               iova += len;
                src += len;
-               buf_vec++;
+
+               if (remain) {
+                       if (unlikely(idx + 1 >= nr_vec))
+                               return -1;
+
+                       buf_vec++;
+                       idx++;
+               }
        }
+
+       *vec_idx = idx;
+       *buf_offset = (uint32_t)len;
+
+       return 0;
 }
 
 static __rte_always_inline int
@@ -1209,9 +1223,6 @@ mbuf_to_desc(struct virtio_net *dev, struct 
vhost_virtqueue *vq,
        uint32_t buf_offset, buf_avail;
        uint64_t buf_addr, buf_iova, buf_len;
        uint32_t cpy_len;
-       uint64_t hdr_addr;
-       struct rte_mbuf *hdr_mbuf;
-       struct virtio_net_hdr_mrg_rxbuf tmp_hdr, *hdr = NULL;
        struct vhost_async *async = vq->async;
 
        if (unlikely(m == NULL))
@@ -1224,24 +1235,35 @@ mbuf_to_desc(struct virtio_net *dev, struct 
vhost_virtqueue *vq,
        if (unlikely(buf_len < dev->vhost_hlen && nr_vec <= 1))
                return -1;
 
-       hdr_mbuf = m;
-       hdr_addr = buf_addr;
+       VHOST_DATA_LOG(dev->ifname, DEBUG, "RX: num merge buffers %d", 
num_buffers);
+
        if (unlikely(buf_len < dev->vhost_hlen)) {
+               struct virtio_net_hdr_mrg_rxbuf tmp_hdr;
+
                memset(&tmp_hdr, 0, sizeof(struct virtio_net_hdr_mrg_rxbuf));
-               hdr = &tmp_hdr;
-       } else
-               hdr = (struct virtio_net_hdr_mrg_rxbuf *)(uintptr_t)hdr_addr;
+               virtio_enqueue_offload(m, &tmp_hdr.hdr);
+               if (rxvq_is_mergeable(dev))
+                       ASSIGN_UNLESS_EQUAL(tmp_hdr.num_buffers, num_buffers);
 
-       VHOST_DATA_LOG(dev->ifname, DEBUG, "RX: num merge buffers %d", 
num_buffers);
+               if (copy_vnet_hdr_to_desc(dev, vq, buf_vec, nr_vec,
+                               &tmp_hdr, &vec_idx, &buf_offset) < 0)
+                       return -1;
 
-       if (unlikely(buf_len < dev->vhost_hlen)) {
-               buf_offset = dev->vhost_hlen - buf_len;
-               vec_idx++;
                buf_addr = buf_vec[vec_idx].buf_addr;
                buf_iova = buf_vec[vec_idx].buf_iova;
                buf_len = buf_vec[vec_idx].buf_len;
                buf_avail = buf_len - buf_offset;
        } else {
+               struct virtio_net_hdr_mrg_rxbuf *hdr;
+
+               hdr = (struct virtio_net_hdr_mrg_rxbuf *)(uintptr_t)buf_addr;
+               virtio_enqueue_offload(m, &hdr->hdr);
+               if (rxvq_is_mergeable(dev))
+                       ASSIGN_UNLESS_EQUAL(hdr->num_buffers, num_buffers);
+
+               PRINT_PACKET(dev, (uintptr_t)buf_addr, dev->vhost_hlen, 0);
+               vhost_log_cache_write_iova(dev, vq, buf_iova, dev->vhost_hlen);
+
                buf_offset = dev->vhost_hlen;
                buf_avail = buf_len - dev->vhost_hlen;
        }
@@ -1277,25 +1299,6 @@ mbuf_to_desc(struct virtio_net *dev, struct 
vhost_virtqueue *vq,
                        mbuf_avail  = rte_pktmbuf_data_len(m);
                }
 
-               if (hdr_addr) {
-                       virtio_enqueue_offload(hdr_mbuf, &hdr->hdr);
-                       if (rxvq_is_mergeable(dev))
-                               ASSIGN_UNLESS_EQUAL(hdr->num_buffers,
-                                               num_buffers);
-
-                       if (unlikely(hdr == &tmp_hdr)) {
-                               copy_vnet_hdr_to_desc(dev, vq, buf_vec, hdr);
-                       } else {
-                               PRINT_PACKET(dev, (uintptr_t)hdr_addr,
-                                               dev->vhost_hlen, 0);
-                               vhost_log_cache_write_iova(dev, vq,
-                                               buf_vec[0].buf_iova,
-                                               dev->vhost_hlen);
-                       }
-
-                       hdr_addr = 0;
-               }
-
                cpy_len = RTE_MIN(buf_avail, mbuf_avail);
 
                if (is_async) {
-- 
2.56.0

Reply via email to