RDMA v7.1 first rc window

- syzbot triggred crash in rxe due to concurrent plug/unplug
 
 - Possible non-zero'd memory exposed to userspace in bnxt_re
 
 - Malicous 'magic packet' with SIW causes a buffer overflow
 
 - Tighten the new uAPI validation code to not crash in debugging prints
   and have the right module dependencies in drivers
 
 - mana was missing the max_msg_sz report to userspace
 
 - UAF in rtrs on an error path
 -----BEGIN PGP SIGNATURE-----
 
 iHUEABYKAB0WIQRRRCHOFoQz/8F5bUaFwuHvBreFYQUCahDmpgAKCRCFwuHvBreF
 YRRTAP0YXV95Y5gcli55IhetKjJUzQbaREz2NueqIpf1IorMbAD+Lns4DgZCU0KW
 bC81x7cGHBSyCju9zogIdBFJhsbxeQ4=
 =MxJz
 -----END PGP SIGNATURE-----

Merge tag 'for-linus' of git://git.kernel.org/pub/scm/linux/kernel/git/rdma/rdma

Pull rdma fixes from Jason Gunthorpe:

 - syzbot triggred crash in rxe due to concurrent plug/unplug

 - Possible non-zero'd memory exposed to userspace in bnxt_re

 - Malicous 'magic packet' with SIW causes a buffer overflow

 - Tighten the new uAPI validation code to not crash in debugging prints
   and have the right module dependencies in drivers

 - mana was missing the max_msg_sz report to userspace

 - UAF in rtrs on an error path

* tag 'for-linus' of git://git.kernel.org/pub/scm/linux/kernel/git/rdma/rdma:
  RDMA/rtrs: Fix use-after-free in path file creation cleanup
  RDMA/mana_ib: Report max_msg_sz in mana_ib_query_port
  RDMA/core: Do not read wild stack memory in uverbs_get_handler_fn()
  RDMA/core: Move the _ib_copy_validate_udata* functions to ib_core_uverbs
  RDMA/siw: Reject MPA FPDU length underflow before signed receive math
  RDMA/bnxt_re: zero shared page before exposing to userspace
  selftests/rdma: explicitly skip tests when required modules are missing
  RDMA/nldev: Add mutual exclusion in nldev_dellink()
This commit is contained in:
Linus Torvalds 2026-05-23 07:17:27 -07:00
commit ab868c1097
13 changed files with 178 additions and 140 deletions

View File

@ -9,6 +9,7 @@
#include <linux/dma-resv.h>
#include "uverbs.h"
#include "core_priv.h"
#include "rdma_core.h"
MODULE_IMPORT_NS("DMA_BUF");
@ -416,3 +417,89 @@ struct ib_device *rdma_udata_to_dev(struct ib_udata *udata)
}
EXPORT_SYMBOL(rdma_udata_to_dev);
#if IS_ENABLED(CONFIG_INFINIBAND_USER_ACCESS)
uverbs_api_ioctl_handler_fn uverbs_get_handler_fn(struct ib_udata *udata)
{
struct uverbs_attr_bundle *bundle =
rdma_udata_to_uverbs_attr_bundle(udata);
lockdep_assert_held(&bundle->ufile->device->disassociate_srcu);
return srcu_dereference(bundle->method_elm->handler,
&bundle->ufile->device->disassociate_srcu);
}
int _ib_copy_validate_udata_in(struct ib_udata *udata, void *req,
size_t kernel_size, size_t minimum_size)
{
int err;
if (udata->inlen < minimum_size) {
ibdev_dbg(
rdma_udata_to_dev(udata),
"System call driver input udata too small (%zu < %zu) for ioctl %ps called by %pSR\n",
udata->inlen, minimum_size,
uverbs_get_handler_fn(udata),
__builtin_return_address(0));
return -EINVAL;
}
err = copy_struct_from_user(req, kernel_size, udata->inbuf,
udata->inlen);
if (err) {
if (err == -E2BIG) {
ibdev_dbg(
rdma_udata_to_dev(udata),
"System call driver input udata not zero from %zu -> %zu for ioctl %ps called by %pSR\n",
minimum_size, udata->inlen,
uverbs_get_handler_fn(udata),
__builtin_return_address(0));
return -EOPNOTSUPP;
}
ibdev_dbg(
rdma_udata_to_dev(udata),
"System call driver input udata EFAULT for ioctl %ps called by %pSR\n",
uverbs_get_handler_fn(udata),
__builtin_return_address(0));
return err;
}
return 0;
}
EXPORT_SYMBOL(_ib_copy_validate_udata_in);
int _ib_copy_validate_udata_cm_fail(struct ib_udata *udata, u64 req_cm,
u64 valid_cm)
{
ibdev_dbg(
rdma_udata_to_dev(udata),
"System call driver input udata has unsupported comp_mask %llx & ~%llx = %llx for ioctl %ps called by %pSR\n",
req_cm, valid_cm, req_cm & ~valid_cm,
uverbs_get_handler_fn(udata), __builtin_return_address(0));
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);
#endif

