Linux SCSI subsystem development
 help / color / mirror / Atom feed
From: sashiko-bot@kernel.org
To: "Nilesh Javali" <njavali@marvell.com>
Cc: linux-scsi@vger.kernel.org
Subject: Re: [PATCH v2 03/13] Revert "scsi: qla2xxx: Validate BSG request_len before reading vendor_cmd[]"
Date: Fri, 23 Oct 2026 10:33:08 +0000	[thread overview]
Message-ID: <sashiko-outbox-182690@kernel.org> (raw)
In-Reply-To: <20261023101600.1431179-4-njavali@marvell.com>

Thank you for your contribution! Sashiko AI review found 2 potential issue(s) to consider:
- [High] scsi: qla2xxx: unchecked vendor_cmd[0] read in qla24xx_bsg_request()
- [High] scsi: qla2xxx: out-of-bounds vendor_cmd[] reads on short BSG requests
--

commit 6dca3d90086c395d6e304de559abf4c8c863ad1f
Author: Nilesh Javali <njavali@marvell.com>

Revert "scsi: qla2xxx: Validate BSG request_len before reading vendor_cmd[]"

This commit reverts commit 4cf38dd9465736141263ebb63375868311a0ec81 to
restore FC_BSG_HST_VENDOR handling for the Marvell qaucli management utility,
which passes a request_len smaller than sizeof(struct fc_bsg_request) plus
the vendor_cmd words checked by that commit.

