Re: [PATCH v6 05/27] libmultipath: Add support for mpath_device management

[email protected] Wed, 29 Jul 2026 11:57:03 +0000
Newsgroups org.kernel.vger.linux-scsi
Message-ID <[email protected]>
Thank you for your contribution! Sashiko AI review found 4 potential issue(s) to consider:
- [High] RCU Use-After-Free Vulnerability in mpath_delete_device()
- [High] NULL Pointer Dereference in sysfs link management
- [High] Race condition causing sysfs link leak and dangling kobject references
- [Medium] TOCTOU race in mpath_remove_sysfs_link
--

--- Patch [5]: [PATCH v6 05/27] libmultipath: Add support for mpath_device management ---
commit a32651ca51a97ca7e06ffd62321d0190b9e9cfa0
Author: John Garry <[email protected]>

libmultipath: Add support for mpath_device management
    
Add support to add or remove a mpath_device as a path.
    
NVMe has almost like-for-like equivalents here:
- nvme_mpath_clear_current_path() -> mpath_clear_current_path()
- nvme_mpath_add_sysfs_link() -> mpath_add_sysfs_link()
- nvme_mpath_remove_sysfs_link() -> mpath_remove_sysfs_link()
- nvme_mpath_revalidate_paths() -> mpath_revalidate_paths()

The functionality in mpath_clear_paths() and mpath_synchronize() have the
same pattern which is frequently used in the NVMe code.

> diff --git a/lib/multipath.c b/lib/multipath.c
> index 93b82eea7d38b..6b8f6e846987a 100644
> --- a/lib/multipath.c
> +++ b/lib/multipath.c

[ ... ]

> @@ -51,6 +51,87 @@ void mpath_synchronize(struct mpath_head *mpath_head)
>  }
>  EXPORT_SYMBOL_GPL(mpath_synchronize);
>  

[ ... ]

> +bool mpath_delete_device(struct mpath_device *mpath_device)
> +{
> +	bool empty;
> +
> +	mutex_lock(&mpath_device->mpath_head->lock);
> +	list_del_rcu(&mpath_device->siblings);
> +	empty = list_empty(&mpath_device->mpath_head->dev_list);
> +	mutex_unlock(&mpath_device->mpath_head->lock);
> +
> +	mpath_synchronize(mpath_device->mpath_head);

[Severity: High]
Does this sequence safely handle RCU readers accessing current_path?

Because mpath_delete_device() removes the device from dev_list and
immediately calls mpath_synchronize() without clearing it from
mpath_head->current_path, the RCU-protected current_path array may still
hold a pointer to the device. Lockless readers can then dereference the
freed device.

If callers attempt to clear current_path before calling
mpath_delete_device(), couldn't the pointer be repopulated from dev_list
by concurrent readers during the race window? If cleared after, the
synchronization has already passed, providing no protection against
use-after-free.

> +
> +	return empty;
> +}
> +EXPORT_SYMBOL_GPL(mpath_delete_device);

[ ... ]

> @@ -489,6 +572,77 @@ void mpath_device_set_live(struct mpath_device *mpath_device)
>  }
>  EXPORT_SYMBOL_GPL(mpath_device_set_live);
>  
> +void mpath_add_sysfs_link(struct mpath_head *mpath_head)
> +{
> +	struct device *target;
> +	struct device *source;
> +	int rc, srcu_idx;
> +	struct kobject *mpath_gd_kobj;
> +	struct mpath_device *mpath_device;
> +
> +	/*
> +	 * Ensure head disk node is already added otherwise we may get invalid
> +	 * kobj for head disk node
> +	 */
> +	if (!test_bit(GD_ADDED, &mpath_head->disk->state))
> +		return;

[Severity: High]
Is it possible for mpath_head->disk to be NULL here?

When a multi-path head disk is removed, mpath_put_disk() is called, which
sets mpath_head->disk = NULL. If a path subsequently goes live,
mpath_add_sysfs_link() appears to dereference it without checking for NULL.

Could this cause a deterministic oops when path teardown outlives the head,
such as during an abrupt controller removal?

> +
> +	mpath_gd_kobj = &disk_to_dev(mpath_head->disk)->kobj;
> +	srcu_idx = srcu_read_lock(&mpath_head->srcu);
> +
> +	list_for_each_entry_srcu(mpath_device, &mpath_head->dev_list, siblings,
> +				 srcu_read_lock_held(&mpath_head->srcu)) {
> +		if (!test_bit(GD_ADDED, &mpath_device->disk->state))
> +			continue;
> +
> +		if (test_and_set_bit(MPATH_DEVICE_SYSFS_ATTR_LINK,
> +					&mpath_device->flags))
> +			continue;
> +
> +		target = disk_to_dev(mpath_device->disk);
> +		source = disk_to_dev(mpath_head->disk);
> +		/*
> +		 * Create sysfs link from head gendisk kobject @kobj to the
> +		 * ns path gendisk kobject @target->kobj.
> +		 */

[Severity: High]
Could this re-create a sysfs link for a device that is in the middle of
being deleted?

During path removal, mpath_remove_sysfs_link() clears the
MPATH_DEVICE_SYSFS_ATTR_LINK bit before mpath_delete_device() removes
the device from the RCU dev_list.

If a different path concurrently goes live, mpath_add_sysfs_link() iterates
over dev_list, observes the cleared bit on the dying device, and might
mistakenly re-create the sysfs link. When the dying device is finally
removed and freed, the re-created sysfs link is permanently leaked and
points to a freed block device kobject.

> +		rc = sysfs_add_link_to_group(mpath_gd_kobj, "multipath",
> +				&target->kobj, dev_name(target));

[ ... ]

> +void mpath_remove_sysfs_link(struct mpath_device *mpath_device)
> +{
> +	struct device *target;
> +	struct kobject *mpath_gd_kobj;
> +	struct mpath_head *mpath_head = mpath_device->mpath_head;
> +
> +	if (!test_bit(MPATH_DEVICE_SYSFS_ATTR_LINK, &mpath_device->flags))
> +		return;
> +
> +	target = disk_to_dev(mpath_device->disk);
> +	mpath_gd_kobj = &disk_to_dev(mpath_head->disk)->kobj;

[Severity: High]
Does mpath_head->disk need to be checked for NULL before dereferencing it
here?

Similar to mpath_add_sysfs_link(), if underlying paths are detached after
mpath_put_disk() sets mpath_head->disk = NULL, this will cause a NULL
pointer dereference during teardown.

> +
> +	sysfs_remove_link_from_group(mpath_gd_kobj, "multipath",
> +			dev_name(target));
> +
> +	clear_bit(MPATH_DEVICE_SYSFS_ATTR_LINK, &mpath_device->flags);

[Severity: Medium]
Is there a race condition between test_bit() at the start of this function
and clear_bit() here?

A concurrent execution for the same device could pass the test_bit() check
and issue duplicate removals to the sysfs core. Should this diverging
non-atomic pattern be replaced with test_and_clear_bit() to match the safe
pattern used in mpath_add_sysfs_link()?

> +}

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