[PATCH v2] scsi: target: iscsi: Reject CDB size exceeding available buffer in iscsit_setup_scsi_cmd
From: ghuicao
Date: Wed Aug 26 2026 - 06:16:39 EST
From: Cao Guanghui <caoguanghui@xxxxxxxxxx>
In iscsit_setup_scsi_cmd(), the CDB is later re-parsed by
scsi_command_size() based on its SCSI opcode. For a VARIABLE_LENGTH_CMD
(0x7f) the returned size is cdb[7] + 8, where both cdb[0] and cdb[7]
come verbatim from the initiator-controlled PDU. The amount of CDB data
actually available is never cross-checked against this opcode-declared
length before the CDB is handed to target_cmd_init_cdb(), which does:
memcpy(cmd->t_task_cdb, cdb, scsi_command_size(cdb));
An initiator can set cdb[0]=0x7f and cdb[7]=252 so that
scsi_command_size() returns 260, while the available CDB space is only
16 bytes (the basic header, when no Extended CDB AHS is present) or the
AHS-provided length. target_cmd_init_cdb() then reads up to 244 bytes
past the end of the CDB buffer, a heap out-of-bounds read. The leaked
bytes are later parsed as the CDB and can be indirectly observed by the
initiator through sense data and responses.
Reject the command when the opcode-declared CDB size exceeds the
available CDB space, before the CDB is passed on:
- Without an Extended CDB AHS the CDB is limited to ISCSI_CDB_SIZE (16)
bytes in the basic header, so reject when scsi_command_size() >
ISCSI_CDB_SIZE.
- With an Extended CDB AHS the buffer is allocated from the AHS-declared
length, so reject when scsi_command_size() > cdb_length.
commit 2f3835771dff ("scsi: target: iscsi: reject invalid size Extended
CDB AHS") fixed the zero-length ahslength overflows and the AHS-buffer
overread, but did not cover the orthogonal "available CDB space vs.
opcode-declared size" path.
Fixes: 8f1f7d297bce ("scsi: target: iscsi: Add support for extended CDB AHS")
Signed-off-by: Cao Guanghui <caoguanghui@xxxxxxxxxx>
---
v2:
- Add bounds check for standard path without AHS (hdr->hlength == 0)
- Use if/else to make the two paths explicit
drivers/target/iscsi/iscsi_target.c | 31 +++++++++++++++++++++++++++++
1 file changed, 31 insertions(+)
diff --git a/drivers/target/iscsi/iscsi_target.c b/drivers/target/iscsi/iscsi_target.c
index 62ada3a52210..56f8fd461192 100644
--- a/drivers/target/iscsi/iscsi_target.c
+++ b/drivers/target/iscsi/iscsi_target.c
@@ -1100,7 +1100,22 @@ int iscsit_setup_scsi_cmd(struct iscsit_conn *conn, struct iscsit_cmd *cmd,
cdb = hdr->cdb;
- if (hdr->hlength) {
+ if (!hdr->hlength) {
+ /*
+ * Without an Extended CDB AHS the CDB is limited to the 16
+ * bytes in the basic header. The CDB is later re-parsed by
+ * scsi_command_size() based on its opcode, which may claim a
+ * larger length (e.g. VARIABLE_LENGTH_CMD with cdb[7]=252).
+ * Reject such a mismatch before handing the CDB to
+ * target_cmd_init_cdb() to avoid an out-of-bounds read.
+ */
+ if (scsi_command_size(hdr->cdb) > ISCSI_CDB_SIZE) {
+ pr_err("SCSI command size %u exceeds CDB size %u, protocol error.\n",
+ scsi_command_size(hdr->cdb), ISCSI_CDB_SIZE);
+ return iscsit_add_reject_cmd(cmd,
+ ISCSI_REASON_PROTOCOL_ERROR, buf);
+ }
+ } else {
ecdb_ahdr = (struct iscsi_ecdb_ahdr *) (hdr + 1);
if (ecdb_ahdr->ahstype != ISCSI_AHSTYPE_CDB) {
pr_err("Additional Header Segment type %d not supported!\n",
@@ -1124,6 +1139,20 @@ int iscsit_setup_scsi_cmd(struct iscsit_conn *conn, struct iscsit_cmd *cmd,
cdb_length = ahslength - 1 + ISCSI_CDB_SIZE;
+ /*
+ * The CDB buffer is later re-parsed by scsi_command_size()
+ * based on its opcode, which may claim a length larger than
+ * the AHS provided. Reject such a mismatch before allocating
+ * to avoid an out-of-bounds read of the CDB buffer in
+ * target_cmd_init_cdb().
+ */
+ if (scsi_command_size(hdr->cdb) > cdb_length) {
+ pr_err("Extended CDB AHS: SCSI command size %u exceeds AHS-provided CDB length %u, protocol error.\n",
+ scsi_command_size(hdr->cdb), cdb_length);
+ return iscsit_add_reject_cmd(cmd,
+ ISCSI_REASON_PROTOCOL_ERROR, buf);
+ }
+
cdb = kmalloc(cdb_length, GFP_KERNEL);
if (cdb == NULL)
return iscsit_add_reject_cmd(cmd,
--
2.34.1