Re: [PATCH v6 16/27] scsi-multipath: clear path when device is blocked

[email protected] Wed, 29 Jul 2026 14:42:29 +0000
Newsgroups org.kernel.vger.linux-scsi
Message-ID <[email protected]>
Thank you for your contribution! Sashiko AI review found 1 potential issue(s) to consider:
- [High] Unprotected, lockless access to `sdev->scsi_mpath_dev` in `scsi_device_block()` causes NULL pointer dereferences and Use-After-Free (UAF) bugs when racing with device initialization or teardown.
--

--- Patch [16]: [PATCH v6 16/27] scsi-multipath: clear path when device is blocked ---
commit 8f07d9f873129f9ecc9b8d3d02fee5bce667c745
Author: John Garry <[email protected]>

scsi-multipath: clear path when device is blocked

Add scsi_mpath_dev_clear_path() to clear a device path when it becomes
blocked.

> diff --git a/drivers/scsi/scsi_lib.c b/drivers/scsi/scsi_lib.c
> index daeb3693fe559..a1ec11050090c 100644
> --- a/drivers/scsi/scsi_lib.c
> +++ b/drivers/scsi/scsi_lib.c

[ ... ]

> @@ -3008,6 +3009,8 @@ static void scsi_device_block(struct scsi_device *sdev, void *data)
>  		scsi_stop_queue(sdev);
>  
>  	mutex_unlock(&sdev->state_mutex);
> +	if ((err == 0) && sdev->scsi_mpath_dev)
> +		scsi_mpath_dev_clear_path(sdev->scsi_mpath_dev);

[Severity: High]
Is it safe to access sdev->scsi_mpath_dev without holding state_mutex?

When scsi_alloc_sdev() adds a new device to shost->__devices, it becomes
globally visible. Then scsi_mpath_dev_alloc() sets sdev->scsi_mpath_dev but
leaves scsi_mpath_head as NULL while issuing SCSI VPD queries.

If a concurrent error on the bus triggers scsi_device_block(), it could
read the partially initialized sdev->scsi_mpath_dev here outside of any locks.

Additionally, if scsi_mpath_dev_alloc() encounters an error and frees
sdev->scsi_mpath_dev, could reading it here cause a use-after-free?

During teardown, if __scsi_remove_device() is called concurrently with
scsi_device_block(), the teardown thread might set scsi_mpath_head to NULL.
Can this lead to a NULL pointer dereference when evaluating
scsi_mpath_dev_clear_path()?

>  
>  	WARN_ONCE(err, "%s: failed to block %s in state %d\n",
>  		  __func__, dev_name(&sdev->sdev_gendev), state);

[ ... ]

> diff --git a/drivers/scsi/scsi_multipath.c b/drivers/scsi/scsi_multipath.c
> index 0d439cac6447a..b23cd6f12c602 100644
> --- a/drivers/scsi/scsi_multipath.c
> +++ b/drivers/scsi/scsi_multipath.c

[ ... ]

> @@ -238,6 +238,16 @@ static int scsi_multipath_sdev_init(struct scsi_device *sdev)
>  	return 0;
>  }
>  
> +void scsi_mpath_dev_clear_path(struct scsi_mpath_device *scsi_mpath_dev)
> +{
> +       struct mpath_device *mpath_device = &scsi_mpath_dev->mpath_device;
> +       struct scsi_mpath_head *scsi_mpath_head = scsi_mpath_dev->scsi_mpath_head;
> +       struct mpath_head *mpath_head = &scsi_mpath_head->mpath_head;

[Severity: High]
Does this code dereference a NULL pointer if it races with initialization or
teardown?

If scsi_mpath_head is NULL due to the races mentioned above, deriving
mpath_head from it will calculate an invalid pointer.

> +
> +       if (mpath_clear_current_path(mpath_device))

[Severity: High]
Could calling mpath_clear_current_path() with a partially initialized
mpath_device lead to a NULL pointer dereference?

If mpath_device->mpath_head is NULL, it will trigger an immediate dereference
in the multipath code:

lib/multipath.c:mpath_clear_current_path() {
    ...
	struct mpath_head *mpath_head = mpath_device->mpath_head;
    ...
		if (mpath_device ==
			rcu_access_pointer(mpath_head->current_path[node])) {
    ...
}

> +               mpath_synchronize(mpath_head);
> +}

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