Re: [PATCH v6 17/27] scsi-multipath: revalidate paths upon device unblock

[email protected] Wed, 29 Jul 2026 14:54:23 +0000
Newsgroups org.kernel.vger.linux-scsi
Message-ID <[email protected]>
Thank you for your contribution! Sashiko AI review found 3 potential issue(s) to consider:

New issues:
- [High] Calling `scsi_mpath_revalidate_paths()` outside `sdev->state_mutex` without a NULL check leads to TOCTOU races with device removal or allocation, causing a NULL pointer dereference.
- [High] Direct callers of `scsi_internal_device_unblock_nowait` bypass path revalidation, which can leave queued bios indefinitely stalled.

Pre-existing issues:
- [High] The patch calls `mpath_revalidate_paths()`, which schedules `requeue_work` on a background workqueue, but the teardown path fails to cancel this work, leading to a Use-After-Free.
--

--- Patch [17]: [PATCH v6 17/27] scsi-multipath: revalidate paths upon device unblock ---
commit 6253cb1fc352f4ec6d0d42ac99c1827b35592ff4
Author: John Garry <[email protected]>

scsi-multipath: revalidate paths upon device unblock

When a scsi_device is unblocked, we need revalidate paths which means that
we clear paths and requeue any queued bios.

> diff --git a/drivers/scsi/scsi_lib.c b/drivers/scsi/scsi_lib.c
> index a1ec11050090c..6a2d7dbfbe8c3 100644
> --- a/drivers/scsi/scsi_lib.c
> +++ b/drivers/scsi/scsi_lib.c
> @@ -3092,6 +3092,8 @@ static int scsi_internal_device_unblock(struct scsi_device *sdev,
>  	mutex_lock(&sdev->state_mutex);
>  	ret = scsi_internal_device_unblock_nowait(sdev, new_state);
>  	mutex_unlock(&sdev->state_mutex);
> +	if ((ret == 0) && sdev->scsi_mpath_dev)
> +		scsi_mpath_revalidate_paths(sdev->scsi_mpath_dev);

[Severity: High]
Is it safe to access sdev->scsi_mpath_dev and call scsi_mpath_revalidate_paths()
outside of sdev->state_mutex?

It seems that concurrently, __scsi_remove_device() could nullify
scsi_mpath_dev->scsi_mpath_head. A similar race might exist during device
initialization before the head is populated.

When scsi_mpath_revalidate_paths() is then called, it blindly computes the
offset to &scsi_mpath_head->mpath_head, which could lead to a NULL pointer
dereference.

[Severity: High]
Does placing this hook in the wrapper bypass path revalidation for some drivers?

Low-level drivers like mpt3sas and scsi_transport_srp call the core API
scsi_internal_device_unblock_nowait() directly. Since the revalidation is
only added to the scsi_internal_device_unblock() wrapper, won't devices managed
by those drivers miss path revalidation when unblocked, potentially leaving
queued bios permanently stalled?

>  
>  	return ret;
>  }
> diff --git a/drivers/scsi/scsi_multipath.c b/drivers/scsi/scsi_multipath.c
> index b23cd6f12c602..12750621a7fc0 100644
> --- a/drivers/scsi/scsi_multipath.c
> +++ b/drivers/scsi/scsi_multipath.c
[ ... ]
> @@ -238,6 +238,14 @@ static int scsi_multipath_sdev_init(struct scsi_device *sdev)
>  	return 0;
>  }
>  
> +void scsi_mpath_revalidate_paths(struct scsi_mpath_device *scsi_mpath_dev)
> +{
> +       struct scsi_mpath_head *scsi_mpath_head = scsi_mpath_dev->scsi_mpath_head;
> +       struct mpath_head *mpath_head = &scsi_mpath_head->mpath_head;
> +
> +       mpath_revalidate_paths(mpath_head);

[Severity: High]
This is a pre-existing issue, but by introducing this new asynchronous trigger,
could we run into a use-after-free during device removal?

Calling mpath_revalidate_paths() queues mpath_head->requeue_work. If the device
is subsequently removed, the teardown path in scsi_mpath_head_release() frees
scsi_mpath_head via kfree():

drivers/scsi/scsi_multipath.c:scsi_mpath_head_release() {
    ...
    mpath_head_uninit(mpath_head);
    kfree(scsi_mpath_head);
}

Because cancel_work_sync(&mpath_head->requeue_work) is missing from the teardown
logic, could the pending work execute on a freed mpath_head structure?

> +}
> +

-- 
Sashiko AI review ยท https://sashiko.dev/#/patchset/[email protected]?part=17