From nobody Tue Sep 29 00:34:00 2026 Received: from mail-pg1-f173.google.com (mail-pg1-f173.google.com [209.85.215.173]) (using TLSv1.2 with cipher ECDHE-RSA-AES128-GCM-SHA256 (128/128 bits)) (No client certificate requested) by smtp.subspace.kernel.org (Postfix) with ESMTPS id 7A32B46AF1A for ; Fri, 14 Aug 2026 12:14:41 +0000 (UTC) Authentication-Results: smtp.subspace.kernel.org; arc=none smtp.client-ip=209.85.215.173 ARC-Seal: i=1; a=rsa-sha256; d=subspace.kernel.org; s=arc-20240116; t=1786709684; cv=none; b=RANQ85uFGrVjTJ+GVrVDCWB3xBaRXz1JVNTvQCEmioQeJk6zDRMQcvaYbjNrXp9KiyKwvwgsaOdWPMM9qJ8fWYzCRwKmzlHkvkGxOB8okOnXQNERDFlHJx+FbhOnbCc9AQDiDoPkNb/8LVWTSxdYW4Liqv4fnEbaXobd4Z2R66c= ARC-Message-Signature: i=1; a=rsa-sha256; d=subspace.kernel.org; s=arc-20240116; t=1786709684; c=relaxed/simple; bh=vS1DaGeA34Vt/UL7hTlZ7x9a+4a9vxNMD5B4EeDQiSw=; h=From:To:Cc:Subject:Date:Message-ID:MIME-Version; b=BCvAJlsU0GP/9pGht+JPEKctKmEBXd4FMZXkYZl+ESbTW5kuFUdH1SOcwxG+/3af1Cvun/7Fqql5V8ZZkqV7LGfej4bW5iMZDSrzEdhyXofa2Nbu71/xpEb/oPfZI42woMYoqmpcm+fMBkk35osMNx6izP7P1fg4SbwQI0nN5V4= ARC-Authentication-Results: i=1; smtp.subspace.kernel.org; dmarc=fail (p=none dis=none) header.from=isslab.korea.ac.kr; spf=none smtp.mailfrom=isslab.korea.ac.kr; dkim=pass (2048-bit key) header.d=isslab-korea-ac-kr.20251104.gappssmtp.com header.i=@isslab-korea-ac-kr.20251104.gappssmtp.com header.b=XTi8PQsh; arc=none smtp.client-ip=209.85.215.173 Authentication-Results: smtp.subspace.kernel.org; dmarc=fail (p=none dis=none) header.from=isslab.korea.ac.kr Authentication-Results: smtp.subspace.kernel.org; spf=none smtp.mailfrom=isslab.korea.ac.kr Authentication-Results: smtp.subspace.kernel.org; dkim=pass (2048-bit key) header.d=isslab-korea-ac-kr.20251104.gappssmtp.com header.i=@isslab-korea-ac-kr.20251104.gappssmtp.com header.b="XTi8PQsh" Received: by mail-pg1-f173.google.com with SMTP id 41be03b00d2f7-ca00f126b7eso555809a12.2 for ; Fri, 14 Aug 2026 05:14:41 -0700 (PDT) DKIM-Signature: v=1; a=rsa-sha256; c=relaxed/relaxed; d=isslab-korea-ac-kr.20251104.gappssmtp.com; s=20251104; t=1786709680; x=1787314480; darn=vger.kernel.org; h=content-transfer-encoding:mime-version:message-id:date:subject:cc :to:from:from:to:cc:subject:date:message-id:reply-to:content-type; bh=mB3Pg/1SEPTtiURwc1+Fo1eQBiAzNFUq+2h1OebH7HE=; b=XTi8PQsh6fGaESnR6L/VGvrUQElm93drqKeJpVA4qqm3ceqbSpXuNwPjOnvF0kcRWe x3LsSy7QKCm5hRjpEKltckoriI+rzVk48arJ78xlKhDXs4oLHUi9zunevntlYRABGGZu 54Mn4ILgUjwmwlC9ewB5pm3bh6KKVxHRYZjCuxKrqKQ5TAQQU8jRqANNyOKxopkg5Cj7 qBtEYRVDFItWtxhigikOGtV3AC6/dW8aeLSnRi4OHRz7ThcR6Riy8kBd/9aFCAONlidJ QqIaAmkCRKeB1yjxa9EXiPYwnwUoxaJ7JKO/PjcpnTnk5AGFO5NcsoAYdXYbBP2kV0ae i71Q== X-Google-DKIM-Signature: v=1; a=rsa-sha256; c=relaxed/relaxed; d=1e100.net; s=20251104; t=1786709680; x=1787314480; h=content-transfer-encoding:mime-version:message-id:date:subject:cc :to:from:x-gm-gg:x-gm-message-state:from:to:cc:subject:date :message-id:reply-to:content-type; bh=mB3Pg/1SEPTtiURwc1+Fo1eQBiAzNFUq+2h1OebH7HE=; b=rdYymov7tjZAx3QrMek00SOapib+l5EKVTjObp0fQKpeeQnsls16cDkxKaqdMXk9aX uBHQ5yr5jIAzvvGhimumeZq+HOM1Ahla+fuQrGxL8QEE8Hm/EHq3IxC2WR53R+nHBMcM iXAfCGukz8O7V2MSD1oZLpsNWEB65S051gJJ+ZIqVNvdqZBBp/vN2YkyPxpY1S8BzKza C8rDNUDpFijhhNAsS5CcTgJ3l2JwJSHhi4wn2K7yIqN8JGbCbI6U13A9AZ4qhHnNYAUo ncKowGrxwl4V1/Mv5eud2H4ZL1NJ+RGo4FX5LphhKSLDhG1oNsmSi/Nd4AEoICdWg4DS 1z1A== X-Forwarded-Encrypted: i=1; AHgh+RokZNDAeEgxxCah7clljpIfeWw/iwzSg8ZMs/RqIGYSUoSyvY3Tk10ysBaV1pCU0DBnRbbqmNFP/WmltsU=@vger.kernel.org X-Gm-Message-State: AOJu0Yzo1hxsQMeE7rZV7lEs5xVlMhGiy8KGKeH0abMlWXsup2WozSFC UCuSo1hmJmMu3uGHgGB/hORd2SO5kp0UwBJ5DRIp7c41wtXzGb5b5BjvISSN3rVFs7w= X-Gm-Gg: AR+sD13qUjVkS/L8PFT74qKwJgfKZ3sIdV9Og2ievDwkx7TKq1i2mTJIw3FyuUBAYmt 8v5T0AdKIo0Fe8ekwjEcG067h7KmZWcprif2HBcAtVG2xCr2UwQ1izVi1VjwkD5Fdvm0guGNoth 8LrsTBCKzZb88QkEMzuDrXX9s1XdynFD+YQFgP/nYuTVxJWr9bFQr/Y8QHd0kU4HOuHvvi9VAfQ IQEy8Gere5YcVYCG0n4wrZUzIHhLJuQvfPd2z+ceUClRcXg3g/HAS7bE/W3X9vy8rEFoLIqnqUG eu2KLilJP3TD9yGolFatmaINbPbrXjsp/8uUGRhY7G5WgPIadtvdCdMF8ijnDQwsTPRGqjv60AC hWkp8LCkIVJZenNCjNz91iFPKg2KwyNrUl+4srZjVJkDSA6X2SHpevyq1MRR1WDbhBzQ+6BxByR tKGbTw0uPUdjdjN7JMWNoKvhB5+tSU9M6mAZTLDYiiTX1lVo76/FC77lY/EwsWOw2zBXhs X-Received: by 2002:a05:6a00:2383:b0:84e:89a:b8ec with SMTP id d2e1a72fcca58-84fde3d4d3emr5107874b3a.11.1786709680448; Fri, 14 Aug 2026 05:14:40 -0700 (PDT) Received: from yhlee-960QFG.. ([125.131.91.97]) by smtp.gmail.com with ESMTPSA id d2e1a72fcca58-8517d267305sm373099b3a.52.2026.08.14.05.14.37 (version=TLS1_3 cipher=TLS_AES_256_GCM_SHA384 bits=256/256); Fri, 14 Aug 2026 05:14:40 -0700 (PDT) From: Yehyeong Lee To: martin.petersen@oracle.com Cc: michael.christie@oracle.com, cleech@redhat.com, lduncan@suse.com, James.Bottomley@HansenPartnership.com, linux-scsi@vger.kernel.org, linux-kernel@vger.kernel.org, Yehyeong Lee , stable@vger.kernel.org Subject: [PATCH] scsi: libiscsi_tcp: check that a read got the data the target claimed Date: Fri, 14 Aug 2026 21:14:28 +0900 Message-ID: <20260814121428.359541-1-yhlee@isslab.korea.ac.kr> X-Mailer: git-send-email 2.43.0 Precedence: bulk X-Mailing-List: linux-kernel@vger.kernel.org List-Id: List-Subscribe: List-Unsubscribe: MIME-Version: 1.0 Content-Transfer-Encoding: quoted-printable Content-Type: text/plain; charset="utf-8" A target can finish a read without sending the data. iscsi_tcp_data_in() bounds each Data-In PDU against the command buffer but never adds them up, so a completion that declares no underflow is believed and the command ends with DID_OK. The pages keep whatever they held. Over a 1 MiB pread of a file that had never been read, 999424 bytes came back as the contents of an unrelated file the same process had written earlier. pread() returned 1048576 and errno was 0. Count the payload per task. Require each Data-In to continue where the last one ended, and at completion compare the total with the buffer length, less the residual when an underflow is declared. A read can end on a Data-In or on a SCSI Response, so check both. Writes are untouched. Cc: stable@vger.kernel.org Signed-off-by: Yehyeong Lee --- No Fixes: tag. iscsi_tcp.c had the same shape before a081c13e39b5 ("[SCSI] iscsi_tcp: split module into lib and lld"), so the check has never been there. Measured with a proxy in front of tgt. Four ways of ending a read early made it return success with the contents of an unrelated file the same process had written: a zero-length Data-In, the status in a SCSI Response, and no Data-In at all each gave 999424 of 1048576 bytes, and rewinding BufferOffset gave 737280. Each is refused now, two runs each, as is a short last PDU with no underflow declared. An honest underflow and ordinary traffic are unchanged. The check does not honour DataPDUInOrder: the target answers that key, so honouring it lets a target switch the check off - measured. A target that legitimately sends Data-In out of buffer order will now fail. Not exercised: the cxgbi offload path, and a digest session - tgt would not negotiate CRC32C. drivers/scsi/libiscsi_tcp.c | 60 +++++++++++++++++++++++++++++++++++-- include/scsi/libiscsi_tcp.h | 1 + 2 files changed, 59 insertions(+), 2 deletions(-) diff --git a/drivers/scsi/libiscsi_tcp.c b/drivers/scsi/libiscsi_tcp.c index e90805ba868fb..ce9c96f968c2a 100644 --- a/drivers/scsi/libiscsi_tcp.c +++ b/drivers/scsi/libiscsi_tcp.c @@ -467,6 +467,39 @@ void iscsi_tcp_cleanup_task(struct iscsi_task *task) } EXPORT_SYMBOL_GPL(iscsi_tcp_cleanup_task); =20 +/** + * iscsi_tcp_check_data_in - verify the Data-In payload that was received + * @task: scsi command task + * @flags: flags of the PDU carrying the status + * @residual: residual count of that PDU + * + * A target that reports success must have sent the whole buffer, or have + * declared the shortfall as an underflow. ISCSI_FLAG_CMD_UNDERFLOW and + * ISCSI_FLAG_DATA_UNDERFLOW have the same value, so both paths can use + * this. + */ +static int iscsi_tcp_check_data_in(struct iscsi_task *task, u32 flags, + u32 residual) +{ + struct iscsi_tcp_task *tcp_task =3D task->dd_data; + struct scsi_cmnd *sc =3D task->sc; + unsigned int expected; + + if (!sc || sc->sc_data_direction =3D=3D DMA_TO_DEVICE) + return 0; + + expected =3D sc->sdb.length; + if (flags & ISCSI_FLAG_DATA_UNDERFLOW) { + if (residual > expected) + return 0; + expected -=3D residual; + } + if (tcp_task->data_in_bytes =3D=3D expected) + return 0; + + return ISCSI_ERR_DATALEN; +} + /** * iscsi_tcp_data_in - SCSI Data-In Response processing * @conn: iscsi connection @@ -488,7 +521,7 @@ static int iscsi_tcp_data_in(struct iscsi_conn *conn, s= truct iscsi_task *task) iscsi_update_cmdsn(conn->session, (struct iscsi_nopin*)rhdr); =20 if (tcp_conn->in.datalen =3D=3D 0) - return 0; + goto status; =20 if (tcp_task->exp_datasn !=3D datasn) { ISCSI_DBG_TCP(conn, "task->exp_datasn(%d) !=3D rhdr->datasn(%d)" @@ -506,7 +539,17 @@ static int iscsi_tcp_data_in(struct iscsi_conn *conn, = struct iscsi_task *task) return ISCSI_ERR_DATA_OFFSET; } =20 + if (tcp_task->data_offset !=3D tcp_task->data_in_bytes) + return ISCSI_ERR_DATA_OFFSET; + + tcp_task->data_in_bytes +=3D tcp_conn->in.datalen; + conn->datain_pdus_cnt++; + +status: + if (rhdr->flags & ISCSI_FLAG_DATA_STATUS) + return iscsi_tcp_check_data_in(task, rhdr->flags, + be32_to_cpu(rhdr->residual_count)); return 0; } =20 @@ -752,13 +795,25 @@ iscsi_tcp_hdr_dissect(struct iscsi_conn *conn, struct= iscsi_hdr *hdr) rc =3D __iscsi_complete_pdu(conn, hdr, NULL, 0); spin_unlock(&conn->session->back_lock); break; - case ISCSI_OP_SCSI_CMD_RSP: + case ISCSI_OP_SCSI_CMD_RSP: { + struct iscsi_scsi_rsp *rsp =3D (struct iscsi_scsi_rsp *)hdr; + + spin_lock(&conn->session->back_lock); + task =3D iscsi_itt_to_ctask(conn, hdr->itt); + if (task) + rc =3D iscsi_tcp_check_data_in(task, rsp->flags, + be32_to_cpu(rsp->residual_count)); + spin_unlock(&conn->session->back_lock); + if (rc) + break; + if (tcp_conn->in.datalen) { iscsi_tcp_data_recv_prep(tcp_conn); return 0; } rc =3D iscsi_complete_pdu(conn, hdr, NULL, 0); break; + } case ISCSI_OP_R2T: if (ahslen) { rc =3D ISCSI_ERR_AHSLEN; @@ -998,6 +1053,7 @@ int iscsi_tcp_task_init(struct iscsi_task *task) =20 BUG_ON(kfifo_len(&tcp_task->r2tqueue)); tcp_task->exp_datasn =3D 0; + tcp_task->data_in_bytes =3D 0; =20 /* Prepare PDU, optionally w/ immediate data */ ISCSI_DBG_TCP(conn, "task deq [itt 0x%x imm %d unsol %d]\n", diff --git a/include/scsi/libiscsi_tcp.h b/include/scsi/libiscsi_tcp.h index ef53d4bea28a0..b38be23b9e10b 100644 --- a/include/scsi/libiscsi_tcp.h +++ b/include/scsi/libiscsi_tcp.h @@ -66,6 +66,7 @@ struct iscsi_tcp_conn { =20 struct iscsi_tcp_task { uint32_t exp_datasn; /* expected target's R2TSN/DataSN */ + u32 data_in_bytes; /* Data-In payload received */ int data_offset; struct iscsi_r2t_info *r2t; /* in progress solict R2T */ struct iscsi_pool r2tpool; --=20 2.43.0