Linux SCSI subsystem development
 help / color / mirror / Atom feed
* [PATCH] scsi: qedi: Initialize callback state before registration
@ 2026-09-10  9:24 Runyu Xiao
  2026-09-10  9:52 ` sashiko-bot
  0 siblings, 1 reply; 4+ messages in thread
From: Runyu Xiao @ 2026-09-10  9:24 UTC (permalink / raw)
  To: Nilesh Javali, Manish Rangankar, GR-QLogic-Storage-Upstream,
	James E . J . Bottomley, Martin K . Petersen
  Cc: linux-scsi, linux-kernel, Jianhao Xu, Runyu Xiao, stable

qedi_get_protocol_tlv_data() can be called asynchronously by QED after
qedi_ops->register_ops(). It takes stats_lock and reads ll2_mtu, but
__qedi_probe() currently registers the callback before initializing the
mutex and assigning the default MTU on the normal probe path.

Initialize the callback-visible state before registering qedi_cb_ops. Keep
the recovery path from reinitializing state because it reuses the existing
qedi context.

Fixes: 3cc5746e5ad7 ("scsi: qedi: Initialize the stats mutex lock")
Cc: stable@vger.kernel.org

Assisted-by: LLM Codex
Signed-off-by: Runyu Xiao <runyu.xiao@seu.edu.cn>
---
 drivers/scsi/qedi/qedi_main.c | 3 +--
 1 file changed, 1 insertion(+), 2 deletions(-)

diff --git a/drivers/scsi/qedi/qedi_main.c b/drivers/scsi/qedi/qedi_main.c
index 227ff7bd1..f394fcbb9 100644
--- a/drivers/scsi/qedi/qedi_main.c
+++ b/drivers/scsi/qedi/qedi_main.c
@@ -2652,8 +2652,6 @@ static int __qedi_probe(struct pci_dev *pdev, int mode)
 	snprintf(host_buf, sizeof(host_buf), "host_%d", qedi->shost->host_no);
 	qedi_ops->common->set_name(qedi->cdev, host_buf);
 
-	qedi_ops->register_ops(qedi->cdev, &qedi_cb_ops, qedi);
-
 	memset(&params, 0, sizeof(params));
 	params.mtu = DEF_PATH_MTU + IPV6_HDR_LEN + TCP_HDR_LEN;
 	qedi->ll2_mtu = DEF_PATH_MTU;
@@ -2670,6 +2668,7 @@ static int __qedi_probe(struct pci_dev *pdev, int mode)
 		spin_lock_init(&qedi->task_idx_lock);
 		mutex_init(&qedi->stats_lock);
 	}
+	qedi_ops->register_ops(qedi->cdev, &qedi_cb_ops, qedi);
 	qedi_ops->ll2->register_cb_ops(qedi->cdev, &qedi_ll2_cb_ops, qedi);
 	qedi_ops->ll2->start(qedi->cdev, &params);
 
-- 
2.34.1


^ permalink raw reply related	[flat|nested] 4+ messages in thread

* Re: [PATCH] scsi: qedi: Initialize callback state before registration
  2026-09-10  9:24 [PATCH] scsi: qedi: Initialize callback state before registration Runyu Xiao
@ 2026-09-10  9:52 ` sashiko-bot
  0 siblings, 0 replies; 4+ messages in thread
From: sashiko-bot @ 2026-09-10  9:52 UTC (permalink / raw)
  To: Runyu Xiao; +Cc: linux-scsi

Thank you for your contribution! Sashiko AI review found 7 potential issue(s) to consider:

Pre-existing issues:
- [Critical] Network packet reception in `qedi_ll2_rx` lacks bounds checks, allowing malformed packets to cause out-of-bounds reads or NULL pointer dereferences.
- [High] Race condition and missing error checks for `kthread_run()` return value lead to invalid pointer dereferences during LL2 Rx processing or module teardown.
- [High] The patch leaves `recovery_work` and `board_disable_work` uninitialized when registering `qedi_cb_ops` callbacks, exposing uninitialized work structures to asynchronous hardware events.
- [High] User-configured custom MTU settings are silently overwritten to defaults during hardware recovery.
- [High] `iscsi_host_add` exposes the device to userspace before crucial connection structures (`ep_tbl`) are allocated, creating a race condition.
- [High] The background LL2 receiver kthread and hardware subsystem are not stopped on the `__qedi_probe` error path, leading to Use-After-Free.
- [High] The error path for `iscsi_host_add` failure calls `iscsi_host_remove`, which will crash when attempting to remove an un-added host.
--

