[PATCH] scsi: libiscsi_tcp: check the data direction of a Data-In PDU
Yehyeong Lee <[email protected]> Sat, 1 Aug 2026 22:36:35 +0900
| Newsgroups | org.kernel.vger.linux-scsi,org.kernel.vger.linux-kernel,org.kernel.vger.stable |
|---|---|
| Message-ID | <[email protected]> |
The Data-In branch of iscsi_tcp_hdr_dissect() resolves the ITT to a task and copies the PDU's data segment into that command's scatterlist without asking whether the command was reading. iscsi_tcp_r2t_rsp() in the same file does ask, and rejects an R2T for a command that is not DMA_TO_DEVICE. A target that answers a WRITE command's ITT with a Data-In therefore has the initiator write target-supplied bytes into the pages that write was about to send. Those are the caller's own pinned pages for an O_DIRECT write, and page cache pages for a buffered one. Observed against a test target that emits one 512-byte Data-In naming a 128 KB write's ITT, after the R2T for that write. With O_DIRECT the caller's buffer ends up holding 512 bytes of the target's data while pwrite() returns 131072. Buffered is quieter: pwrite() and fsync() both succeed, nothing is logged, and reading those blocks back returns the target's bytes out of the page cache without a command going on the wire. Check the direction before using the scatterlist, the way the R2T path already does. Cc: [email protected] Signed-off-by: Yehyeong Lee <[email protected]> --- Reproduced on v7.2-rc5 with the in-tree initiator over TCP against tgt 1.0.97 on loopback, HeaderDigest and DataDigest both None, InitialR2T=Yes, ImmediateData=Yes, FirstBurstLength=65536, MaxXmitDataSegmentLength=8192. A 128 KB O_DIRECT write goes out as 8192 bytes of immediate data, one R2T for the remaining 122880 at offset 8192, and 15 Data-Out PDUs. The test target inserts one Data-In after that R2T: ITT of the write, DataSN 1, buffer offset 8192, 512 bytes, no S bit. Its payload is a repeating 16-byte marker so the extent can be measured exactly. arm caller's buffer after pwrite() target backing store no injection marker 0, non-0x5a 0 of 131072 0x5a 8192 of 8192 injection marker 32 at offset 8192, marker 32, non-0x5a 512 of 131072 0x5a 7680 of 8192 injection, patched marker 0, non-0x5a 0 of 131072 marker 0 pwrite() returns 131072 in the first two rows. With the patch the PDU is rejected with ISCSI_ERR_PROTO, the same return the R2T path uses for the same kind of violation, and the write fails. Two runs per arm; a 4 KB write and the 128 KB write both still complete normally on the patched kernel. The same injection against a buffered write of the same size: arm read back after fsync() target backing store no injection marker 0, non-0x5a 0 of 131072 0x5a 131072 of 131072 injection marker 32 at offset 8192, marker 32, 0x5a 130560 non-0x5a 512 of 131072 injection, patched marker 0 nothing written pwrite() and fsync() both return success in the first two rows and nothing is logged. The read back uses a descriptor opened before the write and puts no command on the wire -- bracketing it with a 512-byte O_DIRECT read at a fixed LBA shows nothing in between on the target side -- so those bytes come out of the page cache. With the patch fsync() fails with EIO and the write does not land. Injecting at offset 126976 instead of 8192 behaves the same. Eight runs of the injected arm at offset 8192 and six at 126976. The read back was corrupt in every one. drivers/scsi/libiscsi_tcp.c | 3 +++ 1 file changed, 3 insertions(+) diff --git a/drivers/scsi/libiscsi_tcp.c b/drivers/scsi/libiscsi_tcp.c index e90805ba868f..0283c4444cd0 100644 --- a/drivers/scsi/libiscsi_tcp.c +++ b/drivers/scsi/libiscsi_tcp.c @@ -480,6 +480,9 @@ static int iscsi_tcp_data_in(struct iscsi_conn *conn, struct iscsi_task *task) int datasn = be32_to_cpu(rhdr->datasn); unsigned total_in_length = task->sc->sdb.length; + if (task->sc->sc_data_direction != DMA_FROM_DEVICE) + return ISCSI_ERR_PROTO; + /* * lib iscsi will update this in the completion handling if there * is status. -- 2.43.0