All of lore.kernel.org
 help / color / mirror / Atom feed
* [PATCH v2 0/6] gpiolib: fix chip removal with lines still requested from user space
@ 2026-10-16  9:23 Adrian Ambrozewicz
  2026-10-16  9:23 ` [PATCH v2 1/6] gpiolib: wait for pending label frees before cleaning up desc_srcu Adrian Ambrozewicz
                   ` (5 more replies)
  0 siblings, 6 replies; 12+ messages in thread
From: Adrian Ambrozewicz @ 2026-10-16  9:23 UTC (permalink / raw)
  To: Bartosz Golaszewski, Linus Walleij
  Cc: Bartosz Golaszewski, Kent Gibson, Paul E. McKenney, Herve Codina,
	Jia Jia, Andy Shevchenko, linux-gpio, linux-kernel

Removing a GPIO chip while user space keeps a line request open, which
is what happens when a USB or otherwise hot-pluggable GPIO controller
goes away, leaves a few things behind on current kernels:

 - the consumer label of each such line leaks (patch 2), and freeing it
   just before the GPIO device is released needs gpiodev_release() to
   wait for pending label frees (patch 1, which can also be hit on its
   own with line hogs);
 - the character device frees the line's IRQ a second time on close,
   after gpiochip_remove() already did, which warns when the IRQ number
   was reused and was reported as a use-after-free with virtio-gpio
   (patch 3), and its IRQs are freed in an order that still lets a
   reused IRQ number or a concurrent close() get in the way (patch 6);
 - a process blocked in read() on the request keeps gpiochip_remove()
   waiting in synchronize_srcu() until it is signalled, and with it
   device_shutdown() on power-off (patch 4);
 - the IRQ name the character device allocated leaks (patch 5).

All five were reproduced with gpio-sim on v7.3-rc5 under QEMU (x86_64,
4 vCPUs) and are still present in gpio/for-next. The loop: create a
gpio-sim chip, request a line through the character device and hold
it, set "live" to 0 while it is held, then optionally set the chip up
again and request the same line, and close the first request.

                      v7.3-rc5 (30 cycles)        with the series (100)
 v2 edges, reuse      30 WARN, 17-30 labels       0 WARN, 0 leaks
 v2 edges             0 WARN, 19-29 labels        0
 v2 sw debounce       30 WARN, 13-30 labels       0
 v1 line event        30 WARN, 14-29 labels       0
 v2 no edges          15-30 labels                0
 v2 edges, consumer   30 WARN, 15-30 labels,      0
                      17-24 IRQ names
 blocked read()       removal waits for the       removal completes,
 (v2, v1, info watch) reader to be killed         read() gets -ENODEV

The counts above are from both a KASAN and a non-KASAN build of v1;
KASAN did not report anything in these runs, the slab use-after-free
from the report needs the descriptor free to race with the close.
kmemleak does not see every leaked object, hence the ranges. The
desc_srcu warning fixed by patch 1 takes srcutree.convert_to_big=1 to
show up within a few dozen cycles of a hogged line (cycle 57 here);
with the series, 1000 cycles pass. That warning was also seen on real
hardware, on arm64 KCSAN kernels with v7.3-rc5 unbinding a USB GPIO
expander whose driver is not yet upstream, built both as an out-of-tree
module and into the kernel.

Patch 3 on its own leaves two windows, which gpio-sim does not hit: if
the chip disposes of its IRQ mappings in gpiochip_irqchip_remove() and
another device gets one of the numbers before the character device's
notifier runs, the notifier's free_irq() warns; and a close() racing
with gpiochip_remove() is not serialized against gpiod_free_irqs().
Patch 6 calls the notifier right after the chip pointer is cleared and
before gpiod_free_irqs(), and makes the release paths free their IRQs
before unregistering from it. With a test-only chip on the gpiolib
hierarchical irqchip that hands an IRQ number out again during the
removal, or sleeps in irq_bus_lock() to widen the race, 30 out of 30
cycles warn in each case with patches 1-5 and none with patch 6.

Patch 6 is kept apart from patch 3 because it changes the order of
gpiochip_remove() for in-kernel users too: interrupts they leave
requested are now freed after the chip pointer is cleared rather than
before, so the irqchip callbacks that free_irq() calls into run with
the gpiod_*() consumer calls on that chip already failing; the driver
data is still set at that point. Patch 3 is enough for the reported
use-after-free and is the smaller backport. Herve's series [1] moved the
character device's cleanup earlier in gpiochip_remove() in 2024 and was
set aside for a fix covering in-kernel users as well, which became
gpiochip_free_remaining_irqs(); patch 6 keeps that and only lets the
character device go first.

