Linux RDMA and InfiniBand development
 help / color / mirror / Atom feed
* [PATCH rdma-next v2 0/5] RDMA/irdma: Adopt robust udata
@ 2026-06-29 23:25 Jacob Moroni
  2026-06-29 23:25 ` [PATCH rdma-next v2 1/5] RDMA/irdma: Enforce empty udata input for no-input ops Jacob Moroni
                   ` (5 more replies)
  0 siblings, 6 replies; 8+ messages in thread
From: Jacob Moroni @ 2026-06-29 23:25 UTC (permalink / raw)
  To: tatyana.e.nikolova, jgg, leon; +Cc: linux-rdma, Jacob Moroni

This series brings irdma up to uverbs_robust_udata compliance.

The driver has been audited to confirm that:

  1. Methods which do not accept udata input perform an explicit
     check for no (or zero value) input.

  2. Methods which do accept input perform the correct validation
     to ensure that additional udata beyond the kernel's current
     ABI definition is zero, and to enforce the required minimum
     length.

  3. Methods which do not return udata responses use the proper
     helper.

The irdma driver is backwards compatible with the legacy i40iw
provider, so special care was taken to avoid breaking any legacy
binaries, as there were some small differences in the ABI/semantics.

This has passed the rdma-unit-test suite using the normal irdma
provider from upstream rdma-core.

Changes in v2:
* Fixed Sashiko findings:
  - Moved ib_respond_empty_udata to beginning of most
    methods to close gaps where it could previously
    return 0 without calling ib_respond_empty_udata. This
    also fixes issues where the ib_respond_empty_udata
    call itself could fail, which may leave resources
    in an inconsistent state (kernel believes object
    is alive, but driver resources have been cleaned up).
  - Moved input validation to beginning of modify_qp
    methods to close gaps where the op could be invoked
    with udata but without input checking.
  - Fixed a QPN leak that could occur during QP create
    by moving input validation earlier.
    
v1: https://lore.kernel.org/linux-rdma/20260627025642.4064973-1-jmoroni@google.com/T/#t

Jacob Moroni (5):
  RDMA/irdma: Enforce empty udata input for no-input ops
  RDMA/irdma: Use robust udata input copy helpers
  RDMA/irdma: Use ib_respond_empty_udata where applicable
  RDMA/irdma: Use robust udata helper for QP creation
  RDMA/irdma: Enable uverbs_robust_udata compliance flag

 drivers/infiniband/hw/irdma/verbs.c | 217 +++++++++++++++++++---------
 include/uapi/rdma/irdma-abi.h       |   1 +
 2 files changed, 149 insertions(+), 69 deletions(-)

-- 
2.55.0.rc0.799.gd6f94ed593-goog


^ permalink raw reply	[flat|nested] 8+ messages in thread

* [PATCH rdma-next v2 1/5] RDMA/irdma: Enforce empty udata input for no-input ops
  2026-06-29 23:25 [PATCH rdma-next v2 0/5] RDMA/irdma: Adopt robust udata Jacob Moroni
