From: John Garry <john.g.garry@oracle.com>
To: sashiko-reviews@lists.linux.dev
Cc: linux-scsi@vger.kernel.org
Subject: Re: [PATCH v3 06/13] libmultipath: Add delayed removal support
Date: Fri, 3 Jul 2026 16:18:56 +0100 [thread overview]
Message-ID: <e5719f31-ee75-4bb2-8a54-b676392bb075@oracle.com> (raw)
In-Reply-To: <20260703104506.9CF051F000E9@smtp.kernel.org>
On 03/07/2026 11:45, sashiko-bot@kernel.org wrote:
> Thank you for your contribution! Sashiko AI review found 7 potential issue(s) to consider:
> - [High] Repeated calls to `mpath_can_remove_head()` leak module references when removal work is already pending.
> - [High] Race condition between `mpath_add_device()`, `mpath_can_remove_head()`, and `mpath_remove_head_work()` leads to use-after-free of mpath_head.
> - [High] Missing cancellation of delayed removal work on `mpath_head` teardown results in Use-After-Free.
> - [High] `mpath_head->drv_module` is never initialized by the subsystem API, leading to a silent failure to pin the driver module.
> - [Medium] Disabling delayed removal via sysfs fails to cancel already pending removal work, leading to unexpected device removal.
> - [Low] Incompatible pointer type passed to `kstrtouint()` causes compiler warnings.
> - [Low] Integer overflow when calculating the delay for `mod_delayed_work()` results in severely shortened delays.
> --
>
> commit c00bb95222f826ebc3e8dbd55ff6884abf84858e
> Author: John Garry <john.g.garry@oracle.com>
>
> libmultipath: Add delayed removal support
>
> Add support for delayed removal, same as exists for NVMe.
>
> The purpose of this feature is to keep the multipath disk and cdev present
> for intermittent periods of no available path.
>
>> diff --git a/lib/multipath.c b/lib/multipath.c
>> index 007aa34796569..78f88b0664c78 100644
>> --- a/lib/multipath.c
>> +++ b/lib/multipath.c
>> @@ -61,6 +61,8 @@ void mpath_add_device(struct mpath_device *mpath_device,
>> mutex_lock(&mpath_head->lock);
>> list_add_tail_rcu(&mpath_device->siblings, &mpath_head->dev_list);
>> mutex_unlock(&mpath_head->lock);
>> + if (cancel_delayed_work(&mpath_head->remove_work))
>> + module_put(mpath_head->drv_module);
>> }
>
> [Severity: High]
> Does this code introduce a use-after-free race condition?
>
> If the delayed removal timer is already executing, cancel_delayed_work()
> returns false.
>
> mpath_add_device() will successfully add the device, but the
> already-executing mpath_remove_head_work() unconditionally calls
> mpath_head->mpdt->remove_head() without checking for newly added devices.
>
> Could the newly added device access the destroyed mpath_head?
Ensuring that new devices are not added while the head is being removed
is the job of the driver.
In the NVMe mainline case, in the nvme_remove_head_work() will
atomically ensure that no new devices appear and then decide to delete
the head list entry reference (so no new devices can reference the same
head)
>
> [ ... ]
>
>> @@ -518,6 +530,39 @@ static void mpath_requeue_work(struct work_struct *work)
>> +bool mpath_can_remove_head(struct mpath_head *mpath_head)
>> +{
>> + bool remove = false;
>> +
>> + mutex_lock(&mpath_head->lock);
>> + /*
>> + * Ensure that no one could remove this module while the head
>> + * remove work is pending.
>> + */
>> + if (mpath_head_queue_if_no_path(mpath_head) &&
>> + try_module_get(mpath_head->drv_module)) {
>
> [Severity: High]
> Is mpath_head->drv_module initialized anywhere in the subsystem API?
>
> If it remains uninitialized (e.g., NULL from zero-allocated structures),
> try_module_get(NULL) will silently succeed.
>
> Could this lead to a failure to pin the driver module, potentially resulting
> in a crash if the module is unloaded while the timer is pending?
The driver needs to ensure that this is set.
It is hard to add a check, as if the driver is builtin then THIS_MODULE
is NULL. Maybe that can be improved in the core code.
>
>> +
>> + mod_delayed_work(mpath_wq, &mpath_head->remove_work,
>> + mpath_head->delayed_removal_secs * HZ);
>
> [Severity: High]
> Does this unconditionally increment the module reference count even when the
> timer is already pending?
>
> If mpath_can_remove_head() is called multiple times, try_module_get()
> will increment the reference count on each call.
It should not be called multiple times. That is the job of the driver.
>
> However, mod_delayed_work() merely modifies the existing timer. When the
> timer eventually fires, mpath_remove_head_work() calls module_put() only
> once, leaking the additional module references and preventing unloading.
>
> [Severity: Low]
> Does this multiplication overflow on 32-bit systems?
>
> mpath_head->delayed_removal_secs is an unsigned int, and HZ is typically
> an integer.
>
> Could their product overflow 32-bit arithmetic before being promoted to
> unsigned long for mod_delayed_work(), leading to shortened delays?
I don't think that this is a problem. The delayed removal secs should
not be so big as to overflow.
>
>> + } else {
>> + remove = true;
>> + }
>> +
>> + mutex_unlock(&mpath_head->lock);
>> + return remove;
>> +}
>> +EXPORT_SYMBOL_GPL(mpath_can_remove_head);
>> +
>> +static void mpath_remove_head_work(struct work_struct *work)
>> +{
>> + struct mpath_head *mpath_head = container_of(to_delayed_work(work),
>> + struct mpath_head, remove_work);
>> + struct module *drv_module = mpath_head->drv_module;
>> +
>> + mpath_head->mpdt->remove_head(mpath_head);
>> + module_put(drv_module);
>> +}
>
> [ ... ]
>
>> @@ -562,6 +607,9 @@ int mpath_alloc_head_disk(struct mpath_head *mpath_head,
>> mpath_head->disk->private_data = mpath_head;
>> mpath_head->disk->fops = &mpath_ops;
>>
>> + INIT_DELAYED_WORK(&mpath_head->remove_work, mpath_remove_head_work);
>> + mpath_head->delayed_removal_secs = 0;
>> +
>> set_bit(GD_SUPPRESS_PART_SCAN, &mpath_head->disk->state);
>
> [Severity: High]
> Is the delayed removal work reliably canceled during final teardown?
>
> During explicit device teardown (like a driver unbind), functions like
> mpath_head_cleanup() are called to free resources.
>
> Since this patch doesn't add a cancel_delayed_work_sync() call to the
> cleanup path, could an active timer fire after the mpath_head has been
> freed, leading to a use-after-free?
Hmmm ... I don't get it. When the delayed work is active, then only the
delayed timer callback will remove the mpath_head
>
> [ ... ]
>
>> @@ -605,6 +653,47 @@ void mpath_device_set_live(struct mpath_device *mpath_device)
>> +ssize_t mpath_delayed_removal_secs_store(struct mpath_head *mpath_head,
>> + const char *buf, size_t count)
>> +{
>> + ssize_t ret;
>> + int sec;
>> +
>> + ret = kstrtouint(buf, 0, &sec);
>
> [Severity: Low]
> Does passing an int pointer to kstrtouint() cause compiler warnings?
>
> sec is declared as an int, but kstrtouint() strictly expects an unsigned
> int *.
ok, I'll make sec as an unsigned int
>
> Could this cause an incompatible pointer type warning and potential undefined
> behavior on architectures with differing int/unsigned int layouts?
>
>> + if (ret < 0)
>> + return ret;
>> +
>> + mutex_lock(&mpath_head->lock);
>> + mpath_head->delayed_removal_secs = sec;
>> + if (sec)
>> + set_bit(MPATH_HEAD_QUEUE_IF_NO_PATH, &mpath_head->flags);
>> + else
>> + clear_bit(MPATH_HEAD_QUEUE_IF_NO_PATH, &mpath_head->flags);
>
> [Severity: Medium]
> Should the active timer be canceled when delayed removal is disabled?
unsetting MPATH_HEAD_QUEUE_IF_NO_PATH has that effect
>
> When a user writes 0 to disable the feature, this code clears the flag but
> misses a call to cancel_delayed_work(&mpath_head->remove_work).
>
> If the timer is already pending, will it continue to run and eventually
> destroy the head, contrary to the user's configuration?
>
>> + mutex_unlock(&mpath_head->lock);
>
next prev parent reply other threads:[~2026-07-03 15:19 UTC|newest]
Thread overview: 33+ messages / expand[flat|nested] mbox.gz Atom feed top
2026-07-03 10:29 [PATCH v3 00/13] libmultipath: a generic multipath lib for block drivers John Garry
2026-07-03 10:29 ` [PATCH v3 01/13] libmultipath: Add initial framework John Garry
2026-07-03 10:43 ` sashiko-bot
2026-07-03 14:06 ` John Garry
2026-07-03 10:29 ` [PATCH v3 02/13] libmultipath: Add basic gendisk support John Garry
2026-07-03 10:51 ` sashiko-bot
2026-07-03 14:19 ` John Garry
2026-07-03 10:29 ` [PATCH v3 03/13] libmultipath: Add path selection support John Garry
2026-07-03 10:45 ` sashiko-bot
2026-07-03 14:37 ` John Garry
2026-07-03 10:29 ` [PATCH v3 04/13] libmultipath: Add bio handling John Garry
2026-07-03 10:49 ` sashiko-bot
2026-07-03 14:48 ` John Garry
2026-07-03 10:29 ` [PATCH v3 05/13] libmultipath: Add support for mpath_device management John Garry
2026-07-03 10:29 ` [PATCH v3 06/13] libmultipath: Add delayed removal support John Garry
2026-07-03 10:45 ` sashiko-bot
2026-07-03 15:18 ` John Garry [this message]
2026-07-03 10:29 ` [PATCH v3 07/13] libmultipath: Add cdev support John Garry
2026-07-03 10:48 ` sashiko-bot
2026-07-06 10:26 ` John Garry
2026-07-03 10:29 ` [PATCH v3 08/13] libmultipath: Add sysfs helpers John Garry
2026-07-03 10:44 ` sashiko-bot
2026-07-06 13:57 ` John Garry
2026-07-03 10:29 ` [PATCH v3 09/13] libmultipath: Add PR support John Garry
2026-07-03 10:48 ` sashiko-bot
2026-07-06 14:12 ` John Garry
2026-07-03 10:29 ` [PATCH v3 10/13] libmultipath: Add mpath_bdev_report_zones() John Garry
2026-07-03 10:29 ` [PATCH v3 11/13] libmultipath: Add support for block device IOCTL John Garry
2026-07-03 10:56 ` sashiko-bot
2026-07-06 14:16 ` John Garry
2026-07-03 10:29 ` [PATCH v3 12/13] libmultipath: Add mpath_bdev_getgeo() John Garry
2026-07-03 10:54 ` sashiko-bot
2026-07-03 10:29 ` [PATCH v3 13/13] libmultipath: Add mpath_bdev_get_unique_id() John Garry
Reply instructions:
You may reply publicly to this message via plain-text email
using any one of the following methods:
* Save the following mbox file, import it into your mail client,
and reply-to-all from there: mbox
Avoid top-posting and favor interleaved quoting:
https://en.wikipedia.org/wiki/Posting_style#Interleaved_style
* Reply using the --to, --cc, and --in-reply-to
switches of git-send-email(1):
git send-email \
--in-reply-to=e5719f31-ee75-4bb2-8a54-b676392bb075@oracle.com \
--to=john.g.garry@oracle.com \
--cc=linux-scsi@vger.kernel.org \
--cc=sashiko-reviews@lists.linux.dev \
/path/to/YOUR_REPLY
https://kernel.org/pub/software/scm/git/docs/git-send-email.html
* If your mail client supports setting the In-Reply-To header
via mailto: links, try the mailto: link
Be sure your reply has a Subject: header at the top and a blank line
before the message body.
This is a public inbox, see mirroring instructions
for how to clone and mirror all data and code used for this inbox