RDMA/rxe: Copy WQE to local buffer in non-SRQ receive path

For non-SRQ QPs, the responder reads WQE fields directly from the
shared queue buffer mapped into userspace. This allows a malicious
user to modify fields like num_sge or sge entries while the kernel
is processing the WQE, leading to out-of-bounds reads in
rxe_resp_check_length() and copy_data().

Introduce get_recv_wqe() that validates num_sge and copies the WQE
to a kernel-local buffer before processing, matching the approach
already used for SRQ WQEs in get_srq_wqe(). The srq_wqe buffer is
reused since SRQ and non-SRQ paths are mutually exclusive per QP.

Fixes: 8700e3e7c4 ("Soft RoCE driver")
Link: https://patch.msgid.link/r/20260518215040.1598586-3-tristan@talencesecurity.com
Signed-off-by: Tristan Madani <tristan@talencesecurity.com>
Reviewed-by: Zhu Yanjun <yanjun.zhu@linux.dev>
Signed-off-by: Jason Gunthorpe <jgg@nvidia.com>
This commit is contained in:
Tristan Madani 2026-05-18 21:50:40 +00:00 committed by Jason Gunthorpe
parent 22b8fbded6
commit d6ab440240

View File

@ -310,6 +310,29 @@ static enum resp_states get_srq_wqe(struct rxe_qp *qp)
return RESPST_CHK_LENGTH;
}
static enum resp_states rxe_get_recv_wqe(struct rxe_qp *qp)
{
struct rxe_queue *q = qp->rq.queue;
struct rxe_recv_wqe *wqe;
unsigned int num_sge;
size_t size;
wqe = queue_head(q, QUEUE_TYPE_FROM_CLIENT);
if (!wqe)
return RESPST_ERR_RNR;
num_sge = wqe->dma.num_sge;
if (unlikely(num_sge > qp->rq.max_sge)) {
rxe_dbg_qp(qp, "invalid num_sge in recv WQE\n");
return RESPST_ERR_MALFORMED_WQE;
}
size = sizeof(*wqe) + num_sge * sizeof(struct rxe_sge);
memcpy(&qp->resp.srq_wqe, wqe, size);
qp->resp.wqe = &qp->resp.srq_wqe.wqe;
return RESPST_CHK_LENGTH;
}
static enum resp_states check_resource(struct rxe_qp *qp,
struct rxe_pkt_info *pkt)
{
@ -330,9 +353,7 @@ static enum resp_states check_resource(struct rxe_qp *qp,
if (srq)
return get_srq_wqe(qp);
qp->resp.wqe = queue_head(qp->rq.queue,
QUEUE_TYPE_FROM_CLIENT);
return (qp->resp.wqe) ? RESPST_CHK_LENGTH : RESPST_ERR_RNR;
return rxe_get_recv_wqe(qp);
}
return RESPST_CHK_LENGTH;