@ 2026-06-29 23:25 ` Jacob Moroni
  2026-06-29 23:25 ` [PATCH rdma-next v2 2/5] RDMA/irdma: Use robust udata input copy helpers Jacob Moroni
                   ` (4 subsequent siblings)
  5 siblings, 0 replies; 8+ messages in thread
From: Jacob Moroni @ 2026-06-29 23:25 UTC (permalink / raw)
  To: tatyana.e.nikolova, jgg, leon; +Cc: linux-rdma, Jacob Moroni

Validate that the udata input buffer is empty for operations
that do not expect input data from userspace.

The irdma rdma-core provider as well as the legacy i40iw
provider were both checked to ensure they never passed any
udata to these ops.

Signed-off-by: Jacob Moroni <jmoroni@google.com>
---
 drivers/infiniband/hw/irdma/verbs.c | 48 +++++++++++++++++++++++++++++
 1 file changed, 48 insertions(+)

diff --git a/drivers/infiniband/hw/irdma/verbs.c b/drivers/infiniband/hw/irdma/verbs.c
index b79f5afe68e5..e1c894fba2af 100644
--- a/drivers/infiniband/hw/irdma/verbs.c
+++ b/drivers/infiniband/hw/irdma/verbs.c
@@ -410,6 +410,10 @@ static int irdma_alloc_pd(struct ib_pd *pd, struct ib_udata *udata)
 	if (udata && udata->outlen < IRDMA_ALLOC_PD_MIN_RESP_LEN)
 		return -EINVAL;
 
+	err = ib_is_udata_in_empty(udata);
+	if (err)
+		return err;
+
 	err = irdma_alloc_rsrc(rf, rf->allocated_pds, rf->max_pd, &pd_id,
 			       &rf->next_pd);
 	if (err)
@@ -445,6 +449,11 @@ static int irdma_dealloc_pd(struct ib_pd *ibpd, struct ib_udata *udata)
 {
 	struct irdma_pd *iwpd = to_iwpd(ibpd);
 	struct irdma_device *iwdev = to_iwdev(ibpd->device);
+	int err;
+
+	err = ib_is_udata_in_empty(udata);
+	if (err)
+		return err;
 
 	irdma_free_rsrc(iwdev->rf, iwdev->rf->allocated_pds, iwpd->sc_pd.pd_id);
 
@@ -542,6 +551,11 @@ static int irdma_destroy_qp(struct ib_qp *ibqp, struct ib_udata *udata)
 {
 	struct irdma_qp *iwqp = to_iwqp(ibqp);
 	struct irdma_device *iwdev = iwqp->iwdev;
+	int err;
+
+	err = ib_is_udata_in_empty(udata);
+	if (err)
+		return err;
 
 	iwqp->sc_qp.qp_uk.destroy_pending = true;
 
@@ -1959,6 +1973,11 @@ static int irdma_destroy_srq(struct ib_srq *ibsrq, struct ib_udata *udata)
 	struct irdma_device *iwdev = to_iwdev(ibsrq->device);
 	struct irdma_srq *iwsrq = to_iwsrq(ibsrq);
 	struct irdma_sc_srq *srq = &iwsrq->sc_srq;
+	int err;
+
+	err = ib_is_udata_in_empty(udata);
+	if (err)
+		return err;
 
 	irdma_srq_wq_destroy(iwdev->rf, srq);
 	irdma_srq_free_rsrc(iwdev->rf, iwsrq);
@@ -1979,6 +1998,11 @@ static int irdma_destroy_cq(struct ib_cq *ib_cq, struct ib_udata *udata)
 	struct irdma_sc_ceq *ceq = dev->ceq[cq->ceq_id];
 	struct irdma_ceq *iwceq = container_of(ceq, struct irdma_ceq, sc_ceq);
 	unsigned long flags;
+	int err;
+
+	err = ib_is_udata_in_empty(udata);
+	if (err)
+		return err;
 
 	spin_lock_irqsave(&iwcq->lock, flags);
 	if (!list_empty(&iwcq->cmpl_generated))
@@ -2195,6 +2219,10 @@ static int irdma_modify_srq(struct ib_srq *ibsrq, struct ib_srq_attr *attr,
 	struct cqp_cmds_info *cqp_info;
 	int status;
 
+	status = ib_is_udata_in_empty(udata);
+	if (status)
+		return status;
+
 	if (attr_mask & IB_SRQ_MAX_WR)
 		return -EINVAL;
 
@@ -3035,6 +3063,10 @@ static int irdma_alloc_mw(struct ib_mw *ibmw, struct ib_udata *udata)
 	int err_code;
 	u32 stag;
 
+	err_code = ib_is_udata_in_empty(udata);
+	if (err_code)
+		return err_code;
+
 	stag = irdma_create_stag(iwdev);
 	if (!stag)
 		return -ENOMEM;
@@ -3785,6 +3817,10 @@ static struct ib_mr *irdma_rereg_user_mr(struct ib_mr *ib_mr, int flags,
 	struct ib_umem_dmabuf *umem_dmabuf;
 	int ret;
 
+	ret = ib_is_udata_in_empty(udata);
+	if (ret)
+		return ERR_PTR(ret);
+
 	if (len > iwdev->rf->sc_dev.hw_attrs.max_mr_size)
 		return ERR_PTR(-EINVAL);
 
@@ -3973,6 +4009,10 @@ static int irdma_dereg_mr(struct ib_mr *ib_mr, struct ib_udata *udata)
 	bool dmabuf_revocable = iwmr->region && iwmr->region->is_dmabuf;
 	int ret;
 
+	ret = ib_is_udata_in_empty(udata);
+	if (ret)
+		return ret;
+
 	if (iwmr->type != IRDMA_MEMREG_TYPE_MEM) {
 		if (iwmr->region) {
 			struct irdma_ucontext *ucontext;
@@ -5292,6 +5332,10 @@ static int irdma_create_user_ah(struct ib_ah *ibah,
 	if (udata->outlen < IRDMA_CREATE_AH_MIN_RESP_LEN)
 		return -EINVAL;
 
+	err = ib_is_udata_in_empty(udata);
+	if (err)
+		return err;
+
 	err = irdma_setup_ah(ibah, attr);
 	if (err)
 		return err;
@@ -5340,6 +5384,10 @@ static int irdma_create_ah(struct ib_ah *ibah, struct rdma_ah_init_attr *attr,
 	struct irdma_device *iwdev = to_iwdev(ibah->pd->device);
 	int err;
 
+	err = ib_is_udata_in_empty(udata);
+	if (err)
+		return err;
+
 	err = irdma_setup_ah(ibah, attr);
 	if (err)
 		return err;
-- 
2.55.0.rc0.799.gd6f94ed593-goog


^ permalink raw reply related	[flat|nested] 8+ messages in thread

* [PATCH rdma-next v2 2/5] RDMA/irdma: Use robust udata input copy helpers
  2026-06-29 23:25 [PATCH rdma-next v2 0/5] RDMA/irdma: Adopt robust udata Jacob Moroni
  2026-06-29 23:25 ` [PATCH rdma-next v2 1/5] RDMA/irdma: Enforce empty udata input for no-input ops Jacob Moroni
