SCSI fixes on 20260531

Two core changes, the only one of significance being the change to kick queues
 in SDEV_CANCEL which had a small window for stuck requests.  The major driver
 fixes are the one to the FC transport class to widen the FPIN counter to
 counter a theoretical (and privileged) fabric traffic injection attack and the
 other is an iscsi fix where a malicious target could trick the kernel into an
 output buffer overrun.  Both the driver fixes were AI assisted.
 
 Signed-off-by: James E.J. Bottomley <James.Bottomley@HansenPartnership.com>
 -----BEGIN PGP SIGNATURE-----
 
 iLgEABMIAGAWIQTnYEDbdso9F2cI+arnQslM7pishQUCahxQ/RsUgAAAAAAEAA5t
 YW51MiwyLjUrMS4xMiwyLDImHGphbWVzLmJvdHRvbWxleUBoYW5zZW5wYXJ0bmVy
 c2hpcC5jb20ACgkQ50LJTO6YrIX+HgD+Mqf+AKbV/EhPhKAfONeBaE0Q5e78KTyK
 3e47Qxjs+mQBAKhWUwodLDS/WSm7Fbdj7tn/kuPSaH+R+JQltnPR4QPG
 =BRJ7
 -----END PGP SIGNATURE-----

Merge tag 'scsi-fixes' of git://git.kernel.org/pub/scm/linux/kernel/git/jejb/scsi

Pull SCSI fixes from James Bottomley:
 "Two core changes, the only one of significance being the change to
  kick queues in SDEV_CANCEL which had a small window for stuck
  requests.

  The major driver fixes are the one to the FC transport class to widen
  the FPIN counter to counter a theoretical (and privileged) fabric
  traffic injection attack and the other is an iscsi fix where a
  malicious target could trick the kernel into an output buffer overrun.

  Both the driver fixes were AI assisted"

* tag 'scsi-fixes' of git://git.kernel.org/pub/scm/linux/kernel/git/jejb/scsi:
  scsi: target: iscsi: Validate CHAP_R length before base64 decode
  scsi: target: iscsi: Bound iscsi_encode_text_output() appends to rsp_buf
  scsi: target: iscsi: Fix CRC overread and double-free in iscsit_handle_text_cmd()
  scsi: fcoe: Reject FIP descriptors with zero fip_dlen in CVL walker
  scsi: scsi_transport_fc: Widen FPIN pname walker counter to u32
  scsi: scsi_debug: Add missing newline in scsi_debug_device_reset()
  scsi: megaraid_sas: Fix NULL pointer dereference on firmware duplicate completion
  scsi: devinfo: Add BLIST_NO_RSOC for Promise VTrak E310f
  scsi: core: Run queues for all non-SDEV_DEL devices from scsi_run_host_queues
This commit is contained in:
Linus Torvalds 2026-05-31 08:45:08 -07:00
commit 13bd441bb9
11 changed files with 155 additions and 58 deletions

View File

