diff options
| author | Michael Bommarito <michael.bommarito@gmail.com> | 2026-05-20 09:30:15 -0400 |
|---|---|---|
| committer | Martin K. Petersen <martin.petersen@oracle.com> | 2026-05-22 21:54:47 -0400 |
| commit | a9a39233ec1fc9f97ea1340a4d09bb7ec2be5153 (patch) | |
| tree | 1dda34cdd834c43bd37206b34dc37781707d7228 | |
| parent | e4bb73bf3ac11b4a93634660345b9d764a4a80df (diff) | |
scsi: scsi_transport_fc: Widen FPIN pname walker counter to u32
An adjacent Fibre Channel fabric actor that can deliver an FPIN ELS
frame to an lpfc or qla2xxx Linux initiator can trigger a non-return in
the generic FC transport. This is not a local userspace or IP network
path; the attacker must be able to inject fabric traffic, for example as
a compromised switch or fabric controller, or as a same-zone N_Port on a
fabric that permits source spoofing.
The Link-Integrity and Peer-Congestion FPIN walkers used a u8 loop
counter against the 32-bit on-wire pname_count field, and did not bound
pname_count by the descriptor body already validated by the TLV walker.
A pname_count of 256 therefore wraps the counter and keeps the loop
condition true indefinitely.
Factor the shared pname_list[] walk into one helper, widen the counter
to u32, and clamp pname_count against the entries that fit in the
descriptor body before iterating.
Fixes: 3dcfe0de5a97 ("scsi: fc: Parse FPIN packets and update statistics")
Cc: stable@vger.kernel.org
Assisted-by: Claude:claude-opus-4-7
Signed-off-by: Michael Bommarito <michael.bommarito@gmail.com>
Reviewed-by: Christoph Hellwig <hch@lst.de>
Reviewed-by: John Garry <john.g.garry@oracle.com>
Link: https://patch.msgid.link/20260520133015.1018937-1-michael.bommarito@gmail.com
Signed-off-by: Martin K. Petersen <martin.petersen@oracle.com>
| -rw-r--r-- | drivers/scsi/scsi_transport_fc.c | 77 |
1 files changed, 41 insertions, 36 deletions
diff --git a/drivers/scsi/scsi_transport_fc.c b/drivers/scsi/scsi_transport_fc.c index dce95e361daf..173ed6373f04 100644 --- a/drivers/scsi/scsi_transport_fc.c +++ b/drivers/scsi/scsi_transport_fc.c @@ -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); } /* |