@ 2026-06-29 23:25 ` Jacob Moroni
  2026-06-29 23:25 ` [PATCH rdma-next v2 3/5] RDMA/irdma: Use ib_respond_empty_udata where applicable Jacob Moroni
                   ` (3 subsequent siblings)
  5 siblings, 0 replies; 8+ messages in thread
From: Jacob Moroni @ 2026-06-29 23:25 UTC (permalink / raw)
  To: tatyana.e.nikolova, jgg, leon; +Cc: linux-rdma, Jacob Moroni

Replace the use of ib_copy_from_udata() with
ib_copy_validate_udata_in() where applicable.

For each modified call site, the last argument of
ib_copy_validate_udata_in() was determined by taking
the last member of the ABI struct as per its original
definition (i.e., when it was first committed).

Some methods like irdma_create_cq required special care
because the last member of the current ABI def is beyond
that of the legacy i40iw's ABI def which we need to
remain compatible with. In some other cases like modify_qp,
the legacy i40iw provider never provided any udata at all
so the validation is only performed if inlen > 0.

irdma_create_qp is more challenging because the legacy ABI
was actually larger but the additional fields were never used,
and even worse, never initialized in the provider. This will
be handled in a followup commit.

Signed-off-by: Jacob Moroni <jmoroni@google.com>
---
 drivers/infiniband/hw/irdma/verbs.c | 73 +++++++++++++----------------
 1 file changed, 33 insertions(+), 40 deletions(-)

diff --git a/drivers/infiniband/hw/irdma/verbs.c b/drivers/infiniband/hw/irdma/verbs.c
index e1c894fba2af..074dfa46b35c 100644
--- a/drivers/infiniband/hw/irdma/verbs.c
+++ b/drivers/infiniband/hw/irdma/verbs.c
@@ -1310,9 +1310,15 @@ int irdma_modify_qp_roce(struct ib_qp *ibqp, struct ib_qp_attr *attr,
 
 	if (udata) {
 		/* udata inlen/outlen can be 0 when supporting legacy libi40iw */
-		if ((udata->inlen && udata->inlen < IRDMA_MODIFY_QP_MIN_REQ_LEN) ||
-		    (udata->outlen && udata->outlen < IRDMA_MODIFY_QP_MIN_RESP_LEN))
+		if (udata->outlen && udata->outlen < IRDMA_MODIFY_QP_MIN_RESP_LEN)
 			return -EINVAL;
+
+		/* For current irdma, validate against ABI def. */
+		if (udata->inlen) {
+			ret = ib_copy_validate_udata_in(udata, ureq, rsvd);
+			if (ret)
+				return ret;
+		}
 	}
 
 	if (attr_mask & ~IB_QP_ATTR_STANDARD_BITS)
@@ -1555,10 +1561,6 @@ int irdma_modify_qp_roce(struct ib_qp *ibqp, struct ib_qp_attr *attr,
 				iwqp->ibqp_state = attr->qp_state;
 				spin_unlock_irqrestore(&iwqp->lock, flags);
 				if (udata && udata->inlen) {
-					if (ib_copy_from_udata(&ureq, udata,
-					    min(sizeof(ureq), udata->inlen)))
-						return -EINVAL;
-
 					irdma_flush_wqes(iwqp,
 					    (ureq.sq_flush ? IRDMA_FLUSH_SQ : 0) |
 					    (ureq.rq_flush ? IRDMA_FLUSH_RQ : 0) |
@@ -1666,9 +1668,14 @@ int irdma_modify_qp(struct ib_qp *ibqp, struct ib_qp_attr *attr, int attr_mask,
 
 	if (udata) {
 		/* udata inlen/outlen can be 0 when supporting legacy libi40iw */
-		if ((udata->inlen && udata->inlen < IRDMA_MODIFY_QP_MIN_REQ_LEN) ||
-		    (udata->outlen && udata->outlen < IRDMA_MODIFY_QP_MIN_RESP_LEN))
+		if (udata->outlen && udata->outlen < IRDMA_MODIFY_QP_MIN_RESP_LEN)
 			return -EINVAL;
+
+		if (udata->inlen) {
+			err = ib_copy_validate_udata_in(udata, ureq, rsvd);
+			if (err)
+				return err;
+		}
 	}
 
 	if (attr_mask & ~IB_QP_ATTR_STANDARD_BITS)
@@ -1758,10 +1765,6 @@ int irdma_modify_qp(struct ib_qp *ibqp, struct ib_qp_attr *attr, int attr_mask,
 				iwqp->ibqp_state = attr->qp_state;
 				spin_unlock_irqrestore(&iwqp->lock, flags);
 				if (udata && udata->inlen) {
-					if (ib_copy_from_udata(&ureq, udata,
-					    min(sizeof(ureq), udata->inlen)))
-						return -EINVAL;
-
 					irdma_flush_wqes(iwqp,
 					    (ureq.sq_flush ? IRDMA_FLUSH_SQ : 0) |
 					    (ureq.rq_flush ? IRDMA_FLUSH_RQ : 0) |
@@ -2033,7 +2036,6 @@ static int irdma_destroy_cq(struct ib_cq *ib_cq, struct ib_udata *udata)
 static int irdma_resize_cq(struct ib_cq *ibcq, unsigned int entries,
 			   struct ib_udata *udata)
 {
-#define IRDMA_RESIZE_CQ_MIN_REQ_LEN offsetofend(struct irdma_resize_cq_req, user_cq_buffer)
 	struct irdma_cq *iwcq = to_iwcq(ibcq);
 	struct irdma_sc_dev *dev = iwcq->sc_cq.dev;
 	struct irdma_cqp_request *cqp_request;
@@ -2057,9 +2059,6 @@ static int irdma_resize_cq(struct ib_cq *ibcq, unsigned int entries,
 	    IRDMA_FEATURE_CQ_RESIZE))
 		return -EOPNOTSUPP;
 
-	if (udata && udata->inlen < IRDMA_RESIZE_CQ_MIN_REQ_LEN)
-		return -EINVAL;
-
 	if (entries > rf->max_cqe)
 		return -EINVAL;
 
@@ -2089,9 +2088,9 @@ static int irdma_resize_cq(struct ib_cq *ibcq, unsigned int entries,
 			rdma_udata_to_drv_context(udata, struct irdma_ucontext,
 						  ibucontext);
 
-		if (ib_copy_from_udata(&req, udata,
-				       min(sizeof(req), udata->inlen)))
-			return -EINVAL;
+		ret = ib_copy_validate_udata_in(udata, req, user_cq_buffer);
+		if (ret)
+			return ret;
 
 		spin_lock_irqsave(&ucontext->cq_reg_mem_list_lock, flags);
 		iwpbl_buf = irdma_get_pbl((unsigned long)req.user_cq_buffer,
@@ -2265,24 +2264,20 @@ static int irdma_setup_umode_srq(struct irdma_device *iwdev,
 				 struct irdma_srq_init_info *info,
 				 struct ib_udata *udata)
 {
-#define IRDMA_CREATE_SRQ_MIN_REQ_LEN \
-	offsetofend(struct irdma_create_srq_req, user_shadow_area)
 	struct irdma_create_srq_req req = {};
 	struct irdma_ucontext *ucontext;
 	struct irdma_srq_mr *srqmr;
 	struct irdma_pbl *iwpbl;
 	unsigned long flags;
+	int ret;
 
 	iwsrq->user_mode = true;
 	ucontext = rdma_udata_to_drv_context(udata, struct irdma_ucontext,
 					     ibucontext);
 
-	if (udata->inlen < IRDMA_CREATE_SRQ_MIN_REQ_LEN)
-		return -EINVAL;
-
-	if (ib_copy_from_udata(&req, udata,
-			       min(sizeof(req), udata->inlen)))
-		return -EFAULT;
+	ret = ib_copy_validate_udata_in(udata, req, user_shadow_area);
+	if (ret)
+		return ret;
 
 	spin_lock_irqsave(&ucontext->srq_reg_mem_list_lock, flags);
 	iwpbl = irdma_get_pbl((unsigned long)req.user_srq_buf,
@@ -2487,7 +2482,6 @@ static int irdma_create_cq(struct ib_cq *ibcq,
 			   const struct ib_cq_init_attr *attr,
 			   struct uverbs_attr_bundle *attrs)
 {
-#define IRDMA_CREATE_CQ_MIN_REQ_LEN offsetofend(struct irdma_create_cq_req, user_cq_buf)
 #define IRDMA_CREATE_CQ_MIN_RESP_LEN offsetofend(struct irdma_create_cq_resp, cq_size)
 	struct ib_udata *udata = &attrs->driver_udata;
 	struct ib_device *ibdev = ibcq->device;
@@ -2511,8 +2505,7 @@ static int irdma_create_cq(struct ib_cq *ibcq,
 	if (err_code)
 		return err_code;
 
-	if (udata && (udata->inlen < IRDMA_CREATE_CQ_MIN_REQ_LEN ||
-		      udata->outlen < IRDMA_CREATE_CQ_MIN_RESP_LEN))
+	if (udata && udata->outlen < IRDMA_CREATE_CQ_MIN_RESP_LEN)
 		return -EINVAL;
 
 	err_code = irdma_alloc_rsrc(rf, rf->allocated_cqs, rf->max_cq, &cq_num,
@@ -2554,11 +2547,14 @@ static int irdma_create_cq(struct ib_cq *ibcq,
 		ucontext =
 			rdma_udata_to_drv_context(udata, struct irdma_ucontext,
 						  ibucontext);
-		if (ib_copy_from_udata(&req, udata,
-				       min(sizeof(req), udata->inlen))) {
-			err_code = -EFAULT;
+		/* Even though the last member of struct irdma_create_cq_req
+		 * was always user_shadow_area, we need backwards compat with
+		 * the legacy i40iw struct i40iw_ucreate_cq which stopped
+		 * at user_cq_buffer.
+		 */
+		err_code = ib_copy_validate_udata_in(udata, req, user_cq_buf);
+		if (err_code)
 			goto cq_free_rsrc;
-		}
 
 		spin_lock_irqsave(&ucontext->cq_reg_mem_list_lock, flags);
 		iwpbl = irdma_get_pbl((unsigned long)req.user_cq_buf,
@@ -3541,7 +3537,6 @@ static struct ib_mr *irdma_reg_user_mr(struct ib_pd *pd, u64 start, u64 len,
 				       struct ib_dmah *dmah,
 				       struct ib_udata *udata)
 {
-#define IRDMA_MEM_REG_MIN_REQ_LEN offsetofend(struct irdma_mem_reg_req, sq_pages)
 	struct irdma_device *iwdev = to_iwdev(pd->device);
 	struct irdma_mem_reg_req req = {};
 	struct ib_umem *region = NULL;
@@ -3554,9 +3549,6 @@ static struct ib_mr *irdma_reg_user_mr(struct ib_pd *pd, u64 start, u64 len,
 	if (len > iwdev->rf->sc_dev.hw_attrs.max_mr_size)
 		return ERR_PTR(-EINVAL);
 
-	if (udata->inlen < IRDMA_MEM_REG_MIN_REQ_LEN)
-		return ERR_PTR(-EINVAL);
-
 	region = ib_umem_get_va(pd->device, start, len, access);
 
 	if (IS_ERR(region)) {
@@ -3565,9 +3557,10 @@ static struct ib_mr *irdma_reg_user_mr(struct ib_pd *pd, u64 start, u64 len,
 		return (struct ib_mr *)region;
 	}
 
-	if (ib_copy_from_udata(&req, udata, min(sizeof(req), udata->inlen))) {
+	err = ib_copy_validate_udata_in(udata, req, sq_pages);
+	if (err) {
 		ib_umem_release(region);
-		return ERR_PTR(-EFAULT);
+		return ERR_PTR(err);
 	}
 
 	iwmr = irdma_alloc_iwmr(region, pd, virt, req.reg_type);
-- 
2.55.0.rc0.799.gd6f94ed593-goog


^ permalink raw reply related	[flat|nested] 8+ messages in thread

* [PATCH rdma-next v2 3/5] RDMA/irdma: Use ib_respond_empty_udata where applicable
  2026-06-29 23:25 [PATCH rdma-next v2 0/5] RDMA/irdma: Adopt robust udata Jacob Moroni
  2026-06-29 23:25 ` [PATCH rdma-next v2 1/5] RDMA/irdma: Enforce empty udata input for no-input ops Jacob Moroni
  2026-06-29 23:25 ` [PATCH rdma-next v2 2/5] RDMA/irdma: Use robust udata input copy helpers Jacob Moroni
@ 2026-06-29 23:25 ` Jacob Moroni
  2026-06-29 23:25 ` [PATCH rdma-next v2 4/5] RDMA/irdma: Use robust udata helper for QP creation Jacob Moroni
                   ` (2 subsequent siblings)
  5 siblings, 0 replies; 8+ messages in thread
