From: Kai Ji <[email protected]>

When finalizing a crypto request, the descriptor index was
re-read from the guest-writable available ring (vq->avail->ring)
instead of using the value saved at request fetch time. A malicious
guest can modify this index between fetch and finalization, causing
an out-of-bounds write to vq->used->ring[desc_idx] with a
guest-controlled index up to 0xFFFF (max ~512 KB past the ring).

Fix by using the desc_idx saved in vc_req->desc_idx directly at
finalization time, eliminating the re-read from guest memory. Add
bounds checks on desc_idx against vq->size in both the fetch loops
and the finalize function to prevent out-of-bounds access.

CVE-2026-86848
CVE-2026-86846
Bugzilla ID: 2001
Bugzilla ID: 2026

Fixes: 939066d96563 ("vhost/crypto: add public function implementation")
Cc: [email protected]

Acked-by: Bruce Richardson <[email protected]>
Signed-off-by: Kai Ji <[email protected]>
---
 lib/vhost/vhost_crypto.c | 26 +++++++++++++++++++-------
 1 file changed, 19 insertions(+), 7 deletions(-)

diff --git a/lib/vhost/vhost_crypto.c b/lib/vhost/vhost_crypto.c
index 648e2d731b..351fa241c8 100644
--- a/lib/vhost/vhost_crypto.c
+++ b/lib/vhost/vhost_crypto.c
@@ -1707,7 +1707,7 @@ vhost_crypto_finalize_one_request(struct rte_crypto_op 
*op,
        struct rte_mbuf *m_src = NULL, *m_dst = NULL;
        struct vhost_crypto_data_req *vc_req;
        struct vhost_virtqueue *vq;
-       uint16_t used_idx, desc_idx;
+       uint16_t desc_idx;
 
        if (op->type == RTE_CRYPTO_OP_TYPE_SYMMETRIC) {
                m_src = op->sym->m_src;
@@ -1725,11 +1725,16 @@ vhost_crypto_finalize_one_request(struct rte_crypto_op 
*op,
                return NULL;
        }
        vq = vc_req->vq;
-       used_idx = vc_req->desc_idx;
+       desc_idx = vc_req->desc_idx;
 
        if (old_vq && (vq != old_vq))
                return vq;
 
+       if (unlikely(desc_idx >= vq->size)) {
+               VC_LOG_ERR("Invalid descriptor index %u", desc_idx);
+               return NULL;
+       }
+
        if (unlikely(op->status != RTE_CRYPTO_OP_STATUS_SUCCESS))
                vc_req->inhdr->status = VIRTIO_CRYPTO_ERR;
        else {
@@ -1737,8 +1742,7 @@ vhost_crypto_finalize_one_request(struct rte_crypto_op 
*op,
                        write_back_data(vc_req);
        }
 
-       desc_idx = vq->avail->ring[used_idx];
-       vq->used->ring[desc_idx].id = vq->avail->ring[desc_idx];
+       vq->used->ring[desc_idx].id = desc_idx;
        vq->used->ring[desc_idx].len = vc_req->len;
 
        if (op->type == RTE_CRYPTO_OP_TYPE_SYMMETRIC) {
@@ -2044,16 +2048,20 @@ rte_vhost_crypto_fetch_requests(int vid, uint32_t qid,
                for (i = 0; i < count; i++) {
                        uint16_t used_idx = (start_idx + i) & (vq->size - 1);
                        uint16_t desc_idx = vq->avail->ring[used_idx];
-                       struct vring_desc *head = &vq->desc[desc_idx];
+                       struct vring_desc *head;
                        struct rte_crypto_op *op = ops[i];
 
+                       if (unlikely(desc_idx >= vq->size))
+                               break;
+
+                       head = &vq->desc[desc_idx];
                        op->sym->m_src = mbufs[i * 2];
                        op->sym->m_dst = mbufs[i * 2 + 1];
                        op->sym->m_src->data_off = 0;
                        op->sym->m_dst->data_off = 0;
 
                        if (unlikely(vhost_crypto_process_one_req(vcrypto, vq,
-                                       op, head, descs, used_idx) < 0))
+                                       op, head, descs, desc_idx) < 0))
                                break;
                }
 
@@ -2074,9 +2082,13 @@ rte_vhost_crypto_fetch_requests(int vid, uint32_t qid,
                for (i = 0; i < count; i++) {
                        uint16_t used_idx = (start_idx + i) & (vq->size - 1);
                        uint16_t desc_idx = vq->avail->ring[used_idx];
-                       struct vring_desc *head = &vq->desc[desc_idx];
+                       struct vring_desc *head;
                        struct rte_crypto_op *op = ops[i];
 
+                       if (unlikely(desc_idx >= vq->size))
+                               break;
+
+                       head = &vq->desc[desc_idx];
                        op->sym->m_src = mbufs[i];
                        op->sym->m_dst = NULL;
                        op->sym->m_src->data_off = 0;
-- 
2.56.0

Reply via email to