Skip to content

Commit aeb3ef5

Browse files
YchameWim Van Sebroeck
authored andcommitted
watchdog: s3c2410: Fix potential deadlock on &wdt->lock
As &wdt->lock is acquired by hard irq s3c2410wdt_irq(), other acquisition of the same lock under process context should disable irq, otherwise deadlock could happen if the irq preempt the execution while the lock is held in process context on the same CPU. [Deadlock Scenario] s3c2410wdt_suspend() -> s3c2410wdt_stop() -> spin_lock(&wdt->lock) <irq iterrupt> -> s3c2410wdt_irq() -> s3c2410wdt_keepalive() -> spin_lock(&wdt->lock) (deadlock here) [Deadlock Scenario] s3c2410wdt_probe() -> s3c2410wdt_start() -> spin_lock(&wdt->lock) <irq iterrupt> -> s3c2410wdt_irq() -> s3c2410wdt_keepalive() -> spin_lock(&wdt->lock) (deadlock here) [Deadlock Scenario] s3c2410wdt_keepalive() -> spin_lock(&wdt->lock) <irq iterrupt> -> s3c2410wdt_irq() -> s3c2410wdt_keepalive() -> spin_lock(&wdt->lock) (deadlock here) This flaw was found by an experimental static analysis tool I am developing for irq-related deadlock, which reported the above warning when analyzing the linux kernel 6.4-rc7 release. The tentative patch fix the potential deadlock by spin_lock_irqsave() under process context. Signed-off-by: Chengfeng Ye <[email protected]> Reviewed-by: Krzysztof Kozlowski <[email protected]> Reviewed-by: Guenter Roeck <[email protected]> Link: https://lore.kernel.org/r/[email protected] Signed-off-by: Guenter Roeck <[email protected]> Signed-off-by: Wim Van Sebroeck <[email protected]>
1 parent f20ca59 commit aeb3ef5

File tree

1 file changed

+9
-6
lines changed

1 file changed

+9
-6
lines changed

drivers/watchdog/s3c2410_wdt.c

Lines changed: 9 additions & 6 deletions
Original file line numberDiff line numberDiff line change
@@ -378,10 +378,11 @@ static int s3c2410wdt_enable(struct s3c2410_wdt *wdt, bool en)
378378
static int s3c2410wdt_keepalive(struct watchdog_device *wdd)
379379
{
380380
struct s3c2410_wdt *wdt = watchdog_get_drvdata(wdd);
381+
unsigned long flags;
381382

382-
spin_lock(&wdt->lock);
383+
spin_lock_irqsave(&wdt->lock, flags);
383384
writel(wdt->count, wdt->reg_base + S3C2410_WTCNT);
384-
spin_unlock(&wdt->lock);
385+
spin_unlock_irqrestore(&wdt->lock, flags);
385386

386387
return 0;
387388
}
@@ -398,10 +399,11 @@ static void __s3c2410wdt_stop(struct s3c2410_wdt *wdt)
398399
static int s3c2410wdt_stop(struct watchdog_device *wdd)
399400
{
400401
struct s3c2410_wdt *wdt = watchdog_get_drvdata(wdd);
402+
unsigned long flags;
401403

402-
spin_lock(&wdt->lock);
404+
spin_lock_irqsave(&wdt->lock, flags);
403405
__s3c2410wdt_stop(wdt);
404-
spin_unlock(&wdt->lock);
406+
spin_unlock_irqrestore(&wdt->lock, flags);
405407

406408
return 0;
407409
}
@@ -410,8 +412,9 @@ static int s3c2410wdt_start(struct watchdog_device *wdd)
410412
{
411413
unsigned long wtcon;
412414
struct s3c2410_wdt *wdt = watchdog_get_drvdata(wdd);
415+
unsigned long flags;
413416

414-
spin_lock(&wdt->lock);
417+
spin_lock_irqsave(&wdt->lock, flags);
415418

416419
__s3c2410wdt_stop(wdt);
417420

@@ -432,7 +435,7 @@ static int s3c2410wdt_start(struct watchdog_device *wdd)
432435
writel(wdt->count, wdt->reg_base + S3C2410_WTDAT);
433436
writel(wdt->count, wdt->reg_base + S3C2410_WTCNT);
434437
writel(wtcon, wdt->reg_base + S3C2410_WTCON);
435-
spin_unlock(&wdt->lock);
438+
spin_unlock_irqrestore(&wdt->lock, flags);
436439

437440
return 0;
438441
}

0 commit comments

Comments
 (0)