From: Jacob Moroni @ 2026-06-29 23:25 UTC (permalink / raw)
  To: tatyana.e.nikolova, jgg, leon; +Cc: linux-rdma, Jacob Moroni

Methods that do not provide any user response should use
the ib_respond_empty_udata() helper to ensure that user
response buffers are cleared.

Signed-off-by: Jacob Moroni <jmoroni@google.com>
---
 drivers/infiniband/hw/irdma/verbs.c | 51 ++++++++++++++++++++++++-----
 1 file changed, 43 insertions(+), 8 deletions(-)

diff --git a/drivers/infiniband/hw/irdma/verbs.c b/drivers/infiniband/hw/irdma/verbs.c
index 074dfa46b35c..c1df9ca1b86b 100644
--- a/drivers/infiniband/hw/irdma/verbs.c
+++ b/drivers/infiniband/hw/irdma/verbs.c
@@ -455,6 +455,10 @@ static int irdma_dealloc_pd(struct ib_pd *ibpd, struct ib_udata *udata)
 	if (err)
 		return err;
 
+	err = ib_respond_empty_udata(udata);
+	if (err)
+		return err;
+
 	irdma_free_rsrc(iwdev->rf, iwdev->rf->allocated_pds, iwpd->sc_pd.pd_id);
 
 	return 0;