The revocable conversion posted for gpiolib [3] keeps all of these as
they are; patches 2, 4 and 6 would need rebasing over it, patch 6
because its notifier call goes right after the revocable_revoke() that
the conversion puts in gpiochip_remove().

On arm64 (QEMU virt, 4 vCPUs, KASAN), 5 cycles per case, the removal
loop gives the same picture: on v7.3-rc5 the v2 edge, debounce, v1 and
consumer cases warn in every cycle, all cases but the blocked readers
leak labels, and the blocked readers hold up the removal; with the
series none of that happens. With the test chip, the close race warns
in 10 out of 10 cycles on v7.3-rc5 for v2 and v1 alike, and in one v2
cycle KASAN reports the use-after-free of [2], free_irq() from
edge_detector_stop() on a freed irq_desc; with the series there is
neither. (The reused IRQ number only leads to a warning once patch 3
frees IRQs from the notifier, so not on v7.3-rc5.) An x86_64 KCSAN
build, default KCSAN options, shows the same warnings and stalls on
v7.3-rc5 and none with the series; KCSAN itself reported no data race
in gpiolib, also with kcsan.skip_watch=100 for the test chip and the
edge, consumer and blocked-reader cases (one report in 32 boots, in
osq_unlock() while a disk was probed during boot). The test kernels
with patch 6 also had the direction fix mentioned below applied, which
touches none of these paths.

For v2, the x86_64 removal loop (10 cycles per case, 2 for the blocked
readers) and the test chip (30 cycles) were run again on a debug and kmemleak build of this series
with the two fixes below, and on KASAN and KCSAN builds that also had
two fixes for existing code in gpiolib.c on top: no warnings, leaks or
stalls. The arm64 runs were not repeated.

User-visible change: with patch 4, a read() that is blocked on a line
request, on a v1 line event or on a chip file with watched lines when
the chip is removed returns the events still queued, if any, and then
fails with -ENODEV. On v7.3-rc5 it stays blocked until it is signalled
or an event arrives, holding up the removal. A read() started after the
removal already failed with -ENODEV, and poll() already reported
EPOLLHUP | EPOLLERR; neither changes.

For stable: patches 3, 5 and 6 fix commit ec8b6f55b981, which is in
6.12 and later. Patch 4 applies to 6.12.y as is; the SRCU read section
it changes replaced the gdev->sem rwsem in 6.9, and 5.15.y, 6.1.y and
6.6.y have that rwsem from the commit it fixes and need a different
change. There the read handlers hold gdev->sem for reading across the
whole read through call_read_locked(), wait included, and
gpiochip_remove() takes it for writing, so the backport has to drop the
rwsem around wait_event_interruptible_locked(), retake it and check
gdev->chip again, with !gdev->chip added to the wait condition. On
6.6.y the device notifier, called with the rwsem held for writing, then
wakes the reader, which gets -ENODEV once the removal drops the rwsem.
5.15.y and 6.1.y have no device notifier: the removal no longer waits,
but a blocked reader is only woken by an event or a signal, as before.

Two fixes found along the way have nothing to do with chip removal and
are sent on their own: one for the line info keeping a direction that
the driver refused (gpiolib-cdev, now at v2), one for the event clock
flags of a released line (gpiolib, v1 unchanged). This series does not
depend on them, nor they on it; all three apply to v7.3-rc5,
gpio/for-next and gpio/for-current in any order. A fix for a debounce
period race in gpiod_free_commit() that Sashiko found on the event
clock fix builds on patch 2, so it will follow once this series is in.

Direction fix: https://lore.kernel.org/r/20261016-gpiolib-cdev-direction-v2-1-aambrozewicz@nvidia.com
Event clock fix: https://lore.kernel.org/r/20261012-gpiolib-event-clock-v1-1-aambrozewicz@nvidia.com

For transparency: an LLM coding assistant helped with the analysis, the
reproducers and test setup, and drafting the patches and messages,
hence the Assisted-by tags.

