From: Leon Romanovsky <leon@kernel.org>
To: Yehyeong Lee <yhlee@isslab.korea.ac.kr>
Cc: sagi@grimberg.me, jgg@ziepe.ca, nab@linux-iscsi.org,
linux-rdma@vger.kernel.org, target-devel@vger.kernel.org,
linux-kernel@vger.kernel.org, stable@vger.kernel.org
Subject: Re: [PATCH] IB/isert: reject PDUs declaring more data than was received
Date: Sun, 26 Jul 2026 09:46:28 +0300 [thread overview]
Message-ID: <20260726064628.GA12003@unreal> (raw)
In-Reply-To: <20260725011846.538191-1-yhlee@isslab.korea.ac.kr>
On Sat, Jul 25, 2026 at 10:18:46AM +0900, Yehyeong Lee wrote:
> isert_recv_done() hands each received PDU to the opcode handlers without
> ever looking at wc->byte_len, the number of bytes the HCA actually placed
> in the receive descriptor. The handlers then copy that many bytes - the
> data-segment length the initiator declared in the BHS
> (ntoh24(hdr->dlength), via the derived unsol_data_len / imm_data_len) -
> out of the fixed-size descriptor:
>
> isert_handle_iscsi_dataout():
> sg_copy_from_buffer(sg_start, sg_nents, isert_get_data(rx_desc),
> unsol_data_len);
> isert_handle_scsi_cmd():
> sg_copy_from_buffer(cmd->se_cmd.t_data_sg, sg_nents,
> isert_get_data(rx_desc), imm_data_len);
>
> Because the declared length is never checked against wc->byte_len, an
> initiator can declare a data segment larger than the bytes it actually
> sent (and larger than the descriptor) and cause an out-of-bounds read of
> the receive buffer.
>
> Nothing upstream of isert closes this door:
>
> - __iscsit_check_dataout_hdr() bounds the inbound payload against
> conn_ops->MaxXmitDataSegmentLength (MXDSL) - a transmit parameter,
> used here for the inbound check.
> - iscsi_set_connection_parameters() sets
> ops->MaxXmitDataSegmentLength = ops->TargetRecvDataSegmentLength;
> and TARGETRECVDATASEGMENTLENGTH is absent from the min()-clamp list in
> iscsi_check_acceptor_state(), so the value the initiator declares is
> adopted verbatim (type range 512..16777215). The initiator effectively
> raises its own ceiling.
> - isert never clamps the negotiated value to its own fixed receive
> descriptor (ISER_RX_SIZE, 9216 bytes), so the target core's bound and
> the descriptor size are unrelated.
>
> The imm_data_len == data_len path is more than an over-read: it aliases
> the receive descriptor via sg_set_buf() and passes it to the backend as
> the data source for the SCSI WRITE, so an over-declared length causes heap
> contents past the descriptor to be written through the backend to the
> backing store. The backend is the victim of the oversized scatterlist
> isert hands it, not the cause; no read-back of the written bytes was
> demonstrated.
>
> Trigger: after login completes (full feature phase), an initiator that has
> declared a large TargetRecvDataSegmentLength and a FirstBurstLength that
> permits unsolicited/immediate data sends a PDU whose declared data-segment
> length exceeds what was received. With KASAN:
>
> BUG: KASAN: slab-out-of-bounds in sg_copy_buffer+0x150/0x1c0
> Read of size 4096 at addr ffff888109720800 by task kworker/1:0H/25
> Workqueue: ib-comp-wq ib_cq_poll_work
> Call Trace:
> sg_copy_buffer+0x150/0x1c0
> isert_recv_done+0xba6/0x2390
> __ib_process_cq+0xe1/0x390
> ib_cq_poll_work+0x46/0x150
>
> isert_recv_done+0xba6 resolves to isert_handle_iscsi_dataout()
> (ib_isert.c:1160), inlined through isert_rx_opcode().
>
> Validate wc->byte_len against the framing in isert_recv_done() before the
> PDU reaches any handler, and reinstate the connection if it is short.
> Because the test compares without subtracting the header length, it also
> rejects PDUs shorter than the iSER and iSCSI headers, which would otherwise
> be parsed out of stale descriptor contents. The sibling login handler,
> isert_login_recv_done(), already rejects PDUs shorter than ISER_HEADERS_LEN
> (commit 29e7b925ae6d ("IB/isert: Reject login PDUs shorter than
> ISER_HEADERS_LEN")); the data handlers had no length check at all.
>
> isert reads the data segment from a fixed offset: isert_get_data()
> returns the iSER header plus ISER_HEADERS_LEN and makes no adjustment for
> an AHS. The bytes the handlers touch are therefore exactly
> [ISER_HEADERS_LEN, ISER_HEADERS_LEN + dlength), and comparing that sum
> against wc->byte_len bounds precisely the region that is read. An AHS
> term would only make the test stricter without bounding anything further,
> and cannot cause a false reject: a PDU carrying an AHS is longer, not
> shorter.
>
> This is a memory-safety fix that verifies the bytes that were actually
> received; it does not touch RFC 7145 length negotiation and is not the
> MaxXmitDataSegmentLength negotiation redesign raised in the 2017 "[Query]
> iSER-Target: QP errors observed on increasing MaxXmitDataSegmentLength"
> discussion. That redesign is explicitly out of scope here.
>
> The patched kernel rejects the malformed DataOut PDU and both
> immediate-data variants with "PDU declares ... bytes were received" and
> continues to pass normal traffic with no regression.
>
> Reproduced with soft-RoCE (rdma_rxe) and a raw rdma_cm/ibv initiator; no
> kernel-side test hooks were needed.
>
> Fixes: b8d26b3be8b3 ("iser-target: Add iSCSI Extensions for RDMA (iSER) target driver")
> Cc: stable@vger.kernel.org
> Signed-off-by: Yehyeong Lee <yhlee@isslab.korea.ac.kr>
> ---
> drivers/infiniband/ulp/isert/ib_isert.c | 15 +++++++++++++++
> 1 file changed, 15 insertions(+)
>
> diff --git a/drivers/infiniband/ulp/isert/ib_isert.c b/drivers/infiniband/ulp/isert/ib_isert.c
> index 1015a51f750a..66435a0a5c7a 100644
> --- a/drivers/infiniband/ulp/isert/ib_isert.c
> +++ b/drivers/infiniband/ulp/isert/ib_isert.c
> @@ -1333,6 +1333,21 @@ isert_recv_done(struct ib_cq *cq, struct ib_wc *wc)
> ib_dma_sync_single_for_cpu(ib_dev, rx_desc->dma_addr,
> ISER_RX_SIZE, DMA_FROM_DEVICE);
>
> + /*
> + * The data segment length declared in the BHS is attacker controlled
> + * and is used further down to read that many bytes out of the fixed
> + * size receive descriptor, so it has to be checked against the number
> + * of bytes that were actually received. Comparing without subtracting
> + * also rejects PDUs shorter than the iSER and iSCSI headers, which
> + * would otherwise be parsed out of stale descriptor contents.
> + */
> + if (unlikely(wc->byte_len < ISER_HEADERS_LEN + ntoh24(hdr->dlength))) {
> + isert_err("PDU declares %u data bytes but only %u bytes were received\n",
> + ntoh24(hdr->dlength), wc->byte_len);
> + iscsit_cause_connection_reinstatement(isert_conn->conn, 0);
> + return;
> + }
1. You should review patches generated by AI.
2. There is a need to add similar check to isert_get_login_rx() too.
Something like this:
Thanks
commit ffe5e4081fa535412fdd5a685369441076a4d1e1 (HEAD -> b4/the-iser-login-handler-fails-to-vali, origin/the-iser-login-handler-fails-to-vali-v1)
Author: Leon Romanovsky <leonro@nvidia.com>
Date: Sat Jul 25 05:40:22 2026 +0300
IB/isert: Reject login PDUs declaring more data than received
isert never compares the iSCSI header's 24-bit data length against the
bytes actually received for a login PDU. A remote initiator can declare far
more data than it sends, and before target lookup or authentication:
isert_rx_login_req()
iscsi_target_locate_portal()
kmemdup_nul()
reads ntoh24(dlength) bytes out of the fixed 8192-byte login buffer,
overrunning it.
Validate the declared length against the received login_req_len in both the
initial (isert_get_login_rx()) and subsequent (isert_login_recv_done())
login paths before any copy. Allow dlength <= login_req_len, since the
received count may include up to three iSCSI padding bytes.
Fixes: b8d26b3be8b3 ("iser-target: Add iSCSI Extensions for RDMA (iSER) target driver")
Signed-off-by: Leon Romanovsky <leonro@nvidia.com>
diff --git a/drivers/infiniband/ulp/isert/ib_isert.c b/drivers/infiniband/ulp/isert/ib_isert.c
index 4691845bf815..02ce4cbe69a1 100644
--- a/drivers/infiniband/ulp/isert/ib_isert.c
+++ b/drivers/infiniband/ulp/isert/ib_isert.c
@@ -970,6 +970,19 @@ isert_put_login_tx(struct iscsit_conn *conn, struct iscsi_login *login,
return 0;
}
+static int
+isert_check_login_req(struct isert_conn *isert_conn)
+{
+ struct iscsi_hdr *hdr =
+ (struct iscsi_hdr *)isert_get_iscsi_hdr(isert_conn->login_desc);
+ u32 dlength = ntoh24(hdr->dlength);
+
+ if (dlength > isert_conn->login_req_len)
+ return -EPROTO;
+
+ return 0;
+}
+
static void
isert_rx_login_req(struct isert_conn *isert_conn)
{
@@ -1393,8 +1406,11 @@ isert_login_recv_done(struct ib_cq *cq, struct ib_wc *wc)
if (isert_conn->conn) {
struct iscsi_login *login = isert_conn->conn->conn_login;
- if (login && !login->first_request)
+ if (login && !login->first_request) {
+ if (isert_check_login_req(isert_conn))
+ return;
isert_rx_login_req(isert_conn);
+ }
}
mutex_lock(&isert_conn->mutex);
@@ -2359,6 +2375,10 @@ isert_get_login_rx(struct iscsit_conn *conn, struct iscsi_login *login)
if (!login->first_request)
return 0;
+ ret = isert_check_login_req(isert_conn);
+ if (ret)
+ return ret;
+
isert_rx_login_req(isert_conn);
isert_info("before login_comp conn: %p\n", conn);
Thanks
> +
> isert_dbg("DMA: 0x%llx, iSCSI opcode: 0x%02x, ITT: 0x%08x, flags: 0x%02x dlen: %d\n",
> rx_desc->dma_addr, hdr->opcode, hdr->itt, hdr->flags,
> (int)(wc->byte_len - ISER_HEADERS_LEN));
>
> base-commit: 248951ddc14de84de3910f9b13f51491a8cd91df
> --
> 2.43.0
>
>
next prev parent reply other threads:[~2026-07-26 6:46 UTC|newest]
Thread overview: 5+ messages / expand[flat|nested] mbox.gz Atom feed top
2026-07-25 1:18 [PATCH] IB/isert: reject PDUs declaring more data than was received Yehyeong Lee
2026-07-26 6:46 ` Leon Romanovsky [this message]
2026-07-26 15:31 ` Yehyeong Lee
2026-07-27 5:15 ` Leon Romanovsky
2026-07-27 10:13 ` Yehyeong Lee
Reply instructions:
You may reply publicly to this message via plain-text email
using any one of the following methods:
* Save the following mbox file, import it into your mail client,
and reply-to-all from there: mbox
Avoid top-posting and favor interleaved quoting:
https://en.wikipedia.org/wiki/Posting_style#Interleaved_style
* Reply using the --to, --cc, and --in-reply-to
switches of git-send-email(1):
git send-email \
--in-reply-to=20260726064628.GA12003@unreal \
--to=leon@kernel.org \
--cc=jgg@ziepe.ca \
--cc=linux-kernel@vger.kernel.org \
--cc=linux-rdma@vger.kernel.org \
--cc=nab@linux-iscsi.org \
--cc=sagi@grimberg.me \
--cc=stable@vger.kernel.org \
--cc=target-devel@vger.kernel.org \
--cc=yhlee@isslab.korea.ac.kr \
/path/to/YOUR_REPLY
https://kernel.org/pub/software/scm/git/docs/git-send-email.html
* If your mail client supports setting the In-Reply-To header
via mailto: links, try the mailto: link
Be sure your reply has a Subject: header at the top and a blank line
before the message body.
This is an external index of several public inboxes,
see mirroring instructions on how to clone and mirror
all data and code used by this external index.