Add request length validation before accessing command_data in the
control virtqueue handler. The request buffer size was only validated
against the 2-byte header (class + command), but the MQ_VQ_PAIRS_SET
handler reads a 2-byte queue_pairs field from command_data without
verifying those bytes exist.

A malicious guest could submit a 2-byte control request causing an
out-of-bounds heap read of bytes [2..3] beyond the allocation.

Propagate data_len through virtio_net_ctrl_elem and validate the
minimum required size (4 bytes) before dereferencing command_data.

CVE-2026-86562
Bugzilla ID: 2007

Cc: [email protected]

Reported-by: Stephen Haruna <[email protected]>
Acked-by: Kevin Traynor <[email protected]>
Signed-off-by: Maxime Coquelin <[email protected]>
---
 lib/vhost/virtio_net_ctrl.c | 14 ++++++++++++--
 1 file changed, 12 insertions(+), 2 deletions(-)

diff --git a/lib/vhost/virtio_net_ctrl.c b/lib/vhost/virtio_net_ctrl.c
index 1e5034457c..51a3baf78e 100644
--- a/lib/vhost/virtio_net_ctrl.c
+++ b/lib/vhost/virtio_net_ctrl.c
@@ -20,6 +20,7 @@ struct virtio_net_ctrl_elem {
        struct virtio_net_ctrl *ctrl_req;
        uint16_t head_idx;
        uint16_t n_descs;
+       uint32_t data_len;
        uint8_t *desc_ack;
 };
 
@@ -135,6 +136,7 @@ virtio_net_ctrl_pop(struct virtio_net *dev, struct 
vhost_virtqueue *cvq,
                VHOST_CONFIG_LOG(dev->ifname, ERR, "Failed to alloc ctrl 
request");
                goto err;
        }
+       ctrl_elem->data_len = data_len;
 
        ctrl_req = (uint8_t *)ctrl_elem->ctrl_req;
 
@@ -213,7 +215,8 @@ virtio_net_ctrl_pop(struct virtio_net *dev, struct 
vhost_virtqueue *cvq,
 }
 
 static uint8_t
-virtio_net_ctrl_handle_req(struct virtio_net *dev, struct virtio_net_ctrl 
*ctrl_req)
+virtio_net_ctrl_handle_req(struct virtio_net *dev, struct virtio_net_ctrl 
*ctrl_req,
+               uint32_t data_len)
 {
        uint8_t ret = VIRTIO_NET_ERR;
 
@@ -222,6 +225,12 @@ virtio_net_ctrl_handle_req(struct virtio_net *dev, struct 
virtio_net_ctrl *ctrl_
                uint16_t queue_pairs;
                uint32_t i;
 
+               if (data_len < sizeof(*ctrl_req) + sizeof(queue_pairs)) {
+                       VHOST_CONFIG_LOG(dev->ifname, ERR,
+                                       "Invalid MQ control request size");
+                       return VIRTIO_NET_ERR;
+               }
+
                queue_pairs = *(uint16_t *)(uintptr_t)ctrl_req->command_data;
                VHOST_CONFIG_LOG(dev->ifname, INFO, "Ctrl req: MQ %u queue 
pairs", queue_pairs);
                ret = VIRTIO_NET_OK;
@@ -296,7 +305,8 @@ virtio_net_ctrl_handle(struct virtio_net *dev)
                if (ret <= 0)
                        break;
 
-               *ctrl_elem.desc_ack = virtio_net_ctrl_handle_req(dev, 
ctrl_elem.ctrl_req);
+               *ctrl_elem.desc_ack = virtio_net_ctrl_handle_req(dev, 
ctrl_elem.ctrl_req,
+                               ctrl_elem.data_len);
 
                ret = virtio_net_ctrl_push(dev, &ctrl_elem);
                if (ret < 0)
-- 
2.56.0

Reply via email to