diff options
| author | Linus Torvalds <torvalds@linux-foundation.org> | 2026-06-09 08:19:48 -0700 |
|---|---|---|
| committer | Linus Torvalds <torvalds@linux-foundation.org> | 2026-06-09 08:19:48 -0700 |
| commit | fed2efe803e014e5c419bc7592caa8633683603e (patch) | |
| tree | 8bfe7ac9f7e96aa8a7f135fb149c17abaae4faf2 | |
| parent | 2d3090a8aeb596a26935db0955d46c9a5db5c6ce (diff) | |
| parent | 13e91fd076306f5d0cdfa14f53d69e37274723c4 (diff) | |
Merge tag 'for-linus' of git://git.kernel.org/pub/scm/linux/kernel/git/rdma/rdma
Pull rdma fixes from Jason Gunthorpe:
"Several significant bug fixes of pre-existing issues:
- Missing validation on ucap fd types passed from userspace
- Missing validation of HW DMA space vs userpace expected sizes in
EFA queue setup
- DMA corruption when using DMA block sizes >= 4G when setting up MRs
in all drivers
- Missing validation of CPU IDs when setting up dma handles
- Missing validation of IB_MR_REREG_ACCESS when changing writability
of a MR
- Missing validation of received message/packet size in ISER and SRP"
* tag 'for-linus' of git://git.kernel.org/pub/scm/linux/kernel/git/rdma/rdma:
RDMA/srp: bound SRP_RSP sense copy by the received length
IB/isert: Reject login PDUs shorter than ISER_HEADERS_LEN
RDMA: During rereg_mr ensure that REREG_ACCESS is compatible
RDMA/core: Validate cpu_id against nr_cpu_ids in DMAH alloc
RDMA/umem: Fix truncation for block sizes >= 4G
RDMA/efa: Validate SQ ring size against max LLQ size
RDMA/core: Validate the passed in fops for ib_get_ucaps()
| -rw-r--r-- | drivers/infiniband/core/iter.c | 4 | ||||
| -rw-r--r-- | drivers/infiniband/core/ucaps.c | 8 | ||||
| -rw-r--r-- | drivers/infiniband/core/umem.c | 16 | ||||
| -rw-r--r-- | drivers/infiniband/core/uverbs_std_types_dmah.c | 5 | ||||
| -rw-r--r-- | drivers/infiniband/hw/efa/efa_verbs.c | 27 | ||||
| -rw-r--r-- | drivers/infiniband/hw/hns/hns_roce_mr.c | 4 | ||||
| -rw-r--r-- | drivers/infiniband/hw/irdma/verbs.c | 4 | ||||
| -rw-r--r-- | drivers/infiniband/hw/mlx4/mr.c | 4 | ||||
| -rw-r--r-- | drivers/infiniband/hw/mlx5/mr.c | 4 | ||||
| -rw-r--r-- | drivers/infiniband/sw/rxe/rxe_verbs.c | 5 | ||||
| -rw-r--r-- | drivers/infiniband/ulp/isert/ib_isert.c | 6 | ||||
| -rw-r--r-- | drivers/infiniband/ulp/srp/ib_srp.c | 30 | ||||
| -rw-r--r-- | include/rdma/ib_umem.h | 8 |
13 files changed, 103 insertions, 22 deletions
diff --git a/drivers/infiniband/core/iter.c b/drivers/infiniband/core/iter.c index 8e543d100657..3ed351e8fcf6 100644 --- a/drivers/infiniband/core/iter.c +++ b/drivers/infiniband/core/iter.c @@ -19,8 +19,8 @@ EXPORT_SYMBOL(__rdma_block_iter_start); bool __rdma_block_iter_next(struct ib_block_iter *biter) { - unsigned int block_offset; - unsigned int delta; + dma_addr_t block_offset; + dma_addr_t delta; if (!biter->__sg_nents || !biter->__sg) return false; diff --git a/drivers/infiniband/core/ucaps.c b/drivers/infiniband/core/ucaps.c index 948093260dbd..5155ff0e538e 100644 --- a/drivers/infiniband/core/ucaps.c +++ b/drivers/infiniband/core/ucaps.c @@ -82,14 +82,12 @@ static int get_ucap_from_devt(dev_t devt, u64 *idx_mask) static int get_devt_from_fd(unsigned int fd, dev_t *ret_dev) { - struct file *file; + CLASS(fd, f)(fd); - file = fget(fd); - if (!file) + if (fd_empty(f) || fd_file(f)->f_op != &ucaps_cdev_fops) return -EBADF; - *ret_dev = file_inode(file)->i_rdev; - fput(file); + *ret_dev = file_inode(fd_file(f))->i_rdev; return 0; } diff --git a/drivers/infiniband/core/umem.c b/drivers/infiniband/core/umem.c index 786fa1aa8e55..4b055712b0d0 100644 --- a/drivers/infiniband/core/umem.c +++ b/drivers/infiniband/core/umem.c @@ -332,3 +332,19 @@ int ib_umem_copy_from(void *dst, struct ib_umem *umem, size_t offset, return 0; } EXPORT_SYMBOL(ib_umem_copy_from); + +/* + * Called during rereg mr if the driver is able to re-use a umem for + * IB_MR_REREG_ACCESS. + */ +int ib_umem_check_rereg(struct ib_umem *umem, int flags, int new_access_flags) +{ + if (!umem) + return 0; + + if ((flags & IB_MR_REREG_ACCESS) && !(flags & IB_MR_REREG_TRANS)) + if (ib_access_writable(new_access_flags) && !umem->writable) + return -EACCES; + return 0; +} +EXPORT_SYMBOL(ib_umem_check_rereg); diff --git a/drivers/infiniband/core/uverbs_std_types_dmah.c b/drivers/infiniband/core/uverbs_std_types_dmah.c index 453ce656c6f2..97101e093826 100644 --- a/drivers/infiniband/core/uverbs_std_types_dmah.c +++ b/drivers/infiniband/core/uverbs_std_types_dmah.c @@ -47,6 +47,11 @@ static int UVERBS_HANDLER(UVERBS_METHOD_DMAH_ALLOC)( if (ret) goto err; + if (dmah->cpu_id >= nr_cpu_ids) { + ret = -EINVAL; + goto err; + } + if (!cpumask_test_cpu(dmah->cpu_id, current->cpus_ptr)) { ret = -EPERM; goto err; diff --git a/drivers/infiniband/hw/efa/efa_verbs.c b/drivers/infiniband/hw/efa/efa_verbs.c index 7bd0838ebc99..9b2b652800e4 100644 --- a/drivers/infiniband/hw/efa/efa_verbs.c +++ b/drivers/infiniband/hw/efa/efa_verbs.c @@ -613,7 +613,8 @@ err_remove_mmap: } static int efa_qp_validate_cap(struct efa_dev *dev, - struct ib_qp_init_attr *init_attr) + struct ib_qp_init_attr *init_attr, + u32 sq_ring_size) { if (init_attr->cap.max_send_wr > dev->dev_attr.max_sq_depth) { ibdev_dbg(&dev->ibdev, @@ -622,6 +623,14 @@ static int efa_qp_validate_cap(struct efa_dev *dev, dev->dev_attr.max_sq_depth); return -EINVAL; } + + if (sq_ring_size > dev->dev_attr.max_llq_size) { + ibdev_dbg(&dev->ibdev, + "qp: requested sq ring size[%u] exceeds the max[%u]\n", + sq_ring_size, dev->dev_attr.max_llq_size); + return -EINVAL; + } + if (init_attr->cap.max_recv_wr > dev->dev_attr.max_rq_depth) { ibdev_dbg(&dev->ibdev, "qp: requested receive wr[%u] exceeds the max[%u]\n", @@ -691,14 +700,6 @@ int efa_create_qp(struct ib_qp *ibqp, struct ib_qp_init_attr *init_attr, ucontext = rdma_udata_to_drv_context(udata, struct efa_ucontext, ibucontext); - err = efa_qp_validate_cap(dev, init_attr); - if (err) - goto err_out; - - err = efa_qp_validate_attr(dev, init_attr); - if (err) - goto err_out; - err = ib_copy_validate_udata_in_cm(udata, cmd, driver_qp_type, 0); if (err) goto err_out; @@ -720,6 +721,14 @@ int efa_create_qp(struct ib_qp *ibqp, struct ib_qp_init_attr *init_attr, goto err_out; } + err = efa_qp_validate_cap(dev, init_attr, cmd.sq_ring_size); + if (err) + goto err_out; + + err = efa_qp_validate_attr(dev, init_attr); + if (err) + goto err_out; + create_qp_params.uarn = ucontext->uarn; create_qp_params.pd = to_epd(ibqp->pd)->pdn; diff --git a/drivers/infiniband/hw/hns/hns_roce_mr.c b/drivers/infiniband/hw/hns/hns_roce_mr.c index 896af1828a38..25bfd3970f5b 100644 --- a/drivers/infiniband/hw/hns/hns_roce_mr.c +++ b/drivers/infiniband/hw/hns/hns_roce_mr.c @@ -300,6 +300,10 @@ struct ib_mr *hns_roce_rereg_user_mr(struct ib_mr *ibmr, int flags, u64 start, goto err_out; } + ret = ib_umem_check_rereg(mr->pbl_mtr.umem, flags, mr_access_flags); + if (ret) + goto err_out; + mailbox = hns_roce_alloc_cmd_mailbox(hr_dev); ret = PTR_ERR_OR_ZERO(mailbox); if (ret) diff --git a/drivers/infiniband/hw/irdma/verbs.c b/drivers/infiniband/hw/irdma/verbs.c index 17086048d2d7..8cd427532805 100644 --- a/drivers/infiniband/hw/irdma/verbs.c +++ b/drivers/infiniband/hw/irdma/verbs.c @@ -3803,6 +3803,10 @@ static struct ib_mr *irdma_rereg_user_mr(struct ib_mr *ib_mr, int flags, if (flags & ~(IB_MR_REREG_TRANS | IB_MR_REREG_PD | IB_MR_REREG_ACCESS)) return ERR_PTR(-EOPNOTSUPP); + ret = ib_umem_check_rereg(iwmr->region, flags, new_access); + if (ret) + return ERR_PTR(ret); + if (dmabuf_revocable) { umem_dmabuf = to_ib_umem_dmabuf(iwmr->region); diff --git a/drivers/infiniband/hw/mlx4/mr.c b/drivers/infiniband/hw/mlx4/mr.c index 650b4a9121ff..6747bca30677 100644 --- a/drivers/infiniband/hw/mlx4/mr.c +++ b/drivers/infiniband/hw/mlx4/mr.c @@ -209,6 +209,10 @@ struct ib_mr *mlx4_ib_rereg_user_mr(struct ib_mr *mr, int flags, u64 start, struct mlx4_mpt_entry **pmpt_entry = &mpt_entry; int err; + err = ib_umem_check_rereg(mmr->umem, flags, mr_access_flags); + if (err) + return ERR_PTR(err); + /* Since we synchronize this call and mlx4_ib_dereg_mr via uverbs, * we assume that the calls can't run concurrently. Otherwise, a * race exists. diff --git a/drivers/infiniband/hw/mlx5/mr.c b/drivers/infiniband/hw/mlx5/mr.c index 3b6da45061a5..fb40b44496f4 100644 --- a/drivers/infiniband/hw/mlx5/mr.c +++ b/drivers/infiniband/hw/mlx5/mr.c @@ -1179,6 +1179,10 @@ struct ib_mr *mlx5_ib_rereg_user_mr(struct ib_mr *ib_mr, int flags, u64 start, if (flags & ~(IB_MR_REREG_TRANS | IB_MR_REREG_PD | IB_MR_REREG_ACCESS)) return ERR_PTR(-EOPNOTSUPP); + err = ib_umem_check_rereg(mr->umem, flags, new_access_flags); + if (err) + return ERR_PTR(err); + if (!(flags & IB_MR_REREG_ACCESS)) new_access_flags = mr->access_flags; if (!(flags & IB_MR_REREG_PD)) diff --git a/drivers/infiniband/sw/rxe/rxe_verbs.c b/drivers/infiniband/sw/rxe/rxe_verbs.c index 4d4891dc2884..4cf04a44189c 100644 --- a/drivers/infiniband/sw/rxe/rxe_verbs.c +++ b/drivers/infiniband/sw/rxe/rxe_verbs.c @@ -1319,6 +1319,7 @@ static struct ib_mr *rxe_rereg_user_mr(struct ib_mr *ibmr, int flags, struct rxe_mr *mr = to_rmr(ibmr); struct rxe_pd *old_pd = to_rpd(ibmr->pd); struct rxe_pd *pd = to_rpd(ibpd); + int err; /* for now only support the two easy cases: * rereg_pd and rereg_access @@ -1328,6 +1329,10 @@ static struct ib_mr *rxe_rereg_user_mr(struct ib_mr *ibmr, int flags, return ERR_PTR(-EOPNOTSUPP); } + err = ib_umem_check_rereg(mr->umem, flags, access); + if (err) + return ERR_PTR(err); + if (flags & IB_MR_REREG_PD) { rxe_put(old_pd); rxe_get(pd); diff --git a/drivers/infiniband/ulp/isert/ib_isert.c b/drivers/infiniband/ulp/isert/ib_isert.c index 348005e71891..1015a51f750a 100644 --- a/drivers/infiniband/ulp/isert/ib_isert.c +++ b/drivers/infiniband/ulp/isert/ib_isert.c @@ -1383,6 +1383,12 @@ isert_login_recv_done(struct ib_cq *cq, struct ib_wc *wc) ib_dma_sync_single_for_cpu(ib_dev, isert_conn->login_desc->dma_addr, ISER_RX_SIZE, DMA_FROM_DEVICE); + if (unlikely(wc->byte_len < ISER_HEADERS_LEN)) { + isert_dbg("login request length %u is too short\n", + wc->byte_len); + return; + } + isert_conn->login_req_len = wc->byte_len - ISER_HEADERS_LEN; if (isert_conn->conn) { diff --git a/drivers/infiniband/ulp/srp/ib_srp.c b/drivers/infiniband/ulp/srp/ib_srp.c index b58868e1cf11..acbd787de265 100644 --- a/drivers/infiniband/ulp/srp/ib_srp.c +++ b/drivers/infiniband/ulp/srp/ib_srp.c @@ -1932,7 +1932,8 @@ static int srp_post_recv(struct srp_rdma_ch *ch, struct srp_iu *iu) return ib_post_recv(ch->qp, &wr, NULL); } -static void srp_process_rsp(struct srp_rdma_ch *ch, struct srp_rsp *rsp) +static void srp_process_rsp(struct srp_rdma_ch *ch, struct srp_rsp *rsp, + u32 byte_len) { struct srp_target_port *target = ch->target; struct srp_request *req; @@ -1973,10 +1974,27 @@ static void srp_process_rsp(struct srp_rdma_ch *ch, struct srp_rsp *rsp) scmnd->result = rsp->status; if (rsp->flags & SRP_RSP_FLAG_SNSVALID) { - memcpy(scmnd->sense_buffer, rsp->data + - be32_to_cpu(rsp->resp_data_len), - min_t(int, be32_to_cpu(rsp->sense_data_len), - SCSI_SENSE_BUFFERSIZE)); + u32 resp_len = be32_to_cpu(rsp->resp_data_len); + u32 sense_len = be32_to_cpu(rsp->sense_data_len); + + /* + * The sense data starts resp_data_len bytes past the + * response data area; both lengths come from the + * target-controlled response. Copy the sense data + * only if it has not been truncated, that is, only if + * the full sense region fits within the bytes actually + * received. Otherwise the copy source would run past + * the receive buffer (sized to the target-chosen + * max_ti_iu_len), reading out of bounds. + */ + if (sizeof(*rsp) + (u64)resp_len + sense_len <= byte_len) + memcpy(scmnd->sense_buffer, + rsp->data + resp_len, + min(sense_len, SCSI_SENSE_BUFFERSIZE)); + else + shost_printk(KERN_ERR, target->scsi_host, + "dropping truncated sense data (resp_data_len %u sense_data_len %u, %u bytes received)\n", + resp_len, sense_len, byte_len); } if (unlikely(rsp->flags & SRP_RSP_FLAG_DIUNDER)) @@ -2086,7 +2104,7 @@ static void srp_recv_done(struct ib_cq *cq, struct ib_wc *wc) switch (opcode) { case SRP_RSP: - srp_process_rsp(ch, iu->buf); + srp_process_rsp(ch, iu->buf, wc->byte_len); break; case SRP_CRED_REQ: diff --git a/include/rdma/ib_umem.h b/include/rdma/ib_umem.h index 2ad52cc1d52b..49172098a8de 100644 --- a/include/rdma/ib_umem.h +++ b/include/rdma/ib_umem.h @@ -156,6 +156,8 @@ void ib_umem_dmabuf_revoke_lock(struct ib_umem_dmabuf *umem_dmabuf); void ib_umem_dmabuf_revoke_unlock(struct ib_umem_dmabuf *umem_dmabuf); void ib_umem_dmabuf_revoke(struct ib_umem_dmabuf *umem_dmabuf); +int ib_umem_check_rereg(struct ib_umem *umem, int flags, int new_access_flags); + #else /* CONFIG_INFINIBAND_USER_MEM */ #include <linux/err.h> @@ -230,5 +232,11 @@ static inline void ib_umem_dmabuf_revoke_lock(struct ib_umem_dmabuf *umem_dmabuf static inline void ib_umem_dmabuf_revoke_unlock(struct ib_umem_dmabuf *umem_dmabuf) {} static inline void ib_umem_dmabuf_revoke(struct ib_umem_dmabuf *umem_dmabuf) {} +static inline int ib_umem_check_rereg(struct ib_umem *umem, int flags, + int new_access_flags) +{ + return -EOPNOTSUPP; +} + #endif /* CONFIG_INFINIBAND_USER_MEM */ #endif /* IB_UMEM_H */ |
