Skip to content

Commit

Permalink
Merge tag 'gpio-v4.9-2' of git://git.kernel.org/pub/scm/linux/kernel/…
Browse files Browse the repository at this point in the history
…git/linusw/linux-gpio

Pull GPIO fixes from Linus Walleij:
 "Here is a set of GPIO fixes for the v4.9 kernel series:

   - Fix up off-by one and line offset validation, info leak to
     userspace, and reject invalid flags. Those are especially valuable
     hardening patches from Lars-Peter Clausen, all tagged for stable.

   - Fix module autoload for TS4800 and ATH79.

   - Correct the IRQ handler for MPC8xxx to use handle_level_irq() as it
     (a) reacts to edges not levels and (b) even implements .irq_ack().
     We were missing IRQs here.

   - Fix the error path for acpi_dev_gpio_irq_get()

   - Fix a memory leak in the MXS driver.

   - Fix an annoying typo in the STMPE driver.

   - Put a dependency on sysfs to the mockup driver"

* tag 'gpio-v4.9-2' of git://git.kernel.org/pub/scm/linux/kernel/git/linusw/linux-gpio:
  gpio: mpc8xxx: Correct irq handler function
  gpio: ath79: Fix module autoload
  gpio: ts4800: Fix module autoload
  gpio: GPIO_GET_LINEEVENT_IOCTL: Reject invalid line and event flags
  gpio: GPIO_GET_LINEHANDLE_IOCTL: Reject invalid line flags
  gpio: GPIOHANDLE_GET_LINE_VALUES_IOCTL: Fix information leak
  gpio: GPIO_GET_LINEEVENT_IOCTL: Validate line offset
  gpio: GPIOHANDLE_GET_LINE_VALUES_IOCTL: Fix information leak
  gpio: GPIO_GET_LINEHANDLE_IOCTL: Validate line offset
  gpio: GPIO_GET_CHIPINFO_IOCTL: Fix information leak
  gpio: GPIO_GET_CHIPINFO_IOCTL: Fix line offset validation
  gpio / ACPI: fix returned error from acpi_dev_gpio_irq_get()
  gpio: mockup: add sysfs dependency
  gpio: stmpe: || vs && typo
  gpio: mxs: Unmap region obtained by of_iomap
  gpio/board.txt: point to gpiod_set_value
  • Loading branch information
Linus Torvalds committed Oct 25, 2016
2 parents aa34e07 + d71cf15 commit 1ce5bdb
Show file tree
Hide file tree
Showing 9 changed files with 64 additions and 12 deletions.
11 changes: 7 additions & 4 deletions Documentation/gpio/board.txt
Original file line number Diff line number Diff line change
Expand Up @@ -6,7 +6,7 @@ Note that it only applies to the new descriptor-based interface. For a
description of the deprecated integer-based GPIO interface please refer to
gpio-legacy.txt (actually, there is no real mapping possible with the old
interface; you just fetch an integer from somewhere and request the
corresponding GPIO.
corresponding GPIO).

All platforms can enable the GPIO library, but if the platform strictly
requires GPIO functionality to be present, it needs to select GPIOLIB from its
Expand Down Expand Up @@ -162,6 +162,9 @@ The driver controlling "foo.0" will then be able to obtain its GPIOs as follows:

Since the "led" GPIOs are mapped as active-high, this example will switch their
signals to 1, i.e. enabling the LEDs. And for the "power" GPIO, which is mapped
as active-low, its actual signal will be 0 after this code. Contrary to the legacy
integer GPIO interface, the active-low property is handled during mapping and is
thus transparent to GPIO consumers.
as active-low, its actual signal will be 0 after this code. Contrary to the
legacy integer GPIO interface, the active-low property is handled during
mapping and is thus transparent to GPIO consumers.

A set of functions such as gpiod_set_value() is available to work with
the new descriptor-oriented interface.
2 changes: 1 addition & 1 deletion drivers/gpio/Kconfig
Original file line number Diff line number Diff line change
Expand Up @@ -284,7 +284,7 @@ config GPIO_MM_LANTIQ