commit 7badf30e1ff5d4629509a7cbca53b6e22f3d67df
Author: Runyu Xiao <runyu.xiao@seu.edu.cn>

scsi: qedi: Initialize callback state before registration

This patch fixes a race condition where qedi_get_protocol_tlv_data() could
be called asynchronously by QED before the stats_lock and ll2_mtu were
initialized. It moves the callback registration to after these initializations
on the normal probe path.

> diff --git a/drivers/scsi/qedi/qedi_main.c b/drivers/scsi/qedi/qedi_main.c
> index 227ff7bd1bdc9..f394fcbb97e7e 100644
> --- a/drivers/scsi/qedi/qedi_main.c
> +++ b/drivers/scsi/qedi/qedi_main.c
> @@ -2652,8 +2652,6 @@ static int __qedi_probe(struct pci_dev *pdev, int mode)
>  	snprintf(host_buf, sizeof(host_buf), "host_%d", qedi->shost->host_no);
>  	qedi_ops->common->set_name(qedi->cdev, host_buf);
>  
> -	qedi_ops->register_ops(qedi->cdev, &qedi_cb_ops, qedi);
> -
>  	memset(&params, 0, sizeof(params));
>  	params.mtu = DEF_PATH_MTU + IPV6_HDR_LEN + TCP_HDR_LEN;
>  	qedi->ll2_mtu = DEF_PATH_MTU;

[Severity: High]
This is a pre-existing issue, but are user-configured custom MTU settings
overwritten during hardware recovery here?

During a hardware recovery event, __qedi_probe() is invoked with
mode == QEDI_MODE_RECOVERY. This unconditional assignment to DEF_PATH_MTU
discards any custom MTU size (such as a 9000-byte jumbo MTU) that the user
may have configured.

> @@ -2670,6 +2668,7 @@ static int __qedi_probe(struct pci_dev *pdev, int mode)
>  		spin_lock_init(&qedi->task_idx_lock);
>  		mutex_init(&qedi->stats_lock);
>  	}
> +	qedi_ops->register_ops(qedi->cdev, &qedi_cb_ops, qedi);

[Severity: High]
This isn't a bug introduced by this patch, but could registering the callbacks
here expose uninitialized work structures?

The qedi_cb_ops expose callback functions like
qedi_schedule_recovery_handler() to the QED core. This handler attempts to
schedule qedi->recovery_work, but INIT_DELAYED_WORK(&qedi->recovery_work, ...)
is not called until much later in the probe sequence.

If an asynchronous hardware event occurs in this window, it looks like
schedule_delayed_work() could be invoked on zero-filled memory.

>  	qedi_ops->ll2->register_cb_ops(qedi->cdev, &qedi_ll2_cb_ops, qedi);
>  	qedi_ops->ll2->start(qedi->cdev, &params);

[Severity: High]
This is a pre-existing issue, but does starting the hardware packet reception
here introduce a race condition?

If a packet arrives immediately, qedi_ll2_rx() calls
wake_up_process(qedi->ll2_recv_thread) while the thread pointer is still NULL.

Additionally, is it safe to ignore the return value of kthread_run() when it
creates qedi->ll2_recv_thread shortly after this?

If kthread_run() fails, it returns an ERR_PTR which is neither checked nor
handled. This could cause subsequent packets or kthread_stop() during teardown
to dereference the ERR_PTR.


[Severity: High]
This is a pre-existing issue, but does exposing the device to userspace before
crucial connection structures are allocated create a race condition?

In drivers/scsi/qedi/qedi_main.c:__qedi_probe():

    if (iscsi_host_add(qedi->shost, &pdev->dev)) {
        ...
    }

    rc = qedi_alloc_uio_rings(qedi);
    ...
    rc = qedi_cm_alloc_mem(qedi);

iscsi_host_add() registers the host with sysfs, emitting a uevent. If
userspace responds by issuing a netlink command to create an endpoint,
qedi_ep_connect() assigns the endpoint to qedi->ep_tbl[iscsi_cid].

However, qedi_cm_alloc_mem() allocates qedi->ep_tbl after iscsi_host_add(),
so qedi->ep_tbl would be NULL during this race.


[Severity: High]
This is a pre-existing issue, but is it possible the error path in
__qedi_probe() fails to clean up the LL2 subsystem and kthread?

If an initialization step fails after the hardware packet reception and
receiver kthread are started:

drivers/scsi/qedi/qedi_main.c:__qedi_probe():

    ...
stop_iscsi_func:
    qedi_ops->stop(qedi->cdev);
