/
phprus
/
github_dpdk
Обзор
Документация
Войти
/
phprus
/
github_dpdk
Код
Задачи
Вики
Пакеты
0
Релизы
0
Аналитика
Безопасность
main
lib/vhost/virtio_net_ctrl.c
302 строки
7 KB
Maxime Coquelin
vhost: fix descriptor chain bounds check in control queue
26 янв 2026, 19:39
26 янв 2026, 19:39
6ec1778
Код
Авторство
О чём код?
/* SPDX-License-Identifier: BSD-3-Clause * Copyright (c) 2023 Red Hat, Inc. */ #include <stdint.h> #include <stdio.h> #include <unistd.h> #include "iotlb.h" #include "vhost.h" #include "virtio_net_ctrl.h" struct virtio_net_ctrl { uint8_t class; uint8_t command; uint8_t command_data[]; }; struct virtio_net_ctrl_elem { struct virtio_net_ctrl *ctrl_req; uint16_t head_idx; uint16_t n_descs; uint8_t *desc_ack; }; static int virtio_net_ctrl_pop(struct virtio_net *dev, struct vhost_virtqueue *cvq, struct virtio_net_ctrl_elem *ctrl_elem) __rte_requires_shared_capability(&cvq->iotlb_lock) { uint16_t avail_idx, desc_idx, n_descs = 0, nr_descs, cnt = 0; uint64_t desc_len, desc_addr, desc_iova, data_len = 0; uint8_t *ctrl_req; struct vring_desc *descs; avail_idx = rte_atomic_load_explicit((unsigned short __rte_atomic *)&cvq->avail->idx, rte_memory_order_acquire); if (avail_idx == cvq->last_avail_idx) { VHOST_CONFIG_LOG(dev->ifname, DEBUG, "Control queue empty"); return 0; } desc_idx = cvq->avail->ring[cvq->last_avail_idx & (cvq->size - 1)]; if (desc_idx >= cvq->size) { VHOST_CONFIG_LOG(dev->ifname, ERR, "Out of range desc index, dropping"); goto err; } ctrl_elem->head_idx = desc_idx; if (cvq->desc[desc_idx].flags & VRING_DESC_F_INDIRECT) { desc_len = cvq->desc[desc_idx].len; desc_iova = cvq->desc[desc_idx].addr; descs = (struct vring_desc *)(uintptr_t)vhost_iova_to_vva(dev, cvq, desc_iova, &desc_len, VHOST_ACCESS_RO); if (!descs || desc_len != cvq->desc[desc_idx].len) { VHOST_CONFIG_LOG(dev->ifname, ERR, "Failed to map ctrl indirect descs"); goto err; } nr_descs = desc_len / sizeof(struct vring_desc); desc_idx = 0; } else { descs = cvq->desc; nr_descs = cvq->size; } while (1) { if (unlikely(desc_idx >= nr_descs || cnt++ >= nr_descs)) { VHOST_CONFIG_LOG(dev->ifname, ERR, "Invalid ctrl descriptor chain"); goto err; } desc_len = descs[desc_idx].len; desc_iova = descs[desc_idx].addr; n_descs++; if (descs[desc_idx].flags & VRING_DESC_F_WRITE) { if (ctrl_elem->desc_ack) { VHOST_CONFIG_LOG(dev->ifname, ERR, "Unexpected ctrl chain layout"); goto err; } if (desc_len != sizeof(uint8_t)) { VHOST_CONFIG_LOG(dev->ifname, ERR, "Invalid ack size for ctrl req, dropping"); goto err; } ctrl_elem->desc_ack = (uint8_t *)(uintptr_t)vhost_iova_to_vva(dev, cvq, desc_iova, &desc_len, VHOST_ACCESS_WO); if (!ctrl_elem->desc_ack || desc_len != sizeof(uint8_t)) { VHOST_CONFIG_LOG(dev->ifname, ERR, "Failed to map ctrl ack descriptor"); goto err; } } else { if (ctrl_elem->desc_ack) { VHOST_CONFIG_LOG(dev->ifname, ERR, "Unexpected ctrl chain layout"); goto err; } data_len += desc_len; } if (!(descs[desc_idx].flags & VRING_DESC_F_NEXT)) break; desc_idx = descs[desc_idx].next; } desc_idx = ctrl_elem->head_idx; if (cvq->desc[desc_idx].flags & VRING_DESC_F_INDIRECT) ctrl_elem->n_descs = 1; else ctrl_elem->n_descs = n_descs; if (!ctrl_elem->desc_ack) { VHOST_CONFIG_LOG(dev->ifname, ERR, "Missing ctrl ack descriptor"); goto err; } if (data_len < sizeof(ctrl_elem->ctrl_req->class) + sizeof(ctrl_elem->ctrl_req->command)) { VHOST_CONFIG_LOG(dev->ifname, ERR, "Invalid control header size"); goto err; } ctrl_elem->ctrl_req = malloc(data_len); if (!ctrl_elem->ctrl_req) { VHOST_CONFIG_LOG(dev->ifname, ERR, "Failed to alloc ctrl request"); goto err; } ctrl_req = (uint8_t *)ctrl_elem->ctrl_req; if (cvq->desc[desc_idx].flags & VRING_DESC_F_INDIRECT) { desc_len = cvq->desc[desc_idx].len; desc_iova = cvq->desc[desc_idx].addr; descs = (struct vring_desc *)(uintptr_t)vhost_iova_to_vva(dev, cvq, desc_iova, &desc_len, VHOST_ACCESS_RO); if (!descs || desc_len != cvq->desc[desc_idx].len) { VHOST_CONFIG_LOG(dev->ifname, ERR, "Failed to map ctrl indirect descs"); goto free_err; } nr_descs = desc_len / sizeof(struct vring_desc); desc_idx = 0; } else { descs = cvq->desc; nr_descs = cvq->size; } cnt = 0; while (1) { if (unlikely(desc_idx >= nr_descs || cnt++ >= nr_descs)) { VHOST_CONFIG_LOG(dev->ifname, ERR, "Invalid ctrl descriptor chain"); goto free_err; } if (descs[desc_idx].flags & VRING_DESC_F_WRITE) break; desc_len = descs[desc_idx].len; desc_iova = descs[desc_idx].addr; desc_addr = vhost_iova_to_vva(dev, cvq, desc_iova, &desc_len, VHOST_ACCESS_RO); if (!desc_addr || desc_len < descs[desc_idx].len) { VHOST_CONFIG_LOG(dev->ifname, ERR, "Failed to map ctrl descriptor"); goto free_err; } memcpy(ctrl_req, (void *)(uintptr_t)desc_addr, desc_len); ctrl_req += desc_len; if (!(descs[desc_idx].flags & VRING_DESC_F_NEXT)) break; desc_idx = descs[desc_idx].next; } cvq->last_avail_idx++; vhost_virtqueue_reconnect_log_split(cvq); if (dev->features & (1ULL << VIRTIO_RING_F_EVENT_IDX)) vhost_avail_event(cvq) = cvq->last_avail_idx; return 1; free_err: free(ctrl_elem->ctrl_req); err: cvq->last_avail_idx++; vhost_virtqueue_reconnect_log_split(cvq); if (dev->features & (1ULL << VIRTIO_RING_F_EVENT_IDX)) vhost_avail_event(cvq) = cvq->last_avail_idx; return -1; } static uint8_t virtio_net_ctrl_handle_req(struct virtio_net *dev, struct virtio_net_ctrl *ctrl_req) { uint8_t ret = VIRTIO_NET_ERR; if (ctrl_req->class == VIRTIO_NET_CTRL_MQ && ctrl_req->command == VIRTIO_NET_CTRL_MQ_VQ_PAIRS_SET) { uint16_t queue_pairs; uint32_t i; 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; for (i = 0; i < dev->nr_vring; i++) { struct vhost_virtqueue *vq = dev->virtqueue[i]; bool enable; if (vq == dev->cvq) continue; if (i < queue_pairs * 2) enable = true; else enable = false; vq->enabled = enable; if (dev->notify_ops->vring_state_changed) dev->notify_ops->vring_state_changed(dev->vid, i, enable); } } return ret; } static int virtio_net_ctrl_push(struct virtio_net *dev, struct virtio_net_ctrl_elem *ctrl_elem) { struct vhost_virtqueue *cvq = dev->cvq; struct vring_used_elem *used_elem; used_elem = &cvq->used->ring[cvq->last_used_idx & (cvq->size - 1)]; used_elem->id = ctrl_elem->head_idx; used_elem->len = sizeof(*ctrl_elem->desc_ack); cvq->last_used_idx++; rte_atomic_store_explicit((unsigned short __rte_atomic *)&cvq->used->idx, cvq->last_used_idx, rte_memory_order_release); vhost_vring_call_split(dev, dev->cvq); free(ctrl_elem->ctrl_req); return 0; } int virtio_net_ctrl_handle(struct virtio_net *dev) { int ret = 0; if (dev->features & (1ULL << VIRTIO_F_RING_PACKED)) { VHOST_CONFIG_LOG(dev->ifname, ERR, "Packed ring not supported yet"); return -1; } if (!dev->cvq) { VHOST_CONFIG_LOG(dev->ifname, ERR, "missing control queue"); return -1; } rte_rwlock_read_lock(&dev->cvq->access_lock); vhost_user_iotlb_rd_lock(dev->cvq); while (1) { struct virtio_net_ctrl_elem ctrl_elem; memset(&ctrl_elem, 0, sizeof(struct virtio_net_ctrl_elem)); ret = virtio_net_ctrl_pop(dev, dev->cvq, &ctrl_elem); if (ret <= 0) break; *ctrl_elem.desc_ack = virtio_net_ctrl_handle_req(dev, ctrl_elem.ctrl_req); ret = virtio_net_ctrl_push(dev, &ctrl_elem); if (ret < 0) break; } vhost_user_iotlb_rd_unlock(dev->cvq); rte_rwlock_read_unlock(&dev->cvq->access_lock); return ret; }