config GPIO_MOCKUP
tristate "GPIO Testing Driver"
depends on GPIOLIB
depends on GPIOLIB && SYSFS
select GPIO_SYSFS
help
This enables GPIO Testing driver, which provides a way to test GPIO
Expand Down
1 change: 1 addition & 0 deletions drivers/gpio/gpio-ath79.c
Original file line number Diff line number Diff line change
Expand Up @@ -219,6 +219,7 @@ static const struct of_device_id ath79_gpio_of_match[] = {
{ .compatible = "qca,ar9340-gpio" },
{},
};
MODULE_DEVICE_TABLE(of, ath79_gpio_of_match);

static int ath79_gpio_probe(struct platform_device *pdev)
{
Expand Down
2 changes: 1 addition & 1 deletion drivers/gpio/gpio-mpc8xxx.c
Original file line number Diff line number Diff line change
Expand Up @@ -239,7 +239,7 @@ static int mpc8xxx_gpio_irq_map(struct irq_domain *h, unsigned int irq,
irq_hw_number_t hwirq)
{
irq_set_chip_data(irq, h->host_data);
irq_set_chip_and_handler(irq, &mpc8xxx_irq_chip, handle_level_irq);
irq_set_chip_and_handler(irq, &mpc8xxx_irq_chip, handle_edge_irq);

return 0;
}
Expand Down
8 changes: 6 additions & 2 deletions drivers/gpio/gpio-mxs.c
Original file line number Diff line number Diff line change
Expand Up @@ -308,8 +308,10 @@ static int mxs_gpio_probe(struct platform_device *pdev)
writel(~0U, port->base + PINCTRL_IRQSTAT(port) + MXS_CLR);

irq_base = irq_alloc_descs(-1, 0, 32, numa_node_id());
if (irq_base < 0)
return irq_base;
if (irq_base < 0) {
err = irq_base;
goto out_iounmap;
}

port->domain = irq_domain_add_legacy(np, 32, irq_base, 0,
&irq_domain_simple_ops, NULL);
Expand Down Expand Up @@ -349,6 +351,8 @@ static int mxs_gpio_probe(struct platform_device *pdev)
irq_domain_remove(port->domain);
out_irqdesc_free:
irq_free_descs(irq_base, 32);
out_iounmap:
iounmap(port->base);
return err;
}

