gpio: gpio-grgpio: fix possible sleep-in-atomic-context bugs in grgpio_irq_map/unmap()
authorJia-Ju Bai <baijiaju1990@gmail.com>
Wed, 18 Dec 2019 13:26:05 +0000 (21:26 +0800)
committerGreg Kroah-Hartman <gregkh@linuxfoundation.org>
Mon, 24 Feb 2020 07:36:26 +0000 (08:36 +0100)
[ Upstream commit e36eaf94be8f7bc4e686246eed3cf92d845e2ef8 ]

The driver may sleep while holding a spinlock.
The function call path (from bottom to top) in Linux 4.19 is:

drivers/gpio/gpio-grgpio.c, 261:
request_irq in grgpio_irq_map
drivers/gpio/gpio-grgpio.c, 255:
_raw_spin_lock_irqsave in grgpio_irq_map

drivers/gpio/gpio-grgpio.c, 318:
free_irq in grgpio_irq_unmap
drivers/gpio/gpio-grgpio.c, 299:
_raw_spin_lock_irqsave in grgpio_irq_unmap

request_irq() and free_irq() can sleep at runtime.

To fix these bugs, request_irq() and free_irq() are called without
holding the spinlock.

These bugs are found by a static analysis tool STCheck written by myself.

Signed-off-by: Jia-Ju Bai <baijiaju1990@gmail.com>
Link: https://lore.kernel.org/r/20191218132605.10594-1-baijiaju1990@gmail.com
Signed-off-by: Linus Walleij <linus.walleij@linaro.org>
Signed-off-by: Sasha Levin <sashal@kernel.org>
drivers/gpio/gpio-grgpio.c

index 08234e6..3224933 100644 (file)
@@ -253,17 +253,16 @@ static int grgpio_irq_map(struct irq_domain *d, unsigned int irq,
        lirq->irq = irq;
        uirq = &priv->uirqs[lirq->index];
        if (uirq->refcnt == 0) {
+               spin_unlock_irqrestore(&priv->gc.bgpio_lock, flags);
                ret = request_irq(uirq->uirq, grgpio_irq_handler, 0,
                                  dev_name(priv->dev), priv);
                if (ret) {
                        dev_err(priv->dev,
                                "Could not request underlying irq %d\n",
                                uirq->uirq);
-
-                       spin_unlock_irqrestore(&priv->gc.bgpio_lock, flags);
-
                        return ret;
                }
+               spin_lock_irqsave(&priv->gc.bgpio_lock, flags);
        }
        uirq->refcnt++;
 
@@ -309,8 +308,11 @@ static void grgpio_irq_unmap(struct irq_domain *d, unsigned int irq)
        if (index >= 0) {
                uirq = &priv->uirqs[lirq->index];
                uirq->refcnt--;
-               if (uirq->refcnt == 0)
+               if (uirq->refcnt == 0) {
+                       spin_unlock_irqrestore(&priv->gc.bgpio_lock, flags);
                        free_irq(uirq->uirq, priv);
+                       return;
+               }
        }
 
        spin_unlock_irqrestore(&priv->gc.bgpio_lock, flags);