stop_slowpath:
    qedi_ops->common->slowpath_stop(qedi->cdev);
    ...

The error handler frees the qedi structure but seems to completely omit
stopping the hardware LL2 interface or killing the kthread, which could lead
to a use-after-free when they access the freed qedi structure.


[Severity: High]
This is a pre-existing issue, but would the error path for iscsi_host_add()
failure cause a crash?

drivers/scsi/qedi/qedi_main.c:__qedi_probe():

    if (iscsi_host_add(qedi->shost, &pdev->dev)) {
        QEDI_ERR(&qedi->dbg_ctx,
                 "Could not add iscsi host\n");
        rc = -ENOMEM;
        goto remove_host;
    }

If iscsi_host_add() fails, the code jumps to remove_host and unconditionally
executes iscsi_host_remove(). Since the host was never successfully added to
the driver core, won't this attempt to unregister an uninitialized device?


[Severity: Critical]
This is a pre-existing issue, but is there a missing bounds check during
network packet reception in qedi_ll2_rx()?

drivers/scsi/qedi/qedi_main.c:qedi_ll2_rx():

    if (eh->h_proto == htons(ETH_P_8021Q)) {
        memmove((u8 *)eh + VLAN_HLEN, eh, ETH_ALEN * 2);
        eh = (struct ethhdr *)skb_pull(skb, VLAN_HLEN);
        skb_reset_mac_header(skb);
    }

    /* Filter out non FIP/FCoE frames here to free them faster */
    if (eh->h_proto != htons(ETH_P_ARP) && ...

The code reads eh->h_proto without verifying skb->len >= ETH_HLEN. If the
protocol is 802.1Q (VLAN), it invokes skb_pull(skb, VLAN_HLEN).

If a malformed packet is smaller than VLAN_HLEN, skb_pull() returns NULL,
which is then unconditionally dereferenced in the subsequent check.

-- 
Sashiko AI review · https://sashiko.dev/#/patchset/20260910092405.1300129-1-runyu.xiao@seu.edu.cn?part=1

^ permalink raw reply	[flat|nested] 4+ messages in thread

* [PATCH] scsi: qedi: Initialize callback state before registration
@ 2026-09-10 11:29 Runyu Xiao
  2026-09-10 11:46 ` sashiko-bot
  0 siblings, 1 reply; 4+ messages in thread
From: Runyu Xiao @ 2026-09-10 11:29 UTC (permalink / raw)
  To: Nilesh Javali, Manish Rangankar, GR-QLogic-Storage-Upstream,
	James E . J . Bottomley, Martin K . Petersen
  Cc: linux-scsi, linux-kernel, Jianhao Xu, Runyu Xiao, stable

qedi_get_protocol_tlv_data() can be called asynchronously by QED after
qedi_ops->register_ops(). It takes stats_lock and reads ll2_mtu, but
__qedi_probe() currently registers the callback before initializing the
mutex and assigning the default MTU on the normal probe path.

Initialize the callback-visible state before registering qedi_cb_ops. Keep
the recovery path from reinitializing state because it reuses the existing
qedi context.

Fixes: 3cc5746e5ad7 ("scsi: qedi: Initialize the stats mutex lock")
Cc: stable@vger.kernel.org

Assisted-by: LLM Codex
Signed-off-by: Runyu Xiao <runyu.xiao@seu.edu.cn>
---
 drivers/scsi/qedi/qedi_main.c | 3 +--
 1 file changed, 1 insertion(+), 2 deletions(-)

diff --git a/drivers/scsi/qedi/qedi_main.c b/drivers/scsi/qedi/qedi_main.c
index 227ff7bd1..f394fcbb9 100644
--- a/drivers/scsi/qedi/qedi_main.c
+++ b/drivers/scsi/qedi/qedi_main.c
@@ -2652,8 +2652,6 @@ static int __qedi_probe(struct pci_dev *pdev, int mode)
 	snprintf(host_buf, sizeof(host_buf), "host_%d", qedi->shost->host_no);
 	qedi_ops->common->set_name(qedi->cdev, host_buf);
 
-	qedi_ops->register_ops(qedi->cdev, &qedi_cb_ops, qedi);
-
 	memset(&params, 0, sizeof(params));
 	params.mtu = DEF_PATH_MTU + IPV6_HDR_LEN + TCP_HDR_LEN;
 	qedi->ll2_mtu = DEF_PATH_MTU;
@@ -2670,6 +2668,7 @@ static int __qedi_probe(struct pci_dev *pdev, int mode)
 		spin_lock_init(&qedi->task_idx_lock);
 		mutex_init(&qedi->stats_lock);
 	}
+	qedi_ops->register_ops(qedi->cdev, &qedi_cb_ops, qedi);
 	qedi_ops->ll2->register_cb_ops(qedi->cdev, &qedi_ll2_cb_ops, qedi);
 	qedi_ops->ll2->start(qedi->cdev, &params);
 
-- 
2.34.1


^ permalink raw reply related	[flat|nested] 4+ messages in thread

* Re: [PATCH] scsi: qedi: Initialize callback state before registration
  2026-09-10 11:29 Runyu Xiao
@ 2026-09-10 11:46 ` sashiko-bot
  0 siblings, 0 replies; 4+ messages in thread
From: sashiko-bot @ 2026-09-10 11:46 UTC (permalink / raw)
  To: Runyu Xiao; +Cc: linux-scsi

Thank you for your contribution! Sashiko AI review found 8 potential issue(s) to consider:

Pre-existing issues:
- [High] The return value of kthread_run() is not checked for errors, resulting in an ERR_PTR being stored and subsequently dereferenced.
- [High] The ll2_recv_thread is leaked on __qedi_probe error paths, causing a task leak and a subsequent use-after-free.
- [High] Missing error check for qedi_ops->ll2->start().
- [High] Memory leak of fastpath arrays and status blocks on __qedi_probe error paths.
- [High] Asynchronous callbacks registered via qedi_ops->register_ops() can execute and schedule uninitialized delayed works, leading to a kernel panic.
- [High] Calling scsi_remove_host() on a host that failed scsi_add_host(), leading to sysfs/device_del warnings or crashes.
- [High] The iSCSI host is exposed to userspace before critical workqueues are allocated, allowing userspace tools to trigger NULL pointer dereferences.
- [High] The driver reads the DMA-updated hardware producer index (sb->pi_array) without a dma_rmb() and without le16_to_cpu().
--

commit 0c41fa3eb18f5cff0e550ef57ea439ee89ae82a1
Author: Runyu Xiao <runyu.xiao@seu.edu.cn>

scsi: qedi: Initialize callback state before registration

qedi_get_protocol_tlv_data() can be called asynchronously by QED after
qedi_ops->register_ops(). It takes stats_lock and reads ll2_mtu, but
__qedi_probe() currently registers the callback before initializing the
mutex and assigning the default MTU on the normal probe path.

Initialize the callback-visible state before registering qedi_cb_ops. Keep
the recovery path from reinitializing state because it reuses the existing
qedi context.

Fixes: 3cc5746e5ad7 ("scsi: qedi: Initialize the stats mutex lock")
Cc: stable@vger.kernel.org

Assisted-by: LLM Codex
Signed-off-by: Runyu Xiao <runyu.xiao@seu.edu.cn>

> diff --git a/drivers/scsi/qedi/qedi_main.c b/drivers/scsi/qedi/qedi_main.c
> index 227ff7bd1bdc9..f394fcbb97e7e 100644
> --- a/drivers/scsi/qedi/qedi_main.c
> +++ b/drivers/scsi/qedi/qedi_main.c
> @@ -2652,8 +2652,6 @@ static int __qedi_probe(struct pci_dev *pdev, int mode)
>  	snprintf(host_buf, sizeof(host_buf), "host_%d", qedi->shost->host_no);
>  	qedi_ops->common->set_name(qedi->cdev, host_buf);
>  
> -	qedi_ops->register_ops(qedi->cdev, &qedi_cb_ops, qedi);
> -
>  	memset(&params, 0, sizeof(params));
>  	params.mtu = DEF_PATH_MTU + IPV6_HDR_LEN + TCP_HDR_LEN;
>  	qedi->ll2_mtu = DEF_PATH_MTU;
> @@ -2670,6 +2668,7 @@ static int __qedi_probe(struct pci_dev *pdev, int mode)
>  		spin_lock_init(&qedi->task_idx_lock);
>  		mutex_init(&qedi->stats_lock);
>  	}
> +	qedi_ops->register_ops(qedi->cdev, &qedi_cb_ops, qedi);

