diff mbox series

usb: host: oxu210hp-hcd: Fix potential deadlock on &oxu->mem_lock

Message ID 20230629121947.59315-1-dg573847474@gmail.com
State New
Headers show
Series usb: host: oxu210hp-hcd: Fix potential deadlock on &oxu->mem_lock | expand

Commit Message

Chengfeng Ye June 29, 2023, 12:19 p.m. UTC
As &udc->lock is acquired by watchdog timer oxu_watchdog() under
softirq context, 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.

The .urb_enqueue callback oxu_urb_enqueue() acquires the lock without
disabling irq inside the function.

Possible deadlock scenario

oxu_urb_enqueue()
    -> oxu_murb_alloc()
    -> spin_lock(&oxu->mem_lock)
        <timer interrupt>
        -> oxu_watchdog()
        -> ehci_work()
        -> scan_async()
        -> qh_completions()
        -> oxu_murb_free()
        -> spin_lock(&oxu->mem_lock)

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 fixes the potential deadlock by spin_lock_irqsave().
x86_64 allyesconfig using GCC shows no new warning, the tool does not
report the warnining after the fix. No runtime testing was performed since
I don't have device with OXU210HP host controller.

Signed-off-by: Chengfeng Ye <dg573847474@gmail.com>
---
 drivers/usb/host/oxu210hp-hcd.c | 5 +++--
 1 file changed, 3 insertions(+), 2 deletions(-)
diff mbox series

Patch

diff --git a/drivers/usb/host/oxu210hp-hcd.c b/drivers/usb/host/oxu210hp-hcd.c
index f998d3f1a78a..944011e059e5 100644
--- a/drivers/usb/host/oxu210hp-hcd.c
+++ b/drivers/usb/host/oxu210hp-hcd.c
@@ -1120,8 +1120,9 @@  static struct oxu_murb *oxu_murb_alloc(struct oxu_hcd *oxu)
 {
 	int i;
 	struct oxu_murb *murb = NULL;
+	unsigned long flags;
 
-	spin_lock(&oxu->mem_lock);
+	spin_lock_irqsave(&oxu->mem_lock, flags);
 
 	for (i = 0; i < MURB_NUM; i++)
 		if (!oxu->murb_used[i])
@@ -1133,7 +1134,7 @@  static struct oxu_murb *oxu_murb_alloc(struct oxu_hcd *oxu)
 		oxu->murb_used[i] = 1;
 	}
 
-	spin_unlock(&oxu->mem_lock);
+	spin_unlock_irqrestore(&oxu->mem_lock, flags);
 
 	return murb;
 }