]> git.ipfire.org Git - thirdparty/kernel/stable.git/commitdiff
RDMA/rxe: Copy WQE to local buffer in non-SRQ receive path
authorTristan Madani <tristmd@gmail.com>
Mon, 18 May 2026 21:50:40 +0000 (21:50 +0000)
committerJason Gunthorpe <jgg@nvidia.com>
Fri, 29 May 2026 23:33:59 +0000 (20:33 -0300)
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: 8700e3e7c485 ("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>
drivers/infiniband/sw/rxe/rxe_resp.c

index 29438bcde46fa9e9d9e0e5fb3c22e6133916158b..d8cbdfa70cdbdd9e2056c08a81650f455d242408 100644 (file)
@@ -310,6 +310,29 @@ event:
        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;