Re: [PATCH v6 24/27] scsi: sd: support multipath disk

John Garry <[email protected]> Wed, 29 Jul 2026 18:24:38 +0100
Newsgroups org.kernel.vger.linux-scsi,dev.linux.lists.sashiko-reviews
Organization Oracle Corporation
Message-ID <[email protected]>
On 29/07/2026 18:01, [email protected] wrote:
> Thank you for your contribution! Sashiko AI review found 5 potential issue(s) to consider:
> 
> New issues:
> - [High] Missing `put_device()` in the `sd_mpath_add_disk()` error path permanently leaks the `sdkp` structure.
> - [High] Inverted device teardown order causes double `device_del()` and sysfs warnings.
> - [High] Cyclic kobject reference between `sd_mpath_disk` and its child gendisk guarantees both are permanently leaked on removal.
> - [Medium] Ignored return value of `scsi_mpath_get_head()` leads to potential kref underflow and use-after-free.
> 
> Pre-existing issues:
> - [High] `kfree()` is called on an active kobject when `sd_large_pool_create()` fails, leading to UAF / Sysfs Corruption.

That needs to be fixed in mkp-scsi tree

> --
> 
> --- Patch [24]: [PATCH v6 24/27] scsi: sd: support multipath disk ---
> commit 1896d58c043437e813525b463ccbb2249c843f33
> Author: John Garry <[email protected]>
> scsi: sd: support multipath disk
> 
> This commit adds support for attaching a multipath disk in the SCSI sd
> subsystem. It allocates gendisks per-path but marks them as hidden, and
> maintains a global list of sd_mpath_disks to match with scsi_devices.
> 
>> --- a/drivers/scsi/sd.c
>> +++ b/drivers/scsi/sd.c
>> @@ -115,12 +115,30 @@ static mempool_t *sd_large_page_pool;
>>   static atomic_t sd_large_page_pool_users = ATOMIC_INIT(0);
>>   static struct lock_class_key sd_bio_compl_lkclass;
>>   #ifdef CONFIG_SCSI_MULTIPATH
>> +static LIST_HEAD(sd_mpath_disks_list);
>> +static DEFINE_MUTEX(sd_mpath_disks_lock);
>> +
>>   struct sd_mpath_disk {
>> +	struct device			dev;
>> +	int				disk_index;
>> +	int				disk_count;
>> +	struct list_head		entry;
>>   	struct scsi_mpath_head		*scsi_mpath_head;
>>   };
>>   
>>   static void sd_mpath_disk_release(struct device *dev)
>>   {
>> +	struct sd_mpath_disk *sd_mpath_disk =
>> +		container_of(dev, struct sd_mpath_disk, dev);
>> +	struct scsi_mpath_head *scsi_mpath_head =
>> +		sd_mpath_disk->scsi_mpath_head;
>> +	struct mpath_head *mpath_head = &scsi_mpath_head->mpath_head;
>> +
>> +	mpath_put_disk(mpath_head);
> 
> [Severity: High]
> Does this create an unbreakable cyclic kobject reference? The child disk is
> created as a child of sd_mpath_disk->dev, meaning it holds a reference to
> the parent. If the child's reference is only dropped via mpath_put_disk()
> inside the parent's release function (sd_mpath_disk_release), the parent's
> release function will never execute because the child still holds a
> reference to it. Will this permanently leak both the sd_mpath_disk and the
> multipath gendisk on removal?

I think that the earlier del_gendisk() -> device_del(disk_to_dev(disk)) 
drops that reference

> 
> [ ... ]
> 
>> @@ -4035,13 +4138,259 @@ static const struct attribute_group sd_mpath_disk_attr_group = {
>>   	.attrs		= sd_mpath_disk_attrs,
>>   };
>>   
>> +static int sd_mpath_probe(struct scsi_disk *sdkp)
>> +{
> [ ... ]
>> +	device_initialize(&sd_mpath_disk->dev);
>> +	sd_mpath_disk->dev.class = &sd_mpath_disk_class;
>> +
>> +	/* undone in sd_mpath_disk_release() */
>> +	scsi_mpath_get_head(scsi_mpath_head);
> 
> [Severity: Medium]
> Is it safe to ignore the return value here? In sd_mpath_probe(),

the count must be non-zero here

later I can add try get variant to make this clear

> scsi_mpath_get_head() is called but its return value is discarded. Since
> scsi_mpath_get_head() explicitly uses kref_get_unless_zero() and can return
> -ENXIO, could a failure lead to a refcount underflow and a use-after-free
> during the release path?
> 
> [ ... ]
> 
>> +static void sd_mpath_remove(struct scsi_disk *sdkp)
>> +{
> [ ... ]
>> +	if (remove) {
>> +		device_del(&sd_mpath_disk->dev);
>> +		mpath_remove_disk(mpath_head);
>> +	}
>> +	sd_mpath_put_disk(sd_mpath_disk);
>> +}
> 
> [Severity: High]
> Is this device teardown order inverted? In sd_mpath_remove() and the related
> sd_mpath_remove_head(), the parent device (sd_mpath_disk->dev) is deleted
> via device_del() before the child gendisk is removed via mpath_remove_disk().
> Does this cause double device_del() operations or sysfs hierarchy corruption,
> since the device model requires children to be unregistered before their
> parents?

maybe it's a problem, but I did not experience an issue. There is a 
later put on the &sd_mpath_disk->dev in the sd_mpath_put_disk() call, so 
the parent is not gone before disk

> 
> [ ... ]
> 
>> +static void sd_mpath_fail_probe(struct scsi_disk *sdkp)
>> +{
> [ ... ]
>> +	if (remove) {
>> +		device_del(&sd_mpath_disk->dev);
>> +		mpath_remove_disk(mpath_head);
>> +	}
>> +	sd_mpath_put_disk(sd_mpath_disk);
>> +}
> 
> [Severity: High]
> Does the same inverted teardown order apply here in sd_mpath_fail_probe()?
> Deleting the parent device before the child gendisk violates the device
> model requirements and can trigger sysfs warnings.

Same answer as above

> 
> [ ... ]
> 
>> @@ -4158,11 +4515,18 @@ static int sd_probe(struct scsi_device *sdp)
>>   	sd_revalidate_disk(gd);
>>   	if (sdp->sector_size > PAGE_SIZE) {
>>   		if (sd_large_pool_create()) {
>> +			sd_mpath_fail_probe(sdkp);
>>   			error = -ENOMEM;
>>   			goto out_free_index;
>>   		}
>>   	}
> 
> [Severity: High]
> This is a pre-existing issue,


Yes, mkp-scsi tree needs fixing


> but does jumping to out_free_index here leak
> the active kobject? In sd_probe(), device_add(&sdkp->disk_dev) has already
> succeeded earlier in the function. When sd_large_pool_create() fails, it
> jumps to out_free_index and eventually calls kfree(sdkp). Should this error
> path use device_del() and put_device() instead to prevent sysfs corruption
> and a use-after-free on the active kobject?
> 
> [ ... ]
> 
>> @@ -4177,6 +4541,7 @@ static int sd_probe(struct scsi_device *sdp)
> [ ... ]
>>   	error = device_add_disk(dev, gd, NULL);
>>   	if (error) {
>> +		sd_mpath_fail_probe(sdkp);
>>   		device_unregister(&sdkp->disk_dev);
>>   		put_disk(gd);
>>   		if (sdp->sector_size > PAGE_SIZE)
>> @@ -4184,6 +4549,19 @@ static int sd_probe(struct scsi_device *sdp)
>>   		goto out;
>>   	}
>>   
>> +	if (sdp->scsi_mpath_dev) {
>> +		error = sd_mpath_add_disk(sdkp);
>> +		if (error) {
>> +			sd_mpath_fail_probe(sdkp);
>> +			device_del(&sdkp->disk_dev);
>> +			del_gendisk(sdkp->disk);
>> +
>> +			put_disk(sdkp->disk);
>> +			if (sdp->sector_size > PAGE_SIZE)
>> +				sd_large_pool_destroy();
>> +			goto out;
>> +		}
>> +	}
> 
> [Severity: High]
> Does this error path leak the sdkp structure? In sd_probe(), if
> sd_mpath_add_disk() fails, the code manually calls device_del() and
> del_gendisk() but entirely omits put_device(&sdkp->disk_dev). 

let's fix mkp-scsi tree first

> Because the
> initial reference count set by device_initialize() is never decremented to
> zero, will this permanently leak the scsi_disk structure and its associated
> resources?
>