summaryrefslogtreecommitdiff
diff options
context:
space:
mode:
authorIbrahim Hashimov <security@auditcode.ai>2026-07-12 14:21:49 +0200
committerLeon Romanovsky <leon@kernel.org>2026-07-20 09:00:40 -0400
commit126c757e4cd46f866ddc283143b58eb4d9bf52cd (patch)
tree1f52379aa117514c0d4dff859be33807eb46e2fc
parent6f7014237405e7f032b5c53a82d9eccf6161c291 (diff)
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") Reviewed-by: Zhu Yanjun <yanjun.zhu@linux.dev> Signed-off-by: Ibrahim Hashimov <security@auditcode.ai> Link: https://patch.msgid.link/20260712122149.78142-1-security@auditcode.ai Assisted-by: AuditCode-AI:2026.07 Signed-off-by: Leon Romanovsky <leon@kernel.org>
-rw-r--r--drivers/infiniband/sw/rxe/rxe_req.c15
1 files changed, 15 insertions, 0 deletions
diff --git a/drivers/infiniband/sw/rxe/rxe_req.c b/drivers/infiniband/sw/rxe/rxe_req.c
index 12d03f390b09..24f5c044363f 100644
--- a/drivers/infiniband/sw/rxe/rxe_req.c
+++ b/drivers/infiniband/sw/rxe/rxe_req.c
@@ -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;