* [PATCH 0/1] RDMA/srpt: Fix read underflow information disclosure @ 2026-11-10 3:08 liuyong 2026-11-10 3:08 ` [PATCH 1/1] RDMA/srpt: Limit read data transfers to the command length liuyong 0 siblings, 1 reply; 6+ messages in thread From: liuyong @ 2026-11-10 3:08 UTC (permalink / raw) To: Bart Van Assche Cc: Jason Gunthorpe, Leon Romanovsky, linux-rdma, target-devel, linux-kernel, security Hi, This patch fixes an SRPT information disclosure that allows a remote initiator with LUN access to read uninitialized target kernel memory, potentially exposing sensitive data from previous uses of those pages. SRPT creates RDMA contexts from the initiator's buffer descriptors before parsing the CDB. For a READ underflow, target core reduces cmd->data_length, and a backend such as rd_mcp fills only that length. The prebuilt RDMA WRITE requests still send the full descriptor-sized buffer, including the uninitialized tail. The underflow itself is valid; the problem is transferring more data than the command provides. The fix caps the data SGEs at cmd->data_length in srpt_queue_response(). The patch is based on Linux 7.4.0-rc1 commit f66469d9905b39d2c260a15d7d6db450f4db67c1. The issue was reproduced on the same commit using software RoCE and an rd_mcp RAMDISK with an explicit ACL-LUN mapping. The triggering READ(10) requests one 512-byte block but provides an 8192-byte data-in descriptor. Relevant target kernel log excerpt: [ 7.496367] ib_srpt Received SRP_LOGIN_REQ with i_port_id 5352:502d:4556:494c:2d49:4e49:542d:3031, t_port_id 5054:00ff:fe12:3456:5054:00ff:fe12:3456 and it_iu_len 512 on port 1 (guid=fe80:0000:0000:0000:5054:00ff:fe12:3456); pkey 0xffff [ 7.528685] TARGET_CORE[srpt]: Expected Transfer Length: 8192 does not match SCSI CDB Length: 512 for SAM Opcode: 0x28 The patch has been compiled and verified to fix the reported issue. A reproducer and additional reproduction materials are available. Please contact me privately if you need them. The issue was discovered and fixed by Qihoo 360's Tulongfeng multi-agent system. Regards, liuyong 360 Vulnerability Research Institute liuyong (1): RDMA/srpt: Limit read data transfers to the command length drivers/infiniband/ulp/srpt/ib_srpt.c | 20 ++++++++++++++++++++ 1 file changed, 20 insertions(+) ^ permalink raw reply [flat|nested] 6+ messages in thread
* [PATCH 1/1] RDMA/srpt: Limit read data transfers to the command length 2026-11-10 3:08 [PATCH 0/1] RDMA/srpt: Fix read underflow information disclosure liuyong @ 2026-11-10 3:08 ` liuyong 2026-11-10 5:28 ` [PATCH v2] " liuyong 0 siblings, 1 reply; 6+ messages in thread From: liuyong @ 2026-11-10 3:08 UTC (permalink / raw) To: Bart Van Assche Cc: Jason Gunthorpe, Leon Romanovsky, linux-rdma, target-devel, linux-kernel, security SRPT builds RDMA contexts from the initiator's buffer descriptors before parsing the CDB. For a READ underflow, target core reduces cmd->data_length, but the prebuilt RDMA WRITE requests still cover the full descriptor length. A backend such as rd_mcp fills only the shortened command length, allowing the uninitialized tail to reach the initiator. A remote initiator with access to the LUN can therefore read uninitialized target kernel memory beyond the requested SCSI data, potentially exposing sensitive data left from previous uses of those pages. For a READ underflow, limit the data SGEs in the RDMA WRITE chain to cmd->data_length before posting it. Preserve memory registration and invalidation requests, and retain zero-length RDMA WRITEs beyond the limit so that work-request accounting and resource cleanup remain unchanged. The issue was discovered and fixed by Qihoo 360's Tulongfeng multi-agent system. Fixes: b99f8e4d7bcd ("IB/srpt: convert to the generic RDMA READ/WRITE API") Cc: stable@vger.kernel.org Signed-off-by: liuyong <pkfxxxing@gmail.com> --- drivers/infiniband/ulp/srpt/ib_srpt.c | 20 ++++++++++++++++++++ 1 file changed, 20 insertions(+) diff --git a/drivers/infiniband/ulp/srpt/ib_srpt.c b/drivers/infiniband/ulp/srpt/ib_srpt.c index 43c3ff1ad..2f7857ef4 100644 --- a/drivers/infiniband/ulp/srpt/ib_srpt.c +++ b/drivers/infiniband/ulp/srpt/ib_srpt.c @@ -2913,6 +2913,26 @@ static void srpt_queue_response(struct se_cmd *cmd) first_wr = rdma_rw_ctx_wrs(&ctx->rw, ch->qp, ch->sport->port, NULL, first_wr); } + + if (cmd->se_cmd_flags & SCF_UNDERFLOW_BIT) { + struct ib_send_wr *wr; + u32 data_len = cmd->data_length; + + /* Keep MR operations and WR accounting unchanged. */ + for (wr = first_wr; wr != &send_wr; wr = wr->next) { + int j; + + if (wr->opcode != IB_WR_RDMA_WRITE) + continue; + for (j = 0; j < wr->num_sge && data_len; j++) { + struct ib_sge *sge = &wr->sg_list[j]; + + sge->length = min(sge->length, data_len); + data_len -= sge->length; + } + wr->num_sge = j; + } + } } if (state != SRPT_STATE_MGMT) ^ permalink raw reply [flat|nested] 6+ messages in thread
* [PATCH v2] RDMA/srpt: Limit read data transfers to the command length 2026-11-10 3:08 ` [PATCH 1/1] RDMA/srpt: Limit read data transfers to the command length liuyong @ 2026-11-10 5:28 ` liuyong 2026-11-11 0:54 ` Bart Van Assche 2026-11-11 6:53 ` [PATCH v3] RDMA/srpt: Prevent uninitialized read buffer disclosure liuyong 0 siblings, 2 replies; 6+ messages in thread From: liuyong @ 2026-11-10 5:28 UTC (permalink / raw) To: Bart Van Assche Cc: Jason Gunthorpe, Leon Romanovsky, linux-rdma, target-devel, linux-kernel, security SRPT builds RDMA contexts from the initiator's buffer descriptors before parsing the CDB. The resulting RDMA WRITE requests can exceed cmd->data_length after target core shortens the command or when an indirect descriptor's buffers exceed its advertised total length. A backend such as rd_mcp fills only cmd->data_length bytes, leaving an uninitialized tail that is still transferred to the initiator. A remote initiator with access to the LUN can therefore read uninitialized target kernel memory beyond the requested SCSI data, potentially exposing sensitive data left from previous uses of those pages. Limit the data SGEs in every read response to cmd->data_length without depending on SCF_UNDERFLOW_BIT. Keep MR operations and WR accounting unchanged by retaining excess RDMA WRITEs as zero-length requests. Clear sg_list when num_sge becomes zero so that providers such as irdma do not program a non-zero first fragment from the original SGE list. The issue was discovered and fixed by Qihoo 360's Tulongfeng multi-agent system. Fixes: b99f8e4d7bcd ("IB/srpt: convert to the generic RDMA READ/WRITE API") Cc: stable@vger.kernel.org Signed-off-by: liuyong <pkfxxxing@gmail.com> --- Changes in v2 (addressing the two issues reported by sashiko-bot): - Limit every read response to cmd->data_length without relying on SCF_UNDERFLOW_BIT. - Clear sg_list when num_sge is zero to avoid stale fragments on irdma. This revision has been compiled and verified to fix the reported issue. drivers/infiniband/ulp/srpt/ib_srpt.c | 20 ++++++++++++++++++++ 1 file changed, 20 insertions(+) diff --git a/drivers/infiniband/ulp/srpt/ib_srpt.c b/drivers/infiniband/ulp/srpt/ib_srpt.c index 43c3ff1ad..9257c6beb 100644 --- a/drivers/infiniband/ulp/srpt/ib_srpt.c +++ b/drivers/infiniband/ulp/srpt/ib_srpt.c @@ -2907,12 +2907,32 @@ static void srpt_queue_response(struct se_cmd *cmd) if (ioctx->cmd.data_direction == DMA_FROM_DEVICE && ioctx->cmd.data_length && !ioctx->queue_status_only) { + struct ib_send_wr *wr; + u32 data_len = cmd->data_length; + for (i = ioctx->n_rw_ctx - 1; i >= 0; i--) { struct srpt_rw_ctx *ctx = &ioctx->rw_ctxs[i]; first_wr = rdma_rw_ctx_wrs(&ctx->rw, ch->qp, ch->sport->port, NULL, first_wr); } + + /* Do not expose uninitialized data beyond cmd->data_length. */ + for (wr = first_wr; wr != &send_wr; wr = wr->next) { + int j; + + if (wr->opcode != IB_WR_RDMA_WRITE) + continue; + for (j = 0; j < wr->num_sge && data_len; j++) { + struct ib_sge *sge = &wr->sg_list[j]; + + sge->length = min(sge->length, data_len); + data_len -= sge->length; + } + wr->num_sge = j; + if (!j) + wr->sg_list = NULL; + } } if (state != SRPT_STATE_MGMT) ^ permalink raw reply [flat|nested] 6+ messages in thread
* Re: [PATCH v2] RDMA/srpt: Limit read data transfers to the command length 2026-11-10 5:28 ` [PATCH v2] " liuyong @ 2026-11-11 0:54 ` Bart Van Assche 2026-11-11 6:28 ` liuyong 2026-11-11 6:53 ` [PATCH v3] RDMA/srpt: Prevent uninitialized read buffer disclosure liuyong 1 sibling, 1 reply; 6+ messages in thread From: Bart Van Assche @ 2026-11-11 0:54 UTC (permalink / raw) To: liuyong Cc: Jason Gunthorpe, Leon Romanovsky, linux-rdma, target-devel, linux-kernel, security On 11/9/26 9:28 PM, liuyong wrote: > SRPT builds RDMA contexts from the initiator's buffer descriptors before > parsing the CDB. The resulting RDMA WRITE requests can exceed > cmd->data_length after target core shortens the command or when an > indirect descriptor's buffers exceed its advertised total length. Agreed that both cases need to be addressed. For the second case, srpt_get_desc_tbl() should reject SRP_CMD requests for which the sum of the direct descriptor lengths does not match the total length in the indirect buffer descriptor instead of building RDMA contexts for an inconsistent descriptor table. That is a protocol violation by the initiator and it is better to catch it while parsing the request than when sending the response. > + /* Do not expose uninitialized data beyond cmd->data_length. */ > + for (wr = first_wr; wr != &send_wr; wr = wr->next) { > + int j; > + > + if (wr->opcode != IB_WR_RDMA_WRITE) > + continue; > + for (j = 0; j < wr->num_sge && data_len; j++) { > + struct ib_sge *sge = &wr->sg_list[j]; > + > + sge->length = min(sge->length, data_len); > + data_len -= sge->length; > + } > + wr->num_sge = j; > + if (!j) > + wr->sg_list = NULL; > + } The work requests and SGEs returned by rdma_rw_ctx_wrs() are owned by the RDMA R/W API. This loop relies on several implementation details of that API: that data is transferred with IB_WR_RDMA_WRITE work requests, that the remote address of each work request has been computed in advance, that the work requests occur in data order and that the work requests and SGE arrays may be modified by the caller. Any future change in drivers/infiniband/core/rw.c that invalidates one of these assumptions would silently reintroduce the information leak. The irdma issue mentioned in the patch description shows another problem with this approach: it causes work requests to be posted that the RDMA R/W API never generates and hence that RDMA drivers have not been tested with. If irdma does not handle num_sge == 0 correctly for RDMA WRITE requests then that should be fixed in the irdma driver rather than worked around in SRPT. Additionally, there is no guarantee that other RDMA drivers handle such requests correctly. Please consider the following approach instead: - Reject inconsistent indirect descriptor tables in srpt_get_desc_tbl(). - In srpt_queue_response(), zero the part of the data buffer beyond cmd->data_length, e.g. with sg_zero_buffer(), if the data buffer is larger than cmd->data_length. This is simpler, does not depend on RDMA R/W API internals or RDMA driver behavior, only adds overhead for commands with a residual and is easy to backport. Thanks, Bart. ^ permalink raw reply [flat|nested] 6+ messages in thread
* Re: [PATCH v2] RDMA/srpt: Limit read data transfers to the command length 2026-11-11 0:54 ` Bart Van Assche @ 2026-11-11 6:28 ` liuyong 0 siblings, 0 replies; 6+ messages in thread From: liuyong @ 2026-11-11 6:28 UTC (permalink / raw) To: Bart Van Assche Cc: Jason Gunthorpe, Leon Romanovsky, linux-rdma, target-devel, linux-kernel, security Hi Bart, > For the second case, > srpt_get_desc_tbl() should reject SRP_CMD requests for which the sum > of the direct descriptor lengths does not match the total length in > the indirect buffer descriptor instead of building RDMA contexts for > an inconsistent descriptor table. Agreed. In v3, srpt_get_desc_tbl() sums the direct descriptor lengths in u64 and rejects a total that differs from idb->len before allocating RDMA contexts. The existing descriptor-count check is preserved. I also initialize the command before returning BUSY for a parsing error, so the response and completion paths have a valid command reference. > - In srpt_queue_response(), zero the part of the data buffer beyond > cmd->data_length, e.g. with sg_zero_buffer(), if the data buffer is > larger than cmd->data_length. I have replaced the WR/SGE modification loop with tail zeroing using sg_zero_buffer(). v3 saves the original buffer length and clears the unused tail before posting the existing RDMA transfers. Each separately mapped SG list is synchronized for device access after zeroing. The work requests and SGEs returned by the RDMA R/W API are left unchanged. Thanks for the review and suggestions. Regards, liuyong 360 Vulnerability Research Institute ^ permalink raw reply [flat|nested] 6+ messages in thread
* [PATCH v3] RDMA/srpt: Prevent uninitialized read buffer disclosure 2026-11-10 5:28 ` [PATCH v2] " liuyong 2026-11-11 0:54 ` Bart Van Assche @ 2026-11-11 6:53 ` liuyong 1 sibling, 0 replies; 6+ messages in thread From: liuyong @ 2026-11-11 6:53 UTC (permalink / raw) To: Bart Van Assche Cc: Jason Gunthorpe, Leon Romanovsky, linux-rdma, target-devel, linux-kernel, security SRPT allocates read buffers and creates RDMA contexts from the initiator's descriptors before parsing the CDB. When target core reduces cmd->data_length, a backend such as rd_mcp fills only the shorter length, but the prebuilt RDMA transfer still sends the full buffer. This can disclose stale target kernel memory to a remote initiator with access to the LUN. Reject indirect descriptor tables whose direct descriptor lengths do not sum to the advertised total before allocating RDMA contexts. Also initialize commands rejected during descriptor parsing so the BUSY response path has valid callbacks and a command reference. Save the original buffer length and zero any read tail beyond cmd->data_length before posting the existing RDMA transfers. Synchronize each mapped SG list for device access after zeroing. The issue was discovered by Qihoo 360's Tulongfeng multi-agent system. Fixes: b99f8e4d7bcd ("IB/srpt: convert to the generic RDMA READ/WRITE API") Cc: stable@vger.kernel.org Suggested-by: Bart Van Assche <bvanassche@acm.org> Signed-off-by: liuyong <pkfxxxing@gmail.com> --- Developed against f66469d9905b39d2c260a15d7d6db450f4db67c1 (7.4-rc1). drivers/infiniband/ulp/srpt/ib_srpt.c | 41 +++++++++++++++++++++++++++ drivers/infiniband/ulp/srpt/ib_srpt.h | 2 ++ 2 files changed, 43 insertions(+) diff --git a/drivers/infiniband/ulp/srpt/ib_srpt.c b/drivers/infiniband/ulp/srpt/ib_srpt.c index 43c3ff1ad8..0bef01e20c 100644 --- a/drivers/infiniband/ulp/srpt/ib_srpt.c +++ b/drivers/infiniband/ulp/srpt/ib_srpt.c @@ -41,6 +41,7 @@ #include <linux/kthread.h> #include <linux/string.h> #include <linux/delay.h> +#include <linux/dma-mapping.h> #include <linux/atomic.h> #include <linux/inet.h> #include <rdma/ib_cache.h> @@ -1115,8 +1116,10 @@ static int srpt_get_desc_tbl(struct srpt_recv_ioctx *recv_ioctx, } else if (((srp_cmd->buf_fmt & 0xf) == SRP_DATA_DESC_INDIRECT) || ((srp_cmd->buf_fmt >> 4) == SRP_DATA_DESC_INDIRECT)) { struct srp_indirect_buf *idb = srpt_get_desc_buf(srp_cmd); + u64 total_len = 0; int nbufs = be32_to_cpu(idb->table_desc.len) / sizeof(struct srp_direct_buf); + int i; if (nbufs > (srp_cmd->data_out_desc_cnt + srp_cmd->data_in_desc_cnt)) { @@ -1128,6 +1131,11 @@ static int srpt_get_desc_tbl(struct srpt_recv_ioctx *recv_ioctx, return -EINVAL; } + for (i = 0; i < nbufs; i++) + total_len += be32_to_cpu(idb->desc_list[i].len); + if (total_len != be32_to_cpu(idb->len)) + return -EINVAL; + *data_len = be32_to_cpu(idb->len); return srpt_alloc_rw_ctxs(ioctx, idb->desc_list, nbufs, sg, sg_cnt); @@ -1304,6 +1312,7 @@ static struct srpt_send_ioctx *srpt_get_send_ioctx(struct srpt_rdma_ch *ch) WARN_ON_ONCE(ioctx->recv_ioctx); ioctx->n_rdma = 0; ioctx->n_rw_ctx = 0; + ioctx->buf_len = 0; ioctx->queue_status_only = false; /* * transport_init_se_cmd() does not initialize all fields, so do it @@ -1597,9 +1606,13 @@ static void srpt_handle_cmd(struct srpt_rdma_ch *ch, pr_err("0x%llx: parsing SRP descriptor table failed.\n", srp_cmd->tag); } + target_init_cmd(cmd, ch->sess, &send_ioctx->sense_data[0], + scsilun_to_int(&srp_cmd->lun), 0, + cmd->sam_task_attr, dir, 0); goto busy; } + send_ioctx->buf_len = data_len; rc = target_init_cmd(cmd, ch->sess, &send_ioctx->sense_data[0], scsilun_to_int(&srp_cmd->lun), data_len, cmd->sam_task_attr, dir, TARGET_SCF_ACK_KREF); @@ -2866,6 +2879,31 @@ static u8 tcm_to_srp_tsk_mgmt_status(const int tcm_mgmt_status) return SRP_TSK_MGMT_FAILED; } +static void srpt_zero_read_tail(struct srpt_send_ioctx *ioctx) +{ + struct ib_device *dev = ioctx->ch->sport->sdev->device; + u32 data_len = ioctx->cmd.data_length; + int i; + + for (i = 0; i < ioctx->n_rw_ctx; i++) { + struct srpt_rw_ctx *ctx = &ioctx->rw_ctxs[i]; + struct scatterlist *sg; + unsigned int j; + + for_each_sg(ctx->sg, sg, ctx->nents, j) { + u32 skip = min(data_len, sg->length); + + data_len -= skip; + if (skip < sg->length) + sg_zero_buffer(sg, 1, sg->length - skip, skip); + } + + if (!ib_uses_virt_dma(dev)) + dma_sync_sg_for_device(dev->dma_device, ctx->sg, + ctx->nents, DMA_TO_DEVICE); + } +} + /** * srpt_queue_response - transmit the response to a SCSI command * @cmd: SCSI target command. @@ -2907,6 +2945,9 @@ static void srpt_queue_response(struct se_cmd *cmd) if (ioctx->cmd.data_direction == DMA_FROM_DEVICE && ioctx->cmd.data_length && !ioctx->queue_status_only) { + if (ioctx->buf_len > cmd->data_length) + srpt_zero_read_tail(ioctx); + for (i = ioctx->n_rw_ctx - 1; i >= 0; i--) { struct srpt_rw_ctx *ctx = &ioctx->rw_ctxs[i]; diff --git a/drivers/infiniband/ulp/srpt/ib_srpt.h b/drivers/infiniband/ulp/srpt/ib_srpt.h index 506c11b843..78bba74148 100644 --- a/drivers/infiniband/ulp/srpt/ib_srpt.h +++ b/drivers/infiniband/ulp/srpt/ib_srpt.h @@ -204,6 +204,7 @@ struct srpt_rw_ctx { * @rdma_cqe: RDMA completion queue element. * @state: I/O context state. * @cmd: Target core command data structure. + * @buf_len: Original SRP data buffer length. * @sense_data: SCSI sense data. * @n_rdma: Number of work requests needed to transfer this ioctx. * @n_rw_ctx: Size of rw_ctxs array. @@ -223,6 +224,7 @@ struct srpt_send_ioctx { struct ib_cqe rdma_cqe; enum srpt_command_state state; struct se_cmd cmd; + u32 buf_len; u8 n_rdma; u8 n_rw_ctx; bool queue_status_only; -- 2.43.0 ^ permalink raw reply [flat|nested] 6+ messages in thread
end of thread, other threads:[~2026-11-11 6:54 UTC | newest] Thread overview: 6+ messages (download: mbox.gz / follow: Atom feed) -- links below jump to the message on this page -- 2026-11-10 3:08 [PATCH 0/1] RDMA/srpt: Fix read underflow information disclosure liuyong 2026-11-10 3:08 ` [PATCH 1/1] RDMA/srpt: Limit read data transfers to the command length liuyong 2026-11-10 5:28 ` [PATCH v2] " liuyong 2026-11-11 0:54 ` Bart Van Assche 2026-11-11 6:28 ` liuyong 2026-11-11 6:53 ` [PATCH v3] RDMA/srpt: Prevent uninitialized read buffer disclosure liuyong
This is a public inbox, see mirroring instructions for how to clone and mirror all data and code used for this inbox
all inboxes | Powered by JetHome®