View File

@ -51,6 +51,7 @@
* a controlled QKEY.
*/
static bool privileged_qkey;
static DEFINE_MUTEX(nldev_dellink_mutex);
typedef int (*res_fill_func_t)(struct sk_buff*, bool,
struct rdma_restrack_entry*, uint32_t);
@ -1846,7 +1847,9 @@ static int nldev_dellink(struct sk_buff *skb, struct nlmsghdr *nlh,
* implicitly scoped to the driver supporting dynamic link deletion like RXE.
*/
if (device->link_ops && device->link_ops->dellink) {
mutex_lock(&nldev_dellink_mutex);
err = device->link_ops->dellink(device);
mutex_unlock(&nldev_dellink_mutex);
if (err)
return err;
}

View File

@ -229,6 +229,40 @@ int uverbs_dealloc_mw(struct ib_mw *mw);
void ib_uverbs_detach_umcast(struct ib_qp *qp,
struct ib_uqp_object *uobj);
struct bundle_alloc_head {
struct_group_tagged(bundle_alloc_head_hdr, hdr,
struct bundle_alloc_head *next;
);
u8 data[];
};
struct bundle_priv {
/* Must be first */
struct bundle_alloc_head_hdr alloc_head;
struct bundle_alloc_head *allocated_mem;
size_t internal_avail;
size_t internal_used;
struct radix_tree_root *radix;
void __rcu **radix_slots;
unsigned long radix_slots_len;
u32 method_key;
struct ib_uverbs_attr __user *user_attrs;
struct ib_uverbs_attr *uattrs;
DECLARE_BITMAP(uobj_finalize, UVERBS_API_ATTR_BKEY_LEN);
DECLARE_BITMAP(spec_finalize, UVERBS_API_ATTR_BKEY_LEN);
DECLARE_BITMAP(uobj_hw_obj_valid, UVERBS_API_ATTR_BKEY_LEN);
/*
* Must be last. bundle ends in a flex array which overlaps
* internal_buffer.
*/
struct uverbs_attr_bundle_hdr bundle;
u64 internal_buffer[32];
};
long ib_uverbs_ioctl(struct file *filp, unsigned int cmd, unsigned long arg);
struct ib_uverbs_flow_spec {

View File

@ -35,54 +35,6 @@
#include "rdma_core.h"
#include "uverbs.h"
struct bundle_alloc_head {
struct_group_tagged(bundle_alloc_head_hdr, hdr,
struct bundle_alloc_head *next;
);
u8 data[];
};
struct bundle_priv {
/* Must be first */
struct bundle_alloc_head_hdr alloc_head;
struct bundle_alloc_head *allocated_mem;
size_t internal_avail;
size_t internal_used;
struct radix_tree_root *radix;
const struct uverbs_api_ioctl_method *method_elm;
void __rcu **radix_slots;
unsigned long radix_slots_len;
u32 method_key;
struct ib_uverbs_attr __user *user_attrs;
struct ib_uverbs_attr *uattrs;
DECLARE_BITMAP(uobj_finalize, UVERBS_API_ATTR_BKEY_LEN);
DECLARE_BITMAP(spec_finalize, UVERBS_API_ATTR_BKEY_LEN);
DECLARE_BITMAP(uobj_hw_obj_valid, UVERBS_API_ATTR_BKEY_LEN);
/*
* Must be last. bundle ends in a flex array which overlaps
* internal_buffer.
*/
struct uverbs_attr_bundle_hdr bundle;
u64 internal_buffer[32];
};
uverbs_api_ioctl_handler_fn uverbs_get_handler_fn(struct ib_udata *udata)
{
struct uverbs_attr_bundle *bundle =
rdma_udata_to_uverbs_attr_bundle(udata);
struct bundle_priv *pbundle =
container_of(&bundle->hdr, struct bundle_priv, bundle);
lockdep_assert_held(&bundle->ufile->device->disassociate_srcu);
return srcu_dereference(pbundle->method_elm->handler,
&bundle->ufile->device->disassociate_srcu);
}
/*
* Each method has an absolute minimum amount of memory it needs to allocate,
* precompute that amount and determine if the onstack memory can be used or
@ -445,13 +397,13 @@ static int ib_uverbs_run_method(struct bundle_priv *pbundle,
struct uverbs_attr_bundle *bundle =
container_of(&pbundle->bundle, struct uverbs_attr_bundle, hdr);
size_t uattrs_size = array_size(sizeof(*pbundle->uattrs), num_attrs);
unsigned int destroy_bkey = pbundle->method_elm->destroy_bkey;
unsigned int destroy_bkey = bundle->method_elm->destroy_bkey;
unsigned int i;
int ret;
/* See uverbs_disassociate_api() */
handler = srcu_dereference(
pbundle->method_elm->handler,
bundle->method_elm->handler,
&pbundle->bundle.ufile->device->disassociate_srcu);
if (!handler)
return -EIO;
@ -469,12 +421,12 @@ static int ib_uverbs_run_method(struct bundle_priv *pbundle,
}
/* User space did not provide all the mandatory attributes */
if (unlikely(!bitmap_subset(pbundle->method_elm->attr_mandatory,
if (unlikely(!bitmap_subset(bundle->method_elm->attr_mandatory,
pbundle->bundle.attr_present,
pbundle->method_elm->key_bitmap_len)))
bundle->method_elm->key_bitmap_len)))
return -EINVAL;
if (pbundle->method_elm->has_udata)
if (bundle->method_elm->has_udata)
uverbs_fill_udata(bundle, &pbundle->bundle.driver_udata,
UVERBS_ATTR_UHW_IN, UVERBS_ATTR_UHW_OUT);
else
@ -499,7 +451,7 @@ static int ib_uverbs_run_method(struct bundle_priv *pbundle,
* assume that the driver wrote to its UHW_OUT and flag userspace
* appropriately.
*/
if (!ret && pbundle->method_elm->has_udata) {
if (!ret && bundle->method_elm->has_udata) {
const struct uverbs_attr *attr =
uverbs_attr_get(bundle, UVERBS_ATTR_UHW_OUT);
@ -520,7 +472,7 @@ static int ib_uverbs_run_method(struct bundle_priv *pbundle,
static void bundle_destroy(struct bundle_priv *pbundle, bool commit)
{
unsigned int key_bitmap_len = pbundle->method_elm->key_bitmap_len;
unsigned int key_bitmap_len = pbundle->bundle.method_elm->key_bitmap_len;
struct uverbs_attr_bundle *bundle =
container_of(&pbundle->bundle, struct uverbs_attr_bundle, hdr);
struct bundle_alloc_head *memblock;
@ -608,7 +560,7 @@ static int ib_uverbs_cmd_verbs(struct ib_uverbs_file *ufile,
}
/* Space for the pbundle->bundle.attrs flex array */
pbundle->method_elm = method_elm;
pbundle->bundle.method_elm = method_elm;
pbundle->method_key = attrs_iter.index;
pbundle->bundle.ufile = ufile;
pbundle->bundle.context = NULL; /* only valid if bundle has uobject */
@ -617,10 +569,12 @@ static int ib_uverbs_cmd_verbs(struct ib_uverbs_file *ufile,
pbundle->radix_slots_len = radix_tree_chunk_size(&attrs_iter);
pbundle->user_attrs = user_attrs;
pbundle->internal_used = ALIGN(pbundle->method_elm->key_bitmap_len *
sizeof(*container_of(&pbundle->bundle,
struct uverbs_attr_bundle, hdr)->attrs),
sizeof(*pbundle->internal_buffer));
pbundle->internal_used = ALIGN(
pbundle->bundle.method_elm->key_bitmap_len *
sizeof(*container_of(&pbundle->bundle,
struct uverbs_attr_bundle, hdr)
->attrs),
sizeof(*pbundle->internal_buffer));
memset(pbundle->bundle.attr_present, 0,
sizeof(pbundle->bundle.attr_present));
memset(pbundle->uobj_finalize, 0, sizeof(pbundle->uobj_finalize));
@ -860,77 +814,3 @@ void uverbs_finalize_uobj_create(const struct uverbs_attr_bundle *bundle,
pbundle->uobj_hw_obj_valid);
}
EXPORT_SYMBOL(uverbs_finalize_uobj_create);
int _ib_copy_validate_udata_in(struct ib_udata *udata, void *req,
size_t kernel_size, size_t minimum_size)
{
int err;
if (udata->inlen < minimum_size) {
ibdev_dbg(
rdma_udata_to_dev(udata),
"System call driver input udata too small (%zu < %zu) for ioctl %ps called by %pSR\n",
udata->inlen, minimum_size,
uverbs_get_handler_fn(udata),
__builtin_return_address(0));
return -EINVAL;
}
err = copy_struct_from_user(req, kernel_size, udata->inbuf,
udata->inlen);
if (err) {
if (err == -E2BIG) {
ibdev_dbg(
rdma_udata_to_dev(udata),
"System call driver input udata not zero from %zu -> %zu for ioctl %ps called by %pSR\n",
minimum_size, udata->inlen,
uverbs_get_handler_fn(udata),
__builtin_return_address(0));
return -EOPNOTSUPP;
}
ibdev_dbg(
rdma_udata_to_dev(udata),
"System call driver input udata EFAULT for ioctl %ps called by %pSR\n",
uverbs_get_handler_fn(udata),
__builtin_return_address(0));
return err;
}
return 0;
}
EXPORT_SYMBOL(_ib_copy_validate_udata_in);
int _ib_copy_validate_udata_cm_fail(struct ib_udata *udata, u64 req_cm,
u64 valid_cm)
{
ibdev_dbg(
rdma_udata_to_dev(udata),
"System call driver input udata has unsupported comp_mask %llx & ~%llx = %llx for ioctl %ps called by %pSR\n",
req_cm, valid_cm, req_cm & ~valid_cm,
uverbs_get_handler_fn(udata), __builtin_return_address(0));
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);

View File

@ -4638,7 +4638,7 @@ int bnxt_re_alloc_ucontext(struct ib_ucontext *ctx, struct ib_udata *udata)
uctx->rdev = rdev;
uctx->shpg = (void *)__get_free_page(GFP_KERNEL);
uctx->shpg = (void *)get_zeroed_page(GFP_KERNEL);
if (!uctx->shpg) {
rc = -ENOMEM;
goto fail;

View File

@ -606,6 +606,7 @@ int mana_ib_query_port(struct ib_device *ibdev, u32 port,
if (mana_ib_is_rnic(dev)) {
props->gid_tbl_len = 16;
props->ip_gids = true;
props->max_msg_sz = SZ_16M;
if (port == 1)
props->port_cap_flags = IB_PORT_CM_SUP;
}

View File

@ -1081,6 +1081,21 @@ static int siw_get_hdr(struct siw_rx_stream *srx)
return -EAGAIN;
}
/*
* Peer-controlled mpa_len must not underflow srx->fpdu_part_rem
* in siw_tcp_rx_data(); a negative value flows as a signed copy
* length into siw_check_mem() and skb_copy_bits().
*/
if (unlikely(be16_to_cpu(c_hdr->mpa_len) + MPA_HDR_SIZE <
iwarp_pktinfo[opcode].hdr_len)) {
pr_warn_ratelimited("siw: short mpa_len %u for opcode %u (hdr_len %u)\n",
be16_to_cpu(c_hdr->mpa_len), opcode,
iwarp_pktinfo[opcode].hdr_len);
siw_init_terminate(rx_qp(srx), TERM_ERROR_LAYER_LLP,
LLP_ETYPE_MPA, LLP_ECODE_FPDU_START, 0);
return -EINVAL;
}
/*
* DDP/RDMAP header receive completed. Check if the current
* DDP segment starts a new RDMAP message or continues a previously

View File

@ -295,8 +295,8 @@ int rtrs_srv_create_path_files(struct rtrs_srv_path *srv_path)
put_kobj:
kobject_del(&srv_path->kobj);
destroy_root:
kobject_put(&srv_path->kobj);
rtrs_srv_destroy_once_sysfs_root_folders(srv_path);
kobject_put(&srv_path->kobj);
return err;
}

View File

@ -635,6 +635,7 @@ struct uverbs_attr_bundle {
struct ib_uverbs_file *ufile;
struct ib_ucontext *context;
struct ib_uobject *uobject;
const struct uverbs_api_ioctl_method *method_elm;
DECLARE_BITMAP(attr_present, UVERBS_API_ATTR_BKEY_LEN);
);
struct uverbs_attr attrs[];

View File

@ -8,6 +8,8 @@ RXE_NAME="rxe6"
PORT=4791
IP6_ADDR="2001:db8::1/64"
source "$(dirname "$0")/../kselftest/ktap_helpers.sh"
exec > /dev/null
# Cleanup function to run on exit (even on failure)
@ -21,8 +23,8 @@ trap cleanup EXIT
# 1. Prerequisites check
for mod in tun veth rdma_rxe; do
if ! modinfo "$mod" >/dev/null 2>&1; then
echo "Error: Kernel module '$mod' not found."
exit 1
echo "SKIP: Kernel module '$mod' not found." >&2
exit $KSFT_SKIP
fi
done

View File

@ -8,6 +8,8 @@ IP_A="1.1.1.1"
IP_B="1.1.1.2"
PORT=4791
source "$(dirname "$0")/../kselftest/ktap_helpers.sh"
exec > /dev/null
# --- Cleanup Routine ---
@ -27,6 +29,11 @@ if [[ $EUID -ne 0 ]]; then
exit 1
fi
if ! modinfo rdma_rxe >/dev/null 2>&1; then
echo "SKIP: Kernel module 'rdma_rxe' not found." >&2
exit $KSFT_SKIP
fi
modprobe rdma_rxe || { echo "Failed to load rdma_rxe"; exit 1; }
# --- Setup Network Topology ---

View File

@ -4,6 +4,8 @@
PORT=4791
MODS=("tun" "rdma_rxe")
source "$(dirname "$0")/../kselftest/ktap_helpers.sh"
exec > /dev/null
# --- Helper: Cleanup Routine ---
@ -26,6 +28,10 @@ if [[ $EUID -ne 0 ]]; then
fi
for m in "${MODS[@]}"; do
if ! modinfo "$m" >/dev/null 2>&1; then
echo "SKIP: Kernel module '$m' not found." >&2
exit $KSFT_SKIP
fi
modprobe "$m" || { echo "Error: Failed to load $m"; exit 1; }
done

View File

@ -5,6 +5,8 @@ DEV_NAME="tun0"
RXE_NAME="rxe0"
RDMA_PORT=4791
source "$(dirname "$0")/../kselftest/ktap_helpers.sh"
exec > /dev/null
# --- Cleanup Routine ---
@ -19,8 +21,8 @@ trap cleanup EXIT
# 1. Dependency Check
if ! modinfo rdma_rxe >/dev/null 2>&1; then
echo "Error: rdma_rxe module not found."
exit 1
echo "SKIP: rdma_rxe module not found." >&2
exit $KSFT_SKIP
fi
modprobe rdma_rxe