]>
Commit | Line | Data |
---|---|---|
b5be2e70 GKH |
1 | From 483d821108791092798f5d230686868112927044 Mon Sep 17 00:00:00 2001 |
2 | From: Johan Hovold <johan@kernel.org> | |
3 | Date: Tue, 21 Apr 2015 17:42:09 +0200 | |
4 | Subject: gpio: sysfs: fix memory leaks and device hotplug | |
5 | ||
6 | From: Johan Hovold <johan@kernel.org> | |
7 | ||
8 | commit 483d821108791092798f5d230686868112927044 upstream. | |
9 | ||
10 | Unregister GPIOs requested through sysfs at chip remove to avoid leaking | |
11 | the associated memory and sysfs entries. | |
12 | ||
13 | The stale sysfs entries prevented the gpio numbers from being exported | |
14 | when the gpio range was later reused (e.g. at device reconnect). | |
15 | ||
16 | This also fixes the related module-reference leak. | |
17 | ||
18 | Note that kernfs makes sure that any on-going sysfs operations finish | |
19 | before the class devices are unregistered and that further accesses | |
20 | fail. | |
21 | ||
22 | The chip exported flag is used to prevent gpiod exports during removal. | |
23 | This also makes it harder to trigger, but does not fix, the related race | |
24 | between gpiochip_remove and export_store, which is really a race with | |
25 | gpiod_request that needs to be addressed separately. | |
26 | ||
27 | Also note that this would prevent the crashes (e.g. NULL-dereferences) | |
28 | at reconnect that affects pre-3.18 kernels, as well as use-after-free on | |
29 | operations on open attribute files on pre-3.14 kernels (prior to | |
30 | kernfs). | |
31 | ||
32 | Fixes: d8f388d8dc8d ("gpio: sysfs interface") | |
33 | Signed-off-by: Johan Hovold <johan@kernel.org> | |
34 | Signed-off-by: Linus Walleij <linus.walleij@linaro.org> | |
35 | Signed-off-by: Greg Kroah-Hartman <gregkh@linuxfoundation.org> | |
36 | ||
37 | --- | |
38 | drivers/gpio/gpiolib.c | 19 +++++++++++++++++++ | |
39 | 1 file changed, 19 insertions(+) | |
40 | ||
41 | --- a/drivers/gpio/gpiolib.c | |
42 | +++ b/drivers/gpio/gpiolib.c | |
43 | @@ -752,6 +752,7 @@ static struct class gpio_class = { | |
44 | */ | |
45 | static int gpiod_export(struct gpio_desc *desc, bool direction_may_change) | |
46 | { | |
47 | + struct gpio_chip *chip; | |
48 | unsigned long flags; | |
49 | int status; | |
50 | const char *ioname = NULL; | |
51 | @@ -769,8 +770,16 @@ static int gpiod_export(struct gpio_desc | |
52 | return -EINVAL; | |
53 | } | |
54 | ||
55 | + chip = desc->chip; | |
56 | + | |
57 | mutex_lock(&sysfs_lock); | |
58 | ||
59 | + /* check if chip is being removed */ | |
60 | + if (!chip || !chip->exported) { | |
61 | + status = -ENODEV; | |
62 | + goto fail_unlock; | |
63 | + } | |
64 | + | |
65 | spin_lock_irqsave(&gpio_lock, flags); | |
66 | if (!test_bit(FLAG_REQUESTED, &desc->flags) || | |
67 | test_bit(FLAG_EXPORT, &desc->flags)) { | |
68 | @@ -1040,6 +1049,8 @@ static void gpiochip_unexport(struct gpi | |
69 | { | |
70 | int status; | |
71 | struct device *dev; | |
72 | + struct gpio_desc *desc; | |
73 | + unsigned int i; | |
74 | ||
75 | mutex_lock(&sysfs_lock); | |
76 | dev = class_find_device(&gpio_class, NULL, chip, match_export); | |
77 | @@ -1047,6 +1058,7 @@ static void gpiochip_unexport(struct gpi | |
78 | sysfs_remove_group(&dev->kobj, &gpiochip_attr_group); | |
79 | put_device(dev); | |
80 | device_unregister(dev); | |
81 | + /* prevent further gpiod exports */ | |
82 | chip->exported = 0; | |
83 | status = 0; | |
84 | } else | |
85 | @@ -1056,6 +1068,13 @@ static void gpiochip_unexport(struct gpi | |
86 | if (status) | |
87 | pr_debug("%s: chip %s status %d\n", __func__, | |
88 | chip->label, status); | |
89 | + | |
90 | + /* unregister gpiod class devices owned by sysfs */ | |
91 | + for (i = 0; i < chip->ngpio; i++) { | |
92 | + desc = &chip->desc[i]; | |
93 | + if (test_and_clear_bit(FLAG_SYSFS, &desc->flags)) | |
94 | + gpiod_free(desc); | |
95 | + } | |
96 | } | |
97 | ||
98 | static int __init gpiolib_sysfs_init(void) |