@@ -557,6 +561,10 @@ static int irdma_destroy_qp(struct ib_qp *ibqp, struct ib_udata *udata)
 	if (err)
 		return err;
 
+	err = ib_respond_empty_udata(udata);
+	if (err)
+		return err;
+
 	iwqp->sc_qp.qp_uk.destroy_pending = true;
 
 	if (iwqp->iwarp_state >= IRDMA_QP_STATE_IDLE)
@@ -1626,14 +1634,14 @@ int irdma_modify_qp_roce(struct ib_qp *ibqp, struct ib_qp_attr *attr,
 				uresp.push_offset = iwqp->sc_qp.push_offset;
 			}
 			ret = ib_respond_udata(udata, uresp);
-			if (ret) {
+			if (ret)
 				irdma_remove_push_mmap_entries(iwqp);
-				return ret;
-			}
+			return ret;
+
 		}
 	}
 
-	return 0;
+	return ib_respond_empty_udata(udata);
 exit:
 	spin_unlock_irqrestore(&iwqp->lock, flags);
 
@@ -1872,13 +1880,12 @@ int irdma_modify_qp(struct ib_qp *ibqp, struct ib_qp_attr *attr, int attr_mask,
 		}
 
 		err = ib_respond_udata(udata, uresp);
-		if (err) {
+		if (err)
 			irdma_remove_push_mmap_entries(iwqp);
-			return err;
-		}
+		return err;
 	}
 
-	return 0;
+	return ib_respond_empty_udata(udata);
 exit:
 	spin_unlock_irqrestore(&iwqp->lock, flags);
 
@@ -1982,6 +1989,10 @@ static int irdma_destroy_srq(struct ib_srq *ibsrq, struct ib_udata *udata)
 	if (err)
 		return err;
 
+	err = ib_respond_empty_udata(udata);
+	if (err)
+		return err;
+
 	irdma_srq_wq_destroy(iwdev->rf, srq);
 	irdma_srq_free_rsrc(iwdev->rf, iwsrq);
 	return 0;
@@ -2007,6 +2018,10 @@ static int irdma_destroy_cq(struct ib_cq *ib_cq, struct ib_udata *udata)
 	if (err)
 		return err;
 
+	err = ib_respond_empty_udata(udata);
+	if (err)
+		return err;
+
 	spin_lock_irqsave(&iwcq->lock, flags);
 	if (!list_empty(&iwcq->cmpl_generated))
 		irdma_remove_cmpls_list(iwcq);