@ -1385,7 +1385,7 @@ static void fcoe_ctlr_recv_clr_vlink(struct fcoe_ctlr *fip,
while (rlen >= sizeof(*desc)) {
dlen = desc->fip_dlen * FIP_BPW;
if (dlen > rlen)
if (dlen < sizeof(*desc) || dlen > rlen)
goto err;
/* Drop CVL if there are duplicate critical descriptors */
if ((desc->fip_dtype < 32) &&

View File

@ -3612,6 +3612,15 @@ complete_cmd_fusion(struct megasas_instance *instance, u32 MSIxIndex,
complete(&cmd_fusion->done);
break;
case MPI2_FUNCTION_SCSI_IO_REQUEST: /*Fast Path IO.*/
/*
* Firmware can send stale/duplicate completions for
* commands already returned to the pool. scmd_local
* would be NULL for such cases. Skip processing to
* avoid NULL pointer access.
*/
if (!scmd_local)
break;
/* Update load balancing info */
if (fusion->load_balance_info &&
(megasas_priv(cmd_fusion->scmd)->status &

View File

@ -6953,7 +6953,7 @@ static int scsi_debug_device_reset(struct scsi_cmnd *SCpnt)
++num_dev_resets;
if (SDEBUG_OPT_ALL_NOISE & sdebug_opts)
sdev_printk(KERN_INFO, sdp, "doing device reset");
sdev_printk(KERN_INFO, sdp, "doing device reset\n");
scsi_debug_stop_all_queued(sdp);
if (devip) {

View File

@ -218,6 +218,7 @@ static struct {
{"PIONEER", "CD-ROM DRM-602X", NULL, BLIST_FORCELUN | BLIST_SINGLELUN},
{"PIONEER", "CD-ROM DRM-604X", NULL, BLIST_FORCELUN | BLIST_SINGLELUN},
{"PIONEER", "CD-ROM DRM-624X", NULL, BLIST_FORCELUN | BLIST_SINGLELUN},
{"Promise", "VTrak E310f", NULL, BLIST_SPARSELUN | BLIST_NO_RSOC},
{"Promise", "VTrak E610f", NULL, BLIST_SPARSELUN | BLIST_NO_RSOC},
{"Promise", "", NULL, BLIST_SPARSELUN},
{"QEMU", "QEMU CD-ROM", NULL, BLIST_SKIP_VPD_PAGES},

View File

@ -575,10 +575,33 @@ void scsi_requeue_run_queue(struct work_struct *work)
void scsi_run_host_queues(struct Scsi_Host *shost)
{
struct scsi_device *sdev;
struct scsi_device *sdev, *prev = NULL;
unsigned long flags;
shost_for_each_device(sdev, shost)
spin_lock_irqsave(shost->host_lock, flags);
__shost_for_each_device(sdev, shost) {
/*
* Only skip devices so deep into removal they will never need
* another kick to their queues. Thus scsi_device_get() cannot
* be used as it would skip devices in SDEV_CANCEL state which
* may need a queue kick.
*/
if (sdev->sdev_state == SDEV_DEL ||
!get_device(&sdev->sdev_gendev))
continue;
spin_unlock_irqrestore(shost->host_lock, flags);
if (prev)
put_device(&prev->sdev_gendev);
scsi_run_queue(sdev->request_queue);
prev = sdev;
spin_lock_irqsave(shost->host_lock, flags);
}
spin_unlock_irqrestore(shost->host_lock, flags);
if (prev)
put_device(&prev->sdev_gendev);
}
static void scsi_uninit_cmd(struct scsi_cmnd *cmd)

View File

@ -737,6 +737,37 @@ fc_cn_stats_update(u16 event_type, struct fc_fpin_stats *stats)
}
}
static void
fc_fpin_pname_stats_update(struct Scsi_Host *shost,
struct fc_rport *attach_rport, u16 event_type,
u32 desc_len, u32 fixed_len, u32 pname_count,
__be64 *pname_list,
void (*stats_update)(u16 event_type,
struct fc_fpin_stats *stats))
{
u32 i;
struct fc_rport *rport;
u64 wwpn;
if (desc_len < fixed_len)
pname_count = 0;
else
pname_count = min(pname_count, (desc_len - fixed_len) /
sizeof(pname_list[0]));
for (i = 0; i < pname_count; i++) {
wwpn = be64_to_cpu(pname_list[i]);
rport = fc_find_rport_by_wwpn(shost, wwpn);
if (rport &&
(rport->roles & FC_PORT_ROLE_FCP_TARGET ||
rport->roles & FC_PORT_ROLE_NVME_TARGET)) {
if (rport == attach_rport)
continue;
stats_update(event_type, &rport->fpin_stats);
}
}
}
/*
* fc_fpin_li_stats_update - routine to update Link Integrity
* event statistics.
@ -747,13 +778,11 @@ fc_cn_stats_update(u16 event_type, struct fc_fpin_stats *stats)
static void
fc_fpin_li_stats_update(struct Scsi_Host *shost, struct fc_tlv_desc *tlv)
{
u8 i;
struct fc_rport *rport = NULL;
struct fc_rport *attach_rport = NULL;
struct fc_host_attrs *fc_host = shost_to_fc_host(shost);
struct fc_fn_li_desc *li_desc = (struct fc_fn_li_desc *)tlv;
u16 event_type = be16_to_cpu(li_desc->event_type);
u64 wwpn;
rport = fc_find_rport_by_wwpn(shost,
be64_to_cpu(li_desc->attached_wwpn));
@ -764,22 +793,11 @@ fc_fpin_li_stats_update(struct Scsi_Host *shost, struct fc_tlv_desc *tlv)
fc_li_stats_update(event_type, &attach_rport->fpin_stats);
}
if (be32_to_cpu(li_desc->pname_count) > 0) {
for (i = 0;
i < be32_to_cpu(li_desc->pname_count);
i++) {
wwpn = be64_to_cpu(li_desc->pname_list[i]);
rport = fc_find_rport_by_wwpn(shost, wwpn);
if (rport &&
(rport->roles & FC_PORT_ROLE_FCP_TARGET ||
rport->roles & FC_PORT_ROLE_NVME_TARGET)) {
if (rport == attach_rport)
continue;
fc_li_stats_update(event_type,
&rport->fpin_stats);
}
}
}
fc_fpin_pname_stats_update(shost, attach_rport, event_type,
be32_to_cpu(li_desc->desc_len),
FC_TLV_DESC_LENGTH_FROM_SZ(*li_desc),
be32_to_cpu(li_desc->pname_count),
li_desc->pname_list, fc_li_stats_update);
if (fc_host->port_name == be64_to_cpu(li_desc->attached_wwpn))
fc_li_stats_update(event_type, &fc_host->fpin_stats);
@ -827,13 +845,11 @@ static void
fc_fpin_peer_congn_stats_update(struct Scsi_Host *shost,
struct fc_tlv_desc *tlv)
{
u8 i;
struct fc_rport *rport = NULL;
struct fc_rport *attach_rport = NULL;
struct fc_fn_peer_congn_desc *pc_desc =
(struct fc_fn_peer_congn_desc *)tlv;
u16 event_type = be16_to_cpu(pc_desc->event_type);
u64 wwpn;
rport = fc_find_rport_by_wwpn(shost,
be64_to_cpu(pc_desc->attached_wwpn));
@ -844,22 +860,11 @@ fc_fpin_peer_congn_stats_update(struct Scsi_Host *shost,
fc_cn_stats_update(event_type, &attach_rport->fpin_stats);
}
if (be32_to_cpu(pc_desc->pname_count) > 0) {
for (i = 0;
i < be32_to_cpu(pc_desc->pname_count);
i++) {
wwpn = be64_to_cpu(pc_desc->pname_list[i]);
rport = fc_find_rport_by_wwpn(shost, wwpn);
if (rport &&
(rport->roles & FC_PORT_ROLE_FCP_TARGET ||
rport->roles & FC_PORT_ROLE_NVME_TARGET)) {
if (rport == attach_rport)
continue;
fc_cn_stats_update(event_type,
&rport->fpin_stats);
}
}
}
fc_fpin_pname_stats_update(shost, attach_rport, event_type,
be32_to_cpu(pc_desc->desc_len),
FC_TLV_DESC_LENGTH_FROM_SZ(*pc_desc),
be32_to_cpu(pc_desc->pname_count),
pc_desc->pname_list, fc_cn_stats_update);
}
/*

View File

@ -2295,7 +2295,9 @@ iscsit_handle_text_cmd(struct iscsit_conn *conn, struct iscsit_cmd *cmd,
goto reject;
if (conn->conn_ops->DataDigest) {
data_crc = iscsit_crc_buf(text_in, rx_size, 0, NULL);
data_crc = iscsit_crc_buf(text_in,
ALIGN(payload_length, 4),
0, NULL);
if (checksum != data_crc) {
pr_err("Text data CRC32C DataDigest"
" 0x%08x does not match computed"
@ -2314,6 +2316,7 @@ iscsit_handle_text_cmd(struct iscsit_conn *conn, struct iscsit_cmd *cmd,
" Command CmdSN: 0x%08x due to"
" DataCRC error.\n", hdr->cmdsn);
kfree(text_in);
cmd->text_in_ptr = NULL;
return 0;
}
} else {

View File

@ -340,13 +340,22 @@ static int chap_server_compute_hash(
goto out;
}
break;
case BASE64:
case BASE64: {
size_t r_len = strlen(chap_r);
while (r_len > 0 && chap_r[r_len - 1] == '=')
r_len--;
if (r_len > DIV_ROUND_UP(chap->digest_size * 4, 3)) {
pr_err("Malformed CHAP_R: base64 payload too long\n");
goto out;
}
if (chap_base64_decode(client_digest, chap_r, strlen(chap_r)) !=
chap->digest_size) {
pr_err("Malformed CHAP_R: invalid BASE64\n");
goto out;
}
break;
}
default:
pr_err("Could not find CHAP_R\n");
goto out;
@ -473,6 +482,14 @@ static int chap_server_compute_hash(
}
break;
case BASE64:
/*
* No overflow check needed: initiatorchg_binhex is
* CHAP_CHALLENGE_STR_LEN bytes and extract_param() caps
* initiatorchg at CHAP_CHALLENGE_STR_LEN characters, so
* the decoded output is at most DIV_ROUND_UP(
* (CHAP_CHALLENGE_STR_LEN - 1) * 3, 4) bytes, which is
* less than CHAP_CHALLENGE_STR_LEN.
*/
initiatorchg_len = chap_base64_decode(initiatorchg_binhex,
initiatorchg,
strlen(initiatorchg));

View File

@ -899,10 +899,14 @@ static int iscsi_target_handle_csg_zero(
SENDER_TARGET,
login->rsp_buf,
&login->rsp_length,
MAX_KEY_VALUE_PAIRS,
conn->param_list,
conn->tpg->tpg_attrib.login_keys_workaround);
if (ret < 0)
if (ret < 0) {
iscsit_tx_login_rsp(conn, ISCSI_STATUS_CLS_INITIATOR_ERR,
ISCSI_LOGIN_STATUS_INIT_ERR);
return -1;
}
if (!iscsi_check_negotiated_keys(conn->param_list)) {
bool auth_required = iscsi_conn_auth_required(conn);
@ -986,6 +990,7 @@ static int iscsi_target_handle_csg_one(struct iscsit_conn *conn, struct iscsi_lo
SENDER_TARGET,
login->rsp_buf,
&login->rsp_length,
MAX_KEY_VALUE_PAIRS,
conn->param_list,
conn->tpg->tpg_attrib.login_keys_workaround);
if (ret < 0) {

View File

@ -1371,19 +1371,42 @@ int iscsi_decode_text_input(
return -1;
}
/*
* Append "key=value" plus a trailing NUL into @textbuf at *@length.
* Returns 0 on success and advances *@length, or -EMSGSIZE if the
* record (including the NUL) would not fit in the remaining buffer.
*/
static int iscsi_encode_text_record(char *textbuf, u32 *length,
u32 textbuf_size,
const char *key, const char *value)
{
int n;
u32 avail;
if (*length >= textbuf_size)
return -EMSGSIZE;
avail = textbuf_size - *length;
n = snprintf(textbuf + *length, avail, "%s=%s", key, value);
if (n < 0 || (u32)n + 1 > avail)
return -EMSGSIZE;
*length += n + 1;
return 0;
}
int iscsi_encode_text_output(
u8 phase,
u8 sender,
char *textbuf,
u32 *length,
u32 textbuf_size,
struct iscsi_param_list *param_list,
bool keys_workaround)
{
char *output_buf = NULL;
struct iscsi_extra_response *er;
struct iscsi_param *param;
output_buf = textbuf + *length;
int ret;
if (iscsi_enforce_integrity_rules(phase, param_list) < 0)
return -1;
@ -1395,10 +1418,12 @@ int iscsi_encode_text_output(
!IS_PSTATE_RESPONSE_SENT(param) &&
!IS_PSTATE_REPLY_OPTIONAL(param) &&
(param->phase & phase)) {
*length += sprintf(output_buf, "%s=%s",
param->name, param->value);
*length += 1;
output_buf = textbuf + *length;
ret = iscsi_encode_text_record(textbuf, length,
textbuf_size,
param->name,
param->value);
if (ret < 0)
goto err_overflow;
SET_PSTATE_RESPONSE_SENT(param);
pr_debug("Sending key: %s=%s\n",
param->name, param->value);
@ -1408,10 +1433,12 @@ int iscsi_encode_text_output(
!IS_PSTATE_ACCEPTOR(param) &&
!IS_PSTATE_PROPOSER(param) &&
(param->phase & phase)) {
*length += sprintf(output_buf, "%s=%s",
param->name, param->value);
*length += 1;
output_buf = textbuf + *length;
ret = iscsi_encode_text_record(textbuf, length,
textbuf_size,
param->name,
param->value);
if (ret < 0)
goto err_overflow;
SET_PSTATE_PROPOSER(param);
iscsi_check_proposer_for_optional_reply(param,
keys_workaround);
@ -1421,14 +1448,21 @@ int iscsi_encode_text_output(
}
list_for_each_entry(er, &param_list->extra_response_list, er_list) {
*length += sprintf(output_buf, "%s=%s", er->key, er->value);
*length += 1;
output_buf = textbuf + *length;
ret = iscsi_encode_text_record(textbuf, length, textbuf_size,
er->key, er->value);
if (ret < 0)
goto err_overflow;
pr_debug("Sending key: %s=%s\n", er->key, er->value);
}
iscsi_release_extra_responses(param_list);
return 0;
err_overflow:
pr_err("iSCSI login response buffer (%u bytes) exhausted, dropping login.\n",
textbuf_size);
iscsi_release_extra_responses(param_list);
return -1;
}
int iscsi_check_negotiated_keys(struct iscsi_param_list *param_list)

View File

@ -43,7 +43,7 @@ extern struct iscsi_param *iscsi_find_param_from_key(char *, struct iscsi_param_
extern int iscsi_extract_key_value(char *, char **, char **);
extern int iscsi_update_param_value(struct iscsi_param *, char *);
extern int iscsi_decode_text_input(u8, u8, char *, u32, struct iscsit_conn *);
extern int iscsi_encode_text_output(u8, u8, char *, u32 *,
extern int iscsi_encode_text_output(u8, u8, char *, u32 *, u32,
struct iscsi_param_list *, bool);
extern int iscsi_check_negotiated_keys(struct iscsi_param_list *);
extern void iscsi_set_connection_parameters(struct iscsi_conn_ops *,