Skip to content

Commit

Permalink
gpiolib: check descriptors validity before use
Browse files Browse the repository at this point in the history
Some functions dereferenced their GPIO descriptor argument without
checking its validity first, potentially leading to an oops when given
an invalid argument.

This patch also makes gpio_get_value() more resilient when given an
invalid GPIO, returning 0 instead of silently crashing.

Signed-off-by: Alexandre Courbot <acourbot@nvidia.com>
Cc: Ryan Mallon <rmallon@gmail.com>
Signed-off-by: Grant Likely <grant.likely@secretlab.ca>
  • Loading branch information
Alexandre Courbot authored and Grant Likely committed Mar 2, 2013
1 parent b0af9cd commit bcabdef
Showing 1 changed file with 65 additions and 47 deletions.
112 changes: 65 additions & 47 deletions drivers/gpio/gpiolib.c
Original file line number Diff line number Diff line change
Expand Up @@ -174,7 +174,7 @@ static int gpio_ensure_requested(struct gpio_desc *desc)
/* caller holds gpio_lock *OR* gpio is marked as requested */
static struct gpio_chip *gpiod_to_chip(struct gpio_desc *desc)
{
return desc->chip;
return desc ? desc->chip : NULL;
}

struct gpio_chip *gpio_to_chip(unsigned gpio)
Expand Down Expand Up @@ -654,6 +654,11 @@ static ssize_t export_store(struct class *class,
goto done;

desc = gpio_to_desc(gpio);
/* reject invalid GPIOs */
if (!desc) {
pr_warn("%s: invalid GPIO %ld\n", __func__, gpio);
return -EINVAL;
}

/* No extra locking here; FLAG_SYSFS just signifies that the
* request and export were done by on behalf of userspace, so
Expand Down Expand Up @@ -690,12 +695,14 @@ static ssize_t unexport_store(struct class *class,
if (status < 0)
goto done;

status = -EINVAL;

desc = gpio_to_desc(gpio);
/* reject bogus commands (gpio_unexport ignores them) */
if (!desc)
goto done;
if (!desc) {
pr_warn("%s: invalid GPIO %ld\n", __func__, gpio);
return -EINVAL;
}

status = -EINVAL;

/* No extra locking here; FLAG_SYSFS just signifies that the
* request and export were done by on behalf of userspace, so
Expand Down Expand Up @@ -846,8 +853,10 @@ static int gpiod_export_link(struct device *dev, const char *name,
{
int status = -EINVAL;

if (!desc)
goto done;
if (!desc) {
pr_warn("%s: invalid GPIO\n", __func__);
return -EINVAL;
}

mutex_lock(&sysfs_lock);

Expand All @@ -865,7 +874,6 @@ static int gpiod_export_link(struct device *dev, const char *name,

mutex_unlock(&sysfs_lock);

done:
if (status)
pr_debug("%s: gpio%d status %d\n", __func__, desc_to_gpio(desc),
status);
Expand Down Expand Up @@ -896,8 +904,10 @@ static int gpiod_sysfs_set_active_low(struct gpio_desc *desc, int value)
struct device *dev = NULL;
int status = -EINVAL;

if (!desc)
goto done;
if (!desc) {
pr_warn("%s: invalid GPIO\n", __func__);
return -EINVAL;
}

mutex_lock(&sysfs_lock);

Expand All @@ -914,7 +924,6 @@ static int gpiod_sysfs_set_active_low(struct gpio_desc *desc, int value)
unlock:
mutex_unlock(&sysfs_lock);

done:
if (status)
pr_debug("%s: gpio%d status %d\n", __func__, desc_to_gpio(desc),
status);
Expand All @@ -940,8 +949,8 @@ static void gpiod_unexport(struct gpio_desc *desc)
struct device *dev = NULL;

if (!desc) {
status = -EINVAL;
goto done;
pr_warn("%s: invalid GPIO\n", __func__);
return;
}

mutex_lock(&sysfs_lock);
Expand All @@ -962,7 +971,7 @@ static void gpiod_unexport(struct gpio_desc *desc)
device_unregister(dev);
put_device(dev);
}
done:

if (status)
pr_debug("%s: gpio%d status %d\n", __func__, desc_to_gpio(desc),
status);
Expand Down Expand Up @@ -1384,12 +1393,13 @@ static int gpiod_request(struct gpio_desc *desc, const char *label)
int status = -EPROBE_DEFER;
unsigned long flags;

spin_lock_irqsave(&gpio_lock, flags);

if (!desc) {
status = -EINVAL;
goto done;
pr_warn("%s: invalid GPIO\n", __func__);
return -EINVAL;
}

spin_lock_irqsave(&gpio_lock, flags);

chip = desc->chip;
if (chip == NULL)
goto done;
Expand Down Expand Up @@ -1432,8 +1442,7 @@ static int gpiod_request(struct gpio_desc *desc, const char *label)
done:
if (status)
pr_debug("_gpio_request: gpio-%d (%s) status %d\n",
desc ? desc_to_gpio(desc) : -1,
label ? : "?", status);
desc_to_gpio(desc), label ? : "?", status);
spin_unlock_irqrestore(&gpio_lock, flags);
return status;
}
Expand Down Expand Up @@ -1616,10 +1625,13 @@ static int gpiod_direction_input(struct gpio_desc *desc)
int status = -EINVAL;
int offset;

if (!desc) {
pr_warn("%s: invalid GPIO\n", __func__);
return -EINVAL;
}

spin_lock_irqsave(&gpio_lock, flags);

if (!desc)
goto fail;
chip = desc->chip;
if (!chip || !chip->get || !chip->direction_input)
goto fail;
Expand Down Expand Up @@ -1655,13 +1667,9 @@ static int gpiod_direction_input(struct gpio_desc *desc)
return status;
fail:
spin_unlock_irqrestore(&gpio_lock, flags);
if (status) {
int gpio = -1;
if (desc)
gpio = desc_to_gpio(desc);
pr_debug("%s: gpio-%d status %d\n",
__func__, gpio, status);
}
if (status)
pr_debug("%s: gpio-%d status %d\n", __func__,
desc_to_gpio(desc), status);
return status;
}

Expand All @@ -1678,6 +1686,11 @@ static int gpiod_direction_output(struct gpio_desc *desc, int value)
int status = -EINVAL;
int offset;

if (!desc) {
pr_warn("%s: invalid GPIO\n", __func__);
return -EINVAL;
}

/* Open drain pin should not be driven to 1 */
if (value && test_bit(FLAG_OPEN_DRAIN, &desc->flags))
return gpiod_direction_input(desc);
Expand All @@ -1688,8 +1701,6 @@ static int gpiod_direction_output(struct gpio_desc *desc, int value)

spin_lock_irqsave(&gpio_lock, flags);

if (!desc)
goto fail;
chip = desc->chip;
if (!chip || !chip->set || !chip->direction_output)
goto fail;
Expand Down Expand Up @@ -1725,13 +1736,9 @@ static int gpiod_direction_output(struct gpio_desc *desc, int value)
return status;
fail:
spin_unlock_irqrestore(&gpio_lock, flags);
if (status) {
int gpio = -1;
if (desc)
gpio = desc_to_gpio(desc);
pr_debug("%s: gpio-%d status %d\n",
__func__, gpio, status);
}
if (status)
pr_debug("%s: gpio-%d status %d\n", __func__,
desc_to_gpio(desc), status);
return status;
}

