| From 5467801f1fcbdc46bc7298a84dbf3ca1ff2a7320 Mon Sep 17 00:00:00 2001 |
| From: Shreeya Patel <shreeya.patel@collabora.com> |
| Date: Mon, 21 Mar 2022 19:02:41 +0530 |
| Subject: gpio: Restrict usage of GPIO chip irq members before initialization |
| |
| From: Shreeya Patel <shreeya.patel@collabora.com> |
| |
| commit 5467801f1fcbdc46bc7298a84dbf3ca1ff2a7320 upstream. |
| |
| GPIO chip irq members are exposed before they could be completely |
| initialized and this leads to race conditions. |
| |
| One such issue was observed for the gc->irq.domain variable which |
| was accessed through the I2C interface in gpiochip_to_irq() before |
| it could be initialized by gpiochip_add_irqchip(). This resulted in |
| Kernel NULL pointer dereference. |
| |
| Following are the logs for reference :- |
| |
| kernel: Call Trace: |
| kernel: gpiod_to_irq+0x53/0x70 |
| kernel: acpi_dev_gpio_irq_get_by+0x113/0x1f0 |
| kernel: i2c_acpi_get_irq+0xc0/0xd0 |
| kernel: i2c_device_probe+0x28a/0x2a0 |
| kernel: really_probe+0xf2/0x460 |
| kernel: RIP: 0010:gpiochip_to_irq+0x47/0xc0 |
| |
| To avoid such scenarios, restrict usage of GPIO chip irq members before |
| they are completely initialized. |
| |
| Signed-off-by: Shreeya Patel <shreeya.patel@collabora.com> |
| Cc: stable@vger.kernel.org |
| Reviewed-by: Andy Shevchenko <andy.shevchenko@gmail.com> |
| Reviewed-by: Linus Walleij <linus.walleij@linaro.org> |
| Signed-off-by: Bartosz Golaszewski <brgl@bgdev.pl> |
| Signed-off-by: Greg Kroah-Hartman <gregkh@linuxfoundation.org> |
| --- |
| drivers/gpio/gpiolib.c | 19 +++++++++++++++++++ |
| include/linux/gpio/driver.h | 9 +++++++++ |
| 2 files changed, 28 insertions(+) |
| |
| --- a/drivers/gpio/gpiolib.c |
| +++ b/drivers/gpio/gpiolib.c |
| @@ -1368,6 +1368,16 @@ static int gpiochip_to_irq(struct gpio_c |
| { |
| struct irq_domain *domain = gc->irq.domain; |
| |
| +#ifdef CONFIG_GPIOLIB_IRQCHIP |
| + /* |
| + * Avoid race condition with other code, which tries to lookup |
| + * an IRQ before the irqchip has been properly registered, |
| + * i.e. while gpiochip is still being brought up. |
| + */ |
| + if (!gc->irq.initialized) |
| + return -EPROBE_DEFER; |
| +#endif |
| + |
| if (!gpiochip_irqchip_irq_valid(gc, offset)) |
| return -ENXIO; |
| |
| @@ -1557,6 +1567,15 @@ static int gpiochip_add_irqchip(struct g |
| |
| acpi_gpiochip_request_interrupts(gc); |
| |
| + /* |
| + * Using barrier() here to prevent compiler from reordering |
| + * gc->irq.initialized before initialization of above |
| + * GPIO chip irq members. |
| + */ |
| + barrier(); |
| + |
| + gc->irq.initialized = true; |
| + |
| return 0; |
| } |
| |
| --- a/include/linux/gpio/driver.h |
| +++ b/include/linux/gpio/driver.h |
| @@ -219,6 +219,15 @@ struct gpio_irq_chip { |
| bool per_parent_data; |
| |
| /** |
| + * @initialized: |
| + * |
| + * Flag to track GPIO chip irq member's initialization. |
| + * This flag will make sure GPIO chip irq members are not used |
| + * before they are initialized. |
| + */ |
| + bool initialized; |
| + |
| + /** |
| * @init_hw: optional routine to initialize hardware before |
| * an IRQ chip will be added. This is quite useful when |
| * a particular driver wants to clear IRQ related registers |