[Severity: High]
This is a pre-existing issue, but registering the callbacks here makes them
live before delayed works are initialized.

If an asynchronous hardware event (like a link state change or fan failure)
triggers early, it could call qedi_schedule_hw_err_handler(), which attempts
to schedule the uninitialized qedi->board_disable_work.

The delayed works are initialized much later in __qedi_probe():

	INIT_DELAYED_WORK(&qedi->recovery_work, qedi_recovery_handler);
	INIT_DELAYED_WORK(&qedi->board_disable_work, qedi_board_disable_work);

Could this early registration lead to a kernel panic if a callback fires and
schedules uninitialized work structures?

>  	qedi_ops->ll2->register_cb_ops(qedi->cdev, &qedi_ll2_cb_ops, qedi);
>  	qedi_ops->ll2->start(qedi->cdev, &params);

[Severity: High]
This isn't a bug introduced by this patch, but does this code ignore the
return value from qedi_ops->ll2->start()?

Could ignoring an initialization failure here lead to unpredictable behavior
later?

[Severity: High]
This is a pre-existing issue, but right after this in __qedi_probe(), the
kthread is created:

	qedi->ll2_recv_thread = kthread_run(qedi_ll2_recv_thread, (void *)qedi,
					    "qedi_ll2_thread");

If kthread_run() fails due to memory pressure, it returns an ERR_PTR.
Does this code assign the ERR_PTR without using IS_ERR() to check for
failure?

