* [PATCH v3 01/11] gpio: Access `gpio_bus_type` in gpiochip_setup_dev()
2026-02-13 9:29 [PATCH v3 00/11] gpio: Adopt revocable mechanism for UAF prevention Tzung-Bi Shih
@ 2026-02-13 9:29 ` Tzung-Bi Shih
2026-02-20 7:46 ` Linus Walleij
2026-02-13 9:29 ` [PATCH v3 02/11] gpio: Remove redundant check for struct gpio_chip Tzung-Bi Shih
` (10 subsequent siblings)
11 siblings, 1 reply; 22+ messages in thread
From: Tzung-Bi Shih @ 2026-02-13 9:29 UTC (permalink / raw)
To: Bartosz Golaszewski, Linus Walleij
Cc: Greg Kroah-Hartman, Rafael J. Wysocki, Danilo Krummrich,
Jonathan Corbet, Shuah Khan, Laurent Pinchart, Wolfram Sang,
Jason Gunthorpe, Johan Hovold, Paul E . McKenney, Dan Williams,
chrome-platform, tzungbi, linux-gpio, linux-kselftest,
linux-kernel
To make the intent clear, access `gpio_bus_type` only when it's ready in
gpiochip_setup_dev().
Signed-off-by: Tzung-Bi Shih <tzungbi@kernel.org>
---
v3:
- No changes.
v2: https://lore.kernel.org/all/20260203061059.975605-2-tzungbi@kernel.org
- No changes.
v1: https://lore.kernel.org/all/20260116081036.352286-7-tzungbi@kernel.org
drivers/gpio/gpiolib.c | 7 ++++---
1 file changed, 4 insertions(+), 3 deletions(-)
diff --git a/drivers/gpio/gpiolib.c b/drivers/gpio/gpiolib.c
index 5757c0475990..f4dad3e5b5a3 100644
--- a/drivers/gpio/gpiolib.c
+++ b/drivers/gpio/gpiolib.c
@@ -902,6 +902,8 @@ static int gpiochip_setup_dev(struct gpio_device *gdev)
struct fwnode_handle *fwnode = dev_fwnode(&gdev->dev);
int ret;
+ gdev->dev.bus = &gpio_bus_type;
+
/*
* If fwnode doesn't belong to another device, it's safe to clear its
* initialized flag.
@@ -1083,7 +1085,6 @@ int gpiochip_add_data_with_key(struct gpio_chip *gc, void *data,
* then make sure they get free():ed there.
*/
gdev->dev.type = &gpio_dev_type;
- gdev->dev.bus = &gpio_bus_type;
gdev->dev.parent = gc->parent;
device_set_node(&gdev->dev, gpiochip_choose_fwnode(gc));
@@ -1221,8 +1222,8 @@ int gpiochip_add_data_with_key(struct gpio_chip *gc, void *data,
* we get a device node entry in sysfs under
* /sys/bus/gpio/devices/gpiochipN/dev that can be used for
* coldplug of device nodes and other udev business.
- * We can do this only if gpiolib has been initialized.
- * Otherwise, defer until later.
+ * We can do this only if gpiolib has been initialized
+ * (i.e., `gpio_bus_type` is ready). Otherwise, defer until later.
*/
if (gpiolib_initialized) {
ret = gpiochip_setup_dev(gdev);
--
2.53.0.310.g728cabbaf7-goog
^ permalink raw reply related [flat|nested] 22+ messages in thread* Re: [PATCH v3 01/11] gpio: Access `gpio_bus_type` in gpiochip_setup_dev()
2026-02-13 9:29 ` [PATCH v3 01/11] gpio: Access `gpio_bus_type` in gpiochip_setup_dev() Tzung-Bi Shih
@ 2026-02-20 7:46 ` Linus Walleij
0 siblings, 0 replies; 22+ messages in thread
From: Linus Walleij @ 2026-02-20 7:46 UTC (permalink / raw)
To: Tzung-Bi Shih
Cc: Bartosz Golaszewski, Greg Kroah-Hartman, Rafael J. Wysocki,
Danilo Krummrich, Jonathan Corbet, Shuah Khan, Laurent Pinchart,
Wolfram Sang, Jason Gunthorpe, Johan Hovold, Paul E . McKenney,
Dan Williams, chrome-platform, linux-gpio, linux-kselftest,
linux-kernel
On Fri, Feb 13, 2026 at 10:31 AM Tzung-Bi Shih <tzungbi@kernel.org> wrote:
> To make the intent clear, access `gpio_bus_type` only when it's ready in
> gpiochip_setup_dev().
>
> Signed-off-by: Tzung-Bi Shih <tzungbi@kernel.org>
Reviewed-by: Linus Walleij <linusw@kernel.org>
Yours,
Linus Walleij
^ permalink raw reply [flat|nested] 22+ messages in thread
* [PATCH v3 02/11] gpio: Remove redundant check for struct gpio_chip
2026-02-13 9:29 [PATCH v3 00/11] gpio: Adopt revocable mechanism for UAF prevention Tzung-Bi Shih
2026-02-13 9:29 ` [PATCH v3 01/11] gpio: Access `gpio_bus_type` in gpiochip_setup_dev() Tzung-Bi Shih
@ 2026-02-13 9:29 ` Tzung-Bi Shih
2026-02-20 7:48 ` Linus Walleij
2026-02-13 9:29 ` [PATCH v3 03/11] gpio: sysfs: " Tzung-Bi Shih
` (9 subsequent siblings)
11 siblings, 1 reply; 22+ messages in thread
From: Tzung-Bi Shih @ 2026-02-13 9:29 UTC (permalink / raw)
To: Bartosz Golaszewski, Linus Walleij
Cc: Greg Kroah-Hartman, Rafael J. Wysocki, Danilo Krummrich,
Jonathan Corbet, Shuah Khan, Laurent Pinchart, Wolfram Sang,
Jason Gunthorpe, Johan Hovold, Paul E . McKenney, Dan Williams,
chrome-platform, tzungbi, linux-gpio, linux-kselftest,
linux-kernel
gpiolib_dbg_show() is only called by gpiolib_seq_show() which has
ensured the struct gpio_chip. Remove the redundant check in
gpiolib_dbg_show().
Signed-off-by: Tzung-Bi Shih <tzungbi@kernel.org>
---
v3:
- No changes.
v2: https://lore.kernel.org/all/20260203061059.975605-3-tzungbi@kernel.org
- No changes.
v1: https://lore.kernel.org/all/20260116081036.352286-8-tzungbi@kernel.org
drivers/gpio/gpiolib.c | 13 ++-----------
1 file changed, 2 insertions(+), 11 deletions(-)
diff --git a/drivers/gpio/gpiolib.c b/drivers/gpio/gpiolib.c
index f4dad3e5b5a3..1a3fd7600835 100644
--- a/drivers/gpio/gpiolib.c
+++ b/drivers/gpio/gpiolib.c
@@ -5313,23 +5313,14 @@ core_initcall(gpiolib_dev_init);
#ifdef CONFIG_DEBUG_FS
-static void gpiolib_dbg_show(struct seq_file *s, struct gpio_device *gdev)
+static void gpiolib_dbg_show(struct seq_file *s, struct gpio_chip *gc)
{
bool active_low, is_irq, is_out;
struct gpio_desc *desc;
unsigned int gpio = 0;
- struct gpio_chip *gc;
unsigned long flags;
int value;
- guard(srcu)(&gdev->srcu);
-
- gc = srcu_dereference(gdev->chip, &gdev->srcu);
- if (!gc) {
- seq_puts(s, "Underlying GPIO chip is gone\n");
- return;
- }
-
for_each_gpio_desc(gc, desc) {
guard(srcu)(&desc->gdev->desc_srcu);
flags = READ_ONCE(desc->flags);
@@ -5442,7 +5433,7 @@ static int gpiolib_seq_show(struct seq_file *s, void *v)
if (gc->dbg_show)
gc->dbg_show(s, gc);
else
- gpiolib_dbg_show(s, gdev);
+ gpiolib_dbg_show(s, gc);
return 0;
}
--
2.53.0.310.g728cabbaf7-goog
^ permalink raw reply related [flat|nested] 22+ messages in thread* Re: [PATCH v3 02/11] gpio: Remove redundant check for struct gpio_chip
2026-02-13 9:29 ` [PATCH v3 02/11] gpio: Remove redundant check for struct gpio_chip Tzung-Bi Shih
@ 2026-02-20 7:48 ` Linus Walleij
0 siblings, 0 replies; 22+ messages in thread
From: Linus Walleij @ 2026-02-20 7:48 UTC (permalink / raw)
To: Tzung-Bi Shih
Cc: Bartosz Golaszewski, Greg Kroah-Hartman, Rafael J. Wysocki,
Danilo Krummrich, Jonathan Corbet, Shuah Khan, Laurent Pinchart,
Wolfram Sang, Jason Gunthorpe, Johan Hovold, Paul E . McKenney,
Dan Williams, chrome-platform, linux-gpio, linux-kselftest,
linux-kernel
On Fri, Feb 13, 2026 at 10:31 AM Tzung-Bi Shih <tzungbi@kernel.org> wrote:
> gpiolib_dbg_show() is only called by gpiolib_seq_show() which has
> ensured the struct gpio_chip. Remove the redundant check in
> gpiolib_dbg_show().
>
> Signed-off-by: Tzung-Bi Shih <tzungbi@kernel.org>
That's a very nice patch, good observation!
Reviewed-by: Linus Walleij <linusw@kernel.org>
Yours,
Linus Walleij
^ permalink raw reply [flat|nested] 22+ messages in thread
* [PATCH v3 03/11] gpio: sysfs: Remove redundant check for struct gpio_chip
2026-02-13 9:29 [PATCH v3 00/11] gpio: Adopt revocable mechanism for UAF prevention Tzung-Bi Shih
2026-02-13 9:29 ` [PATCH v3 01/11] gpio: Access `gpio_bus_type` in gpiochip_setup_dev() Tzung-Bi Shih
2026-02-13 9:29 ` [PATCH v3 02/11] gpio: Remove redundant check for struct gpio_chip Tzung-Bi Shih
@ 2026-02-13 9:29 ` Tzung-Bi Shih
2026-02-20 7:51 ` Linus Walleij
2026-02-13 9:29 ` [PATCH v3 04/11] gpio: Ensure struct gpio_chip for gpiochip_setup_dev() Tzung-Bi Shih
` (8 subsequent siblings)
11 siblings, 1 reply; 22+ messages in thread
From: Tzung-Bi Shih @ 2026-02-13 9:29 UTC (permalink / raw)
To: Bartosz Golaszewski, Linus Walleij
Cc: Greg Kroah-Hartman, Rafael J. Wysocki, Danilo Krummrich,
Jonathan Corbet, Shuah Khan, Laurent Pinchart, Wolfram Sang,
Jason Gunthorpe, Johan Hovold, Paul E . McKenney, Dan Williams,
chrome-platform, tzungbi, linux-gpio, linux-kselftest,
linux-kernel
gpiochip_sysfs_unregister() is only called by gpiochip_remove() where
the struct gpio_chip is ensured.
Remove the redundant check.
Signed-off-by: Tzung-Bi Shih <tzungbi@kernel.org>
---
v3:
- Pass struct gpio_chip * only.
v2: https://lore.kernel.org/all/20260203061059.975605-4-tzungbi@kernel.org
- No changes.
v1: https://lore.kernel.org/all/20260116081036.352286-9-tzungbi@kernel.org
drivers/gpio/gpiolib-sysfs.c | 10 ++--------
drivers/gpio/gpiolib-sysfs.h | 4 ++--
drivers/gpio/gpiolib.c | 2 +-
3 files changed, 5 insertions(+), 11 deletions(-)
diff --git a/drivers/gpio/gpiolib-sysfs.c b/drivers/gpio/gpiolib-sysfs.c
index cd553acf3055..97e269f30945 100644
--- a/drivers/gpio/gpiolib-sysfs.c
+++ b/drivers/gpio/gpiolib-sysfs.c
@@ -1048,11 +1048,11 @@ int gpiochip_sysfs_register(struct gpio_device *gdev)
return 0;
}
-void gpiochip_sysfs_unregister(struct gpio_device *gdev)
+void gpiochip_sysfs_unregister(struct gpio_chip *chip)
{
+ struct gpio_device *gdev = chip->gpiodev;
struct gpiodev_data *data;
struct gpio_desc *desc;
- struct gpio_chip *chip;
scoped_guard(mutex, &sysfs_lock) {
data = gdev_get_data(gdev);
@@ -1066,12 +1066,6 @@ void gpiochip_sysfs_unregister(struct gpio_device *gdev)
kfree(data);
}
- guard(srcu)(&gdev->srcu);
-
- chip = srcu_dereference(gdev->chip, &gdev->srcu);
- if (!chip)
- return;
-
/* unregister gpiod class devices owned by sysfs */
for_each_gpio_desc_with_flag(chip, desc, GPIOD_FLAG_SYSFS) {
gpiod_unexport(desc);
diff --git a/drivers/gpio/gpiolib-sysfs.h b/drivers/gpio/gpiolib-sysfs.h
index b794b396d6a5..9ee2f9dd268f 100644
--- a/drivers/gpio/gpiolib-sysfs.h
+++ b/drivers/gpio/gpiolib-sysfs.h
@@ -8,7 +8,7 @@ struct gpio_device;
#ifdef CONFIG_GPIO_SYSFS
int gpiochip_sysfs_register(struct gpio_device *gdev);
-void gpiochip_sysfs_unregister(struct gpio_device *gdev);
+void gpiochip_sysfs_unregister(struct gpio_chip *chip);
#else
@@ -17,7 +17,7 @@ static inline int gpiochip_sysfs_register(struct gpio_device *gdev)
return 0;
}
-static inline void gpiochip_sysfs_unregister(struct gpio_device *gdev)
+static inline void gpiochip_sysfs_unregister(struct gpio_chip *chip)
{
}
diff --git a/drivers/gpio/gpiolib.c b/drivers/gpio/gpiolib.c
index 1a3fd7600835..3ff57b1e51bf 100644
--- a/drivers/gpio/gpiolib.c
+++ b/drivers/gpio/gpiolib.c
@@ -1286,7 +1286,7 @@ void gpiochip_remove(struct gpio_chip *gc)
struct gpio_device *gdev = gc->gpiodev;
/* FIXME: should the legacy sysfs handling be moved to gpio_device? */
- gpiochip_sysfs_unregister(gdev);
+ gpiochip_sysfs_unregister(gc);
gpiochip_free_hogs(gc);
gpiochip_free_remaining_irqs(gc);
--
2.53.0.310.g728cabbaf7-goog
^ permalink raw reply related [flat|nested] 22+ messages in thread* Re: [PATCH v3 03/11] gpio: sysfs: Remove redundant check for struct gpio_chip
2026-02-13 9:29 ` [PATCH v3 03/11] gpio: sysfs: " Tzung-Bi Shih
@ 2026-02-20 7:51 ` Linus Walleij
2026-02-23 6:17 ` Tzung-Bi Shih
0 siblings, 1 reply; 22+ messages in thread
From: Linus Walleij @ 2026-02-20 7:51 UTC (permalink / raw)
To: Tzung-Bi Shih
Cc: Bartosz Golaszewski, Greg Kroah-Hartman, Rafael J. Wysocki,
Danilo Krummrich, Jonathan Corbet, Shuah Khan, Laurent Pinchart,
Wolfram Sang, Jason Gunthorpe, Johan Hovold, Paul E . McKenney,
Dan Williams, chrome-platform, linux-gpio, linux-kselftest,
linux-kernel
Hi Tzung-Bi,
thanks for your patch!
This is semantically correct, so the comments are not about that.
On Fri, Feb 13, 2026 at 10:31 AM Tzung-Bi Shih <tzungbi@kernel.org> wrote:
> gpiochip_sysfs_unregister() is only called by gpiochip_remove() where
> the struct gpio_chip is ensured.
>
> Remove the redundant check.
>
> Signed-off-by: Tzung-Bi Shih <tzungbi@kernel.org>
(...)
> -void gpiochip_sysfs_unregister(struct gpio_device *gdev)
> +void gpiochip_sysfs_unregister(struct gpio_chip *chip)
Here it is chip
> -static inline void gpiochip_sysfs_unregister(struct gpio_device *gdev)
> +static inline void gpiochip_sysfs_unregister(struct gpio_chip *chip)
And here.
> @@ -1286,7 +1286,7 @@ void gpiochip_remove(struct gpio_chip *gc)
> struct gpio_device *gdev = gc->gpiodev;
But you can see that we call it "gc" (gpiochip).
Chip is more ambiguous I think, can you use "gc" everywhere?
Either way, because the patch is so nice:
Reviewed-by: Linus Walleij <linusw@kernel.org>
Yours,
Linus Walleij
^ permalink raw reply [flat|nested] 22+ messages in thread
* Re: [PATCH v3 03/11] gpio: sysfs: Remove redundant check for struct gpio_chip
2026-02-20 7:51 ` Linus Walleij
@ 2026-02-23 6:17 ` Tzung-Bi Shih
0 siblings, 0 replies; 22+ messages in thread
From: Tzung-Bi Shih @ 2026-02-23 6:17 UTC (permalink / raw)
To: Linus Walleij
Cc: Bartosz Golaszewski, Greg Kroah-Hartman, Rafael J. Wysocki,
Danilo Krummrich, Jonathan Corbet, Shuah Khan, Laurent Pinchart,
Wolfram Sang, Jason Gunthorpe, Johan Hovold, Paul E . McKenney,
Dan Williams, chrome-platform, linux-gpio, linux-kselftest,
linux-kernel
On Fri, Feb 20, 2026 at 08:51:29AM +0100, Linus Walleij wrote:
> On Fri, Feb 13, 2026 at 10:31 AM Tzung-Bi Shih <tzungbi@kernel.org> wrote:
> > -void gpiochip_sysfs_unregister(struct gpio_device *gdev)
> > +void gpiochip_sysfs_unregister(struct gpio_chip *chip)
>
> Here it is chip
>
> > -static inline void gpiochip_sysfs_unregister(struct gpio_device *gdev)
> > +static inline void gpiochip_sysfs_unregister(struct gpio_chip *chip)
>
> And here.
>
> > @@ -1286,7 +1286,7 @@ void gpiochip_remove(struct gpio_chip *gc)
> > struct gpio_device *gdev = gc->gpiodev;
>
> But you can see that we call it "gc" (gpiochip).
>
> Chip is more ambiguous I think, can you use "gc" everywhere?
Ack, will fix in v4.
^ permalink raw reply [flat|nested] 22+ messages in thread
* [PATCH v3 04/11] gpio: Ensure struct gpio_chip for gpiochip_setup_dev()
2026-02-13 9:29 [PATCH v3 00/11] gpio: Adopt revocable mechanism for UAF prevention Tzung-Bi Shih
` (2 preceding siblings ...)
2026-02-13 9:29 ` [PATCH v3 03/11] gpio: sysfs: " Tzung-Bi Shih
@ 2026-02-13 9:29 ` Tzung-Bi Shih
2026-02-18 10:24 ` Bartosz Golaszewski
2026-02-13 9:29 ` [PATCH v3 05/11] gpio: cdev: Don't check struct gpio_chip in gpio_chrdev_open() Tzung-Bi Shih
` (7 subsequent siblings)
11 siblings, 1 reply; 22+ messages in thread
From: Tzung-Bi Shih @ 2026-02-13 9:29 UTC (permalink / raw)
To: Bartosz Golaszewski, Linus Walleij
Cc: Greg Kroah-Hartman, Rafael J. Wysocki, Danilo Krummrich,
Jonathan Corbet, Shuah Khan, Laurent Pinchart, Wolfram Sang,
Jason Gunthorpe, Johan Hovold, Paul E . McKenney, Dan Williams,
chrome-platform, tzungbi, linux-gpio, linux-kselftest,
linux-kernel
Ensure struct gpio_chip for gpiochip_setup_dev(). This eliminates a few
checks for struct gpio_chip.
Signed-off-by: Tzung-Bi Shih <tzungbi@kernel.org>
---
v3:
- Pass struct gpio_chip * only.
v2: https://lore.kernel.org/all/20260203061059.975605-5-tzungbi@kernel.org
- No changes.
v1: https://lore.kernel.org/all/20260116081036.352286-10-tzungbi@kernel.org
drivers/gpio/gpiolib-cdev.c | 12 ++----------
drivers/gpio/gpiolib-cdev.h | 2 +-
drivers/gpio/gpiolib-sysfs.c | 13 +++----------
drivers/gpio/gpiolib-sysfs.h | 4 ++--
drivers/gpio/gpiolib.c | 24 +++++++++++++++++-------
5 files changed, 25 insertions(+), 30 deletions(-)
diff --git a/drivers/gpio/gpiolib-cdev.c b/drivers/gpio/gpiolib-cdev.c
index 2adc3c070908..b028198adba8 100644
--- a/drivers/gpio/gpiolib-cdev.c
+++ b/drivers/gpio/gpiolib-cdev.c
@@ -2782,9 +2782,9 @@ static const struct file_operations gpio_fileops = {
#endif
};
-int gpiolib_cdev_register(struct gpio_device *gdev, dev_t devt)
+int gpiolib_cdev_register(struct gpio_chip *gc, dev_t devt)
{
- struct gpio_chip *gc;
+ struct gpio_device *gdev = gc->gpiodev;
int ret;
cdev_init(&gdev->chrdev, &gpio_fileops);
@@ -2802,14 +2802,6 @@ int gpiolib_cdev_register(struct gpio_device *gdev, dev_t devt)
return ret;
}
- guard(srcu)(&gdev->srcu);
- gc = srcu_dereference(gdev->chip, &gdev->srcu);
- if (!gc) {
- cdev_device_del(&gdev->chrdev, &gdev->dev);
- destroy_workqueue(gdev->line_state_wq);
- return -ENODEV;
- }
-
gpiochip_dbg(gc, "added GPIO chardev (%d:%d)\n", MAJOR(devt), gdev->id);
return 0;
diff --git a/drivers/gpio/gpiolib-cdev.h b/drivers/gpio/gpiolib-cdev.h
index b42644cbffb8..4a9cb3335d99 100644
--- a/drivers/gpio/gpiolib-cdev.h
+++ b/drivers/gpio/gpiolib-cdev.h
@@ -7,7 +7,7 @@
struct gpio_device;
-int gpiolib_cdev_register(struct gpio_device *gdev, dev_t devt);
+int gpiolib_cdev_register(struct gpio_chip *gc, dev_t devt);
void gpiolib_cdev_unregister(struct gpio_device *gdev);
#endif /* GPIOLIB_CDEV_H */
diff --git a/drivers/gpio/gpiolib-sysfs.c b/drivers/gpio/gpiolib-sysfs.c
index 97e269f30945..3fdcfa0c7d42 100644
--- a/drivers/gpio/gpiolib-sysfs.c
+++ b/drivers/gpio/gpiolib-sysfs.c
@@ -978,10 +978,10 @@ void gpiod_unexport(struct gpio_desc *desc)
}
EXPORT_SYMBOL_GPL(gpiod_unexport);
-int gpiochip_sysfs_register(struct gpio_device *gdev)
+int gpiochip_sysfs_register(struct gpio_chip *chip)
{
+ struct gpio_device *gdev = chip->gpiodev;
struct gpiodev_data *data;
- struct gpio_chip *chip;
struct device *parent;
int err;
@@ -994,12 +994,6 @@ int gpiochip_sysfs_register(struct gpio_device *gdev)
if (!class_is_registered(&gpio_class))
return 0;
- guard(srcu)(&gdev->srcu);
-
- chip = srcu_dereference(gdev->chip, &gdev->srcu);
- if (!chip)
- return -ENODEV;
-
/*
* For sysfs backward compatibility we need to preserve this
* preferred parenting to the gpio_chip parent field, if set.
@@ -1080,10 +1074,9 @@ void gpiochip_sysfs_unregister(struct gpio_chip *chip)
*/
static int gpiofind_sysfs_register(struct gpio_chip *gc, const void *data)
{
- struct gpio_device *gdev = gc->gpiodev;
int ret;
- ret = gpiochip_sysfs_register(gdev);
+ ret = gpiochip_sysfs_register(gc);
if (ret)
gpiochip_err(gc, "failed to register the sysfs entry: %d\n", ret);
diff --git a/drivers/gpio/gpiolib-sysfs.h b/drivers/gpio/gpiolib-sysfs.h
index 9ee2f9dd268f..aacc8e6454a4 100644
--- a/drivers/gpio/gpiolib-sysfs.h
+++ b/drivers/gpio/gpiolib-sysfs.h
@@ -7,12 +7,12 @@ struct gpio_device;
#ifdef CONFIG_GPIO_SYSFS
-int gpiochip_sysfs_register(struct gpio_device *gdev);
+int gpiochip_sysfs_register(struct gpio_chip *chip);
void gpiochip_sysfs_unregister(struct gpio_chip *chip);
#else
-static inline int gpiochip_sysfs_register(struct gpio_device *gdev)
+static inline int gpiochip_sysfs_register(struct gpio_chip *chip)
{
return 0;
}
diff --git a/drivers/gpio/gpiolib.c b/drivers/gpio/gpiolib.c
index 3ff57b1e51bf..74ee7bfb974c 100644
--- a/drivers/gpio/gpiolib.c
+++ b/drivers/gpio/gpiolib.c
@@ -882,14 +882,14 @@ static const struct device_type gpio_dev_type = {
};
#ifdef CONFIG_GPIO_CDEV
-#define gcdev_register(gdev, devt) gpiolib_cdev_register((gdev), (devt))
+#define gcdev_register(gc, devt) gpiolib_cdev_register((gc), (devt))
#define gcdev_unregister(gdev) gpiolib_cdev_unregister((gdev))
#else
/*
* gpiolib_cdev_register() indirectly calls device_add(), which is still
* required even when cdev is not selected.
*/
-#define gcdev_register(gdev, devt) device_add(&(gdev)->dev)
+#define gcdev_register(gc, devt) device_add(&(gc)->gpiodev->dev)
#define gcdev_unregister(gdev) device_del(&(gdev)->dev)
#endif
@@ -897,8 +897,9 @@ static const struct device_type gpio_dev_type = {
* An initial reference count has been held in gpiochip_add_data_with_key().
* The caller should drop the reference via gpio_device_put() on errors.
*/
-static int gpiochip_setup_dev(struct gpio_device *gdev)
+static int gpiochip_setup_dev(struct gpio_chip *gc)
{
+ struct gpio_device *gdev = gc->gpiodev;
struct fwnode_handle *fwnode = dev_fwnode(&gdev->dev);
int ret;
@@ -911,11 +912,11 @@ static int gpiochip_setup_dev(struct gpio_device *gdev)
if (fwnode && !fwnode->dev)
fwnode_dev_initialized(fwnode, false);
- ret = gcdev_register(gdev, gpio_devt);
+ ret = gcdev_register(gc, gpio_devt);
if (ret)
return ret;
- ret = gpiochip_sysfs_register(gdev);
+ ret = gpiochip_sysfs_register(gc);
if (ret)
goto err_remove_device;
@@ -962,13 +963,22 @@ static void machine_gpiochip_add(struct gpio_chip *gc)
static void gpiochip_setup_devs(void)
{
struct gpio_device *gdev;
+ struct gpio_chip *gc;
int ret;
guard(srcu)(&gpio_devices_srcu);
list_for_each_entry_srcu(gdev, &gpio_devices, list,
srcu_read_lock_held(&gpio_devices_srcu)) {
- ret = gpiochip_setup_dev(gdev);
+ guard(srcu)(&gdev->srcu);
+
+ gc = srcu_dereference(gdev->chip, &gdev->srcu);
+ if (!gc) {
+ dev_err(&gdev->dev, "Underlying GPIO chip is gone\n");
+ continue;
+ }
+
+ ret = gpiochip_setup_dev(gc);
if (ret) {
gpio_device_put(gdev);
dev_err(&gdev->dev,
@@ -1226,7 +1236,7 @@ int gpiochip_add_data_with_key(struct gpio_chip *gc, void *data,
* (i.e., `gpio_bus_type` is ready). Otherwise, defer until later.
*/
if (gpiolib_initialized) {
- ret = gpiochip_setup_dev(gdev);
+ ret = gpiochip_setup_dev(gc);
if (ret)
goto err_teardown_shared;
}
--
2.53.0.310.g728cabbaf7-goog
^ permalink raw reply related [flat|nested] 22+ messages in thread* Re: [PATCH v3 04/11] gpio: Ensure struct gpio_chip for gpiochip_setup_dev()
2026-02-13 9:29 ` [PATCH v3 04/11] gpio: Ensure struct gpio_chip for gpiochip_setup_dev() Tzung-Bi Shih
@ 2026-02-18 10:24 ` Bartosz Golaszewski
0 siblings, 0 replies; 22+ messages in thread
From: Bartosz Golaszewski @ 2026-02-18 10:24 UTC (permalink / raw)
To: Tzung-Bi Shih
Cc: Linus Walleij, Greg Kroah-Hartman, Rafael J. Wysocki,
Danilo Krummrich, Jonathan Corbet, Shuah Khan, Laurent Pinchart,
Wolfram Sang, Jason Gunthorpe, Johan Hovold, Paul E . McKenney,
Dan Williams, chrome-platform, linux-gpio, linux-kselftest,
linux-kernel
On Fri, Feb 13, 2026 at 10:31 AM Tzung-Bi Shih <tzungbi@kernel.org> wrote:
>
> Ensure struct gpio_chip for gpiochip_setup_dev(). This eliminates a few
> checks for struct gpio_chip.
>
> Signed-off-by: Tzung-Bi Shih <tzungbi@kernel.org>
> ---
>
> -int gpiochip_sysfs_register(struct gpio_device *gdev)
> +int gpiochip_sysfs_register(struct gpio_chip *chip)
> {
> + struct gpio_device *gdev = chip->gpiodev;
> struct gpiodev_data *data;
> - struct gpio_chip *chip;
> struct device *parent;
> int err;
>
> @@ -994,12 +994,6 @@ int gpiochip_sysfs_register(struct gpio_device *gdev)
> if (!class_is_registered(&gpio_class))
> return 0;
>
> - guard(srcu)(&gdev->srcu);
> -
> - chip = srcu_dereference(gdev->chip, &gdev->srcu);
> - if (!chip)
> - return -ENODEV;
I think it would make sense to add lockdep checks when removing guards
in non-static functions.
Bart
^ permalink raw reply [flat|nested] 22+ messages in thread
* [PATCH v3 05/11] gpio: cdev: Don't check struct gpio_chip in gpio_chrdev_open()
2026-02-13 9:29 [PATCH v3 00/11] gpio: Adopt revocable mechanism for UAF prevention Tzung-Bi Shih
` (3 preceding siblings ...)
2026-02-13 9:29 ` [PATCH v3 04/11] gpio: Ensure struct gpio_chip for gpiochip_setup_dev() Tzung-Bi Shih
@ 2026-02-13 9:29 ` Tzung-Bi Shih
2026-02-20 7:51 ` Linus Walleij
2026-02-13 9:29 ` [PATCH v3 06/11] selftests: gpio: Add gpio-cdev-uaf tests Tzung-Bi Shih
` (6 subsequent siblings)
11 siblings, 1 reply; 22+ messages in thread
From: Tzung-Bi Shih @ 2026-02-13 9:29 UTC (permalink / raw)
To: Bartosz Golaszewski, Linus Walleij
Cc: Greg Kroah-Hartman, Rafael J. Wysocki, Danilo Krummrich,
Jonathan Corbet, Shuah Khan, Laurent Pinchart, Wolfram Sang,
Jason Gunthorpe, Johan Hovold, Paul E . McKenney, Dan Williams,
chrome-platform, tzungbi, linux-gpio, linux-kselftest,
linux-kernel
It's harmless even if: chrdev_open() and cdev_device_del() run at the
same time, and gpio_chrdev_open() gets called after the underlying GPIO
chip has gone. The subsequent file operations check the availability
of struct gpio_chip anyway.
Don't check struct gpio_chip in gpio_chrdev_open().
Signed-off-by: Tzung-Bi Shih <tzungbi@kernel.org>
---
v3:
- No changes.
v2: https://lore.kernel.org/all/20260203061059.975605-6-tzungbi@kernel.org
- No changes.
v1: https://lore.kernel.org/all/20260116081036.352286-11-tzungbi@kernel.org
drivers/gpio/gpiolib-cdev.c | 6 ------
1 file changed, 6 deletions(-)
diff --git a/drivers/gpio/gpiolib-cdev.c b/drivers/gpio/gpiolib-cdev.c
index b028198adba8..2e3484a89a3b 100644
--- a/drivers/gpio/gpiolib-cdev.c
+++ b/drivers/gpio/gpiolib-cdev.c
@@ -2689,12 +2689,6 @@ static int gpio_chrdev_open(struct inode *inode, struct file *file)
struct gpio_chardev_data *cdev;
int ret = -ENOMEM;
- guard(srcu)(&gdev->srcu);
-
- /* Fail on open if the backing gpiochip is gone */
- if (!rcu_access_pointer(gdev->chip))
- return -ENODEV;
-
cdev = kzalloc(sizeof(*cdev), GFP_KERNEL);
if (!cdev)
return -ENOMEM;
--
2.53.0.310.g728cabbaf7-goog
^ permalink raw reply related [flat|nested] 22+ messages in thread* Re: [PATCH v3 05/11] gpio: cdev: Don't check struct gpio_chip in gpio_chrdev_open()
2026-02-13 9:29 ` [PATCH v3 05/11] gpio: cdev: Don't check struct gpio_chip in gpio_chrdev_open() Tzung-Bi Shih
@ 2026-02-20 7:51 ` Linus Walleij
0 siblings, 0 replies; 22+ messages in thread
From: Linus Walleij @ 2026-02-20 7:51 UTC (permalink / raw)
To: Tzung-Bi Shih
Cc: Bartosz Golaszewski, Greg Kroah-Hartman, Rafael J. Wysocki,
Danilo Krummrich, Jonathan Corbet, Shuah Khan, Laurent Pinchart,
Wolfram Sang, Jason Gunthorpe, Johan Hovold, Paul E . McKenney,
Dan Williams, chrome-platform, linux-gpio, linux-kselftest,
linux-kernel
On Fri, Feb 13, 2026 at 10:31 AM Tzung-Bi Shih <tzungbi@kernel.org> wrote:
> It's harmless even if: chrdev_open() and cdev_device_del() run at the
> same time, and gpio_chrdev_open() gets called after the underlying GPIO
> chip has gone. The subsequent file operations check the availability
> of struct gpio_chip anyway.
>
> Don't check struct gpio_chip in gpio_chrdev_open().
>
> Signed-off-by: Tzung-Bi Shih <tzungbi@kernel.org>
Reviewed-by: Linus Walleij <linusw@kernel.org>
Yours,
Linus Walleij
^ permalink raw reply [flat|nested] 22+ messages in thread
* [PATCH v3 06/11] selftests: gpio: Add gpio-cdev-uaf tests
2026-02-13 9:29 [PATCH v3 00/11] gpio: Adopt revocable mechanism for UAF prevention Tzung-Bi Shih
` (4 preceding siblings ...)
2026-02-13 9:29 ` [PATCH v3 05/11] gpio: cdev: Don't check struct gpio_chip in gpio_chrdev_open() Tzung-Bi Shih
@ 2026-02-13 9:29 ` Tzung-Bi Shih
2026-02-20 7:52 ` Linus Walleij
2026-02-13 9:29 ` [PATCH v3 07/11] gpio: Add revocable provider handle for struct gpio_chip Tzung-Bi Shih
` (5 subsequent siblings)
11 siblings, 1 reply; 22+ messages in thread
From: Tzung-Bi Shih @ 2026-02-13 9:29 UTC (permalink / raw)
To: Bartosz Golaszewski, Linus Walleij
Cc: Greg Kroah-Hartman, Rafael J. Wysocki, Danilo Krummrich,
Jonathan Corbet, Shuah Khan, Laurent Pinchart, Wolfram Sang,
Jason Gunthorpe, Johan Hovold, Paul E . McKenney, Dan Williams,
chrome-platform, tzungbi, linux-gpio, linux-kselftest,
linux-kernel
Add tests for gpiolib-cdev to make sure accessing to dangling resources
via the opening file descriptor won't crash the system after the
underlying resource providers have gone.
Signed-off-by: Tzung-Bi Shih <tzungbi@kernel.org>
---
v3:
- No changes.
v2: https://lore.kernel.org/all/20260203061059.975605-7-tzungbi@kernel.org
- Remove fdinfo test which is redundant.
v1: https://lore.kernel.org/all/20260116081036.352286-12-tzungbi@kernel.org
tools/testing/selftests/gpio/Makefile | 5 +-
tools/testing/selftests/gpio/gpio-cdev-uaf.c | 292 ++++++++++++++++++
tools/testing/selftests/gpio/gpio-cdev-uaf.sh | 63 ++++
3 files changed, 358 insertions(+), 2 deletions(-)
create mode 100644 tools/testing/selftests/gpio/gpio-cdev-uaf.c
create mode 100755 tools/testing/selftests/gpio/gpio-cdev-uaf.sh
diff --git a/tools/testing/selftests/gpio/Makefile b/tools/testing/selftests/gpio/Makefile
index 7bfe315f7001..741ab21e1260 100644
--- a/tools/testing/selftests/gpio/Makefile
+++ b/tools/testing/selftests/gpio/Makefile
@@ -1,8 +1,9 @@
# SPDX-License-Identifier: GPL-2.0
-TEST_PROGS := gpio-mockup.sh gpio-sim.sh gpio-aggregator.sh
+TEST_PROGS := gpio-mockup.sh gpio-sim.sh gpio-aggregator.sh gpio-cdev-uaf.sh
TEST_FILES := gpio-mockup-sysfs.sh
-TEST_GEN_PROGS_EXTENDED := gpio-mockup-cdev gpio-chip-info gpio-line-name
+TEST_GEN_PROGS_EXTENDED := gpio-mockup-cdev gpio-chip-info gpio-line-name \
+ gpio-cdev-uaf
CFLAGS += -O2 -g -Wall $(KHDR_INCLUDES)
include ../lib.mk
diff --git a/tools/testing/selftests/gpio/gpio-cdev-uaf.c b/tools/testing/selftests/gpio/gpio-cdev-uaf.c
new file mode 100644
index 000000000000..765d3cc4f0ef
--- /dev/null
+++ b/tools/testing/selftests/gpio/gpio-cdev-uaf.c
@@ -0,0 +1,292 @@
+// SPDX-License-Identifier: GPL-2.0-or-later
+/*
+ * GPIO character device helper for UAF tests.
+ *
+ * Copyright 2026 Google LLC
+ */
+
+#include <errno.h>
+#include <fcntl.h>
+#include <linux/gpio.h>
+#include <poll.h>
+#include <stdio.h>
+#include <stdlib.h>
+#include <string.h>
+#include <sys/ioctl.h>
+#include <sys/stat.h>
+#include <sys/types.h>
+#include <unistd.h>
+
+#define CONFIGFS_DIR "/sys/kernel/config/gpio-sim"
+#define PROCFS_DIR "/proc"
+
+static void print_usage(void)
+{
+ printf("usage:\n");
+ printf(" gpio-cdev-uaf [chip|handle|event|req] [poll|read|ioctl]\n");
+}
+
+static int _create_chip(const char *name, int create)
+{
+ char path[64];
+
+ snprintf(path, sizeof(path), CONFIGFS_DIR "/%s", name);
+
+ if (create)
+ return mkdir(path, 0755);
+ else
+ return rmdir(path);
+}
+
+static int create_chip(const char *name)
+{
+ return _create_chip(name, 1);
+}
+
+static void remove_chip(const char *name)
+{
+ _create_chip(name, 0);
+}
+
+static int _create_bank(const char *chip_name, const char *name, int create)
+{
+ char path[64];
+
+ snprintf(path, sizeof(path), CONFIGFS_DIR "/%s/%s", chip_name, name);
+
+ if (create)
+ return mkdir(path, 0755);
+ else
+ return rmdir(path);
+}
+
+static int create_bank(const char *chip_name, const char *name)
+{
+ return _create_bank(chip_name, name, 1);
+}
+
+static void remove_bank(const char *chip_name, const char *name)
+{
+ _create_bank(chip_name, name, 0);
+}
+
+static int _enable_chip(const char *name, int enable)
+{
+ char path[64];
+ int fd, ret;
+
+ snprintf(path, sizeof(path), CONFIGFS_DIR "/%s/live", name);
+
+ fd = open(path, O_WRONLY);
+ if (fd == -1)
+ return fd;
+
+ if (enable)
+ ret = write(fd, "1", 1);
+ else
+ ret = write(fd, "0", 1);
+
+ close(fd);
+ return ret == 1 ? 0 : -1;
+}
+
+static int enable_chip(const char *name)
+{
+ return _enable_chip(name, 1);
+}
+
+static void disable_chip(const char *name)
+{
+ _enable_chip(name, 0);
+}
+
+static int open_chip(const char *chip_name, const char *bank_name)
+{
+ char path[64], dev_name[32];
+ int ret, fd;
+
+ ret = create_chip(chip_name);
+ if (ret) {
+ fprintf(stderr, "failed to create chip\n");
+ return ret;
+ }
+
+ ret = create_bank(chip_name, bank_name);
+ if (ret) {
+ fprintf(stderr, "failed to create bank\n");
+ goto err_remove_chip;
+ }
+
+ ret = enable_chip(chip_name);
+ if (ret) {
+ fprintf(stderr, "failed to enable chip\n");
+ goto err_remove_bank;
+ }
+
+ snprintf(path, sizeof(path), CONFIGFS_DIR "/%s/%s/chip_name",
+ chip_name, bank_name);
+
+ fd = open(path, O_RDONLY);
+ if (fd == -1) {
+ ret = fd;
+ fprintf(stderr, "failed to open %s\n", path);
+ goto err_disable_chip;
+ }
+
+ ret = read(fd, dev_name, sizeof(dev_name) - 1);
+ close(fd);
+ if (ret == -1) {
+ fprintf(stderr, "failed to read %s\n", path);
+ goto err_disable_chip;
+ }
+ dev_name[ret] = '\0';
+ if (ret && dev_name[ret - 1] == '\n')
+ dev_name[ret - 1] = '\0';
+
+ snprintf(path, sizeof(path), "/dev/%s", dev_name);
+
+ fd = open(path, O_RDWR);
+ if (fd == -1) {
+ ret = fd;
+ fprintf(stderr, "failed to open %s\n", path);
+ goto err_disable_chip;
+ }
+
+ return fd;
+err_disable_chip:
+ disable_chip(chip_name);
+err_remove_bank:
+ remove_bank(chip_name, bank_name);
+err_remove_chip:
+ remove_chip(chip_name);
+ return ret;
+}
+
+static void close_chip(const char *chip_name, const char *bank_name)
+{
+ disable_chip(chip_name);
+ remove_bank(chip_name, bank_name);
+ remove_chip(chip_name);
+}
+
+static int test_poll(int fd)
+{
+ struct pollfd pfds;
+
+ pfds.fd = fd;
+ pfds.events = POLLIN;
+ pfds.revents = 0;
+
+ if (poll(&pfds, 1, 0) == -1)
+ return -1;
+
+ return (pfds.revents & ~(POLLHUP | POLLERR)) ? -1 : 0;
+}
+
+static int test_read(int fd)
+{
+ char data;
+
+ if (read(fd, &data, 1) == -1 && errno == ENODEV)
+ return 0;
+ return -1;
+}
+
+static int test_ioctl(int fd)
+{
+ if (ioctl(fd, 0, NULL) == -1 && errno == ENODEV)
+ return 0;
+ return -1;
+}
+
+int main(int argc, char **argv)
+{
+ int cfd, fd, ret;
+ int (*test_func)(int);
+
+ if (argc != 3) {
+ print_usage();
+ return EXIT_FAILURE;
+ }
+
+ if (strcmp(argv[1], "chip") == 0 ||
+ strcmp(argv[1], "event") == 0 ||
+ strcmp(argv[1], "req") == 0) {
+ if (strcmp(argv[2], "poll") &&
+ strcmp(argv[2], "read") &&
+ strcmp(argv[2], "ioctl")) {
+ fprintf(stderr, "unknown command: %s\n", argv[2]);
+ return EXIT_FAILURE;
+ }
+ } else if (strcmp(argv[1], "handle") == 0) {
+ if (strcmp(argv[2], "ioctl")) {
+ fprintf(stderr, "unknown command: %s\n", argv[2]);
+ return EXIT_FAILURE;
+ }
+ } else {
+ fprintf(stderr, "unknown command: %s\n", argv[1]);
+ return EXIT_FAILURE;
+ }
+
+ if (strcmp(argv[2], "poll") == 0)
+ test_func = test_poll;
+ else if (strcmp(argv[2], "read") == 0)
+ test_func = test_read;
+ else /* strcmp(argv[2], "ioctl") == 0 */
+ test_func = test_ioctl;
+
+ cfd = open_chip("chip", "bank");
+ if (cfd == -1) {
+ fprintf(stderr, "failed to open chip\n");
+ return EXIT_FAILURE;
+ }
+
+ /* Step 1: Hold a FD to the test target. */
+ if (strcmp(argv[1], "chip") == 0) {
+ fd = cfd;
+ } else if (strcmp(argv[1], "handle") == 0) {
+ struct gpiohandle_request req = {0};
+
+ req.lines = 1;
+ if (ioctl(cfd, GPIO_GET_LINEHANDLE_IOCTL, &req) == -1) {
+ fprintf(stderr, "failed to get handle FD\n");
+ goto err_close_chip;
+ }
+
+ close(cfd);
+ fd = req.fd;
+ } else if (strcmp(argv[1], "event") == 0) {
+ struct gpioevent_request req = {0};
+
+ if (ioctl(cfd, GPIO_GET_LINEEVENT_IOCTL, &req) == -1) {
+ fprintf(stderr, "failed to get event FD\n");
+ goto err_close_chip;
+ }
+
+ close(cfd);
+ fd = req.fd;
+ } else { /* strcmp(argv[1], "req") == 0 */
+ struct gpio_v2_line_request req = {0};
+
+ req.num_lines = 1;
+ if (ioctl(cfd, GPIO_V2_GET_LINE_IOCTL, &req) == -1) {
+ fprintf(stderr, "failed to get req FD\n");
+ goto err_close_chip;
+ }
+
+ close(cfd);
+ fd = req.fd;
+ }
+
+ /* Step 2: Free the chip. */
+ close_chip("chip", "bank");
+
+ /* Step 3: Access the dangling FD to trigger UAF. */
+ ret = test_func(fd);
+ close(fd);
+ return ret ? EXIT_FAILURE : EXIT_SUCCESS;
+err_close_chip:
+ close(cfd);
+ close_chip("chip", "bank");
+ return EXIT_FAILURE;
+}
diff --git a/tools/testing/selftests/gpio/gpio-cdev-uaf.sh b/tools/testing/selftests/gpio/gpio-cdev-uaf.sh
new file mode 100755
index 000000000000..6e47533019cf
--- /dev/null
+++ b/tools/testing/selftests/gpio/gpio-cdev-uaf.sh
@@ -0,0 +1,63 @@
+#!/bin/sh
+# SPDX-License-Identifier: GPL-2.0
+# Copyright 2026 Google LLC
+
+BASE_DIR=`dirname $0`
+MODULE="gpio-cdev-uaf"
+
+fail() {
+ echo "$*" >&2
+ echo "GPIO $MODULE test FAIL"
+ exit 1
+}
+
+skip() {
+ echo "$*" >&2
+ echo "GPIO $MODULE test SKIP"
+ exit 4
+}
+
+# Load the gpio-sim module. This will pull in configfs if needed too.
+modprobe gpio-sim || skip "unable to load the gpio-sim module"
+# Make sure configfs is mounted at /sys/kernel/config. Wait a bit if needed.
+for _ in `seq 5`; do
+ mountpoint -q /sys/kernel/config && break
+ mount -t configfs none /sys/kernel/config
+ sleep 0.1
+done
+mountpoint -q /sys/kernel/config || \
+ skip "configfs not mounted at /sys/kernel/config"
+
+echo "1. GPIO"
+
+echo "1.1. poll"
+$BASE_DIR/gpio-cdev-uaf chip poll || fail "failed to test chip poll"
+echo "1.2. read"
+$BASE_DIR/gpio-cdev-uaf chip read || fail "failed to test chip read"
+echo "1.3. ioctl"
+$BASE_DIR/gpio-cdev-uaf chip ioctl || fail "failed to test chip ioctl"
+
+echo "2. linehandle"
+
+echo "2.1. ioctl"
+$BASE_DIR/gpio-cdev-uaf handle ioctl || fail "failed to test handle ioctl"
+
+echo "3. lineevent"
+
+echo "3.1. read"
+$BASE_DIR/gpio-cdev-uaf event read || fail "failed to test event read"
+echo "3.2. poll"
+$BASE_DIR/gpio-cdev-uaf event poll || fail "failed to test event poll"
+echo "3.3. ioctl"
+$BASE_DIR/gpio-cdev-uaf event ioctl || fail "failed to test event ioctl"
+
+echo "4. linereq"
+
+echo "4.1. read"
+$BASE_DIR/gpio-cdev-uaf req read || fail "failed to test req read"
+echo "4.2. poll"
+$BASE_DIR/gpio-cdev-uaf req poll || fail "failed to test req poll"
+echo "4.3. ioctl"
+$BASE_DIR/gpio-cdev-uaf req ioctl || fail "failed to test req ioctl"
+
+echo "GPIO $MODULE test PASS"
--
2.53.0.310.g728cabbaf7-goog
^ permalink raw reply related [flat|nested] 22+ messages in thread* Re: [PATCH v3 06/11] selftests: gpio: Add gpio-cdev-uaf tests
2026-02-13 9:29 ` [PATCH v3 06/11] selftests: gpio: Add gpio-cdev-uaf tests Tzung-Bi Shih
@ 2026-02-20 7:52 ` Linus Walleij
0 siblings, 0 replies; 22+ messages in thread
From: Linus Walleij @ 2026-02-20 7:52 UTC (permalink / raw)
To: Tzung-Bi Shih
Cc: Bartosz Golaszewski, Greg Kroah-Hartman, Rafael J. Wysocki,
Danilo Krummrich, Jonathan Corbet, Shuah Khan, Laurent Pinchart,
Wolfram Sang, Jason Gunthorpe, Johan Hovold, Paul E . McKenney,
Dan Williams, chrome-platform, linux-gpio, linux-kselftest,
linux-kernel
On Fri, Feb 13, 2026 at 10:31 AM Tzung-Bi Shih <tzungbi@kernel.org> wrote:
> Add tests for gpiolib-cdev to make sure accessing to dangling resources
> via the opening file descriptor won't crash the system after the
> underlying resource providers have gone.
>
> Signed-off-by: Tzung-Bi Shih <tzungbi@kernel.org>
Neat!
Reviewed-by: Linus Walleij <linusw@kernel.org>
Yours,
Linus Walleij
^ permalink raw reply [flat|nested] 22+ messages in thread
* [PATCH v3 07/11] gpio: Add revocable provider handle for struct gpio_chip
2026-02-13 9:29 [PATCH v3 00/11] gpio: Adopt revocable mechanism for UAF prevention Tzung-Bi Shih
` (5 preceding siblings ...)
2026-02-13 9:29 ` [PATCH v3 06/11] selftests: gpio: Add gpio-cdev-uaf tests Tzung-Bi Shih
@ 2026-02-13 9:29 ` Tzung-Bi Shih
2026-02-18 10:28 ` Bartosz Golaszewski
2026-02-13 9:29 ` [PATCH v3 08/11] gpio: cdev: Leverage revocable for accessing " Tzung-Bi Shih
` (4 subsequent siblings)
11 siblings, 1 reply; 22+ messages in thread
From: Tzung-Bi Shih @ 2026-02-13 9:29 UTC (permalink / raw)
To: Bartosz Golaszewski, Linus Walleij
Cc: Greg Kroah-Hartman, Rafael J. Wysocki, Danilo Krummrich,
Jonathan Corbet, Shuah Khan, Laurent Pinchart, Wolfram Sang,
Jason Gunthorpe, Johan Hovold, Paul E . McKenney, Dan Williams,
chrome-platform, tzungbi, linux-gpio, linux-kselftest,
linux-kernel
The underlying chip can be removed asynchronously. `gdev->srcu` is used
to ensure the synchronization before accessing `gdev->chip`.
Revocable encapsulates the details. Add revocable provider handle for
the corresponding struct gpio_chip in struct gpio_device so that it can
start to hide the synchronization details.
Signed-off-by: Tzung-Bi Shih <tzungbi@kernel.org>
---
v3:
- Change revocable API usages accordingly.
v2: https://lore.kernel.org/all/20260203061059.975605-8-tzungbi@kernel.org
- Change usages accordingly after applying
https://lore.kernel.org/all/20260129143733.45618-2-tzungbi@kernel.org.
- Add __rcu for `chip_rp`.
- Pass pointer of pointer to revocable_provider_revoke().
- Rebase accordingly after applying
https://lore.kernel.org/all/20260203060210.972243-1-tzungbi@kernel.org.
v1: https://lore.kernel.org/all/20260116081036.352286-13-tzungbi@kernel.org
drivers/gpio/gpiolib.c | 16 ++++++++++++++--
drivers/gpio/gpiolib.h | 2 ++
2 files changed, 16 insertions(+), 2 deletions(-)
diff --git a/drivers/gpio/gpiolib.c b/drivers/gpio/gpiolib.c
index 74ee7bfb974c..7ef4dc4e6e9e 100644
--- a/drivers/gpio/gpiolib.c
+++ b/drivers/gpio/gpiolib.c
@@ -22,6 +22,7 @@
#include <linux/nospec.h>
#include <linux/of.h>
#include <linux/pinctrl/consumer.h>
+#include <linux/revocable.h>
#include <linux/seq_file.h>
#include <linux/slab.h>
#include <linux/srcu.h>
@@ -869,6 +870,8 @@ static void gpiodev_release(struct device *dev)
synchronize_srcu(&gdev->desc_srcu);
cleanup_srcu_struct(&gdev->desc_srcu);
+ if (gdev->chip_rp)
+ revocable_put(gdev->chip_rp);
ida_free(&gpio_ida, gdev->id);
kfree_const(gdev->label);
kfree(gdev->descs);
@@ -1115,6 +1118,12 @@ int gpiochip_add_data_with_key(struct gpio_chip *gc, void *data,
goto err_put_device;
}
+ gdev->chip_rp = revocable_alloc(gc);
+ if (!gdev->chip_rp) {
+ ret = -ENOMEM;
+ goto err_put_device;
+ }
+
gdev->can_sleep = gc->can_sleep;
rwlock_init(&gdev->line_state_lock);
RAW_INIT_NOTIFIER_HEAD(&gdev->line_state_notifier);
@@ -1144,7 +1153,7 @@ int gpiochip_add_data_with_key(struct gpio_chip *gc, void *data,
if (base < 0) {
ret = base;
base = 0;
- goto err_put_device;
+ goto err_free_rp;
}
/*
@@ -1164,7 +1173,7 @@ int gpiochip_add_data_with_key(struct gpio_chip *gc, void *data,
ret = gpiodev_add_to_list_unlocked(gdev);
if (ret) {
gpiochip_err(gc, "GPIO integer space overlap, cannot add chip\n");
- goto err_put_device;
+ goto err_free_rp;
}
}
@@ -1261,6 +1270,8 @@ int gpiochip_add_data_with_key(struct gpio_chip *gc, void *data,
scoped_guard(mutex, &gpio_devices_lock)
list_del_rcu(&gdev->list);
synchronize_srcu(&gpio_devices_srcu);
+err_free_rp:
+ revocable_revoke(gdev->chip_rp);
err_put_device:
gpio_device_put(gdev);
goto err_print_message;
@@ -1307,6 +1318,7 @@ void gpiochip_remove(struct gpio_chip *gc)
/* Numb the device, cancelling all outstanding operations */
rcu_assign_pointer(gdev->chip, NULL);
synchronize_srcu(&gdev->srcu);
+ revocable_revoke(gdev->chip_rp);
gpio_device_teardown_shared(gdev);
gpiochip_irqchip_remove(gc);
acpi_gpiochip_remove(gc);
diff --git a/drivers/gpio/gpiolib.h b/drivers/gpio/gpiolib.h
index 3abb90385829..56643f40e87e 100644
--- a/drivers/gpio/gpiolib.h
+++ b/drivers/gpio/gpiolib.h
@@ -52,6 +52,7 @@
* @device_notifier: used to notify character device wait queues about the GPIO
* device being unregistered
* @srcu: protects the pointer to the underlying GPIO chip
+ * @chip_rp: revocable provider handle for the corresponding struct gpio_chip.
* @pin_ranges: range of pins served by the GPIO driver
*
* This state container holds most of the runtime variable data
@@ -79,6 +80,7 @@ struct gpio_device {
struct workqueue_struct *line_state_wq;
struct blocking_notifier_head device_notifier;
struct srcu_struct srcu;
+ struct revocable *chip_rp;
#ifdef CONFIG_PINCTRL
/*
--
2.53.0.310.g728cabbaf7-goog
^ permalink raw reply related [flat|nested] 22+ messages in thread* Re: [PATCH v3 07/11] gpio: Add revocable provider handle for struct gpio_chip
2026-02-13 9:29 ` [PATCH v3 07/11] gpio: Add revocable provider handle for struct gpio_chip Tzung-Bi Shih
@ 2026-02-18 10:28 ` Bartosz Golaszewski
0 siblings, 0 replies; 22+ messages in thread
From: Bartosz Golaszewski @ 2026-02-18 10:28 UTC (permalink / raw)
To: Tzung-Bi Shih
Cc: Linus Walleij, Greg Kroah-Hartman, Rafael J. Wysocki,
Danilo Krummrich, Jonathan Corbet, Shuah Khan, Laurent Pinchart,
Wolfram Sang, Jason Gunthorpe, Johan Hovold, Paul E . McKenney,
Dan Williams, chrome-platform, linux-gpio, linux-kselftest,
linux-kernel
On Fri, Feb 13, 2026 at 10:31 AM Tzung-Bi Shih <tzungbi@kernel.org> wrote:
>
> The underlying chip can be removed asynchronously. `gdev->srcu` is used
> to ensure the synchronization before accessing `gdev->chip`.
>
> Revocable encapsulates the details. Add revocable provider handle for
> the corresponding struct gpio_chip in struct gpio_device so that it can
> start to hide the synchronization details.
>
> diff --git a/drivers/gpio/gpiolib.h b/drivers/gpio/gpiolib.h
> index 3abb90385829..56643f40e87e 100644
> --- a/drivers/gpio/gpiolib.h
> +++ b/drivers/gpio/gpiolib.h
> @@ -52,6 +52,7 @@
> * @device_notifier: used to notify character device wait queues about the GPIO
> * device being unregistered
> * @srcu: protects the pointer to the underlying GPIO chip
> + * @chip_rp: revocable provider handle for the corresponding struct gpio_chip.
> * @pin_ranges: range of pins served by the GPIO driver
> *
> * This state container holds most of the runtime variable data
> @@ -79,6 +80,7 @@ struct gpio_device {
> struct workqueue_struct *line_state_wq;
> struct blocking_notifier_head device_notifier;
> struct srcu_struct srcu;
> + struct revocable *chip_rp;
I like it much better than with __rcu but see my comment under the
revocable series: is there any reason why we can't do:
struct revocable chip_rp;
?
Bartosz
^ permalink raw reply [flat|nested] 22+ messages in thread
* [PATCH v3 08/11] gpio: cdev: Leverage revocable for accessing struct gpio_chip
2026-02-13 9:29 [PATCH v3 00/11] gpio: Adopt revocable mechanism for UAF prevention Tzung-Bi Shih
` (6 preceding siblings ...)
2026-02-13 9:29 ` [PATCH v3 07/11] gpio: Add revocable provider handle for struct gpio_chip Tzung-Bi Shih
@ 2026-02-13 9:29 ` Tzung-Bi Shih
2026-02-13 9:29 ` [PATCH v3 09/11] gpio: Remove gpio_chip_guard by using revocable Tzung-Bi Shih
` (3 subsequent siblings)
11 siblings, 0 replies; 22+ messages in thread
From: Tzung-Bi Shih @ 2026-02-13 9:29 UTC (permalink / raw)
To: Bartosz Golaszewski, Linus Walleij
Cc: Greg Kroah-Hartman, Rafael J. Wysocki, Danilo Krummrich,
Jonathan Corbet, Shuah Khan, Laurent Pinchart, Wolfram Sang,
Jason Gunthorpe, Johan Hovold, Paul E . McKenney, Dan Williams,
chrome-platform, tzungbi, linux-gpio, linux-kselftest,
linux-kernel
Struct gpio_device now provides a revocable provider to the underlying
struct gpio_chip. Leverage revocable for accessing the struct
gpio_chip.
Signed-off-by: Tzung-Bi Shih <tzungbi@kernel.org>
---
v3:
- Change revocable API usages accordingly.
v2: https://lore.kernel.org/all/20260203061059.975605-9-tzungbi@kernel.org
- Change usages accordingly after applying
https://lore.kernel.org/all/20260129143733.45618-4-tzungbi@kernel.org.
- Preserve a local storage for `struct revocable`.
- Combine multiple patches (see "v1:").
- Fix a race condition reported in
https://lore.kernel.org/all/CAMRc=McDaipt85OHm0MksLkuf6E79dY1uNSqqbcJnoQTUs81Pw@mail.gmail.com/
and analyzed in
https://lore.kernel.org/all/aXEEUWwkxHZzCnaI@tzungbi-laptop/.
In v1, the blocking_notifier_chain_unregister() will be skipped if the
chip has been removed, leading an UAF in gpiolib_cdev_unregister().
In v2, it won't skip blocking_notifier_chain_unregister().
v1:
- https://lore.kernel.org/all/20260116081036.352286-14-tzungbi@kernel.org
- https://lore.kernel.org/all/20260116081036.352286-15-tzungbi@kernel.org
- https://lore.kernel.org/all/20260116081036.352286-16-tzungbi@kernel.org
- https://lore.kernel.org/all/20260116081036.352286-17-tzungbi@kernel.org
- https://lore.kernel.org/all/20260116081036.352286-18-tzungbi@kernel.org
drivers/gpio/gpiolib-cdev.c | 68 ++++++++++++++-----------------------
1 file changed, 26 insertions(+), 42 deletions(-)
diff --git a/drivers/gpio/gpiolib-cdev.c b/drivers/gpio/gpiolib-cdev.c
index 2e3484a89a3b..b491e2737ef2 100644
--- a/drivers/gpio/gpiolib-cdev.c
+++ b/drivers/gpio/gpiolib-cdev.c
@@ -22,6 +22,7 @@
#include <linux/overflow.h>
#include <linux/pinctrl/consumer.h>
#include <linux/poll.h>
+#include <linux/revocable.h>
#include <linux/seq_file.h>
#include <linux/spinlock.h>
#include <linux/string.h>
@@ -210,11 +211,9 @@ static long linehandle_ioctl(struct file *file, unsigned int cmd,
DECLARE_BITMAP(vals, GPIOHANDLES_MAX);
unsigned int i;
int ret;
+ struct gpio_chip *gc;
- guard(srcu)(&lh->gdev->srcu);
-
- if (!rcu_access_pointer(lh->gdev->chip))
- return -ENODEV;
+ revocable_try_access_or_return(lh->gdev->chip_rp, gc);
switch (cmd) {
case GPIOHANDLE_GET_LINE_VALUES_IOCTL:
@@ -1432,11 +1431,9 @@ static long linereq_ioctl(struct file *file, unsigned int cmd,
{
struct linereq *lr = file->private_data;
void __user *ip = (void __user *)arg;
+ struct gpio_chip *gc;
- guard(srcu)(&lr->gdev->srcu);
-
- if (!rcu_access_pointer(lr->gdev->chip))
- return -ENODEV;
+ revocable_try_access_or_return(lr->gdev->chip_rp, gc);
switch (cmd) {
case GPIO_V2_LINE_GET_VALUES_IOCTL:
@@ -1463,10 +1460,10 @@ static __poll_t linereq_poll(struct file *file,
{
struct linereq *lr = file->private_data;
__poll_t events = 0;
+ struct gpio_chip *gc;
- guard(srcu)(&lr->gdev->srcu);
-
- if (!rcu_access_pointer(lr->gdev->chip))
+ revocable_try_access_with(lr->gdev->chip_rp, gc);
+ if (!gc)
return EPOLLHUP | EPOLLERR;
poll_wait(file, &lr->wait, wait);
@@ -1485,11 +1482,9 @@ static ssize_t linereq_read(struct file *file, char __user *buf,
struct gpio_v2_line_event le;
ssize_t bytes_read = 0;
int ret;
+ struct gpio_chip *gc;
- guard(srcu)(&lr->gdev->srcu);
-
- if (!rcu_access_pointer(lr->gdev->chip))
- return -ENODEV;
+ revocable_try_access_or_return(lr->gdev->chip_rp, gc);
if (count < sizeof(le))
return -EINVAL;
@@ -1781,10 +1776,10 @@ static __poll_t lineevent_poll(struct file *file,
{
struct lineevent_state *le = file->private_data;
__poll_t events = 0;
+ struct gpio_chip *gc;
- guard(srcu)(&le->gdev->srcu);
-
- if (!rcu_access_pointer(le->gdev->chip))
+ revocable_try_access_with(le->gdev->chip_rp, gc);
+ if (!gc)
return EPOLLHUP | EPOLLERR;
poll_wait(file, &le->wait, wait);
@@ -1819,11 +1814,9 @@ static ssize_t lineevent_read(struct file *file, char __user *buf,
ssize_t bytes_read = 0;
ssize_t ge_size;
int ret;
+ struct gpio_chip *gc;
- guard(srcu)(&le->gdev->srcu);
-
- if (!rcu_access_pointer(le->gdev->chip))
- return -ENODEV;
+ revocable_try_access_or_return(le->gdev->chip_rp, gc);
/*
* When compatible system call is being used the struct gpioevent_data,
@@ -1901,11 +1894,9 @@ static long lineevent_ioctl(struct file *file, unsigned int cmd,
struct lineevent_state *le = file->private_data;
void __user *ip = (void __user *)arg;
struct gpiohandle_data ghd;
+ struct gpio_chip *gc;
- guard(srcu)(&le->gdev->srcu);
-
- if (!rcu_access_pointer(le->gdev->chip))
- return -ENODEV;
+ revocable_try_access_or_return(le->gdev->chip_rp, gc);
/*
* We can get the value for an event line but not set it,
@@ -2434,12 +2425,10 @@ static long gpio_ioctl(struct file *file, unsigned int cmd, unsigned long arg)
struct gpio_chardev_data *cdev = file->private_data;
struct gpio_device *gdev = cdev->gdev;
void __user *ip = (void __user *)arg;
-
- guard(srcu)(&gdev->srcu);
+ struct gpio_chip *gc;
/* We fail any subsequent ioctl():s when the chip is gone */
- if (!rcu_access_pointer(gdev->chip))
- return -ENODEV;
+ revocable_try_access_or_return(gdev->chip_rp, gc);
/* Fill in the struct and pass to userspace */
switch (cmd) {
@@ -2497,12 +2486,9 @@ static void lineinfo_changed_func(struct work_struct *work)
* Pin functions are in general much more static and while it's
* not 100% bullet-proof, it's good enough for most cases.
*/
- scoped_guard(srcu, &ctx->gdev->srcu) {
- gc = srcu_dereference(ctx->gdev->chip, &ctx->gdev->srcu);
- if (gc &&
- !pinctrl_gpio_can_use_line(gc, ctx->chg.info.offset))
+ revocable_try_access_with_scoped(ctx->gdev->chip_rp, gc)
+ if (!pinctrl_gpio_can_use_line(gc, ctx->chg.info.offset))
ctx->chg.info.flags |= GPIO_V2_LINE_FLAG_USED;
- }
}
ret = kfifo_in_spinlocked(&ctx->cdev->events, &ctx->chg, 1,
@@ -2583,10 +2569,10 @@ static __poll_t lineinfo_watch_poll(struct file *file,
{
struct gpio_chardev_data *cdev = file->private_data;
__poll_t events = 0;
+ struct gpio_chip *gc;
- guard(srcu)(&cdev->gdev->srcu);
-
- if (!rcu_access_pointer(cdev->gdev->chip))
+ revocable_try_access_with(cdev->gdev->chip_rp, gc);
+ if (!gc)
return EPOLLHUP | EPOLLERR;
poll_wait(file, &cdev->wait, pollt);
@@ -2606,11 +2592,9 @@ static ssize_t lineinfo_watch_read(struct file *file, char __user *buf,
ssize_t bytes_read = 0;
int ret;
size_t event_size;
+ struct gpio_chip *gc;
- guard(srcu)(&cdev->gdev->srcu);
-
- if (!rcu_access_pointer(cdev->gdev->chip))
- return -ENODEV;
+ revocable_try_access_or_return(cdev->gdev->chip_rp, gc);
#ifndef CONFIG_GPIO_CDEV_V1
event_size = sizeof(struct gpio_v2_line_info_changed);
--
2.53.0.310.g728cabbaf7-goog
^ permalink raw reply related [flat|nested] 22+ messages in thread* [PATCH v3 09/11] gpio: Remove gpio_chip_guard by using revocable
2026-02-13 9:29 [PATCH v3 00/11] gpio: Adopt revocable mechanism for UAF prevention Tzung-Bi Shih
` (7 preceding siblings ...)
2026-02-13 9:29 ` [PATCH v3 08/11] gpio: cdev: Leverage revocable for accessing " Tzung-Bi Shih
@ 2026-02-13 9:29 ` Tzung-Bi Shih
2026-02-13 9:29 ` [PATCH v3 10/11] gpio: Leverage revocable for accessing struct gpio_chip Tzung-Bi Shih
` (2 subsequent siblings)
11 siblings, 0 replies; 22+ messages in thread
From: Tzung-Bi Shih @ 2026-02-13 9:29 UTC (permalink / raw)
To: Bartosz Golaszewski, Linus Walleij
Cc: Greg Kroah-Hartman, Rafael J. Wysocki, Danilo Krummrich,
Jonathan Corbet, Shuah Khan, Laurent Pinchart, Wolfram Sang,
Jason Gunthorpe, Johan Hovold, Paul E . McKenney, Dan Williams,
chrome-platform, tzungbi, linux-gpio, linux-kselftest,
linux-kernel
Struct gpio_device now provides a revocable provider to the underlying
struct gpio_chip. Leverage revocable for accessing the struct
gpio_chip instead of using gpio_chip_guard.
Signed-off-by: Tzung-Bi Shih <tzungbi@kernel.org>
---
v3:
- Change revocable API usages accordingly.
v2: https://lore.kernel.org/all/20260203061059.975605-10-tzungbi@kernel.org
- Separate from v1 for including gpio_chip_guard only.
v1: https://lore.kernel.org/all/20260116081036.352286-23-tzungbi@kernel.org
drivers/gpio/gpiolib-cdev.c | 9 +-
drivers/gpio/gpiolib-sysfs.c | 30 +++----
drivers/gpio/gpiolib.c | 163 ++++++++++++++++-------------------
drivers/gpio/gpiolib.h | 21 -----
4 files changed, 89 insertions(+), 134 deletions(-)
diff --git a/drivers/gpio/gpiolib-cdev.c b/drivers/gpio/gpiolib-cdev.c
index b491e2737ef2..2a61c4aa3e18 100644
--- a/drivers/gpio/gpiolib-cdev.c
+++ b/drivers/gpio/gpiolib-cdev.c
@@ -2205,10 +2205,9 @@ static void gpio_desc_to_lineinfo(struct gpio_desc *desc,
u32 debounce_period_us;
unsigned long dflags;
const char *label;
+ struct gpio_chip *gc;
- CLASS(gpio_chip_guard, guard)(desc);
- if (!guard.gc)
- return;
+ revocable_try_access_or_return_void(desc->gdev->chip_rp, gc);
memset(info, 0, sizeof(*info));
info->offset = gpiod_hwgpio(desc);
@@ -2241,10 +2240,10 @@ static void gpio_desc_to_lineinfo(struct gpio_desc *desc,
test_bit(GPIOD_FLAG_IS_HOGGED, &dflags) ||
test_bit(GPIOD_FLAG_EXPORT, &dflags) ||
test_bit(GPIOD_FLAG_SYSFS, &dflags) ||
- !gpiochip_line_is_valid(guard.gc, info->offset)) {
+ !gpiochip_line_is_valid(gc, info->offset)) {
info->flags |= GPIO_V2_LINE_FLAG_USED;
} else if (!atomic) {
- if (!pinctrl_gpio_can_use_line(guard.gc, info->offset))
+ if (!pinctrl_gpio_can_use_line(gc, info->offset))
info->flags |= GPIO_V2_LINE_FLAG_USED;
}
diff --git a/drivers/gpio/gpiolib-sysfs.c b/drivers/gpio/gpiolib-sysfs.c
index 3fdcfa0c7d42..9bacd89cba93 100644
--- a/drivers/gpio/gpiolib-sysfs.c
+++ b/drivers/gpio/gpiolib-sysfs.c
@@ -215,10 +215,9 @@ static int gpio_sysfs_request_irq(struct gpiod_data *data, unsigned char flags)
struct gpio_desc *desc = data->desc;
unsigned long irq_flags;
int ret;
+ struct gpio_chip *gc;
- CLASS(gpio_chip_guard, guard)(desc);
- if (!guard.gc)
- return -ENODEV;
+ revocable_try_access_or_return(desc->gdev->chip_rp, gc);
data->irq = gpiod_to_irq(desc);
if (data->irq < 0)
@@ -244,7 +243,7 @@ static int gpio_sysfs_request_irq(struct gpiod_data *data, unsigned char flags)
* Remove this redundant call (along with the corresponding unlock)
* when those drivers have been fixed.
*/
- ret = gpiochip_lock_as_irq(guard.gc, gpiod_hwgpio(desc));
+ ret = gpiochip_lock_as_irq(gc, gpiod_hwgpio(desc));
if (ret < 0)
goto err_clr_bits;
@@ -258,7 +257,7 @@ static int gpio_sysfs_request_irq(struct gpiod_data *data, unsigned char flags)
return 0;
err_unlock:
- gpiochip_unlock_as_irq(guard.gc, gpiod_hwgpio(desc));
+ gpiochip_unlock_as_irq(gc, gpiod_hwgpio(desc));
err_clr_bits:
clear_bit(GPIOD_FLAG_EDGE_RISING, &desc->flags);
clear_bit(GPIOD_FLAG_EDGE_FALLING, &desc->flags);
@@ -273,14 +272,13 @@ static int gpio_sysfs_request_irq(struct gpiod_data *data, unsigned char flags)
static void gpio_sysfs_free_irq(struct gpiod_data *data)
{
struct gpio_desc *desc = data->desc;
+ struct gpio_chip *gc;
- CLASS(gpio_chip_guard, guard)(desc);
- if (!guard.gc)
- return;
+ revocable_try_access_or_return_void(desc->gdev->chip_rp, gc);
data->irq_flags = 0;
free_irq(data->irq, data);
- gpiochip_unlock_as_irq(guard.gc, gpiod_hwgpio(desc));
+ gpiochip_unlock_as_irq(gc, gpiod_hwgpio(desc));
clear_bit(GPIOD_FLAG_EDGE_RISING, &desc->flags);
clear_bit(GPIOD_FLAG_EDGE_FALLING, &desc->flags);
}
@@ -473,13 +471,12 @@ static DEVICE_ATTR_RO(ngpio);
static int export_gpio_desc(struct gpio_desc *desc)
{
int offset, ret;
+ struct gpio_chip *gc;
- CLASS(gpio_chip_guard, guard)(desc);
- if (!guard.gc)
- return -ENODEV;
+ revocable_try_access_or_return(desc->gdev->chip_rp, gc);
offset = gpiod_hwgpio(desc);
- if (!gpiochip_line_is_valid(guard.gc, offset)) {
+ if (!gpiochip_line_is_valid(gc, offset)) {
pr_debug_ratelimited("%s: GPIO %d masked\n", __func__,
gpiod_hwgpio(desc));
return -EINVAL;
@@ -732,6 +729,7 @@ int gpiod_export(struct gpio_desc *desc, bool direction_may_change)
struct gpio_device *gdev;
struct attribute **attrs;
int status;
+ struct gpio_chip *gc;
/* can't export until sysfs is available ... */
if (!class_is_registered(&gpio_class)) {
@@ -744,9 +742,7 @@ int gpiod_export(struct gpio_desc *desc, bool direction_may_change)
return -EINVAL;
}
- CLASS(gpio_chip_guard, guard)(desc);
- if (!guard.gc)
- return -ENODEV;
+ revocable_try_access_or_return(desc->gdev->chip_rp, gc);
if (test_and_set_bit(GPIOD_FLAG_EXPORT, &desc->flags))
return -EPERM;
@@ -769,7 +765,7 @@ int gpiod_export(struct gpio_desc *desc, bool direction_may_change)
desc_data->desc = desc;
mutex_init(&desc_data->mutex);
- if (guard.gc->direction_input && guard.gc->direction_output)
+ if (gc->direction_input && gc->direction_output)
desc_data->direction_can_change = direction_may_change;
else
desc_data->direction_can_change = false;
diff --git a/drivers/gpio/gpiolib.c b/drivers/gpio/gpiolib.c
index 7ef4dc4e6e9e..34f2665a9311 100644
--- a/drivers/gpio/gpiolib.c
+++ b/drivers/gpio/gpiolib.c
@@ -449,14 +449,13 @@ int gpiod_get_direction(struct gpio_desc *desc)
unsigned long flags;
unsigned int offset;
int ret;
+ struct gpio_chip *gc;
ret = validate_desc(desc, __func__);
if (ret <= 0)
return -EINVAL;
- CLASS(gpio_chip_guard, guard)(desc);
- if (!guard.gc)
- return -ENODEV;
+ revocable_try_access_or_return(desc->gdev->chip_rp, gc);
offset = gpiod_hwgpio(desc);
flags = READ_ONCE(desc->flags);
@@ -469,7 +468,7 @@ int gpiod_get_direction(struct gpio_desc *desc)
test_bit(GPIOD_FLAG_IS_OUT, &flags))
return 0;
- ret = gpiochip_get_direction(guard.gc, offset);
+ ret = gpiochip_get_direction(gc, offset);
if (ret < 0)
return ret;
@@ -2475,31 +2474,30 @@ int gpiod_request_commit(struct gpio_desc *desc, const char *label)
{
unsigned int offset;
int ret;
+ struct gpio_chip *gc;
- CLASS(gpio_chip_guard, guard)(desc);
- if (!guard.gc)
- return -ENODEV;
+ revocable_try_access_or_return(desc->gdev->chip_rp, gc);
if (test_and_set_bit(GPIOD_FLAG_REQUESTED, &desc->flags))
return -EBUSY;
offset = gpiod_hwgpio(desc);
- if (!gpiochip_line_is_valid(guard.gc, offset))
+ if (!gpiochip_line_is_valid(gc, offset))
return -EINVAL;
/* NOTE: gpio_request() can be called in early boot,
* before IRQs are enabled, for non-sleeping (SOC) GPIOs.
*/
- if (guard.gc->request) {
- ret = guard.gc->request(guard.gc, offset);
+ if (gc->request) {
+ ret = gc->request(gc, offset);
if (ret > 0)
ret = -EBADE;
if (ret)
goto out_clear_bit;
}
- if (guard.gc->get_direction)
+ if (gc->get_direction)
gpiod_get_direction(desc);
ret = desc_set_label(desc, label ? : "?");
@@ -2536,16 +2534,17 @@ int gpiod_request(struct gpio_desc *desc, const char *label)
void gpiod_free_commit(struct gpio_desc *desc)
{
unsigned long flags;
+ struct gpio_chip *gc;
might_sleep();
- CLASS(gpio_chip_guard, guard)(desc);
+ revocable_try_access_or_return_void(desc->gdev->chip_rp, gc);
flags = READ_ONCE(desc->flags);
- if (guard.gc && test_bit(GPIOD_FLAG_REQUESTED, &flags)) {
- if (guard.gc->free)
- guard.gc->free(guard.gc, gpiod_hwgpio(desc));
+ if (test_bit(GPIOD_FLAG_REQUESTED, &flags)) {
+ if (gc->free)
+ gc->free(gc, gpiod_hwgpio(desc));
clear_bit(GPIOD_FLAG_ACTIVE_LOW, &flags);
clear_bit(GPIOD_FLAG_REQUESTED, &flags);
@@ -2697,15 +2696,14 @@ EXPORT_SYMBOL_GPL(gpiochip_free_own_desc);
int gpio_do_set_config(struct gpio_desc *desc, unsigned long config)
{
int ret;
+ struct gpio_chip *gc;
- CLASS(gpio_chip_guard, guard)(desc);
- if (!guard.gc)
- return -ENODEV;
+ revocable_try_access_or_return(desc->gdev->chip_rp, gc);
- if (!guard.gc->set_config)
+ if (!gc->set_config)
return -ENOTSUPP;
- ret = guard.gc->set_config(guard.gc, gpiod_hwgpio(desc), config);
+ ret = gc->set_config(gc, gpiod_hwgpio(desc), config);
if (ret > 0)
ret = -EBADE;
@@ -2874,17 +2872,16 @@ EXPORT_SYMBOL_GPL(gpiod_direction_input);
int gpiod_direction_input_nonotify(struct gpio_desc *desc)
{
int ret = 0, dir;
+ struct gpio_chip *gc;
- CLASS(gpio_chip_guard, guard)(desc);
- if (!guard.gc)
- return -ENODEV;
+ revocable_try_access_or_return(desc->gdev->chip_rp, gc);
/*
* It is legal to have no .get() and .direction_input() specified if
* the chip is output-only, but you can't specify .direction_input()
* and not support the .get() operation, that doesn't make sense.
*/
- if (!guard.gc->get && guard.gc->direction_input) {
+ if (!gc->get && gc->direction_input) {
gpiod_warn(desc,
"%s: missing get() but have direction_input()\n",
__func__);
@@ -2897,11 +2894,10 @@ int gpiod_direction_input_nonotify(struct gpio_desc *desc)
* direction (if .get_direction() is supported) else we silently
* assume we are in input mode after this.
*/
- if (guard.gc->direction_input) {
- ret = gpiochip_direction_input(guard.gc,
- gpiod_hwgpio(desc));
- } else if (guard.gc->get_direction) {
- dir = gpiochip_get_direction(guard.gc, gpiod_hwgpio(desc));
+ if (gc->direction_input) {
+ ret = gpiochip_direction_input(gc, gpiod_hwgpio(desc));
+ } else if (gc->get_direction) {
+ dir = gpiochip_get_direction(gc, gpiod_hwgpio(desc));
if (dir < 0)
return dir;
@@ -2941,31 +2937,28 @@ static int gpiochip_set(struct gpio_chip *gc, unsigned int offset, int value)
static int gpiod_direction_output_raw_commit(struct gpio_desc *desc, int value)
{
int val = !!value, ret = 0, dir;
+ struct gpio_chip *gc;
- CLASS(gpio_chip_guard, guard)(desc);
- if (!guard.gc)
- return -ENODEV;
+ revocable_try_access_or_return(desc->gdev->chip_rp, gc);
/*
* It's OK not to specify .direction_output() if the gpiochip is
* output-only, but if there is then not even a .set() operation it
* is pretty tricky to drive the output line.
*/
- if (!guard.gc->set && !guard.gc->direction_output) {
+ if (!gc->set && !gc->direction_output) {
gpiod_warn(desc,
"%s: missing set() and direction_output() operations\n",
__func__);
return -EIO;
}
- if (guard.gc->direction_output) {
- ret = gpiochip_direction_output(guard.gc,
- gpiod_hwgpio(desc), val);
+ if (gc->direction_output) {
+ ret = gpiochip_direction_output(gc, gpiod_hwgpio(desc), val);
} else {
/* Check that we are in output mode if we can */
- if (guard.gc->get_direction) {
- dir = gpiochip_get_direction(guard.gc,
- gpiod_hwgpio(desc));
+ if (gc->get_direction) {
+ dir = gpiochip_get_direction(gc, gpiod_hwgpio(desc));
if (dir < 0)
return dir;
@@ -2980,7 +2973,7 @@ static int gpiod_direction_output_raw_commit(struct gpio_desc *desc, int value)
* If we can't actively set the direction, we are some
* output-only chip, so just drive the output as desired.
*/
- ret = gpiochip_set(guard.gc, gpiod_hwgpio(desc), val);
+ ret = gpiochip_set(gc, gpiod_hwgpio(desc), val);
if (ret)
return ret;
}
@@ -3118,20 +3111,18 @@ int gpiod_direction_output_nonotify(struct gpio_desc *desc, int value)
int gpiod_enable_hw_timestamp_ns(struct gpio_desc *desc, unsigned long flags)
{
int ret;
+ struct gpio_chip *gc;
VALIDATE_DESC(desc);
- CLASS(gpio_chip_guard, guard)(desc);
- if (!guard.gc)
- return -ENODEV;
+ revocable_try_access_or_return(desc->gdev->chip_rp, gc);
- if (!guard.gc->en_hw_timestamp) {
+ if (!gc->en_hw_timestamp) {
gpiod_warn(desc, "%s: hw ts not supported\n", __func__);
return -ENOTSUPP;
}
- ret = guard.gc->en_hw_timestamp(guard.gc,
- gpiod_hwgpio(desc), flags);
+ ret = gc->en_hw_timestamp(gc, gpiod_hwgpio(desc), flags);
if (ret)
gpiod_warn(desc, "%s: hw ts request failed\n", __func__);
@@ -3151,20 +3142,18 @@ EXPORT_SYMBOL_GPL(gpiod_enable_hw_timestamp_ns);
int gpiod_disable_hw_timestamp_ns(struct gpio_desc *desc, unsigned long flags)
{
int ret;
+ struct gpio_chip *gc;
VALIDATE_DESC(desc);
- CLASS(gpio_chip_guard, guard)(desc);
- if (!guard.gc)
- return -ENODEV;
+ revocable_try_access_or_return(desc->gdev->chip_rp, gc);
- if (!guard.gc->dis_hw_timestamp) {
+ if (!gc->dis_hw_timestamp) {
gpiod_warn(desc, "%s: hw ts not supported\n", __func__);
return -ENOTSUPP;
}
- ret = guard.gc->dis_hw_timestamp(guard.gc, gpiod_hwgpio(desc),
- flags);
+ ret = gc->dis_hw_timestamp(gc, gpiod_hwgpio(desc), flags);
if (ret)
gpiod_warn(desc, "%s: hw ts release failed\n", __func__);
@@ -3424,31 +3413,29 @@ int gpiod_get_array_value_complex(bool raw, bool can_sleep,
unsigned long *mask, *bits;
int first, j;
- CLASS(gpio_chip_guard, guard)(desc_array[i]);
- if (!guard.gc)
- return -ENODEV;
+ revocable_try_access_or_return(desc_array[i]->gdev->chip_rp, gc);
- if (likely(guard.gc->ngpio <= FASTPATH_NGPIO)) {
+ if (likely(gc->ngpio <= FASTPATH_NGPIO)) {
mask = fastpath_mask;
bits = fastpath_bits;
} else {
gfp_t flags = can_sleep ? GFP_KERNEL : GFP_ATOMIC;
- mask = bitmap_alloc(guard.gc->ngpio, flags);
+ mask = bitmap_alloc(gc->ngpio, flags);
if (!mask)
return -ENOMEM;
- bits = bitmap_alloc(guard.gc->ngpio, flags);
+ bits = bitmap_alloc(gc->ngpio, flags);
if (!bits) {
bitmap_free(mask);
return -ENOMEM;
}
}
- bitmap_zero(mask, guard.gc->ngpio);
+ bitmap_zero(mask, gc->ngpio);
if (!can_sleep)
- WARN_ON(guard.gc->can_sleep);
+ WARN_ON(gc->can_sleep);
/* collect all inputs belonging to the same chip */
first = i;
@@ -3463,9 +3450,9 @@ int gpiod_get_array_value_complex(bool raw, bool can_sleep,
i = find_next_zero_bit(array_info->get_mask,
array_size, i);
} while ((i < array_size) &&
- gpio_device_chip_cmp(desc_array[i]->gdev, guard.gc));
+ gpio_device_chip_cmp(desc_array[i]->gdev, gc));
- ret = gpio_chip_get_multiple(guard.gc, mask, bits);
+ ret = gpio_chip_get_multiple(gc, mask, bits);
if (ret) {
if (mask != fastpath_mask)
bitmap_free(mask);
@@ -3614,15 +3601,14 @@ EXPORT_SYMBOL_GPL(gpiod_get_array_value);
static int gpio_set_open_drain_value_commit(struct gpio_desc *desc, bool value)
{
int ret = 0, offset = gpiod_hwgpio(desc);
+ struct gpio_chip *gc;
- CLASS(gpio_chip_guard, guard)(desc);
- if (!guard.gc)
- return -ENODEV;
+ revocable_try_access_or_return(desc->gdev->chip_rp, gc);
if (value) {
- ret = gpiochip_direction_input(guard.gc, offset);
+ ret = gpiochip_direction_input(gc, offset);
} else {
- ret = gpiochip_direction_output(guard.gc, offset, 0);
+ ret = gpiochip_direction_output(gc, offset, 0);
if (!ret)
set_bit(GPIOD_FLAG_IS_OUT, &desc->flags);
}
@@ -3643,17 +3629,16 @@ static int gpio_set_open_drain_value_commit(struct gpio_desc *desc, bool value)
static int gpio_set_open_source_value_commit(struct gpio_desc *desc, bool value)
{
int ret = 0, offset = gpiod_hwgpio(desc);
+ struct gpio_chip *gc;
- CLASS(gpio_chip_guard, guard)(desc);
- if (!guard.gc)
- return -ENODEV;
+ revocable_try_access_or_return(desc->gdev->chip_rp, gc);
if (value) {
- ret = gpiochip_direction_output(guard.gc, offset, 1);
+ ret = gpiochip_direction_output(gc, offset, 1);
if (!ret)
set_bit(GPIOD_FLAG_IS_OUT, &desc->flags);
} else {
- ret = gpiochip_direction_input(guard.gc, offset);
+ ret = gpiochip_direction_input(gc, offset);
}
trace_gpio_direction(desc_to_gpio(desc), !value, ret);
if (ret < 0)
@@ -3666,15 +3651,15 @@ static int gpio_set_open_source_value_commit(struct gpio_desc *desc, bool value)
static int gpiod_set_raw_value_commit(struct gpio_desc *desc, bool value)
{
+ struct gpio_chip *gc;
+
if (unlikely(!test_bit(GPIOD_FLAG_IS_OUT, &desc->flags)))
return -EPERM;
- CLASS(gpio_chip_guard, guard)(desc);
- if (!guard.gc)
- return -ENODEV;
+ revocable_try_access_or_return(desc->gdev->chip_rp, gc);
trace_gpio_value(desc_to_gpio(desc), 0, value);
- return gpiochip_set(guard.gc, gpiod_hwgpio(desc), value);
+ return gpiochip_set(gc, gpiod_hwgpio(desc), value);
}
/*
@@ -3769,31 +3754,29 @@ int gpiod_set_array_value_complex(bool raw, bool can_sleep,
unsigned long *mask, *bits;
int count = 0;
- CLASS(gpio_chip_guard, guard)(desc_array[i]);
- if (!guard.gc)
- return -ENODEV;
+ revocable_try_access_or_return(desc_array[i]->gdev->chip_rp, gc);
- if (likely(guard.gc->ngpio <= FASTPATH_NGPIO)) {
+ if (likely(gc->ngpio <= FASTPATH_NGPIO)) {
mask = fastpath_mask;
bits = fastpath_bits;
} else {
gfp_t flags = can_sleep ? GFP_KERNEL : GFP_ATOMIC;
- mask = bitmap_alloc(guard.gc->ngpio, flags);
+ mask = bitmap_alloc(gc->ngpio, flags);
if (!mask)
return -ENOMEM;
- bits = bitmap_alloc(guard.gc->ngpio, flags);
+ bits = bitmap_alloc(gc->ngpio, flags);
if (!bits) {
bitmap_free(mask);
return -ENOMEM;
}
}
- bitmap_zero(mask, guard.gc->ngpio);
+ bitmap_zero(mask, gc->ngpio);
if (!can_sleep)
- WARN_ON(guard.gc->can_sleep);
+ WARN_ON(gc->can_sleep);
do {
struct gpio_desc *desc = desc_array[i];
@@ -3832,10 +3815,10 @@ int gpiod_set_array_value_complex(bool raw, bool can_sleep,
i = find_next_zero_bit(array_info->set_mask,
array_size, i);
} while ((i < array_size) &&
- gpio_device_chip_cmp(desc_array[i]->gdev, guard.gc));
+ gpio_device_chip_cmp(desc_array[i]->gdev, gc));
/* push collected bits to outputs */
if (count != 0) {
- ret = gpiochip_set_multiple(guard.gc, mask, bits);
+ ret = gpiochip_set_multiple(gc, mask, bits);
if (ret)
return ret;
}
@@ -5051,18 +5034,16 @@ int gpiod_hog(struct gpio_desc *desc, const char *name,
struct gpio_desc *local_desc;
int hwnum;
int ret;
+ struct gpio_chip *gc;
- CLASS(gpio_chip_guard, guard)(desc);
- if (!guard.gc)
- return -ENODEV;
+ revocable_try_access_or_return(desc->gdev->chip_rp, gc);
if (test_and_set_bit(GPIOD_FLAG_IS_HOGGED, &desc->flags))
return 0;
hwnum = gpiod_hwgpio(desc);
- local_desc = gpiochip_request_own_desc(guard.gc, hwnum, name,
- lflags, dflags);
+ local_desc = gpiochip_request_own_desc(gc, hwnum, name, lflags, dflags);
if (IS_ERR(local_desc)) {
clear_bit(GPIOD_FLAG_IS_HOGGED, &desc->flags);
ret = PTR_ERR(local_desc);
diff --git a/drivers/gpio/gpiolib.h b/drivers/gpio/gpiolib.h
index 56643f40e87e..a254a57f37f5 100644
--- a/drivers/gpio/gpiolib.h
+++ b/drivers/gpio/gpiolib.h
@@ -224,27 +224,6 @@ struct gpio_desc {
#define gpiod_not_found(desc) (IS_ERR(desc) && PTR_ERR(desc) == -ENOENT)
-struct gpio_chip_guard {
- struct gpio_device *gdev;
- struct gpio_chip *gc;
- int idx;
-};
-
-DEFINE_CLASS(gpio_chip_guard,
- struct gpio_chip_guard,
- srcu_read_unlock(&_T.gdev->srcu, _T.idx),
- ({
- struct gpio_chip_guard _guard;
-
- _guard.gdev = desc->gdev;
- _guard.idx = srcu_read_lock(&_guard.gdev->srcu);
- _guard.gc = srcu_dereference(_guard.gdev->chip,
- &_guard.gdev->srcu);
-
- _guard;
- }),
- struct gpio_desc *desc)
-
int gpiod_request(struct gpio_desc *desc, const char *label);
int gpiod_request_commit(struct gpio_desc *desc, const char *label);
void gpiod_free(struct gpio_desc *desc);
--
2.53.0.310.g728cabbaf7-goog
^ permalink raw reply related [flat|nested] 22+ messages in thread* [PATCH v3 10/11] gpio: Leverage revocable for accessing struct gpio_chip
2026-02-13 9:29 [PATCH v3 00/11] gpio: Adopt revocable mechanism for UAF prevention Tzung-Bi Shih
` (8 preceding siblings ...)
2026-02-13 9:29 ` [PATCH v3 09/11] gpio: Remove gpio_chip_guard by using revocable Tzung-Bi Shih
@ 2026-02-13 9:29 ` Tzung-Bi Shih
2026-02-13 9:29 ` [PATCH v3 11/11] gpio: Remove unused `chip` and `srcu` in struct gpio_device Tzung-Bi Shih
2026-02-18 10:26 ` [PATCH v3 00/11] gpio: Adopt revocable mechanism for UAF prevention Bartosz Golaszewski
11 siblings, 0 replies; 22+ messages in thread
From: Tzung-Bi Shih @ 2026-02-13 9:29 UTC (permalink / raw)
To: Bartosz Golaszewski, Linus Walleij
Cc: Greg Kroah-Hartman, Rafael J. Wysocki, Danilo Krummrich,
Jonathan Corbet, Shuah Khan, Laurent Pinchart, Wolfram Sang,
Jason Gunthorpe, Johan Hovold, Paul E . McKenney, Dan Williams,
chrome-platform, tzungbi, linux-gpio, linux-kselftest,
linux-kernel
Struct gpio_device now provides a revocable provider to the underlying
struct gpio_chip. Leverage revocable for accessing the struct
gpio_chip.
Signed-off-by: Tzung-Bi Shih <tzungbi@kernel.org>
---
v3:
- Change revocable API usages accordingly.
v2: https://lore.kernel.org/all/20260203061059.975605-11-tzungbi@kernel.org
- Separate from v1(a) for excluding gpio_chip_guard and combine v1(b).
v1(a):
- https://lore.kernel.org/all/20260116081036.352286-23-tzungbi@kernel.org
v1(b):
- https://lore.kernel.org/all/20260116081036.352286-19-tzungbi@kernel.org
drivers/gpio/gpiolib.c | 60 +++++++++++++-----------------------------
1 file changed, 18 insertions(+), 42 deletions(-)
diff --git a/drivers/gpio/gpiolib.c b/drivers/gpio/gpiolib.c
index 34f2665a9311..2ba78add3417 100644
--- a/drivers/gpio/gpiolib.c
+++ b/drivers/gpio/gpiolib.c
@@ -338,7 +338,10 @@ EXPORT_SYMBOL(gpio_device_get_label);
*/
struct gpio_chip *gpio_device_get_chip(struct gpio_device *gdev)
{
- return rcu_dereference_check(gdev->chip, 1);
+ struct gpio_chip *gc;
+
+ revocable_try_access_with(gdev->chip_rp, gc);
+ return gc;
}
EXPORT_SYMBOL_GPL(gpio_device_get_chip);
@@ -556,9 +559,7 @@ static struct gpio_desc *gpio_name_to_desc(const char * const name)
list_for_each_entry_srcu(gdev, &gpio_devices, list,
srcu_read_lock_held(&gpio_devices_srcu)) {
- guard(srcu)(&gdev->srcu);
-
- gc = srcu_dereference(gdev->chip, &gdev->srcu);
+ revocable_try_access_with(gdev->chip_rp, gc);
if (!gc)
continue;
@@ -972,9 +973,7 @@ static void gpiochip_setup_devs(void)
list_for_each_entry_srcu(gdev, &gpio_devices, list,
srcu_read_lock_held(&gpio_devices_srcu)) {
- guard(srcu)(&gdev->srcu);
-
- gc = srcu_dereference(gdev->chip, &gdev->srcu);
+ revocable_try_access_with(gdev->chip_rp, gc);
if (!gc) {
dev_err(&gdev->dev, "Underlying GPIO chip is gone\n");
continue;
@@ -1378,11 +1377,11 @@ struct gpio_device *gpio_device_find(const void *data,
if (!device_is_registered(&gdev->dev))
continue;
- guard(srcu)(&gdev->srcu);
-
- gc = srcu_dereference(gdev->chip, &gdev->srcu);
+ revocable_try_access_with(gdev->chip_rp, gc);
+ if (!gc)
+ continue;
- if (gc && match(gc, data))
+ if (match(gc, data))
return gpio_device_get(gdev);
}
@@ -3310,18 +3309,10 @@ static int gpio_chip_get_value(struct gpio_chip *gc, const struct gpio_desc *des
static int gpiod_get_raw_value_commit(const struct gpio_desc *desc)
{
- struct gpio_device *gdev;
struct gpio_chip *gc;
int value;
- /* FIXME Unable to use gpio_chip_guard due to const desc. */
- gdev = desc->gdev;
-
- guard(srcu)(&gdev->srcu);
-
- gc = srcu_dereference(gdev->chip, &gdev->srcu);
- if (!gc)
- return -ENODEV;
+ revocable_try_access_or_return(desc->gdev->chip_rp, gc);
value = gpio_chip_get_value(gc, desc);
value = value < 0 ? value : !!value;
@@ -3360,9 +3351,10 @@ static int gpio_chip_get_multiple(struct gpio_chip *gc,
/* The 'other' chip must be protected with its GPIO device's SRCU. */
static bool gpio_device_chip_cmp(struct gpio_device *gdev, struct gpio_chip *gc)
{
- guard(srcu)(&gdev->srcu);
+ struct gpio_chip *chip;
- return gc == srcu_dereference(gdev->chip, &gdev->srcu);
+ revocable_try_access_with(gdev->chip_rp, chip);
+ return chip ? chip == gc : false;
}
int gpiod_get_array_value_complex(bool raw, bool can_sleep,
@@ -3385,11 +3377,7 @@ int gpiod_get_array_value_complex(bool raw, bool can_sleep,
if (!can_sleep)
WARN_ON(array_info->gdev->can_sleep);
- guard(srcu)(&array_info->gdev->srcu);
- gc = srcu_dereference(array_info->gdev->chip,
- &array_info->gdev->srcu);
- if (!gc)
- return -ENODEV;
+ revocable_try_access_or_return(array_info->gdev->chip_rp, gc);
ret = gpio_chip_get_multiple(gc, array_info->get_mask,
value_bitmap);
@@ -3726,11 +3714,7 @@ int gpiod_set_array_value_complex(bool raw, bool can_sleep,
return -EPERM;
}
- guard(srcu)(&array_info->gdev->srcu);
- gc = srcu_dereference(array_info->gdev->chip,
- &array_info->gdev->srcu);
- if (!gc)
- return -ENODEV;
+ revocable_try_access_or_return(array_info->gdev->chip_rp, gc);
if (!raw && !bitmap_empty(array_info->invert_mask, array_size))
bitmap_xor(value_bitmap, value_bitmap,
@@ -4024,7 +4008,6 @@ EXPORT_SYMBOL_GPL(gpiod_is_shared);
*/
int gpiod_to_irq(const struct gpio_desc *desc)
{
- struct gpio_device *gdev;
struct gpio_chip *gc;
int offset;
int ret;
@@ -4033,12 +4016,7 @@ int gpiod_to_irq(const struct gpio_desc *desc)
if (ret <= 0)
return -EINVAL;
- gdev = desc->gdev;
- /* FIXME Cannot use gpio_chip_guard due to const desc. */
- guard(srcu)(&gdev->srcu);
- gc = srcu_dereference(gdev->chip, &gdev->srcu);
- if (!gc)
- return -ENODEV;
+ revocable_try_access_or_return(desc->gdev->chip_rp, gc);
offset = gpiod_hwgpio(desc);
if (gc->to_irq) {
@@ -5413,9 +5391,7 @@ static int gpiolib_seq_show(struct seq_file *s, void *v)
if (priv->newline)
seq_putc(s, '\n');
- guard(srcu)(&gdev->srcu);
-
- gc = srcu_dereference(gdev->chip, &gdev->srcu);
+ revocable_try_access_with(gdev->chip_rp, gc);
if (!gc) {
seq_printf(s, "%s: (dangling chip)\n", dev_name(&gdev->dev));
return 0;
--
2.53.0.310.g728cabbaf7-goog
^ permalink raw reply related [flat|nested] 22+ messages in thread* [PATCH v3 11/11] gpio: Remove unused `chip` and `srcu` in struct gpio_device
2026-02-13 9:29 [PATCH v3 00/11] gpio: Adopt revocable mechanism for UAF prevention Tzung-Bi Shih
` (9 preceding siblings ...)
2026-02-13 9:29 ` [PATCH v3 10/11] gpio: Leverage revocable for accessing struct gpio_chip Tzung-Bi Shih
@ 2026-02-13 9:29 ` Tzung-Bi Shih
2026-02-18 10:26 ` [PATCH v3 00/11] gpio: Adopt revocable mechanism for UAF prevention Bartosz Golaszewski
11 siblings, 0 replies; 22+ messages in thread
From: Tzung-Bi Shih @ 2026-02-13 9:29 UTC (permalink / raw)
To: Bartosz Golaszewski, Linus Walleij
Cc: Greg Kroah-Hartman, Rafael J. Wysocki, Danilo Krummrich,
Jonathan Corbet, Shuah Khan, Laurent Pinchart, Wolfram Sang,
Jason Gunthorpe, Johan Hovold, Paul E . McKenney, Dan Williams,
chrome-platform, tzungbi, linux-gpio, linux-kselftest,
linux-kernel
`chip` and `srcu` in struct gpio_device are unused as their usages are
replaced to use revocable. Remove them.
Signed-off-by: Tzung-Bi Shih <tzungbi@kernel.org>
---
v3:
- No changes.
v2: https://lore.kernel.org/all/20260203061059.975605-12-tzungbi@kernel.org
- No changes.
v1: https://lore.kernel.org/all/20260116081036.352286-24-tzungbi@kernel.org
drivers/gpio/gpiolib.c | 26 +-------------------------
drivers/gpio/gpiolib.h | 4 ----
2 files changed, 1 insertion(+), 29 deletions(-)
diff --git a/drivers/gpio/gpiolib.c b/drivers/gpio/gpiolib.c
index 2ba78add3417..01be9f6978f2 100644
--- a/drivers/gpio/gpiolib.c
+++ b/drivers/gpio/gpiolib.c
@@ -423,8 +423,6 @@ static int gpiochip_get_direction(struct gpio_chip *gc, unsigned int offset)
{
int ret;
- lockdep_assert_held(&gc->gpiodev->srcu);
-
if (WARN_ON(!gc->get_direction))
return -EOPNOTSUPP;
@@ -875,7 +873,6 @@ static void gpiodev_release(struct device *dev)
ida_free(&gpio_ida, gdev->id);
kfree_const(gdev->label);
kfree(gdev->descs);
- cleanup_srcu_struct(&gdev->srcu);
kfree(gdev);
}
@@ -1076,14 +1073,9 @@ int gpiochip_add_data_with_key(struct gpio_chip *gc, void *data,
goto err_free_gdev;
gdev->id = ret;
- ret = init_srcu_struct(&gdev->srcu);
- if (ret)
- goto err_free_ida;
- rcu_assign_pointer(gdev->chip, gc);
-
ret = init_srcu_struct(&gdev->desc_srcu);
if (ret)
- goto err_cleanup_gdev_srcu;
+ goto err_free_ida;
ret = dev_set_name(&gdev->dev, GPIOCHIP_NAME "%d", gdev->id);
if (ret)
@@ -1276,8 +1268,6 @@ int gpiochip_add_data_with_key(struct gpio_chip *gc, void *data,
err_cleanup_desc_srcu:
cleanup_srcu_struct(&gdev->desc_srcu);
-err_cleanup_gdev_srcu:
- cleanup_srcu_struct(&gdev->srcu);
err_free_ida:
ida_free(&gpio_ida, gdev->id);
err_free_gdev:
@@ -1314,8 +1304,6 @@ void gpiochip_remove(struct gpio_chip *gc)
synchronize_srcu(&gpio_devices_srcu);
/* Numb the device, cancelling all outstanding operations */
- rcu_assign_pointer(gdev->chip, NULL);
- synchronize_srcu(&gdev->srcu);
revocable_revoke(gdev->chip_rp);
gpio_device_teardown_shared(gdev);
gpiochip_irqchip_remove(gc);
@@ -2815,8 +2803,6 @@ static int gpiochip_direction_input(struct gpio_chip *gc, unsigned int offset)
{
int ret;
- lockdep_assert_held(&gc->gpiodev->srcu);
-
if (WARN_ON(!gc->direction_input))
return -EOPNOTSUPP;
@@ -2832,8 +2818,6 @@ static int gpiochip_direction_output(struct gpio_chip *gc, unsigned int offset,
{
int ret;
- lockdep_assert_held(&gc->gpiodev->srcu);
-
if (WARN_ON(!gc->direction_output))
return -EOPNOTSUPP;
@@ -2921,8 +2905,6 @@ static int gpiochip_set(struct gpio_chip *gc, unsigned int offset, int value)
{
int ret;
- lockdep_assert_held(&gc->gpiodev->srcu);
-
if (WARN_ON(unlikely(!gc->set)))
return -EOPNOTSUPP;
@@ -3270,8 +3252,6 @@ static int gpiochip_get(struct gpio_chip *gc, unsigned int offset)
{
int ret;
- lockdep_assert_held(&gc->gpiodev->srcu);
-
/* Make sure this is called after checking for gc->get(). */
ret = gc->get(gc, offset);
if (ret > 1)
@@ -3323,8 +3303,6 @@ static int gpiod_get_raw_value_commit(const struct gpio_desc *desc)
static int gpio_chip_get_multiple(struct gpio_chip *gc,
unsigned long *mask, unsigned long *bits)
{
- lockdep_assert_held(&gc->gpiodev->srcu);
-
if (gc->get_multiple) {
int ret;
@@ -3668,8 +3646,6 @@ static int gpiochip_set_multiple(struct gpio_chip *gc,
unsigned int i;
int ret;
- lockdep_assert_held(&gc->gpiodev->srcu);
-
if (gc->set_multiple) {
ret = gc->set_multiple(gc, mask, bits);
if (ret > 0)
diff --git a/drivers/gpio/gpiolib.h b/drivers/gpio/gpiolib.h
index a254a57f37f5..6a97d9726c20 100644
--- a/drivers/gpio/gpiolib.h
+++ b/drivers/gpio/gpiolib.h
@@ -28,7 +28,6 @@
* @chrdev: character device for the GPIO device
* @id: numerical ID number for the GPIO chip
* @owner: helps prevent removal of modules exporting active GPIOs
- * @chip: pointer to the corresponding gpiochip, holding static
* data for this device
* @descs: array of ngpio descriptors.
* @valid_mask: If not %NULL, holds bitmask of GPIOs which are valid to be
@@ -51,7 +50,6 @@
* process context
* @device_notifier: used to notify character device wait queues about the GPIO
* device being unregistered
- * @srcu: protects the pointer to the underlying GPIO chip
* @chip_rp: revocable provider handle for the corresponding struct gpio_chip.
* @pin_ranges: range of pins served by the GPIO driver
*
@@ -65,7 +63,6 @@ struct gpio_device {
struct cdev chrdev;
int id;
struct module *owner;
- struct gpio_chip __rcu *chip;
struct gpio_desc *descs;
unsigned long *valid_mask;
struct srcu_struct desc_srcu;
@@ -79,7 +76,6 @@ struct gpio_device {
rwlock_t line_state_lock;
struct workqueue_struct *line_state_wq;
struct blocking_notifier_head device_notifier;
- struct srcu_struct srcu;
struct revocable *chip_rp;
#ifdef CONFIG_PINCTRL
--
2.53.0.310.g728cabbaf7-goog
^ permalink raw reply related [flat|nested] 22+ messages in thread* Re: [PATCH v3 00/11] gpio: Adopt revocable mechanism for UAF prevention
2026-02-13 9:29 [PATCH v3 00/11] gpio: Adopt revocable mechanism for UAF prevention Tzung-Bi Shih
` (10 preceding siblings ...)
2026-02-13 9:29 ` [PATCH v3 11/11] gpio: Remove unused `chip` and `srcu` in struct gpio_device Tzung-Bi Shih
@ 2026-02-18 10:26 ` Bartosz Golaszewski
2026-02-23 6:21 ` Tzung-Bi Shih
11 siblings, 1 reply; 22+ messages in thread
From: Bartosz Golaszewski @ 2026-02-18 10:26 UTC (permalink / raw)
To: Tzung-Bi Shih
Cc: Linus Walleij, Greg Kroah-Hartman, Rafael J. Wysocki,
Danilo Krummrich, Jonathan Corbet, Shuah Khan, Laurent Pinchart,
Wolfram Sang, Jason Gunthorpe, Johan Hovold, Paul E . McKenney,
Dan Williams, chrome-platform, linux-gpio, linux-kselftest,
linux-kernel
On Fri, Feb 13, 2026 at 10:31 AM Tzung-Bi Shih <tzungbi@kernel.org> wrote:
>
> This series transitions the UAF prevention logic within the GPIO core
> (gpiolib) to use the 'revocable' mechanism.
>
> The existing code aims to prevent UAF issues when the underlying GPIO
> chip is removed. This series replaces that custom logic with the
> generic 'revocable' API, which is designed to handle such lifecycle
> dependencies. There should be no changes in behavior.
>
Patches 1-6 look good to me, I think they should go into linux-next
after v7.0-rc1 is tagged to lessen the burden on the subsequent
revocable work.
Bart
^ permalink raw reply [flat|nested] 22+ messages in thread* Re: [PATCH v3 00/11] gpio: Adopt revocable mechanism for UAF prevention
2026-02-18 10:26 ` [PATCH v3 00/11] gpio: Adopt revocable mechanism for UAF prevention Bartosz Golaszewski
@ 2026-02-23 6:21 ` Tzung-Bi Shih
0 siblings, 0 replies; 22+ messages in thread
From: Tzung-Bi Shih @ 2026-02-23 6:21 UTC (permalink / raw)
To: Bartosz Golaszewski
Cc: Linus Walleij, Greg Kroah-Hartman, Rafael J. Wysocki,
Danilo Krummrich, Jonathan Corbet, Shuah Khan, Laurent Pinchart,
Wolfram Sang, Jason Gunthorpe, Johan Hovold, Paul E . McKenney,
Dan Williams, chrome-platform, linux-gpio, linux-kselftest,
linux-kernel
On Wed, Feb 18, 2026 at 11:26:00AM +0100, Bartosz Golaszewski wrote:
> On Fri, Feb 13, 2026 at 10:31 AM Tzung-Bi Shih <tzungbi@kernel.org> wrote:
> >
> > This series transitions the UAF prevention logic within the GPIO core
> > (gpiolib) to use the 'revocable' mechanism.
> >
> > The existing code aims to prevent UAF issues when the underlying GPIO
> > chip is removed. This series replaces that custom logic with the
> > generic 'revocable' API, which is designed to handle such lifecycle
> > dependencies. There should be no changes in behavior.
> >
>
> Patches 1-6 look good to me, I think they should go into linux-next
> after v7.0-rc1 is tagged to lessen the burden on the subsequent
> revocable work.
Separated, rebased to v7.0-rc1, and addressed comments for the first 6
patches:
https://lore.kernel.org/all/20260223061726.82161-1-tzungbi@kernel.org
^ permalink raw reply [flat|nested] 22+ messages in thread