* [PATCH] gpio: sim: snapshot configfs leaf items before depending on them
@ 2026-10-26 6:00 Yu Zhang
0 siblings, 0 replies; only message in thread
From: Yu Zhang @ 2026-10-26 6:00 UTC (permalink / raw)
To: Bartosz Golaszewski, Linus Walleij
Cc: Koichiro Den, linux-gpio, linux-kernel, stable, Yu Zhang
gpio_sim_device_lockup_configfs() walks dev->bank_list and each
bank->line_list to depend (or undepend) the leaf configfs items when a
simulated device is activated or deactivated. The walk runs without
holding dev->lock:
list_for_each_entry(bank, &dev->bank_list, siblings) {
list_for_each_entry(line, &bank->line_list, siblings) {
...
configfs_depend_item_unlocked(subsys, item);
Every other traversal of these lists holds dev->lock, and the lists are
mutated under it: gpio_sim_line_config_group_release() and
gpio_sim_bank_config_group_release() both do list_del() under dev->lock
and then free the line/bank. A line or bank can be removed by rmdir(2)
while it is not yet (or no longer) depended on, i.e. in the window around
activation before configfs_depend_item_unlocked() has pinned it, and in
the window around deactivation after configfs_undepend_item_unlocked()
has released it. The lockless walk can then dereference a line or bank
that a concurrent release is freeing, or follow a ->siblings pointer into
a freed node, resulting in a use-after-free / list corruption.
The walk cannot simply be moved under dev->lock, because
configfs_[un]depend_item_unlocked() take configfs-internal locks and
holding dev->lock across them would invert the lock order against the
configfs rmdir path, which calls the release callbacks (taking dev->lock)
with those configfs locks held.
Take a pinned snapshot of the leaf items under dev->lock instead. The
list pointers are now only followed with the lock held; each item is
pinned with config_item_get_unless_zero() so it cannot be freed once the
lock is dropped, and items already being torn down (get returns NULL) are
skipped, since they must not be depended on anyway. The snapshot is then
depended/undepended with the lock dropped - preserving the required lock
order - and each item is put afterwards.
Fixes: 8bd76b3d3f3a ("gpio: sim: lock up configfs that an instantiated device depends on")
Cc: stable@vger.kernel.org
Signed-off-by: Yu Zhang <yuz08559@gmail.com>
---
drivers/gpio/gpio-sim.c | 56 +++++++++++++++++++++++++++++++++--------
1 file changed, 45 insertions(+), 11 deletions(-)
diff --git a/drivers/gpio/gpio-sim.c b/drivers/gpio/gpio-sim.c
index 0c73bec..562f305 100644
--- a/drivers/gpio/gpio-sim.c
+++ b/drivers/gpio/gpio-sim.c
@@ -978,27 +978,61 @@ static void
gpio_sim_device_lockup_configfs(struct gpio_sim_device *dev, bool lock)
{
struct configfs_subsystem *subsys = dev->group.cg_subsys;
+ struct config_item **items = NULL, *item;
struct gpio_sim_bank *bank;
struct gpio_sim_line *line;
- struct config_item *item;
+ size_t i, n;
/*
* The device only needs to depend on leaf entries. This is
* sufficient to lock up all the configfs entries that the
* instantiated, alive device depends on.
+ *
+ * The bank and line lists are only stable under dev->lock, but
+ * configfs_[un]depend_item_unlocked() must not be called under it:
+ * they take configfs-internal locks and that would invert the lock
+ * order against the configfs rmdir path, which removes lines and
+ * banks from these lists and frees them under dev->lock. So take a
+ * pinned snapshot of the leaf items under the lock - skipping any
+ * that are already being torn down - and then depend/undepend the
+ * snapshot with the lock dropped.
*/
- list_for_each_entry(bank, &dev->bank_list, siblings) {
- list_for_each_entry(line, &bank->line_list, siblings) {
- item = line->hog ? &line->hog->item
- : &line->group.cg_item;
-
- if (lock)
- WARN_ON(configfs_depend_item_unlocked(subsys,
- item));
- else
- configfs_undepend_item_unlocked(item);
+ scoped_guard(mutex, &dev->lock) {
+ n = 0;
+ list_for_each_entry(bank, &dev->bank_list, siblings)
+ list_for_each_entry(line, &bank->line_list, siblings)
+ n++;
+
+ if (!n)
+ return;
+
+ items = kcalloc(n, sizeof(*items), GFP_KERNEL);
+ if (!items)
+ return;
+
+ i = 0;
+ list_for_each_entry(bank, &dev->bank_list, siblings) {
+ list_for_each_entry(line, &bank->line_list, siblings) {
+ item = line->hog ? &line->hog->item
+ : &line->group.cg_item;
+ item = config_item_get_unless_zero(item);
+ if (item)
+ items[i++] = item;
+ }
}
+ n = i;
}
+
+ for (i = 0; i < n; i++) {
+ if (lock)
+ WARN_ON(configfs_depend_item_unlocked(subsys,
+ items[i]));
+ else
+ configfs_undepend_item_unlocked(items[i]);
+ config_item_put(items[i]);
+ }
+
+ kfree(items);
}
static ssize_t
--
2.43.0
^ permalink raw reply [flat|nested] only message in thread
only message in thread, other threads:[~2026-10-26 6:02 UTC | newest]
Thread overview: (only message) (download: mbox.gz / follow: Atom feed)
-- links below jump to the message on this page --
2026-10-26 6:00 [PATCH] gpio: sim: snapshot configfs leaf items before depending on them Yu Zhang
This is a public inbox, see mirroring instructions
for how to clone and mirror all data and code used for this inbox
all inboxes | Powered by JetHome®