Skip to contentSkip to conversation

RDMA/rxe: validate num_sge/cur_sge before indexing wqe->dma.sge[]

PATCH Applied Ibrahim Hashimov 2 participants 2 messages Jul 12 – Jul 16

Subsystems (3)
lore
Share & tools
lore
#0 lore raw

PATCH v3 1/1 RDMA/rxe: validate num_sge/cur_sge before indexing wqe->dma.sge[]

For a user QP, qp->sq.queue is a ring the application writes directly, so rxe_post_send() takes the is_user branch and only schedules send_task without validating the WQE. rxe_requester() consumes it in place via req_next_wqe() and calls copy_data(), which indexes &wqe->dma.sge[cur_sge] with the attacker-controlled num_sge/cur_sge. Only the kernel path bounds num_sge (validate_send_wr()); the user WQE is never checked, so a local unprivileged user can post a WQE with an out-of-range cur_sge or oversized num_sge and force an out-of-bounds read of the per-WQE sge array in copy_data() (vmalloc OOB read, local DoS).

Bound num_sge to qp->sq.max_sge in rxe_requester() before use, the way get_srq_wqe() already guards SRQ entries, and bound cur_sge only when the WQE carries payload (dma.resid): copy_data() returns early on a zero-length copy before touching dma->sge[], so a zero-payload WQE -- the only kind a max_sge == 0 QP can post -- stays valid.

Reproduced under KASAN; the vmalloc-out-of-bounds in copy_data() is gone.

Fixes 8700e3e7c485 ("Soft RoCE driver")
Assisted-by AuditCode-AI:2026.07

---
v3: rewrite the commit message to describe the rxe_post_send() -> rxe_requester() -> copy_data() call flow directly and terse per Leon's review; resend as a standalone patch (not in-reply-to the previous series). No code change from v2; carries Zhu Yanjun's Reviewed-by.

 drivers/infiniband/sw/rxe/rxe_req.c | 15 +++++++++++++++
 1 file changed, 15 insertions(+)
drivers/infiniband/sw/rxe/rxe_req.c +15 -0
index 12d03f390b09..24f5c044363f 100644
@@ -701,6 +701,21 @@ int rxe_requester(struct rxe_qp *qp)
 	if (unlikely(!wqe))
 		goto exit;
 
+	/*
+	 * Don't trust user space data: a user QP's WQE comes from an mmap'd
+	 * ring, so num_sge/cur_sge are attacker-controlled. Bound num_sge like
+	 * get_srq_wqe(); bound cur_sge only when payload exists (dma.resid),
+	 * since copy_data() skips dma->sge[] on a zero-length copy (all a
+	 * max_sge == 0 QP can post).
+	 */
+	if (unlikely(wqe->dma.num_sge > qp->sq.max_sge ||
+		     (wqe->dma.resid &&
+		      wqe->dma.cur_sge >= qp->sq.max_sge))) {
+		rxe_dbg_qp(qp, "invalid num_sge/cur_sge in send wqe\n");
+		wqe->status = IB_WC_LOC_QP_OP_ERR;
+		goto err;
+	}
+
 	if (rxe_wqe_is_fenced(qp, wqe)) {
 		qp->req.wait_fence = 1;
 		goto exit;
Signature · 1 line
2.50.1 (Apple Git-155)
#1 lore raw
Quoted 11 lines · Ibrahim Hashimov

For a user QP, qp->sq.queue is a ring the application writes directly, so rxe_post_send() takes the is_user branch and only schedules send_task without validating the WQE. rxe_requester() consumes it in place via req_next_wqe() and calls copy_data(), which indexes &wqe->dma.sge[cur_sge] with the attacker-controlled num_sge/cur_sge. Only the kernel path bounds num_sge (validate_send_wr()); the user WQE is never checked, so a local unprivileged user can post a WQE with an out-of-range cur_sge or oversized num_sge and force an out-of-bounds read of the per-WQE sge array in copy_data() (vmalloc OOB read, local DoS).

Quoted 1 line

[...]

Applied, thanks!

[1/1] RDMA/rxe: validate num_sge/cur_sge before indexing wqe->dma.sge[]

      https://git.kernel.org/rdma/rdma/c/59d3005eb36f37

Best regards,

Signature · 1 line
Leon Romanovsky <[email protected]>