Expand Down
2 changes: 1 addition & 1 deletion drivers/gpio/gpio-stmpe.c
Original file line number Diff line number Diff line change
Expand Up @@ -409,7 +409,7 @@ static irqreturn_t stmpe_gpio_irq(int irq, void *dev)
* 801/1801/1600, bits are cleared when read.
* Edge detect register is not present on 801/1600/1801
*/
if (stmpe->partnum != STMPE801 || stmpe->partnum != STMPE1600 ||
if (stmpe->partnum != STMPE801 && stmpe->partnum != STMPE1600 &&
stmpe->partnum != STMPE1801) {
stmpe_reg_write(stmpe, statmsbreg + i, status[i]);
stmpe_reg_write(stmpe,
Expand Down
1 change: 1 addition & 0 deletions drivers/gpio/gpio-ts4800.c
Original file line number Diff line number Diff line change
Expand Up @@ -66,6 +66,7 @@ static const struct of_device_id ts4800_gpio_of_match[] = {
{ .compatible = "technologic,ts4800-gpio", },
{},
};
MODULE_DEVICE_TABLE(of, ts4800_gpio_of_match);

static struct platform_driver ts4800_gpio_driver = {
.driver = {
Expand Down
7 changes: 5 additions & 2 deletions drivers/gpio/gpiolib-acpi.c
Original file line number Diff line number Diff line change
Expand Up @@ -653,14 +653,17 @@ int acpi_dev_gpio_irq_get(struct acpi_device *adev, int index)
{
int idx, i;
unsigned int irq_flags;
int ret = -ENOENT;

for (i = 0, idx = 0; idx <= index; i++) {
struct acpi_gpio_info info;
struct gpio_desc *desc;

desc = acpi_get_gpiod_by_index(adev, NULL, i, &info);
if (IS_ERR(desc))
if (IS_ERR(desc)) {
ret = PTR_ERR(desc);
break;
}
if (info.gpioint && idx++ == index) {
int irq = gpiod_to_irq(desc);

Expand All @@ -679,7 +682,7 @@ int acpi_dev_gpio_irq_get(struct acpi_device *adev, int index)
}

}
return -ENOENT;
return ret;
}
EXPORT_SYMBOL_GPL(acpi_dev_gpio_irq_get);

Expand Down
42 changes: 41 additions & 1 deletion drivers/gpio/gpiolib.c
Original file line number Diff line number Diff line change
Expand Up @@ -333,6 +333,13 @@ struct linehandle_state {
u32 numdescs;
};

#define GPIOHANDLE_REQUEST_VALID_FLAGS \
(GPIOHANDLE_REQUEST_INPUT | \
GPIOHANDLE_REQUEST_OUTPUT | \
GPIOHANDLE_REQUEST_ACTIVE_LOW | \
GPIOHANDLE_REQUEST_OPEN_DRAIN | \
GPIOHANDLE_REQUEST_OPEN_SOURCE)

static long linehandle_ioctl(struct file *filep, unsigned int cmd,
unsigned long arg)
{
Expand All @@ -344,6 +351,8 @@ static long linehandle_ioctl(struct file *filep, unsigned int cmd,
if (cmd == GPIOHANDLE_GET_LINE_VALUES_IOCTL) {
int val;

memset(&ghd, 0, sizeof(ghd));

/* TODO: check if descriptors are really input */
for (i = 0; i < lh->numdescs; i++) {
val = gpiod_get_value_cansleep(lh->descs[i]);
Expand Down Expand Up @@ -444,6 +453,17 @@ static int linehandle_create(struct gpio_device *gdev, void __user *ip)
u32 lflags = handlereq.flags;
struct gpio_desc *desc;

if (offset >= gdev->ngpio) {
ret = -EINVAL;
goto out_free_descs;
}

/* Return an error if a unknown flag is set */
if (lflags & ~GPIOHANDLE_REQUEST_VALID_FLAGS) {
ret = -EINVAL;
goto out_free_descs;
}

desc = &gdev->descs[offset];
ret = gpiod_request(desc, lh->label);
if (ret)
Expand Down Expand Up @@ -536,6 +556,10 @@ struct lineevent_state {
struct mutex read_lock;
};

#define GPIOEVENT_REQUEST_VALID_FLAGS \
(GPIOEVENT_REQUEST_RISING_EDGE | \
GPIOEVENT_REQUEST_FALLING_EDGE)

static unsigned int lineevent_poll(struct file *filep,
struct poll_table_struct *wait)
{
Expand Down Expand Up @@ -623,6 +647,8 @@ static long lineevent_ioctl(struct file *filep, unsigned int cmd,
if (cmd == GPIOHANDLE_GET_LINE_VALUES_IOCTL) {
int val;

memset(&ghd, 0, sizeof(ghd));

val = gpiod_get_value_cansleep(le->desc);
if (val < 0)
return val;
Expand Down Expand Up @@ -726,6 +752,18 @@ static int lineevent_create(struct gpio_device *gdev, void __user *ip)
lflags = eventreq.handleflags;
eflags = eventreq.eventflags;

if (offset >= gdev->ngpio) {
ret = -EINVAL;
goto out_free_label;
}

/* Return an error if a unknown flag is set */
if ((lflags & ~GPIOHANDLE_REQUEST_VALID_FLAGS) ||
(eflags & ~GPIOEVENT_REQUEST_VALID_FLAGS)) {
ret = -EINVAL;
goto out_free_label;
}

/* This is just wrong: we don't look for events on output lines */
if (lflags & GPIOHANDLE_REQUEST_OUTPUT) {
ret = -EINVAL;
Expand Down Expand Up @@ -823,6 +861,8 @@ static long gpio_ioctl(struct file *filp, unsigned int cmd, unsigned long arg)
if (cmd == GPIO_GET_CHIPINFO_IOCTL) {
struct gpiochip_info chipinfo;

memset(&chipinfo, 0, sizeof(chipinfo));

strncpy(chipinfo.name, dev_name(&gdev->dev),
sizeof(chipinfo.name));
chipinfo.name[sizeof(chipinfo.name)-1] = '\0';
Expand All @@ -839,7 +879,7 @@ static long gpio_ioctl(struct file *filp, unsigned int cmd, unsigned long arg)

if (copy_from_user(&lineinfo, ip, sizeof(lineinfo)))
return -EFAULT;
if (lineinfo.line_offset > gdev->ngpio)
if (lineinfo.line_offset >= gdev->ngpio)
return -EINVAL;

desc = &gdev->descs[lineinfo.line_offset];
Expand Down

0 comments on commit 1ce5bdb

Please sign in to comment.