summaryrefslogtreecommitdiff
path: root/drivers/gpio
diff options
context:
space:
mode:
authorJohan Hovold <johan@kernel.org>2015-01-12 19:12:25 +0300
committerLinus Walleij <linus.walleij@linaro.org>2015-01-14 16:24:29 +0300
commit225fce83cb72e1bffb712a33ce47c210c770f8ab (patch)
treeff753de5fbb156b1990dc83782a195d8ec5cc081 /drivers/gpio
parent5539b3c938d64a60cb1fc442ac3ce9263d52de0c (diff)
downloadlinux-225fce83cb72e1bffb712a33ce47c210c770f8ab.tar.xz
gpio: fix gpio-chip list corruption
Fix potential corruption of gpio-chip list due to failure to remove the chip from the list before returning in gpiochip_add error path. The chip could be long gone when the global list is next traversed, something which could lead to a null-pointer dereference. In the best case (chip not deallocated) we are just leaking the gpio range. Fixes: 14e85c0e69d5 ("gpio: remove gpio_descs global array") Signed-off-by: Johan Hovold <johan@kernel.org> Signed-off-by: Linus Walleij <linus.walleij@linaro.org>
Diffstat (limited to 'drivers/gpio')
-rw-r--r--drivers/gpio/gpiolib.c17
1 files changed, 10 insertions, 7 deletions
diff --git a/drivers/gpio/gpiolib.c b/drivers/gpio/gpiolib.c
index 89c59f5f1924..ac5944b4e4d8 100644
--- a/drivers/gpio/gpiolib.c
+++ b/drivers/gpio/gpiolib.c
@@ -248,7 +248,8 @@ int gpiochip_add(struct gpio_chip *chip)
base = gpiochip_find_base(chip->ngpio);
if (base < 0) {
status = base;
- goto unlock;
+ spin_unlock_irqrestore(&gpio_lock, flags);
+ goto err_free_descs;
}
chip->base = base;
}
@@ -288,11 +289,8 @@ int gpiochip_add(struct gpio_chip *chip)
acpi_gpiochip_add(chip);
status = gpiochip_export(chip);
- if (status) {
- acpi_gpiochip_remove(chip);
- of_gpiochip_remove(chip);
- goto fail;
- }
+ if (status)
+ goto err_remove_chip;
pr_debug("%s: registered GPIOs %d to %d on device: %s\n", __func__,
chip->base, chip->base + chip->ngpio - 1,
@@ -300,9 +298,14 @@ int gpiochip_add(struct gpio_chip *chip)
return 0;
-unlock:
+err_remove_chip:
+ acpi_gpiochip_remove(chip);
+ of_gpiochip_remove(chip);
+ spin_lock_irqsave(&gpio_lock, flags);
+ list_del(&chip->list);
spin_unlock_irqrestore(&gpio_lock, flags);
fail:
+err_free_descs:
kfree(descs);
chip->desc = NULL;