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

John Garry <[email protected]> Wed, 29 Jul 2026 16:27:27 +0100
Newsgroups org.kernel.vger.linux-scsi,dev.linux.lists.sashiko-reviews
Organization Oracle Corporation
Message-ID <[email protected]>
On 29/07/2026 15:54, [email protected] wrote:
> 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.

Same as v5 response, this should not happen

> - [High] Direct callers of `scsi_internal_device_unblock_nowait` bypass path revalidation, which can leave queued bios indefinitely stalled.

can you name relevant situations?

> 
> 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.

this is done by (disk) sd driver through calling mpath_put_disk()

> --
> 
> --- 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?
> 
>> +}
>> +
>