[PATCH 24.11 01/14] vhost/crypto: fix TOCTOU OOB write in finalize path

Maxime Coquelin maxime.coquelin at redhat.com
Wed Oct 14 17:01:41 CEST 2026


From: Kai Ji <kai.ji at intel.com>

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")

Acked-by: Bruce Richardson <bruce.richardson at intel.com>
Signed-off-by: Kai Ji <kai.ji at intel.com>
---
 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 7a5e290822..4c7ad77905 100644
--- a/lib/vhost/vhost_crypto.c
+++ b/lib/vhost/vhost_crypto.c
@@ -1298,18 +1298,23 @@ vhost_crypto_finalize_one_request(struct rte_crypto_op *op,
 	struct rte_mbuf *m_dst = op->sym->m_dst;
 	struct vhost_crypto_data_req *vc_req = rte_mbuf_to_priv(m_src);
 	struct vhost_virtqueue *vq;
-	uint16_t used_idx, desc_idx;
+	uint16_t desc_idx;
 
 	if (unlikely(!vc_req)) {
 		VC_LOG_ERR("Failed to retrieve vc_req");
 		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 {
@@ -1317,8 +1322,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;
 
 	rte_mempool_put(m_src->pool, (void *)m_src);
@@ -1617,16 +1621,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;
 		}
 
@@ -1647,9 +1655,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



More information about the stable mailing list