Expand All @@ -1753,10 +1760,13 @@ static int gpiod_set_debounce(struct gpio_desc *desc, unsigned debounce)
int status = -EINVAL;
int offset;

if (!desc) {
pr_warn("%s: invalid GPIO\n", __func__);
return -EINVAL;
}

spin_lock_irqsave(&gpio_lock, flags);

if (!desc)
goto fail;
chip = desc->chip;
if (!chip || !chip->set || !chip->set_debounce)
goto fail;
Expand All @@ -1776,13 +1786,9 @@ static int gpiod_set_debounce(struct gpio_desc *desc, unsigned debounce)

fail:
spin_unlock_irqrestore(&gpio_lock, flags);
if (status) {
int gpio = -1;
if (desc)
gpio = desc_to_gpio(desc);
pr_debug("%s: gpio-%d status %d\n",
__func__, gpio, status);
}
if (status)
pr_debug("%s: gpio-%d status %d\n", __func__,
desc_to_gpio(desc), status);

return status;
}
Expand Down Expand Up @@ -1830,6 +1836,8 @@ static int gpiod_get_value(struct gpio_desc *desc)
int value;
int offset;

if (!desc)
return 0;
chip = desc->chip;
offset = gpio_chip_hwgpio(desc);
/* Should be using gpio_get_value_cansleep() */
Expand Down Expand Up @@ -1912,6 +1920,8 @@ static void gpiod_set_value(struct gpio_desc *desc, int value)
{
struct gpio_chip *chip;

if (!desc)
return;
chip = desc->chip;
/* Should be using gpio_set_value_cansleep() */
WARN_ON(chip->can_sleep);
Expand Down Expand Up @@ -1940,6 +1950,8 @@ EXPORT_SYMBOL_GPL(__gpio_set_value);
*/
static int gpiod_cansleep(struct gpio_desc *desc)
{
if (!desc)
return 0;
/* only call this on GPIOs that are valid! */
return desc->chip->can_sleep;
}
Expand All @@ -1964,6 +1976,8 @@ static int gpiod_to_irq(struct gpio_desc *desc)
struct gpio_chip *chip;
int offset;

if (!desc)
return -EINVAL;
chip = desc->chip;
offset = gpio_chip_hwgpio(desc);
return chip->to_irq ? chip->to_irq(chip, offset) : -ENXIO;
Expand All @@ -1987,6 +2001,8 @@ static int gpiod_get_value_cansleep(struct gpio_desc *desc)
int offset;

might_sleep_if(extra_checks);
if (!desc)
return 0;
chip = desc->chip;
offset = gpio_chip_hwgpio(desc);
value = chip->get ? chip->get(chip, offset) : 0;
Expand All @@ -2005,6 +2021,8 @@ static void gpiod_set_value_cansleep(struct gpio_desc *desc, int value)
struct gpio_chip *chip;

might_sleep_if(extra_checks);
if (!desc)
return;
chip = desc->chip;
trace_gpio_value(desc_to_gpio(desc), 0, value);
if (test_bit(FLAG_OPEN_DRAIN, &desc->flags))
Expand Down

0 comments on commit bcabdef

Please sign in to comment.