* [PATCH v3 1/2] RDMA/srpt: bound the indirect descriptor table to the IU
2026-10-22 14:38 [PATCH v3 0/2] RDMA/srpt: fix the indirect descriptor table bound and the context counters Yehyeong Lee
@ 2026-10-22 14:39 ` Yehyeong Lee
2026-10-22 15:56 ` Bart Van Assche
2026-10-22 18:15 ` Yehyeong Lee
2026-10-22 14:39 ` [PATCH v3 2/2] RDMA/srpt: widen the indirect RDMA context counters Yehyeong Lee
1 sibling, 2 replies; 7+ messages in thread
From: Yehyeong Lee @ 2026-10-22 14:39 UTC (permalink / raw)
To: Bart Van Assche
Cc: Leon Romanovsky, Jason Gunthorpe, linux-rdma, target-devel,
linux-kernel, stable, yhlee
srpt_get_desc_tbl() takes the number of INDIRECT descriptors from
idb->table_desc.len, which the initiator controls, and checks it only
against data_out_desc_cnt + data_in_desc_cnt, two more initiator fields.
Both are u8, so the pair admits up to 510 descriptors, and nothing
compares the table against the receive IU it has to live in.
srpt_alloc_rw_ctxs() then walks idb->desc_list for that many entries.
With the default srp_max_req_size of 8276 the table fits as long as the
additional CDB is short, but ADDITIONAL CDB LENGTH is an initiator field
too, and at its maximum of 252 bytes 510 descriptors end at
48 + 252 + 20 + 510 * 16 = 8480, which is past the end of the object:
BUG: KASAN: slab-out-of-bounds in srpt_alloc_rw_ctxs+0x64c/0x820
Read of size 4 at addr ffff888002d3a05c by task kunit_try_catch/44
CPU: 0 UID: 0 PID: 44 Comm: kunit_try_catch Tainted: G N 7.3.0-dirty #1 PREEMPT(lazy)
Tainted: [N]=TEST
Call Trace:
<TASK>
dump_stack_lvl+0x3f/0x50
print_report+0xcd/0x620
? alloc_pages_node_noprof+0xa2/0x140
? srpt_alloc_rw_ctxs+0x64c/0x820
kasan_report+0xde/0x110
? srpt_alloc_rw_ctxs+0x64c/0x820
srpt_alloc_rw_ctxs+0x64c/0x820
srpt_test_run.constprop.0+0x13b/0xc50
? __pfx_srpt_test_run.constprop.0+0x10/0x10
? pick_task_fair+0x126/0x1950
? update_curr.constprop.0+0x4a/0x390
? __set_cpus_allowed_ptr+0x181/0x240
? __pfx_read_tsc+0x10/0x10
? ktime_get_ts64+0x192/0x250
kunit_try_run_case+0x1ca/0x4b0
? __pfx_kunit_try_run_case+0x10/0x10
? kthread_affine_node+0x247/0x360
? __pfx_kthread_affine_node+0x10/0x10
? __pfx_kunit_try_run_case+0x10/0x10
? __pfx_kunit_generic_run_threadfn_adapter+0x10/0x10
kunit_generic_run_threadfn_adapter+0x7b/0xe0
kthread+0x36a/0x4f0
? __pfx_kthread+0x10/0x10
ret_from_fork+0x34d/0x620
? __pfx_ret_from_fork+0x10/0x10
? __switch_to+0x22/0xde0
? __pfx_kthread+0x10/0x10
ret_from_fork_asm+0x19/0x30
</TASK>
Allocated by task 44:
kasan_save_stack+0x33/0x60
kasan_save_track+0x14/0x30
__kasan_slab_alloc+0x6e/0x70
kmem_cache_alloc_noprof+0x11d/0x2f0
srpt_test_ctx_init.constprop.0+0x481/0xfa0
srpt_test_run.constprop.0+0xf4/0xc50
kunit_try_run_case+0x1ca/0x4b0
kunit_generic_run_threadfn_adapter+0x7b/0xe0
kthread+0x36a/0x4f0
ret_from_fork+0x34d/0x620
ret_from_fork_asm+0x19/0x30
The buggy address belongs to the object at ffff888002d38000
which belongs to the cache srpt-kunit-iu of size 8276
The buggy address is located 8 bytes to the right of
allocated 8276-byte region [ffff888002d38000, ffff888002d3a054)
The KUnit harness that forces this is not part of this patch.
Compare the count against the room the IU actually leaves for the table.
recv_ioctx->byte_len is signed, so a byte_len too small to cover even the
descriptor header yields a negative room and the same test refuses it.
The header itself is always inside the buffer. The largest offset
srpt_get_desc_buf() can return is 48 + 252 and the header is 20 bytes, so
it ends at 320, while srp_max_req_size is at least MIN_MAX_REQ_SIZE,
which is 996. Only the table needs bounding, so one test placed after the
existing count check is enough.
Fixes: a42d985bd5b2 ("ib_srpt: Initial SRP Target merge for v3.3-rc1")
Cc: stable@vger.kernel.org
Signed-off-by: Yehyeong Lee <yhlee@isslab.korea.ac.kr>
---
drivers/infiniband/ulp/srpt/ib_srpt.c | 10 ++++++++++
1 file changed, 10 insertions(+)
diff --git a/drivers/infiniband/ulp/srpt/ib_srpt.c b/drivers/infiniband/ulp/srpt/ib_srpt.c
index 7197d95f2216b..97758082f71d2 100644
--- a/drivers/infiniband/ulp/srpt/ib_srpt.c
+++ b/drivers/infiniband/ulp/srpt/ib_srpt.c
@@ -1117,6 +1117,8 @@ static int srpt_get_desc_tbl(struct srpt_recv_ioctx *recv_ioctx,
struct srp_indirect_buf *idb = srpt_get_desc_buf(srp_cmd);
int nbufs = be32_to_cpu(idb->table_desc.len) /
sizeof(struct srp_direct_buf);
+ int desc_off = (u8 *)idb - (u8 *)srp_cmd;
+ int room;
if (nbufs >
(srp_cmd->data_out_desc_cnt + srp_cmd->data_in_desc_cnt)) {
@@ -1128,6 +1130,14 @@ static int srpt_get_desc_tbl(struct srpt_recv_ioctx *recv_ioctx,
return -EINVAL;
}
+ room = (recv_ioctx->byte_len - desc_off - (int)sizeof(*idb)) /
+ (int)sizeof(struct srp_direct_buf);
+ if (nbufs > room) {
+ pr_err("received SRP_CMD with %d descriptors but room for %d in the IU\n",
+ nbufs, room);
+ return -EINVAL;
+ }
+
*data_len = be32_to_cpu(idb->len);
return srpt_alloc_rw_ctxs(ioctx, idb->desc_list, nbufs,
sg, sg_cnt);
--
2.43.0
^ permalink raw reply [flat|nested] 7+ messages in thread* [PATCH v3 2/2] RDMA/srpt: widen the indirect RDMA context counters
2026-10-22 14:38 [PATCH v3 0/2] RDMA/srpt: fix the indirect descriptor table bound and the context counters Yehyeong Lee
2026-10-22 14:39 ` [PATCH v3 1/2] RDMA/srpt: bound the indirect descriptor table to the IU Yehyeong Lee
@ 2026-10-22 14:39 ` Yehyeong Lee
2026-10-22 15:57 ` Bart Van Assche
2026-10-22 18:15 ` Yehyeong Lee
1 sibling, 2 replies; 7+ messages in thread
From: Yehyeong Lee @ 2026-10-22 14:39 UTC (permalink / raw)
To: Bart Van Assche
Cc: Leon Romanovsky, Jason Gunthorpe, linux-rdma, target-devel,
linux-kernel, stable, yhlee
srpt_send_ioctx::n_rdma and ::n_rw_ctx are u8, and neither of the values
stored in them is bounded by 255.
n_rw_ctx counts the INDIRECT descriptors a command carries, and the count
guard in srpt_get_desc_tbl() admits data_out_desc_cnt plus
data_in_desc_cnt, so up to 510. At 256 descriptors n_rw_ctx wraps to
zero, and srpt_release_cmd() frees the contexts only when it is non-zero,
so every scatterlist and RDMA context the command allocated leaks.
n_rdma is not a descriptor count at all. It is the sum of the
rdma_rw_ctx_init() return values, which is the number of work requests
each context needs, accumulated by ioctx->n_rdma += ret. A handful of
descriptors for large transfers can pass 255 on their own, with no
unusual descriptor count involved. A wrapped n_rdma is then handed to
atomic_sub_return(ioctx->n_rdma, &ch->sq_wr_avail) and tested by
WARN_ON(ioctx->n_rdma <= 0), so the send queue work request accounting
drifts too.
Make both counters int, the type the values they accumulate already have.
Fixes: b99f8e4d7bcd ("IB/srpt: convert to the generic RDMA READ/WRITE API")
Cc: stable@vger.kernel.org
Signed-off-by: Yehyeong Lee <yhlee@isslab.korea.ac.kr>
---
drivers/infiniband/ulp/srpt/ib_srpt.c | 2 +-
drivers/infiniband/ulp/srpt/ib_srpt.h | 4 ++--
2 files changed, 3 insertions(+), 3 deletions(-)
diff --git a/drivers/infiniband/ulp/srpt/ib_srpt.c b/drivers/infiniband/ulp/srpt/ib_srpt.c
index 97758082f71d2..bdea47316539f 100644
--- a/drivers/infiniband/ulp/srpt/ib_srpt.c
+++ b/drivers/infiniband/ulp/srpt/ib_srpt.c
@@ -959,7 +959,7 @@ static int srpt_alloc_rw_ctxs(struct srpt_send_ioctx *ioctx,
struct srpt_rdma_ch *ch = ioctx->ch;
struct scatterlist *prev = NULL;
unsigned prev_nents;
- u8 n_rdma, n_rw_ctx;
+ int n_rdma, n_rw_ctx;
int ret, i;
if (nbufs == 1) {
diff --git a/drivers/infiniband/ulp/srpt/ib_srpt.h b/drivers/infiniband/ulp/srpt/ib_srpt.h
index 4c46b301eea18..1dce9bcb490f0 100644
--- a/drivers/infiniband/ulp/srpt/ib_srpt.h
+++ b/drivers/infiniband/ulp/srpt/ib_srpt.h
@@ -223,8 +223,8 @@ struct srpt_send_ioctx {
struct ib_cqe rdma_cqe;
enum srpt_command_state state;
struct se_cmd cmd;
- u8 n_rdma;
- u8 n_rw_ctx;
+ int n_rdma;
+ int n_rw_ctx;
bool queue_status_only;
u8 sense_data[TRANSPORT_SENSE_BUFFER];
};
--
2.43.0
^ permalink raw reply [flat|nested] 7+ messages in thread