| From fb9e284e8858ba087bf24862639d7dd139edf8cc Mon Sep 17 00:00:00 2001 |
| From: Sasha Levin <sashal@kernel.org> |
| Date: Wed, 18 Dec 2019 21:26:05 +0800 |
| Subject: gpio: gpio-grgpio: fix possible sleep-in-atomic-context bugs in |
| grgpio_irq_map/unmap() |
| |
| From: Jia-Ju Bai <baijiaju1990@gmail.com> |
| |
| [ 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 | 10 ++++++---- |
| 1 file changed, 6 insertions(+), 4 deletions(-) |
| |
| diff --git a/drivers/gpio/gpio-grgpio.c b/drivers/gpio/gpio-grgpio.c |
| index 08234e64993a9..3224933f4c8f4 100644 |
| --- a/drivers/gpio/gpio-grgpio.c |
| +++ b/drivers/gpio/gpio-grgpio.c |
| @@ -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); |
| -- |
| 2.20.1 |
| |