]> git.ipfire.org Git - thirdparty/kernel/linux.git/commitdiff
RDMA: Add ib_respond_udata()
authorJason Gunthorpe <jgg@nvidia.com>
Tue, 3 Mar 2026 19:50:02 +0000 (15:50 -0400)
committerJason Gunthorpe <jgg@nvidia.com>
Sun, 8 Mar 2026 10:20:25 +0000 (06:20 -0400)
Wrap the common copy_to_user() pattern used in drivers and enhance it
to zero pad as well. Include debug logging on failures.

Link: https://patch.msgid.link/r/5-v3-bd56dd443069+49-bnxt_re_uapi_jgg@nvidia.com
Tested-by: Sriharsha Basavapatna <sriharsha.basavapatna@broadcom.com>
Acked-by: Sriharsha Basavapatna <sriharsha.basavapatna@broadcom.com>
Signed-off-by: Jason Gunthorpe <jgg@nvidia.com>
drivers/infiniband/core/uverbs_ioctl.c
include/rdma/uverbs_ioctl.h

index 5e5b00c6236fa8e09e2ecf17011f517d509d53b2..b61af625e679b2cf112d0f3731499f84300f110f 100644 (file)
@@ -910,3 +910,27 @@ int _ib_copy_validate_udata_cm_fail(struct ib_udata *udata, u64 req_cm,
        return -EOPNOTSUPP;
 }
 EXPORT_SYMBOL(_ib_copy_validate_udata_cm_fail);
+
+int _ib_respond_udata(struct ib_udata *udata, const void *src, size_t len)
+{
+       size_t copy_len;
+
+       /* 0 length copy_len is a NOP for copy_to_user() and doesn't fail. */
+       copy_len = min(len, udata->outlen);
+       if (copy_to_user(udata->outbuf, src, copy_len))
+               goto err_fault;
+       if (copy_len < udata->outlen) {
+               if (clear_user(udata->outbuf + copy_len,
+                              udata->outlen - copy_len))
+                       goto err_fault;
+       }
+       return 0;
+err_fault:
+       ibdev_dbg(
+               rdma_udata_to_dev(udata),
+               "System call driver out udata has EFAULT (%zu into %zu) for ioctl %ps called by %pSR\n",
+               len, udata->outlen, uverbs_get_handler_fn(udata),
+               __builtin_return_address(0));
+       return -EFAULT;
+}
+EXPORT_SYMBOL(_ib_respond_udata);
index a73016a977a12d7ac99d0765593e3184fe6c3c91..38a11bfe137430336a071dd90885d8c7a984c086 100644 (file)
@@ -900,6 +900,7 @@ int uverbs_copy_to_struct_or_zero(const struct uverbs_attr_bundle *bundle,
 
 int _ib_copy_validate_udata_in(struct ib_udata *udata, void *req,
                               size_t kernel_size, size_t minimum_size);
+int _ib_respond_udata(struct ib_udata *udata, const void *src, size_t len);
 #else
 static inline int
 uverbs_get_flags64(u64 *to, const struct uverbs_attr_bundle *attrs_bundle,
@@ -964,6 +965,11 @@ static inline int _ib_copy_validate_udata_in(struct ib_udata *udata, void *req,
        return -EINVAL;
 }
 
+static inline int _ib_respond_udata(struct ib_udata *udata, const void *src,
+                                   size_t len)
+{
+       return -EINVAL;
+}
 #endif
 
 #define uverbs_get_const_signed(_to, _attrs_bundle, _idx)                      \
@@ -1069,4 +1075,31 @@ int _ib_copy_validate_udata_cm_fail(struct ib_udata *udata, u64 req_cm,
                ret;                                                          \
        })
 
+/**
+ * ib_respond_udata - Copy a driver data response to userspace
+ * @_udata: The system calls ib_udata struct
+ * @_rep: Kernel buffer containing the response driver data on the stack
+ *
+ * Copy driver data response structures back to userspace in a way that
+ * is forwards and backwards compatible. Longer kernel structs are truncated,
+ * userspace has made some kind of error if it needed the truncated information.
+ * Shorter structs are zero padded.
+ */
+#define ib_respond_udata(_udata, _rep) \
+       _ib_respond_udata(_udata, &(_rep), sizeof(_rep))
+
+/**
+ * ib_respond_empty_udata - Zero fill the response buffer to userspace
+ * @_udata: The system calls ib_udata struct
+ *
+ * Used when there is no driver response data to return. Provides forward
+ * compatability by zeroing any buffer the user may have provided.
+ */
+static inline int ib_respond_empty_udata(struct ib_udata *udata)
+{
+       if (udata && udata->outlen && clear_user(udata->outbuf, udata->outlen))
+               return -EFAULT;
+       return 0;
+}
+
 #endif