Cc'd beyond the GPIO maintainers and list: Paul for the SRCU side,
since patch 1 replaces synchronize_srcu() with srcu_barrier() before
cleanup_srcu_struct(), patch 4 moves sleeping waits out of the
gdev->srcu read section, and patch 6 relies on synchronize_srcu() on
gdev->srcu to rule out new IRQ requests through the character device;
Herve for [1], which patch 6 revisits; Jia Jia for the virtio-gpio
use-after-free report [2] that patch 3 addresses; Kent as the author of
the v2 character device; Andy, who acked the commit that patch 2 fixes.

Changes in v2:
- Patches 1, 2, 3 and 5: backtraces cut down to the warning and the
  frames that matter (Andy).
- Patch 1: Paul's Reviewed-by.
- Patch 2: gpiod_free_commit() returns early for a line that is not
  requested on a live chip and clears the label once, before the chip
  check, so now before ->free() (Andy).
- Patch 3: the IRQ check moved into free_line_irq(), with a local for
  the IRQ name; container_of() on one line; loop variable declared in
  the for (Andy).
- Patch 4: the three chip checks of the read() paths factored into
  gpiodev_check_chip() (Andy).
- Patch 5: follows the free_line_irq() shape from patch 3 (Andy).
- Patch 6: mutex_destroy() for config_mutex and irq_lock; the missing
  ->to_irq() case folded into the irq <= 0 check (Andy).
- Assisted-by tags; see the note on tools above.
- Link to v1: https://lore.kernel.org/r/20261012-gpiolib-chip-removal-v1-0-aambrozewicz@nvidia.com

[1] https://lore.kernel.org/all/20240227113426.253232-1-herve.codina@bootlin.com/
[2] https://lore.kernel.org/all/20260817070429.157993-1-physicalmtea@gmail.com/
[3] https://lore.kernel.org/all/20261011121246.162759-1-tzungbi@kernel.org/

Adrian Ambrozewicz (6):
  gpiolib: wait for pending label frees before cleaning up desc_srcu
  gpiolib: free the label of lines released after their chip was removed
  gpiolib: cdev: do not free line IRQs again after the chip is removed
  gpiolib: cdev: don't sleep in the SRCU read section in read()
  gpiolib: cdev: don't leak the IRQ name when the chip is removed
  gpiolib: let the character device free its IRQs first on chip removal

 drivers/gpio/gpiolib-cdev.c | 146 +++++++++++++++++++++++++++---------
 drivers/gpio/gpiolib.c      |  39 ++++++++--
 2 files changed, 140 insertions(+), 45 deletions(-)


base-commit: 72d3fcf802c45d00b300f25b848a93c3a2bd7c7e
-- 
2.43.0


^ permalink raw reply	[flat|nested] 12+ messages in thread

end of thread, other threads:[~2026-10-16 13:41 UTC | newest]

Thread overview: 12+ messages (download: mbox.gz follow: Atom feed
-- links below jump to the message on this page --
2026-10-16  9:23 [PATCH v2 0/6] gpiolib: fix chip removal with lines still requested from user space Adrian Ambrozewicz
2026-10-16  9:23 ` [PATCH v2 1/6] gpiolib: wait for pending label frees before cleaning up desc_srcu Adrian Ambrozewicz
2026-10-16  9:23 ` [PATCH v2 2/6] gpiolib: free the label of lines released after their chip was removed Adrian Ambrozewicz
2026-10-16 13:38   ` Andy Shevchenko
2026-10-16  9:23 ` [PATCH v2 3/6] gpiolib: cdev: do not free line IRQs again after the chip is removed Adrian Ambrozewicz
2026-10-16  9:23 ` [PATCH v2 4/6] gpiolib: cdev: don't sleep in the SRCU read section in read() Adrian Ambrozewicz
2026-10-16  9:23 ` [PATCH v2 5/6] gpiolib: cdev: don't leak the IRQ name when the chip is removed Adrian Ambrozewicz
2026-10-16 13:41   ` Andy Shevchenko
2026-10-16  9:23 ` [PATCH v2 6/6] gpiolib: let the character device free its IRQs first on chip removal Adrian Ambrozewicz
2026-10-16  9:45   ` sashiko-bot
2026-10-16 10:01     ` Adrian Ambrozewicz
2026-10-16 13:40   ` Kent Gibson

This is an external index of several public inboxes,
see mirroring instructions on how to clone and mirror
all data and code used by this external index.