The Linux Kernel Mailing List
 help / color / mirror / Atom feed
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
> 
> 

  reply	other threads:[~2026-07-26  6:46 UTC|newest]

Thread overview: 3+ 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

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 a public inbox, see mirroring instructions
for how to clone and mirror all data and code used for this inbox