[<prev] [next>] [thread-next>] [day] [month] [year] [list]
Message-ID: <5b337389-73b9-4ee4-a83e-7e82bf5af87a@kzalloc.com>
Date: Sat, 16 Aug 2025 11:38:14 +0900
From: Yunseong Kim <ysk@...lloc.com>
To: gregkh@...uxfoundation.org, stern@...land.harvard.edu,
linux-usb@...r.kernel.org
Cc: Thomas Gleixner <tglx@...utronix.de>,
Sebastian Andrzej Siewior <bigeasy@...utronix.de>,
Clark Williams <clrkwllms@...nel.org>, Steven Rostedt <rostedt@...dmis.org>,
Nam Cao <namcao@...utronix.de>, Marcello Sylvester Bauer <sylv@...v.io>,
Krzysztof Kozlowski <krzysztof.kozlowski@...aro.org>,
Uwe Kleine-König <u.kleine-koenig@...libre.com>,
Al Viro <viro@...iv.linux.org.uk>, andreyknvl@...il.com,
Austin Kim <austindh.kim@...il.com>, linux-rt-users@...r.kernel.org,
linux-kernel@...r.kernel.org, syzkaller@...glegroups.com
Subject: [BUG] usb: gadget: dummy_hcd: Sleeping function called from invalid
context in dummy_dequeue on PREEMPT_RT
While testing a PREEMPT_RT enabled kernel (based on v6.17.0-rc1),
I encountered a "BUG: sleeping function called from invalid context" error
originating from the dummy_dequeue function in the dummy USB driver.
The call flow (triggered via ioctl):
Userspace (syz.0.2514 via ioctl)
|
v
raw_ioctl() -> usb_ep_dequeue()
|
v
dummy_dequeue() [drivers/usb/gadget/udc/dummy_hcd.c]
|
| (Context: Process Context, IRQs Enabled)
|
|---> local_irq_save(flags);
|
| *** STATE CHANGE: IRQs Disabled (Atomic Context) ***
|
|---> spin_lock(&dum->lock); <--- Trace location (dummy_hcd.c:769)
|
| [On PREEMPT_RT]
v
rt_spin_lock() [kernel/locking/spinlock_rt.c]
|
v
[May Sleep if contended]
|
X <--- BUG: Sleeping in atomic context!
|
|---> spin_unlock(&dum->lock);
|
| (Note: IRQs are still disabled here)
|
|---> usb_gadget_giveback_request();
|
v
local_irq_restore(flags);
(Context: IRQs Enabled)
Stack trace excerpt:
BUG: sleeping function called from invalid context at kernel/locking/spinlock_rt.c:48
in_atomic(): 0, irqs_disabled(): 1, non_block: 0, pid: 27291, name: syz.0.2514
preempt_count: 0, expected: 0
RCU nest depth: 0, expected: 0
CPU: 1 UID: 0 PID: 27291 Comm: syz.0.2514 Kdump: loaded Not tainted 6.17.0-rc1-00001-g1149a5db27c8-dirty #55 PREEMPT_RT
Hardware name: QEMU KVM Virtual Machine, BIOS 2025.02-8ubuntu1 06/11/2025
Call trace:
show_stack+0x2c/0x3c arch/arm64/kernel/stacktrace.c:499 (C)
__dump_stack+0x30/0x40 lib/dump_stack.c:94
dump_stack_lvl+0x148/0x1d8 lib/dump_stack.c:120
dump_stack+0x1c/0x3c lib/dump_stack.c:129
__might_resched+0x2e4/0x52c kernel/sched/core.c:8957
__rt_spin_lock kernel/locking/spinlock_rt.c:48 [inline]
rt_spin_lock+0xa8/0x1bc kernel/locking/spinlock_rt.c:57
spin_lock include/linux/spinlock_rt.h:44 [inline]
dummy_dequeue+0x9c/0x3b8 drivers/usb/gadget/udc/dummy_hcd.c:769
usb_ep_dequeue+0x70/0x21c drivers/usb/gadget/udc/core.c:330
raw_process_ep0_io+0x2e8/0x704 drivers/usb/gadget/legacy/raw_gadget.c:738
raw_ioctl_ep0_write drivers/usb/gadget/legacy/raw_gadget.c:766 [inline]
raw_ioctl+0x1b44/0x3288 drivers/usb/gadget/legacy/raw_gadget.c:1312
vfs_ioctl fs/ioctl.c:51 [inline]
__do_sys_ioctl fs/ioctl.c:598 [inline]
__se_sys_ioctl fs/ioctl.c:584 [inline]
__arm64_sys_ioctl+0x14c/0x1c4 fs/ioctl.c:584
__invoke_syscall arch/arm64/kernel/syscall.c:35 [inline]
invoke_syscall+0x98/0x2b8 arch/arm64/kernel/syscall.c:49
el0_svc_common+0x130/0x23c arch/arm64/kernel/syscall.c:132
do_el0_svc+0x48/0x58 arch/arm64/kernel/syscall.c:151
el0_svc+0x40/0x140 arch/arm64/kernel/entry-common.c:879
el0t_64_sync_handler+0x84/0x12c arch/arm64/kernel/entry-common.c:898
el0t_64_sync+0x1ac/0x1b0 arch/arm64/kernel/entry.S:596
On PREEMPT_RT configurations, spin_lock() is replaced by rt_spin_lock(),
which may sleep when contended. Therefore, it is forbidden to call
spin_lock() while interrupts are hard-disabled (atomic context).
The issue occurs in dummy_dequeue because it explicitly disables interrupts
using local_irq_save() before attempting to acquire dum->lock via
spin_lock().
static int dummy_dequeue(struct usb_ep *_ep, struct usb_request *_req)
{
// ... (snip) ...
ep = usb_ep_to_dummy_ep(_ep);
dum = ep_to_dummy(ep);
if (!dum->driver)
return -ESHUTDOWN;
local_irq_save(flags); // <--- 1. IRQs Disabled (Enters Atomic Context)
spin_lock(&dum->lock); // <--- 2. Calls rt_spin_lock (May sleep) -> BUG
list_for_each_entry(iter, &ep->queue, queue) {
// ... (critical section) ...
break;
}
spin_unlock(&dum->lock);
if (retval == 0) {
// ... (snip) ...
// Note: Called while IRQs are still disabled.
usb_gadget_giveback_request(_ep, _req);
}
local_irq_restore(flags); // <--- 3. IRQs restored
return retval;
}
The pattern of manually disabling IRQs and then taking a spinlock
local_irq_save() + spin_lock() is unsafe on PREEMPT_RT, the current code
structure keeps IRQs disabled even after spin_unlock(&dum->lock) while
calling usb_gadget_giveback_request(). This extended atomic context can
also be problematic if the completion handler attempts to acquire another
sleepable lock.
I request a review and correction of this locking mechanism to ensure
stability on PREEMPT_RT configurations. Kernel config, full logs, and
reproduction steps can be provided on request.
Thanks,
Best Regards,
Yunseong Kim
Powered by blists - more mailing lists