RDMA/rxe: validate num_sge/cur_sge before indexing wqe->dma.sge[]
Subsystems (3)
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.
---
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
@@ -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)
Leon Romanovsky
AppliedQuoted 11 lines · Ibrahim Hashimov
For a user QP,
qp->sq.queueis a ring the application writes directly, sorxe_post_send()takes theis_userbranch and only schedulessend_taskwithout validating the WQE.rxe_requester()consumes it in place viareq_next_wqe()and callscopy_data(), which indexes &wqe->dma.sge[cur_sge] with the attacker-controlled num_sge/cur_sge. Only the kernel path boundsnum_sge(validate_send_wr()); the user WQE is never checked, so a local unprivileged user can post a WQE with an out-of-rangecur_sgeor oversizednum_sgeand force an out-of-bounds read of the per-WQE sge array incopy_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]>