> diff --git a/drivers/scsi/qla2xxx/qla_bsg.c b/drivers/scsi/qla2xxx/qla_bsg.c
> index ab559048dbb82..19a514a153aef 100644
> --- a/drivers/scsi/qla2xxx/qla_bsg.c
> +++ b/drivers/scsi/qla2xxx/qla_bsg.c
> @@ -160,12 +160,6 @@ qla24xx_proc_fcp_prio_cfg_cmd(struct bsg_job *bsg_job)
>  		goto exit_fcp_prio_cfg;
>  	}
>  
> -	if (bsg_job->request_len <
> -	    sizeof(struct fc_bsg_request) + 2 * sizeof(uint32_t)) {
> -		ret = -EINVAL;
> -		goto exit_fcp_prio_cfg;
> -	}
> -
>  	/* Get the sub command */
>  	oper = bsg_request->rqst_data.h_vendor.vendor_cmd[1];
>  
> @@ -764,10 +758,6 @@ qla2x00_process_loopback(struct bsg_job *bsg_job)
>  		return -EIO;
>  	}
>  
> -	if (bsg_job->request_len <
> -	    sizeof(struct fc_bsg_request) + 3 * sizeof(uint32_t))
> -		return -EINVAL;
> -
>  	memset(&elreq, 0, sizeof(elreq));
>  
>  	elreq.req_sg_cnt = dma_map_sg(&ha->pdev->dev,
> @@ -1000,10 +990,6 @@ qla84xx_reset(struct bsg_job *bsg_job)
>  		return -EINVAL;
>  	}
>  
> -	if (bsg_job->request_len <
> -	    sizeof(struct fc_bsg_request) + 2 * sizeof(uint32_t))
> -		return -EINVAL;
> -
>  	flag = bsg_request->rqst_data.h_vendor.vendor_cmd[1];
>  
>  	rval = qla84xx_reset_chip(vha, flag == A84_ISSUE_RESET_DIAG_FW);
> @@ -1048,10 +1034,6 @@ qla84xx_updatefw(struct bsg_job *bsg_job)
>  		return -EINVAL;
>  	}
>  
> -	if (bsg_job->request_len <
> -	    sizeof(struct fc_bsg_request) + 2 * sizeof(uint32_t))
> -		return -EINVAL;
> -
>  	sg_cnt = dma_map_sg(&ha->pdev->dev, bsg_job->request_payload.sg_list,
>  		bsg_job->request_payload.sg_cnt, DMA_TO_DEVICE);
>  	if (!sg_cnt) {
> @@ -1529,15 +1511,9 @@ qla2x00_read_optrom(struct bsg_job *bsg_job)
>  	struct Scsi_Host *host = fc_bsg_to_shost(bsg_job);
>  	scsi_qla_host_t *vha = shost_priv(host);
>  	struct qla_hw_data *ha = vha->hw;
> -	uint32_t start;
> +	uint32_t start = bsg_request->rqst_data.h_vendor.vendor_cmd[1];
>  	int rval = 0;
>  
> -	if (bsg_job->request_len <
> -	    sizeof(struct fc_bsg_request) + 2 * sizeof(uint32_t))
> -		return -EINVAL;
> -
> -	start = bsg_request->rqst_data.h_vendor.vendor_cmd[1];
> -
>  	if (ha->flags.nic_core_reset_hdlr_active)
>  		return -EBUSY;
>  
> @@ -1580,15 +1556,9 @@ qla2x00_update_optrom(struct bsg_job *bsg_job)
>  	struct Scsi_Host *host = fc_bsg_to_shost(bsg_job);
>  	scsi_qla_host_t *vha = shost_priv(host);
>  	struct qla_hw_data *ha = vha->hw;
> -	uint32_t start;
> +	uint32_t start = bsg_request->rqst_data.h_vendor.vendor_cmd[1];
>  	int rval = 0;
>  
> -	if (bsg_job->request_len <
> -	    sizeof(struct fc_bsg_request) + 2 * sizeof(uint32_t))
> -		return -EINVAL;
> -
> -	start = bsg_request->rqst_data.h_vendor.vendor_cmd[1];
> -
>  	mutex_lock(&ha->optrom_mutex);
>  	rval = qla2x00_optrom_setup(bsg_job, vha, start, 1);
>  	if (rval) {
> @@ -2453,11 +2423,6 @@ qlafx00_mgmt_cmd(struct bsg_job *bsg_job)
>  	struct fc_port *fcport;
>  	char  *type = "FC_BSG_HST_FX_MGMT";
>  
> -	if (bsg_job->request_len <
> -	    sizeof(struct fc_bsg_request) + sizeof(uint32_t) +
> -	    sizeof(struct qla_mt_iocb_rqst_fx00))
> -		return -EINVAL;
> -
>  	/* Copy the IOCB specific information */
>  	piocb_rqst = (struct qla_mt_iocb_rqst_fx00 *)
>  	    &bsg_request->rqst_data.h_vendor.vendor_cmd[1];
> @@ -3379,13 +3344,6 @@ qla2x00_process_vendor_specific(struct scsi_qla_host *vha, struct bsg_job *bsg_j
>  {
>  	struct fc_bsg_request *bsg_request = bsg_job->request;
>  
> -	if (bsg_job->request_len <
> -	    sizeof(struct fc_bsg_request) + sizeof(uint32_t)) {
> -		ql_log(ql_log_warn, vha, 0x7000,
> -		       "BSG request too small for vendor cmd.\n");
> -		return -EINVAL;
> -	}
> -
>  	ql_dbg(ql_dbg_edif, vha, 0x911b, "%s FC_BSG_HST_VENDOR cmd[0]=0x%x\n",
>  	    __func__, bsg_request->rqst_data.h_vendor.vendor_cmd[0]);

[Severity: High]
Does removing all of the request_len checks reintroduce out-of-bounds heap
reads of bsg_request->rqst_data.h_vendor.vendor_cmd[] when userspace submits
a short FC_BSG_HST_VENDOR request?

In bsg_transport_sg_io_fn(), job->request is allocated with the exact
user-supplied hdr->request_len:

block/bsg-lib.c:bsg_transport_sg_io_fn() {
    ...
    job->request_len = hdr->request_len;
    job->request = memdup_user(uptr64(hdr->request), hdr->request_len);
    ...
}

And fc_bsg_host_dispatch() only verifies that job->request_len is at least
sizeof(uint32_t) (4 bytes) before calling into qla24xx_bsg_request() ->
qla2x00_process_vendor_specific().

In struct fc_bsg_request, rqst_data is a union whose total size is 16 bytes
(sized by struct fc_bsg_host_ct), making sizeof(struct fc_bsg_request) 20
bytes. However, struct fc_bsg_host_vendor only has an 8-byte vendor_id before
the flexible array vendor_cmd[]:

include/uapi/scsi/scsi_bsg_fc.h:
    struct fc_bsg_host_vendor {
        __u64 vendor_id;
        __u32 vendor_cmd[];
    };

So offsetof(struct fc_bsg_request, rqst_data.h_vendor.vendor_cmd) is 12
bytes, meaning vendor_cmd[0] is at bytes 12..15 and vendor_cmd[1] is at bytes
16..19 (both within a 20-byte struct fc_bsg_request). Commit 4cf38dd94657
rejected 20-byte qaucli requests because it checked against
sizeof(struct fc_bsg_request) + N * sizeof(uint32_t) (20 + 4*N) rather than
offsetof(struct fc_bsg_request, rqst_data.h_vendor.vendor_cmd) +
N * sizeof(uint32_t) (12 + 4*N).

Without bounds checks anchored at offsetof(struct fc_bsg_request,
rqst_data.h_vendor.vendor_cmd), short requests read past the end of
bsg_job->request in:
- qla2x00_process_vendor_specific() reading vendor_cmd[0]
- qla24xx_proc_fcp_prio_cfg_cmd(), qla84xx_reset(), qla84xx_updatefw(),
  qla2x00_read_optrom(), qla2x00_update_optrom(), and
  qla28xx_validate_flash_image() -> qla28xx_do_validate_flash_image()
  reading vendor_cmd[1]
- qla2x00_process_loopback() reading vendor_cmd[1] and vendor_cmd[2]
- qlafx00_mgmt_cmd() overlaying a 36-byte struct qla_mt_iocb_rqst_fx00 at
  &vendor_cmd[1]

> @@ -3529,11 +3487,8 @@ qla24xx_bsg_request(struct bsg_job *bsg_job)
>  	}
>  
>  	/* Disable port will bring down the chip, allow enable command */
> -	if (bsg_request->msgcode == FC_BSG_HST_VENDOR &&
> -	    bsg_job->request_len >=
> -		sizeof(struct fc_bsg_request) + sizeof(uint32_t) &&
> -	    (bsg_request->rqst_data.h_vendor.vendor_cmd[0] == QL_VND_MANAGE_HOST_PORT ||
> -	     bsg_request->rqst_data.h_vendor.vendor_cmd[0] == QL_VND_GET_HOST_STATS))
> +	if (bsg_request->rqst_data.h_vendor.vendor_cmd[0] == QL_VND_MANAGE_HOST_PORT ||
> +	    bsg_request->rqst_data.h_vendor.vendor_cmd[0] == QL_VND_GET_HOST_STATS)
>  		goto skip_chip_chk;

[Severity: High]
In qla24xx_bsg_request(), does dropping the bsg_request->msgcode ==
FC_BSG_HST_VENDOR check and length check cause bsg_request->rqst_data.h_vendor.vendor_cmd[0]
to be read unconditionally on every BSG request, including non-vendor
requests?

In struct fc_bsg_request, rqst_data is a union:

include/uapi/scsi/scsi_bsg_fc.h:
    struct fc_bsg_request {
        __u32 msgcode;
        union {
            struct fc_bsg_host_add_rport    h_addrport;
            struct fc_bsg_host_del_rport    h_delrport;
            struct fc_bsg_host_els          h_els;
            struct fc_bsg_host_ct           h_ct;
            struct fc_bsg_host_vendor       h_vendor;

            struct fc_bsg_rport_els         r_els;
            struct fc_bsg_rport_ct          r_ct;
        } rqst_data;
    } __attribute__((packed));

Because rqst_data.h_vendor.vendor_cmd[0] is at byte offset 12..15:

1. For legitimate non-vendor BSG requests smaller than 16 bytes, such as
   FC_BSG_RPT_ELS (where msgcode + struct fc_bsg_rport_els is 5 bytes) or
   FC_BSG_HST_ELS_NOLOGIN (where msgcode + struct fc_bsg_host_els is 8 bytes),
   reading vendor_cmd[0] reads past the end of the bsg_job->request buffer
   allocated by memdup_user().

2. For FC_BSG_HST_CT and FC_BSG_RPT_CT requests, bytes 12..15 alias
   rqst_data.h_ct.preamble_word1 and rqst_data.r_ct.preamble_word2. If that
   field happens to equal QL_VND_MANAGE_HOST_PORT (0x26) or
   QL_VND_GET_HOST_STATS (0x24), qla24xx_bsg_request() jumps to skip_chip_chk
   and bypasses the port_isolated, qla2x00_chip_is_down(vha), and
   PFLG_DRIVER_REMOVING checks:

drivers/scsi/qla2xxx/qla_bsg.c:qla24xx_bsg_request() {
    ...
    if (bsg_request->rqst_data.h_vendor.vendor_cmd[0] == QL_VND_MANAGE_HOST_PORT ||
        bsg_request->rqst_data.h_vendor.vendor_cmd[0] == QL_VND_GET_HOST_STATS)
        goto skip_chip_chk;

    if (vha->hw->flags.port_isolated) {
        bsg_reply->result = DID_ERROR;
        /* operation not permitted */
        return -EPERM;
    }

    if (qla2x00_chip_is_down(vha)) {
        ...
        return -EBUSY;
    }

    if (test_bit(PFLG_DRIVER_REMOVING, &vha->pci_flags)) {
        SET_DID_STATUS(bsg_reply->result, DID_ERROR);
        return -EIO;
    }

skip_chip_chk:
    ...
}

Should qla24xx_bsg_request() retain the bsg_request->msgcode ==
FC_BSG_HST_VENDOR check along with a check that bsg_job->request_len >=
offsetof(struct fc_bsg_request, rqst_data.h_vendor.vendor_cmd) +
sizeof(uint32_t)?

-- 
Sashiko AI review · https://sashiko.dev/#/patchset/20261023101600.1431179-1-njavali@marvell.com?part=3

  reply	other threads:[~2026-10-23 10:33 UTC|newest]

Thread overview: 22+ messages / expand[flat|nested]  mbox.gz  Atom feed  top
2026-10-23 10:15 [PATCH v2 00/13] scsi: qla2xxx: Misc. Bug fixes Nilesh Javali
2026-10-23 10:15 ` [PATCH v2 01/13] scsi: qla2xxx: Update MAINTAINERS with new FC storage alias Nilesh Javali
2026-10-23 10:15 ` [PATCH v2 02/13] scsi: qla2xxx: Update few AENs for 29xx handling Nilesh Javali
2026-10-23 10:36   ` sashiko-bot
2026-10-23 10:15 ` [PATCH v2 03/13] Revert "scsi: qla2xxx: Validate BSG request_len before reading vendor_cmd[]" Nilesh Javali
2026-10-23 10:33   ` sashiko-bot [this message]
2026-10-23 10:15 ` [PATCH v2 04/13] scsi: qla2xxx: Wait for session deletion before full-login LIP Nilesh Javali
2026-10-23 10:35   ` sashiko-bot
2026-10-23 10:15 ` [PATCH v2 05/13] scsi: qla2xxx: Fix double addition of fcport to gnl.fcport list Nilesh Javali
2026-10-23 10:38   ` sashiko-bot
2026-10-23 10:15 ` [PATCH v2 06/13] scsi: qla2xxx: Fix scan state machine race and IRQ-unsafe sp_unmap Nilesh Javali
2026-10-23 10:34   ` sashiko-bot
2026-10-23 10:15 ` [PATCH v2 07/13] scsi: qla2xxx: Skip vport DPC processing during vport deletion Nilesh Javali
2026-10-23 10:15 ` [PATCH v2 08/13] scsi: qla2xxx: Add validation check for base vport index during delete Nilesh Javali
2026-10-23 10:15 ` [PATCH v2 09/13] scsi: qla2xxx: Validate vport before deletion Nilesh Javali
2026-10-23 10:15 ` [PATCH v2 10/13] scsi: qla2xxx: Fix use-after-free races during vport deletion Nilesh Javali
2026-10-23 10:53   ` sashiko-bot
2026-10-23 10:15 ` [PATCH v2 11/13] scsi: qla2xxx: Re-acquire vport_slock and refresh tvp during vport DPC Nilesh Javali
2026-10-23 10:53   ` sashiko-bot
2026-10-23 10:15 ` [PATCH v2 12/13] scsi: qla2xxx: Fix purex IOCB copy size on non-29xx adapters Nilesh Javali
2026-10-23 10:43   ` sashiko-bot
2026-10-23 10:16 ` [PATCH v2 13/13] scsi: qla2xxx: Update version to 12.00.00.2610b1 Nilesh Javali

Reply instructions:

You may reply publicly to this message via plain-text email
using any one of the following methods:

* Save the following mbox file, import it into your mail client,
  and reply-to-all from there: mbox

  Avoid top-posting and favor interleaved quoting:
  https://en.wikipedia.org/wiki/Posting_style#Interleaved_style

* Reply using the --to, --cc, and --in-reply-to
  switches of git-send-email(1):

  git send-email \
    --in-reply-to=sashiko-outbox-182690@kernel.org \
    --to=sashiko-bot@kernel.org \
    --cc=linux-scsi@vger.kernel.org \
    --cc=njavali@marvell.com \
    --cc=sashiko-reviews@lists.linux.dev \
    /path/to/YOUR_REPLY

  https://kernel.org/pub/software/scm/git/docs/git-send-email.html

* If your mail client supports setting the In-Reply-To header
  via mailto: links, try the mailto: link
Be sure your reply has a Subject: header at the top and a blank line before the message body.
This is a public inbox, see mirroring instructions
for how to clone and mirror all data and code used for this inbox