Skip to content

Commit d83cee3

Browse files
author
Bartosz Golaszewski
committed
gpio: protect the pointer to gpio_chip in gpio_device with SRCU
Ensure we cannot crash if the GPIO device gets unregistered (and the chip pointer set to NULL) during any of the API calls. To that end: wait for all users of gdev->chip to exit their read-only SRCU critical sections in gpiochip_remove(). For brevity: add a guard class which can be instantiated at the top of every function requiring read-only access to the chip pointer and use it in all API calls taking a GPIO descriptor as argument. In places where we only deal with the GPIO device - use regular guard() helpers and rcu_dereference() for chip access. Do the same in API calls taking a const pointer to gpio_desc. Signed-off-by: Bartosz Golaszewski <[email protected]> Reviewed-by: Linus Walleij <[email protected]> Acked-by: Andy Shevchenko <[email protected]>
1 parent 47d8b4c commit d83cee3

File tree

4 files changed

+271
-129
lines changed

4 files changed

+271
-129
lines changed

drivers/gpio/gpiolib-cdev.c

Lines changed: 36 additions & 28 deletions
Original file line numberDiff line numberDiff line change
@@ -24,7 +24,6 @@
2424
#include <linux/pinctrl/consumer.h>
2525
#include <linux/poll.h>
2626
#include <linux/rbtree.h>
27-
#include <linux/rwsem.h>
2827
#include <linux/seq_file.h>
2928
#include <linux/spinlock.h>
3029
#include <linux/timekeeping.h>
@@ -205,9 +204,9 @@ static long linehandle_ioctl(struct file *file, unsigned int cmd,
205204
unsigned int i;
206205
int ret;
207206

208-
guard(rwsem_read)(&lh->gdev->sem);
207+
guard(srcu)(&lh->gdev->srcu);
209208

210-
if (!lh->gdev->chip)
209+
if (!rcu_dereference(lh->gdev->chip))
211210
return -ENODEV;
212211

213212
switch (cmd) {
@@ -1520,9 +1519,9 @@ static long linereq_ioctl(struct file *file, unsigned int cmd,
15201519
struct linereq *lr = file->private_data;
15211520
void __user *ip = (void __user *)arg;
15221521

1523-
guard(rwsem_read)(&lr->gdev->sem);
1522+
guard(srcu)(&lr->gdev->srcu);
15241523

1525-
if (!lr->gdev->chip)
1524+
if (!rcu_dereference(lr->gdev->chip))
15261525
return -ENODEV;
15271526

15281527
switch (cmd) {
@@ -1551,9 +1550,9 @@ static __poll_t linereq_poll(struct file *file,
15511550
struct linereq *lr = file->private_data;
15521551
__poll_t events = 0;
15531552

1554-
guard(rwsem_read)(&lr->gdev->sem);
1553+
guard(srcu)(&lr->gdev->srcu);
15551554

1556-
if (!lr->gdev->chip)
1555+
if (!rcu_dereference(lr->gdev->chip))
15571556
return EPOLLHUP | EPOLLERR;
15581557

15591558
poll_wait(file, &lr->wait, wait);
@@ -1573,9 +1572,9 @@ static ssize_t linereq_read(struct file *file, char __user *buf,
15731572
ssize_t bytes_read = 0;
15741573
int ret;
15751574

1576-
guard(rwsem_read)(&lr->gdev->sem);
1575+
guard(srcu)(&lr->gdev->srcu);
15771576

1578-
if (!lr->gdev->chip)
1577+
if (!rcu_dereference(lr->gdev->chip))
15791578
return -ENODEV;
15801579

15811580
if (count < sizeof(le))
@@ -1874,9 +1873,9 @@ static __poll_t lineevent_poll(struct file *file,
18741873
struct lineevent_state *le = file->private_data;
18751874
__poll_t events = 0;
18761875

1877-
guard(rwsem_read)(&le->gdev->sem);
1876+
guard(srcu)(&le->gdev->srcu);
18781877

1879-
if (!le->gdev->chip)
1878+
if (!rcu_dereference(le->gdev->chip))
18801879
return EPOLLHUP | EPOLLERR;
18811880

18821881
poll_wait(file, &le->wait, wait);
@@ -1912,9 +1911,9 @@ static ssize_t lineevent_read(struct file *file, char __user *buf,
19121911
ssize_t ge_size;
19131912
int ret;
19141913

1915-
guard(rwsem_read)(&le->gdev->sem);
1914+
guard(srcu)(&le->gdev->srcu);
19161915

1917-
if (!le->gdev->chip)
1916+
if (!rcu_dereference(le->gdev->chip))
19181917
return -ENODEV;
19191918

19201919
/*
@@ -1995,9 +1994,9 @@ static long lineevent_ioctl(struct file *file, unsigned int cmd,
19951994
void __user *ip = (void __user *)arg;
19961995
struct gpiohandle_data ghd;
19971996

1998-
guard(rwsem_read)(&le->gdev->sem);
1997+
guard(srcu)(&le->gdev->srcu);
19991998

2000-
if (!le->gdev->chip)
1999+
if (!rcu_dereference(le->gdev->chip))
20012000
return -ENODEV;
20022001

20032002
/*
@@ -2295,10 +2294,13 @@ static void gpio_v2_line_info_changed_to_v1(
22952294
static void gpio_desc_to_lineinfo(struct gpio_desc *desc,
22962295
struct gpio_v2_line_info *info)
22972296
{
2298-
struct gpio_chip *gc = desc->gdev->chip;
22992297
unsigned long dflags;
23002298
const char *label;
23012299

2300+
CLASS(gpio_chip_guard, guard)(desc);
2301+
if (!guard.gc)
2302+
return;
2303+
23022304
memset(info, 0, sizeof(*info));
23032305
info->offset = gpio_chip_hwgpio(desc);
23042306

@@ -2331,8 +2333,8 @@ static void gpio_desc_to_lineinfo(struct gpio_desc *desc,
23312333
test_bit(FLAG_USED_AS_IRQ, &dflags) ||
23322334
test_bit(FLAG_EXPORT, &dflags) ||
23332335
test_bit(FLAG_SYSFS, &dflags) ||
2334-
!gpiochip_line_is_valid(gc, info->offset) ||
2335-
!pinctrl_gpio_can_use_line(gc, info->offset))
2336+
!gpiochip_line_is_valid(guard.gc, info->offset) ||
2337+
!pinctrl_gpio_can_use_line(guard.gc, info->offset))
23362338
info->flags |= GPIO_V2_LINE_FLAG_USED;
23372339

23382340
if (test_bit(FLAG_IS_OUT, &dflags))
@@ -2505,10 +2507,10 @@ static long gpio_ioctl(struct file *file, unsigned int cmd, unsigned long arg)
25052507
struct gpio_device *gdev = cdev->gdev;
25062508
void __user *ip = (void __user *)arg;
25072509

2508-
guard(rwsem_read)(&gdev->sem);
2510+
guard(srcu)(&gdev->srcu);
25092511

25102512
/* We fail any subsequent ioctl():s when the chip is gone */
2511-
if (!gdev->chip)
2513+
if (!rcu_dereference(gdev->chip))
25122514
return -ENODEV;
25132515

25142516
/* Fill in the struct and pass to userspace */
@@ -2591,9 +2593,9 @@ static __poll_t lineinfo_watch_poll(struct file *file,
25912593
struct gpio_chardev_data *cdev = file->private_data;
25922594
__poll_t events = 0;
25932595

2594-
guard(rwsem_read)(&cdev->gdev->sem);
2596+
guard(srcu)(&cdev->gdev->srcu);
25952597

2596-
if (!cdev->gdev->chip)
2598+
if (!rcu_dereference(cdev->gdev->chip))
25972599
return EPOLLHUP | EPOLLERR;
25982600

25992601
poll_wait(file, &cdev->wait, pollt);
@@ -2614,9 +2616,9 @@ static ssize_t lineinfo_watch_read(struct file *file, char __user *buf,
26142616
int ret;
26152617
size_t event_size;
26162618

2617-
guard(rwsem_read)(&cdev->gdev->sem);
2619+
guard(srcu)(&cdev->gdev->srcu);
26182620

2619-
if (!cdev->gdev->chip)
2621+
if (!rcu_dereference(cdev->gdev->chip))
26202622
return -ENODEV;
26212623

26222624
#ifndef CONFIG_GPIO_CDEV_V1
@@ -2691,10 +2693,10 @@ static int gpio_chrdev_open(struct inode *inode, struct file *file)
26912693
struct gpio_chardev_data *cdev;
26922694
int ret = -ENOMEM;
26932695

2694-
guard(rwsem_read)(&gdev->sem);
2696+
guard(srcu)(&gdev->srcu);
26952697

26962698
/* Fail on open if the backing gpiochip is gone */
2697-
if (!gdev->chip)
2699+
if (!rcu_dereference(gdev->chip))
26982700
return -ENODEV;
26992701

27002702
cdev = kzalloc(sizeof(*cdev), GFP_KERNEL);
@@ -2781,6 +2783,7 @@ static const struct file_operations gpio_fileops = {
27812783

27822784
int gpiolib_cdev_register(struct gpio_device *gdev, dev_t devt)
27832785
{
2786+
struct gpio_chip *gc;
27842787
int ret;
27852788

27862789
cdev_init(&gdev->chrdev, &gpio_fileops);
@@ -2791,8 +2794,13 @@ int gpiolib_cdev_register(struct gpio_device *gdev, dev_t devt)
27912794
if (ret)
27922795
return ret;
27932796

2794-
chip_dbg(gdev->chip, "added GPIO chardev (%d:%d)\n",
2795-
MAJOR(devt), gdev->id);
2797+
guard(srcu)(&gdev->srcu);
2798+
2799+
gc = rcu_dereference(gdev->chip);
2800+
if (!gc)
2801+
return -ENODEV;
2802+
2803+
chip_dbg(gc, "added GPIO chardev (%d:%d)\n", MAJOR(devt), gdev->id);
27962804

27972805
return 0;
27982806
}

drivers/gpio/gpiolib-sysfs.c

Lines changed: 41 additions & 16 deletions
Original file line numberDiff line numberDiff line change
@@ -171,6 +171,10 @@ static int gpio_sysfs_request_irq(struct device *dev, unsigned char flags)
171171
unsigned long irq_flags;
172172
int ret;
173173

174+
CLASS(gpio_chip_guard, guard)(desc);
175+
if (!guard.gc)
176+
return -ENODEV;
177+
174178
data->irq = gpiod_to_irq(desc);
175179
if (data->irq < 0)
176180
return -EIO;
@@ -195,7 +199,7 @@ static int gpio_sysfs_request_irq(struct device *dev, unsigned char flags)
195199
* Remove this redundant call (along with the corresponding
196200
* unlock) when those drivers have been fixed.
197201
*/
198-
ret = gpiochip_lock_as_irq(desc->gdev->chip, gpio_chip_hwgpio(desc));
202+
ret = gpiochip_lock_as_irq(guard.gc, gpio_chip_hwgpio(desc));
199203
if (ret < 0)
200204
goto err_put_kn;
201205

@@ -209,7 +213,7 @@ static int gpio_sysfs_request_irq(struct device *dev, unsigned char flags)
209213
return 0;
210214

211215
err_unlock:
212-
gpiochip_unlock_as_irq(desc->gdev->chip, gpio_chip_hwgpio(desc));
216+
gpiochip_unlock_as_irq(guard.gc, gpio_chip_hwgpio(desc));
213217
err_put_kn:
214218
sysfs_put(data->value_kn);
215219

@@ -225,9 +229,13 @@ static void gpio_sysfs_free_irq(struct device *dev)
225229
struct gpiod_data *data = dev_get_drvdata(dev);
226230
struct gpio_desc *desc = data->desc;
227231

232+
CLASS(gpio_chip_guard, guard)(desc);
233+
if (!guard.gc)
234+
return;
235+
228236
data->irq_flags = 0;
229237
free_irq(data->irq, data);
230-
gpiochip_unlock_as_irq(desc->gdev->chip, gpio_chip_hwgpio(desc));
238+
gpiochip_unlock_as_irq(guard.gc, gpio_chip_hwgpio(desc));
231239
sysfs_put(data->value_kn);
232240
}
233241

@@ -444,23 +452,26 @@ static ssize_t export_store(const struct class *class,
444452
const char *buf, size_t len)
445453
{
446454
struct gpio_desc *desc;
447-
struct gpio_chip *gc;
448455
int status, offset;
449456
long gpio;
450457

451458
status = kstrtol(buf, 0, &gpio);
452-
if (status < 0)
453-
goto done;
459+
if (status)
460+
return status;
454461

455462
desc = gpio_to_desc(gpio);
456463
/* reject invalid GPIOs */
457464
if (!desc) {
458465
pr_warn("%s: invalid GPIO %ld\n", __func__, gpio);
459466
return -EINVAL;
460467
}
461-
gc = desc->gdev->chip;
468+
469+
CLASS(gpio_chip_guard, guard)(desc);
470+
if (!guard.gc)
471+
return -ENODEV;
472+
462473
offset = gpio_chip_hwgpio(desc);
463-
if (!gpiochip_line_is_valid(gc, offset)) {
474+
if (!gpiochip_line_is_valid(guard.gc, offset)) {
464475
pr_warn("%s: GPIO %ld masked\n", __func__, gpio);
465476
return -EINVAL;
466477
}
@@ -563,7 +574,6 @@ int gpiod_export(struct gpio_desc *desc, bool direction_may_change)
563574
const char *ioname = NULL;
564575
struct gpio_device *gdev;
565576
struct gpiod_data *data;
566-
struct gpio_chip *chip;
567577
struct device *dev;
568578
int status, offset;
569579

@@ -578,16 +588,19 @@ int gpiod_export(struct gpio_desc *desc, bool direction_may_change)
578588
return -EINVAL;
579589
}
580590

591+
CLASS(gpio_chip_guard, guard)(desc);
592+
if (!guard.gc)
593+
return -ENODEV;
594+
581595
if (!test_and_set_bit(FLAG_EXPORT, &desc->flags))
582596
return -EPERM;
583597

584598
gdev = desc->gdev;
585-
chip = gdev->chip;
586599

587600
mutex_lock(&sysfs_lock);
588601

589602
/* check if chip is being removed */
590-
if (!chip || !gdev->mockdev) {
603+
if (!gdev->mockdev) {
591604
status = -ENODEV;
592605
goto err_unlock;
593606
}
@@ -606,14 +619,14 @@ int gpiod_export(struct gpio_desc *desc, bool direction_may_change)
606619

607620
data->desc = desc;
608621
mutex_init(&data->mutex);
609-
if (chip->direction_input && chip->direction_output)
622+
if (guard.gc->direction_input && guard.gc->direction_output)
610623
data->direction_can_change = direction_may_change;
611624
else
612625
data->direction_can_change = false;
613626

614627
offset = gpio_chip_hwgpio(desc);
615-
if (chip->names && chip->names[offset])
616-
ioname = chip->names[offset];
628+
if (guard.gc->names && guard.gc->names[offset])
629+
ioname = guard.gc->names[offset];
617630

618631
dev = device_create_with_groups(&gpio_class, &gdev->dev,
619632
MKDEV(0, 0), data, gpio_groups,
@@ -728,7 +741,7 @@ EXPORT_SYMBOL_GPL(gpiod_unexport);
728741

729742
int gpiochip_sysfs_register(struct gpio_device *gdev)
730743
{
731-
struct gpio_chip *chip = gdev->chip;
744+
struct gpio_chip *chip;
732745
struct device *parent;
733746
struct device *dev;
734747

@@ -741,6 +754,12 @@ int gpiochip_sysfs_register(struct gpio_device *gdev)
741754
if (!class_is_registered(&gpio_class))
742755
return 0;
743756

757+
guard(srcu)(&gdev->srcu);
758+
759+
chip = rcu_dereference(gdev->chip);
760+
if (!chip)
761+
return -ENODEV;
762+
744763
/*
745764
* For sysfs backward compatibility we need to preserve this
746765
* preferred parenting to the gpio_chip parent field, if set.
@@ -767,7 +786,7 @@ int gpiochip_sysfs_register(struct gpio_device *gdev)
767786
void gpiochip_sysfs_unregister(struct gpio_device *gdev)
768787
{
769788
struct gpio_desc *desc;
770-
struct gpio_chip *chip = gdev->chip;
789+
struct gpio_chip *chip;
771790

772791
scoped_guard(mutex, &sysfs_lock) {
773792
if (!gdev->mockdev)
@@ -779,6 +798,12 @@ void gpiochip_sysfs_unregister(struct gpio_device *gdev)
779798
gdev->mockdev = NULL;
780799
}
781800

801+
guard(srcu)(&gdev->srcu);
802+
803+
chip = rcu_dereference(gdev->chip);
804+
if (chip)
805+
return;
806+
782807
/* unregister gpiod class devices owned by sysfs */
783808
for_each_gpio_desc_with_flag(chip, desc, FLAG_SYSFS) {
784809
gpiod_unexport(desc);

0 commit comments

Comments
 (0)