@@ -2052,6 +2067,10 @@ static int irdma_resize_cq(struct ib_cq *ibcq, unsigned int entries,
 	u8 cqe_size;
 	int ret;
 
+	ret = ib_respond_empty_udata(udata);
+	if (ret)
+		return ret;
+
 	iwdev = to_iwdev(ibcq->device);
 	rf = iwdev->rf;
 
@@ -2222,6 +2241,10 @@ static int irdma_modify_srq(struct ib_srq *ibsrq, struct ib_srq_attr *attr,
 	if (status)
 		return status;
 
+	status = ib_respond_empty_udata(udata);
+	if (status)
+		return status;
+
 	if (attr_mask & IB_SRQ_MAX_WR)
 		return -EINVAL;
 
@@ -3063,6 +3086,10 @@ static int irdma_alloc_mw(struct ib_mw *ibmw, struct ib_udata *udata)
 	if (err_code)
 		return err_code;
 
+	err_code = ib_respond_empty_udata(udata);
+	if (err_code)
+		return err_code;
+
 	stag = irdma_create_stag(iwdev);
 	if (!stag)
 		return -ENOMEM;
@@ -4006,6 +4033,10 @@ static int irdma_dereg_mr(struct ib_mr *ib_mr, struct ib_udata *udata)
 	if (ret)
 		return ret;
 
+	ret = ib_respond_empty_udata(udata);
+	if (ret)
+		return ret;
+
 	if (iwmr->type != IRDMA_MEMREG_TYPE_MEM) {
 		if (iwmr->region) {
 			struct irdma_ucontext *ucontext;
@@ -5381,6 +5412,10 @@ static int irdma_create_ah(struct ib_ah *ibah, struct rdma_ah_init_attr *attr,
 	if (err)
 		return err;
 
+	err = ib_respond_empty_udata(udata);
+	if (err)
+		return err;
+
 	err = irdma_setup_ah(ibah, attr);
 	if (err)
 		return err;
-- 
2.55.0.rc0.799.gd6f94ed593-goog


^ permalink raw reply related	[flat|nested] 8+ messages in thread

* [PATCH rdma-next v2 4/5] RDMA/irdma: Use robust udata helper for QP creation
  2026-06-29 23:25 [PATCH rdma-next v2 0/5] RDMA/irdma: Adopt robust udata Jacob Moroni
                   ` (2 preceding siblings ...)
  2026-06-29 23:25 ` [PATCH rdma-next v2 3/5] RDMA/irdma: Use ib_respond_empty_udata where applicable Jacob Moroni
@ 2026-06-29 23:25 ` Jacob Moroni
  2026-06-29 23:25 ` [PATCH rdma-next v2 5/5] RDMA/irdma: Enable uverbs_robust_udata compliance flag Jacob Moroni
  2026-06-30 17:23 ` [PATCH rdma-next v2 0/5] RDMA/irdma: Adopt robust udata Jacob Moroni
  5 siblings, 0 replies; 8+ messages in thread
From: Jacob Moroni @ 2026-06-29 23:25 UTC (permalink / raw)
  To: tatyana.e.nikolova, jgg, leon; +Cc: linux-rdma, Jacob Moroni

Replace the manual udata input copy and validation during
QP creation with the robust helper.

The irdma driver is backwards compatible with the legacy
i40iw userspace provider. The current create_qp ABI contains
two 8 byte fields. The legacy i40iw ABI was the same but
also contained two additional fields which were never actually
used. Furthermore, the i40iw userspace provider never explicitly
zero-initialized those extra fields, so there is a chance that
existing binaries are passing non-zero garbage values down
to the kernel.

Previously, the irdma driver only copied out the first 16
bytes and did not have any check for the rest of the buffer
being zero, so that additional garbage didn't matter.

By switching to ib_copy_validate_udata_in(), we will now be
checking to ensure that data beyond the kernel's definition
of the request is all zero.

In order to avoid breaking legacy binaries, we therefore need
to increase the request structure size to cover those garbage
fields.

- Legacy binaries will continue to pass down a 32 byte request,
  with the driver copying the entire 32 bytes out but ignoring
  the second 16 bytes, just as before.

- Newer binaries will pass down the normal 16 byte request. The
  ib_copy_validate_udata_in() call will allow this to succeed
  because we use user_compl_ctx as our minimum length (16 bytes).

- If the request is ever extended, the new fields would be
  added after the "don't use" fields and would work as per
  the normal uAPI mechanism.

Signed-off-by: Jacob Moroni <jmoroni@google.com>
---
 drivers/infiniband/hw/irdma/verbs.c | 44 +++++++++++++++--------------
 include/uapi/rdma/irdma-abi.h       |  1 +
 2 files changed, 24 insertions(+), 21 deletions(-)

diff --git a/drivers/infiniband/hw/irdma/verbs.c b/drivers/infiniband/hw/irdma/verbs.c
index c1df9ca1b86b..30f2483bdc33 100644
--- a/drivers/infiniband/hw/irdma/verbs.c
+++ b/drivers/infiniband/hw/irdma/verbs.c
@@ -627,37 +627,29 @@ static void irdma_setup_virt_qp(struct irdma_device *iwdev,
 
 /**
  * irdma_setup_umode_qp - setup sq and rq size in user mode qp
- * @udata: udata
+ * @ucontext: user context
+ * @req: user request pointer
  * @iwdev: iwarp device
  * @iwqp: qp ptr (user or kernel)
  * @info: initialize info to return
  * @init_attr: Initial QP create attributes
  */
-static int irdma_setup_umode_qp(struct ib_udata *udata,
+static int irdma_setup_umode_qp(struct irdma_ucontext *ucontext,
+				struct irdma_create_qp_req *req,
 				struct irdma_device *iwdev,
 				struct irdma_qp *iwqp,
 				struct irdma_qp_init_info *info,
 				struct ib_qp_init_attr *init_attr)
 {
-	struct irdma_ucontext *ucontext = rdma_udata_to_drv_context(udata,
-				struct irdma_ucontext, ibucontext);
 	struct irdma_qp_uk_init_info *ukinfo = &info->qp_uk_init_info;
-	struct irdma_create_qp_req req;
 	unsigned long flags;
 	int ret;
 
-	ret = ib_copy_from_udata(&req, udata,
-				 min(sizeof(req), udata->inlen));
-	if (ret) {
-		ibdev_dbg(&iwdev->ibdev, "VERBS: ib_copy_from_data fail\n");
-		return ret;
-	}
-
-	iwqp->ctx_info.qp_compl_ctx = req.user_compl_ctx;
+	iwqp->ctx_info.qp_compl_ctx = req->user_compl_ctx;
 	iwqp->user_mode = 1;
-	if (req.user_wqe_bufs) {
+	if (req->user_wqe_bufs) {
 		spin_lock_irqsave(&ucontext->qp_reg_mem_list_lock, flags);
-		iwqp->iwpbl = irdma_get_pbl((unsigned long)req.user_wqe_bufs,
+		iwqp->iwpbl = irdma_get_pbl((unsigned long)req->user_wqe_bufs,
 					    &ucontext->qp_reg_mem_list);
 		spin_unlock_irqrestore(&ucontext->qp_reg_mem_list_lock, flags);
 
@@ -970,7 +962,6 @@ static int irdma_create_qp(struct ib_qp *ibqp,
 			   struct ib_qp_init_attr *init_attr,
 			   struct ib_udata *udata)
 {
-#define IRDMA_CREATE_QP_MIN_REQ_LEN offsetofend(struct irdma_create_qp_req, user_compl_ctx)
 #define IRDMA_CREATE_QP_MIN_RESP_LEN offsetofend(struct irdma_create_qp_resp, rsvd)
 	struct ib_pd *ibpd = ibqp->pd;
 	struct irdma_pd *iwpd = to_iwpd(ibpd);
@@ -985,6 +976,7 @@ static int irdma_create_qp(struct ib_qp *ibqp,
 	struct irdma_uk_attrs *uk_attrs = &dev->hw_attrs.uk_attrs;
 	struct irdma_qp_init_info init_info = {};
 	struct irdma_qp_host_ctx_info *ctx_info;
+	struct irdma_create_qp_req ureq = {};
 	struct irdma_srq *iwsrq;
 	bool srq_valid = false;
 	u32 srq_id = 0;
@@ -1002,9 +994,14 @@ static int irdma_create_qp(struct ib_qp *ibqp,
 	if (err_code)
 		return err_code;
 
-	if (udata && (udata->inlen < IRDMA_CREATE_QP_MIN_REQ_LEN ||
-		      udata->outlen < IRDMA_CREATE_QP_MIN_RESP_LEN))
-		return -EINVAL;
+	if (udata) {
+		if (udata->outlen < IRDMA_CREATE_QP_MIN_RESP_LEN)
+			return -EINVAL;
+
+		err_code = ib_copy_validate_udata_in(udata, ureq, user_compl_ctx);
+		if (err_code)
+			return err_code;
+	}
 
 	init_info.vsi = &iwdev->vsi;
 	init_info.qp_uk_init_info.uk_attrs = uk_attrs;
@@ -1063,9 +1060,14 @@ static int irdma_create_qp(struct ib_qp *ibqp,
 	init_waitqueue_head(&iwqp->mod_qp_waitq);
 
 	if (udata) {
+		struct irdma_ucontext *ucontext =
+			rdma_udata_to_drv_context(udata,
+						  struct irdma_ucontext,
+						  ibucontext);
+
 		init_info.qp_uk_init_info.abi_ver = iwpd->sc_pd.abi_ver;
-		err_code = irdma_setup_umode_qp(udata, iwdev, iwqp, &init_info,
-						init_attr);
+		err_code = irdma_setup_umode_qp(ucontext, &ureq, iwdev, iwqp,
+						&init_info, init_attr);
 	} else {
 		INIT_DELAYED_WORK(&iwqp->dwork_flush, irdma_flush_worker);
 		init_info.qp_uk_init_info.abi_ver = IRDMA_ABI_VER;
diff --git a/include/uapi/rdma/irdma-abi.h b/include/uapi/rdma/irdma-abi.h
index 36f20802bcc8..38155affc8b4 100644
--- a/include/uapi/rdma/irdma-abi.h
+++ b/include/uapi/rdma/irdma-abi.h
@@ -88,6 +88,7 @@ struct irdma_create_srq_resp {
 struct irdma_create_qp_req {
 	__aligned_u64 user_wqe_bufs;
 	__aligned_u64 user_compl_ctx;
+	__aligned_u64 legacy_dontuse[2];
 };
 
 struct irdma_mem_reg_req {
-- 
2.55.0.rc0.799.gd6f94ed593-goog


^ permalink raw reply related	[flat|nested] 8+ messages in thread

* [PATCH rdma-next v2 5/5] RDMA/irdma: Enable uverbs_robust_udata compliance flag
  2026-06-29 23:25 [PATCH rdma-next v2 0/5] RDMA/irdma: Adopt robust udata Jacob Moroni
                   ` (3 preceding siblings ...)
  2026-06-29 23:25 ` [PATCH rdma-next v2 4/5] RDMA/irdma: Use robust udata helper for QP creation Jacob Moroni
@ 2026-06-29 23:25 ` Jacob Moroni
  2026-06-30 17:23 ` [PATCH rdma-next v2 0/5] RDMA/irdma: Adopt robust udata Jacob Moroni
  5 siblings, 0 replies; 8+ messages in thread
From: Jacob Moroni @ 2026-06-29 23:25 UTC (permalink / raw)
  To: tatyana.e.nikolova, jgg, leon; +Cc: linux-rdma, Jacob Moroni

The irdma driver has been audited to confirm that:

1. Methods which do not accept udata input perform an explicit
   check for no (or zero value) input.
2. Methods which do accept input perform the correct validation
   to ensure that additional udata beyond the kernel's current
   ABI definition is zero, and to enforce the required minimum
   length.
3. Methods which do not return udata responses use the proper
   helper.

Signed-off-by: Jacob Moroni <jmoroni@google.com>
---
 drivers/infiniband/hw/irdma/verbs.c | 1 +
 1 file changed, 1 insertion(+)

diff --git a/drivers/infiniband/hw/irdma/verbs.c b/drivers/infiniband/hw/irdma/verbs.c
index 30f2483bdc33..1170c688937f 100644
--- a/drivers/infiniband/hw/irdma/verbs.c
+++ b/drivers/infiniband/hw/irdma/verbs.c
@@ -5500,6 +5500,7 @@ static const struct ib_device_ops irdma_dev_ops = {
 	.owner = THIS_MODULE,
 	.driver_id = RDMA_DRIVER_IRDMA,
 	.uverbs_abi_ver = IRDMA_ABI_VER,
+	.uverbs_robust_udata = 1,
 
 	.alloc_hw_port_stats = irdma_alloc_hw_port_stats,
 	.alloc_mr = irdma_alloc_mr,
-- 
2.55.0.rc0.799.gd6f94ed593-goog


^ permalink raw reply related	[flat|nested] 8+ messages in thread

* Re: [PATCH rdma-next v2 0/5] RDMA/irdma: Adopt robust udata
  2026-06-29 23:25 [PATCH rdma-next v2 0/5] RDMA/irdma: Adopt robust udata Jacob Moroni
                   ` (4 preceding siblings ...)
  2026-06-29 23:25 ` [PATCH rdma-next v2 5/5] RDMA/irdma: Enable uverbs_robust_udata compliance flag Jacob Moroni
@ 2026-06-30 17:23 ` Jacob Moroni
  2026-06-30 19:18   ` Jason Gunthorpe
  5 siblings, 1 reply; 8+ messages in thread
From: Jacob Moroni @ 2026-06-30 17:23 UTC (permalink / raw)
  To: tatyana.e.nikolova, jgg, leon; +Cc: linux-rdma

Hi Jason,

I need to respin this yet again to fix a few more sashiko findings (sorry), but
I was curious:

Looking at bnxt_re and irdma, in many cases where we use ib_respond_empty_udata,
there is also a check for ib_is_udata_in_empty.

Should I make a combined helper? Something like "ib_check_no_udata_io"?

Also, for bnxt_re, I think there are places where the ib_respond_empty_udata
call at the end of the method could be problematic, particularly for "destroy"
ops since the driver doesn't check the return value. V1 of this series did the
same for irdma and sashiko freaked out.

The issue is that ib_respond_empty_udata can return a failure but the driver
doesn't check for it. If this happens, the kernel will keep the object around
and will try to destroy it later when the process exits, but the driver has
likely already cleaned up its resources on the first destroy call.

Does this seem legitimate? Anyway, a unified helper that is called right
at the beginning would eliminate the issue if so.

Thanks,
- Jake

^ permalink raw reply	[flat|nested] 8+ messages in thread

* Re: [PATCH rdma-next v2 0/5] RDMA/irdma: Adopt robust udata
  2026-06-30 17:23 ` [PATCH rdma-next v2 0/5] RDMA/irdma: Adopt robust udata Jacob Moroni
@ 2026-06-30 19:18   ` Jason Gunthorpe
  0 siblings, 0 replies; 8+ messages in thread
From: Jason Gunthorpe @ 2026-06-30 19:18 UTC (permalink / raw)
  To: Jacob Moroni; +Cc: tatyana.e.nikolova, leon, linux-rdma

On Tue, Jun 30, 2026 at 01:23:04PM -0400, Jacob Moroni wrote:
> Hi Jason,
> 
> I need to respin this yet again to fix a few more sashiko findings (sorry), but
> I was curious:
> 
> Looking at bnxt_re and irdma, in many cases where we use
> ib_respond_empty_udata, there is also a check for
> ib_is_udata_in_empty.
> 
> Should I make a combined helper? Something like "ib_check_no_udata_io"?

I imagined that the functions should all have a common pattern,
validate the input at the top and generate the response on the success
path.

> Also, for bnxt_re, I think there are places where the ib_respond_empty_udata
> call at the end of the method could be problematic, particularly for "destroy"
> ops since the driver doesn't check the return value. V1 of this series did the
> same for irdma and sashiko freaked out.

It should generally be called at the end of the method, and everything
should be careful to undo their stuff if it fails. Much of that is
bundled into the uobject abort mechanism so it happens transparently
in many cases.

> The issue is that ib_respond_empty_udata can return a failure but the driver
> doesn't check for it.

It does check, I guess this is problematic that the object is already
destroyed and cannot be undestroyed at this point, yet could be
destroyed again.

> Does this seem legitimate? Anyway, a unified helper that is called right
> at the beginning would eliminate the issue if so.

Yes, that does seem like a good reason to make that kind of change.

Jason

^ permalink raw reply	[flat|nested] 8+ messages in thread

end of thread, other threads:[~2026-06-30 19:18 UTC | newest]

Thread overview: 8+ messages (download: mbox.gz follow: Atom feed
-- links below jump to the message on this page --
2026-06-29 23:25 [PATCH rdma-next v2 0/5] RDMA/irdma: Adopt robust udata Jacob Moroni
2026-06-29 23:25 ` [PATCH rdma-next v2 1/5] RDMA/irdma: Enforce empty udata input for no-input ops Jacob Moroni
2026-06-29 23:25 ` [PATCH rdma-next v2 2/5] RDMA/irdma: Use robust udata input copy helpers Jacob Moroni
2026-06-29 23:25 ` [PATCH rdma-next v2 3/5] RDMA/irdma: Use ib_respond_empty_udata where applicable Jacob Moroni
2026-06-29 23:25 ` [PATCH rdma-next v2 4/5] RDMA/irdma: Use robust udata helper for QP creation Jacob Moroni
2026-06-29 23:25 ` [PATCH rdma-next v2 5/5] RDMA/irdma: Enable uverbs_robust_udata compliance flag Jacob Moroni
2026-06-30 17:23 ` [PATCH rdma-next v2 0/5] RDMA/irdma: Adopt robust udata Jacob Moroni
2026-06-30 19:18   ` Jason Gunthorpe

This is a public inbox, see mirroring instructions
for how to clone and mirror all data and code used for this inbox