If so, later in teardown or RX paths, if (qedi->ll2_recv_thread) will
evaluate to true, and passing the ERR_PTR to kthread_stop() in
__qedi_remove() or wake_up_process() in qedi_ll2_rx() could cause a panic.

[Severity: High]
This is a pre-existing issue, but if __qedi_probe() fails after successfully
creating qedi->ll2_recv_thread, it jumps to error paths:

	rc = qedi_ops->start(qedi->cdev, &qedi->tasks, qedi,
			     qedi_iscsi_event_cb);
	if (rc) {
		...
		goto stop_slowpath;
	}

The error path frees the qedi struct via iscsi_host_free() without stopping
the background thread. Does this leak the thread and result in a
use-after-free when the orphaned thread continues to access qedi?

[Severity: High]
This is a pre-existing issue, but when iscsi_host_add() fails in
__qedi_probe(), the code jumps to remove_host:

	if (iscsi_host_add(qedi->shost, &pdev->dev)) {
		rc = -ENOMEM;
		goto remove_host;
	}

The remove_host label unconditionally calls iscsi_host_remove(), which wraps
scsi_remove_host(). Can calling scsi_remove_host() on a host that failed
scsi_add_host() lead to sysfs or kobject core warnings and crashes?

[Severity: High]
This is a pre-existing issue, but iscsi_host_add() is called and exposes the
device to userspace before critical workqueues are allocated:

	if (iscsi_host_add(qedi->shost, &pdev->dev)) {
		...

And later:

	qedi->offload_thread = alloc_workqueue("qedi_ofld%d", ...);

Could userspace immediately respond via netlink to create an endpoint,
invoking qedi_ep_connect() which calls queue_work(qedi->offload_thread, ...),
resulting in a NULL pointer dereference because the workqueue isn't allocated
yet?

[Severity: High]
This isn't a bug introduced by this patch, but memory and DMA blocks are
allocated in qedi_prepare_fp() earlier in __qedi_probe():

	rc = qedi_prepare_fp(qedi);
	if (rc) { ... }

If a subsequent initialization step fails, the error path does not call
qedi_destroy_fp(qedi). Does this code leak the fastpath arrays and status
blocks on error paths?

[Severity: High]
This is a pre-existing issue, but the driver reads the DMA-updated hardware
producer index without a memory barrier or endianness conversion:

drivers/scsi/qedi/qedi_main.c:qedi_process_completions() {
	...
	prod_idx = sb->pi_array[QEDI_PROTO_CQ_PROD_IDX];
	...
}

Does reading sb->pi_array (an array of __le16 in DMA memory) directly into a
native u16 without le16_to_cpu(), and without a subsequent dma_rmb() before
fetching ring entries, lead to reading stale data on relaxed memory models or
broken functionality on big-endian architectures?

-- 
Sashiko AI review · https://sashiko.dev/#/patchset/20260910112947.1350877-1-runyu.xiao@seu.edu.cn?part=1

^ permalink raw reply	[flat|nested] 4+ messages in thread

end of thread, other threads:[~2026-09-10 11:46 UTC | newest]

Thread overview: 4+ messages (download: mbox.gz follow: Atom feed
-- links below jump to the message on this page --
2026-09-10  9:24 [PATCH] scsi: qedi: Initialize callback state before registration Runyu Xiao
2026-09-10  9:52 ` sashiko-bot
  -- strict thread matches above, loose matches on Subject: below --
2026-09-10 11:29 Runyu Xiao
2026-09-10 11:46 ` sashiko-bot

This is a public inbox, see mirroring instructions
for how to clone and mirror all data and code used for this inbox