From: Aleksandr Nogikh <nogikh@google.com>
The event_handler function in drivers/usb/usbip/usbip_event.c is a
workqueue item responsible for processing events for a struct usbip_device.
During device teardown, usbip_stop_eh() is called to wait for the event
handler to finish processing. However, usbip_stop_eh() uses
wait_event_interruptible() and ignores its return value. If the process
unbinding the driver receives a signal, wait_event_interruptible() returns
immediately, causing the teardown process to falsely assume the event
handler has finished. The teardown process then proceeds to free the
usbip_device memory. Meanwhile, the event_handler workqueue is still
running and attempts to access the freed usbip_device, resulting in a KASAN
slab-use-after-free crash.
BUG: KASAN: slab-use-after-free in __mutex_lock_common
kernel/locking/rtmutex_api.c:559 [inline]
BUG: KASAN: slab-use-after-free in mutex_lock_nested+0x5a/0x1d0
kernel/locking/rtmutex_api.c:578
Read of size 1 at addr ffff8881145245b0 by task kworker/u8:5/6177
Call Trace:
<TASK>
lock_acquire+0x84/0x350 kernel/locking/lockdep.c:5842
__mutex_lock_common kernel/locking/rtmutex_api.c:559 [inline]
mutex_lock_nested+0x5a/0x1d0 kernel/locking/rtmutex_api.c:578
event_handler+0x1e3/0x4a0 drivers/usb/usbip/usbip_event.c:73
process_one_work kernel/workqueue.c:3322 [inline]
process_scheduled_works+0xa8e/0x14e0 kernel/workqueue.c:3405
worker_thread+0xa47/0xfb0 kernel/workqueue.c:3486
kthread+0x388/0x470 kernel/kthread.c:436
ret_from_fork+0x514/0xb70 arch/x86/kernel/process.c:158
ret_from_fork_asm+0x1a/0x30 arch/x86/entry/entry_64.S:245
</TASK>
To fix this, change wait_event_interruptible() to wait_event() in
usbip_stop_eh(). This ensures that the teardown process strictly waits for
the event handler to finish its execution and drop all references to the
usbip_device before the memory is freed, preventing the use-after-free.
This change is safe from deadlocks because usbip_stop_eh() is never called
with locks held that the event_handler would need to acquire.
Fixes: bb7871ad99ea ("usbip: event handler as one thread")
Assisted-by: Gemini:gemini-3.5-flash Gemini:gemini-3.1-pro-preview syzbot
Reported-by: syzbot+f4518ac0a3dd3aafde38@syzkaller.appspotmail.com
Closes: https://syzkaller.appspot.com/bug?extid=f4518ac0a3dd3aafde38
Link: https://syzkaller.appspot.com/ai_job?id=88101c8e-4da9-45de-b879-0549d6fe5649
Signed-off-by: Aleksandr Nogikh <nogikh@google.com>
---
diff --git a/drivers/usb/usbip/usbip_event.c b/drivers/usb/usbip/usbip_event.c
index 0e00c2d00..81d761891 100644
--- a/drivers/usb/usbip/usbip_event.c
+++ b/drivers/usb/usbip/usbip_event.c
@@ -115,7 +115,7 @@ void usbip_stop_eh(struct usbip_device *ud)
if (pending)
usbip_dbg_eh("usbip_eh waiting completion %lx\n", pending);
- wait_event_interruptible(ud->eh_waitq, !(ud->event & ~USBIP_EH_BYE));
+ wait_event(ud->eh_waitq, !(ud->event & ~USBIP_EH_BYE));
usbip_dbg_eh("usbip_eh has stopped\n");
}
EXPORT_SYMBOL_GPL(usbip_stop_eh);
base-commit: 075b74841bd0065a3bda3440873c747938e69b68
--
See https://goo.gle/syzbot-ai-patches for information about AI-generated patches.
You can comment on the patch as usual, syzbot will try to address
the comments and send a new version of the patch if necessary.
syzbot engineers can be reached at syzkaller@googlegroups.com.
On 8/3/26 09:05, syzbot wrote: > From: Aleksandr Nogikh <nogikh@google.com> > > The event_handler function in drivers/usb/usbip/usbip_event.c is a > workqueue item responsible for processing events for a struct usbip_device. > During device teardown, usbip_stop_eh() is called to wait for the event > handler to finish processing. However, usbip_stop_eh() uses > wait_event_interruptible() and ignores its return value. If the process > unbinding the driver receives a signal, wait_event_interruptible() returns > immediately, causing the teardown process to falsely assume the event > handler has finished. Does this mean unbinding didn't happen? The teardown process then proceeds to free the > usbip_device memory. Meanwhile, the event_handler workqueue is still > running and attempts to access the freed usbip_device, resulting in a KASAN > slab-use-after-free crash. > > BUG: KASAN: slab-use-after-free in __mutex_lock_common > kernel/locking/rtmutex_api.c:559 [inline] > BUG: KASAN: slab-use-after-free in mutex_lock_nested+0x5a/0x1d0 > kernel/locking/rtmutex_api.c:578 > Read of size 1 at addr ffff8881145245b0 by task kworker/u8:5/6177 > > Call Trace: > <TASK> > lock_acquire+0x84/0x350 kernel/locking/lockdep.c:5842 > __mutex_lock_common kernel/locking/rtmutex_api.c:559 [inline] > mutex_lock_nested+0x5a/0x1d0 kernel/locking/rtmutex_api.c:578 > event_handler+0x1e3/0x4a0 drivers/usb/usbip/usbip_event.c:73 > process_one_work kernel/workqueue.c:3322 [inline] > process_scheduled_works+0xa8e/0x14e0 kernel/workqueue.c:3405 > worker_thread+0xa47/0xfb0 kernel/workqueue.c:3486 > kthread+0x388/0x470 kernel/kthread.c:436 > ret_from_fork+0x514/0xb70 arch/x86/kernel/process.c:158 > ret_from_fork_asm+0x1a/0x30 arch/x86/entry/entry_64.S:245 > </TASK> > > To fix this, change wait_event_interruptible() to wait_event() in > usbip_stop_eh(). This ensures that the teardown process strictly waits for > the event handler to finish its execution and drop all references to the > usbip_device before the memory is freed, preventing the use-after-free. > This change is safe from deadlocks because usbip_stop_eh() is never called > with locks held that the event_handler would need to acquire. Correct - usbip_stop_eh() is called without lock hold after updating the shutdown_busid status to true. However I am curious what happens to the unbinding? Should usbip_stop_eh() check the return value of wait_event_interruptible() and handle the error instead? thanks, -- Shuah
Hi Shuah, Thanks for reviewing the patch! On Tue, Aug 4, 2026 at 7:31 PM Shuah Khan <skhan@linuxfoundation.org> wrote: > > On 8/3/26 09:05, syzbot wrote: > > From: Aleksandr Nogikh <nogikh@google.com> > > > > The event_handler function in drivers/usb/usbip/usbip_event.c is a > > workqueue item responsible for processing events for a struct usbip_device. > > During device teardown, usbip_stop_eh() is called to wait for the event > > handler to finish processing. However, usbip_stop_eh() uses > > wait_event_interruptible() and ignores its return value. If the process > > unbinding the driver receives a signal, wait_event_interruptible() returns > > immediately, causing the teardown process to falsely assume the event > > handler has finished. > > Does this mean unbinding didn't happen? The unbinding did happen (and finished freeing the memory), but without actually waiting for event_handler() in drivers/usb/usbip/usbip_event.c to finish processing the removal event. As the device memory was prematurely freed, we got the use-after-free crash in event_handler(). For reference, here's a C reproducer for the original bug: https://syzkaller.appspot.com/text?tag=ReproC&x=16ffb7b9580000 It sets up a timer to deliver a signal and wakes up the wait_event_interruptible() call. > > The teardown process then proceeds to free the > > usbip_device memory. Meanwhile, the event_handler workqueue is still > > running and attempts to access the freed usbip_device, resulting in a KASAN > > slab-use-after-free crash. > > > > BUG: KASAN: slab-use-after-free in __mutex_lock_common > > kernel/locking/rtmutex_api.c:559 [inline] > > BUG: KASAN: slab-use-after-free in mutex_lock_nested+0x5a/0x1d0 > > kernel/locking/rtmutex_api.c:578 > > Read of size 1 at addr ffff8881145245b0 by task kworker/u8:5/6177 > > > > Call Trace: > > <TASK> > > lock_acquire+0x84/0x350 kernel/locking/lockdep.c:5842 > > __mutex_lock_common kernel/locking/rtmutex_api.c:559 [inline] > > mutex_lock_nested+0x5a/0x1d0 kernel/locking/rtmutex_api.c:578 > > event_handler+0x1e3/0x4a0 drivers/usb/usbip/usbip_event.c:73 > > process_one_work kernel/workqueue.c:3322 [inline] > > process_scheduled_works+0xa8e/0x14e0 kernel/workqueue.c:3405 > > worker_thread+0xa47/0xfb0 kernel/workqueue.c:3486 > > kthread+0x388/0x470 kernel/kthread.c:436 > > ret_from_fork+0x514/0xb70 arch/x86/kernel/process.c:158 > > ret_from_fork_asm+0x1a/0x30 arch/x86/entry/entry_64.S:245 > > </TASK> > > > > To fix this, change wait_event_interruptible() to wait_event() in > > usbip_stop_eh(). This ensures that the teardown process strictly waits for > > the event handler to finish its execution and drop all references to the > > usbip_device before the memory is freed, preventing the use-after-free. > > This change is safe from deadlocks because usbip_stop_eh() is never called > > with locks held that the event_handler would need to acquire. > > Correct - usbip_stop_eh() is called without lock hold after updating > the shutdown_busid status to true. However I am curious what happens > to the unbinding? Should usbip_stop_eh() check the return value of > wait_event_interruptible() and handle the error instead? During unbinding, we must wait for the scheduled removal event to finish before we can safely free the device structures. From what I see in the code, once we have reached wait_event_interruptible(), there's no way to abort the process or somehow gracefully handle the error. -- Aleksandr > > thanks, > -- Shuah
On 8/7/26 08:18, Aleksandr Nogikh wrote: > Hi Shuah, > > Thanks for reviewing the patch! > > On Tue, Aug 4, 2026 at 7:31 PM Shuah Khan <skhan@linuxfoundation.org> wrote: >> >> On 8/3/26 09:05, syzbot wrote: >>> From: Aleksandr Nogikh <nogikh@google.com> >>> >>> The event_handler function in drivers/usb/usbip/usbip_event.c is a >>> workqueue item responsible for processing events for a struct usbip_device. >>> During device teardown, usbip_stop_eh() is called to wait for the event >>> handler to finish processing. However, usbip_stop_eh() uses >>> wait_event_interruptible() and ignores its return value. If the process >>> unbinding the driver receives a signal, wait_event_interruptible() returns >>> immediately, causing the teardown process to falsely assume the event >>> handler has finished. >> >> Does this mean unbinding didn't happen? > > The unbinding did happen (and finished freeing the memory), but > without actually waiting for event_handler() in > drivers/usb/usbip/usbip_event.c to finish processing the removal > event. As the device memory was prematurely freed, we got the > use-after-free crash in event_handler(). > > For reference, here's a C reproducer for the original bug: > https://syzkaller.appspot.com/text?tag=ReproC&x=16ffb7b9580000 > It sets up a timer to deliver a signal and wakes up the > wait_event_interruptible() call. Thanks. > >> >> The teardown process then proceeds to free the >>> usbip_device memory. Meanwhile, the event_handler workqueue is still >>> running and attempts to access the freed usbip_device, resulting in a KASAN >>> slab-use-after-free crash. >>> >>> BUG: KASAN: slab-use-after-free in __mutex_lock_common >>> kernel/locking/rtmutex_api.c:559 [inline] >>> BUG: KASAN: slab-use-after-free in mutex_lock_nested+0x5a/0x1d0 >>> kernel/locking/rtmutex_api.c:578 >>> Read of size 1 at addr ffff8881145245b0 by task kworker/u8:5/6177 >>> >>> Call Trace: >>> <TASK> >>> lock_acquire+0x84/0x350 kernel/locking/lockdep.c:5842 >>> __mutex_lock_common kernel/locking/rtmutex_api.c:559 [inline] >>> mutex_lock_nested+0x5a/0x1d0 kernel/locking/rtmutex_api.c:578 >>> event_handler+0x1e3/0x4a0 drivers/usb/usbip/usbip_event.c:73 >>> process_one_work kernel/workqueue.c:3322 [inline] >>> process_scheduled_works+0xa8e/0x14e0 kernel/workqueue.c:3405 >>> worker_thread+0xa47/0xfb0 kernel/workqueue.c:3486 >>> kthread+0x388/0x470 kernel/kthread.c:436 >>> ret_from_fork+0x514/0xb70 arch/x86/kernel/process.c:158 >>> ret_from_fork_asm+0x1a/0x30 arch/x86/entry/entry_64.S:245 >>> </TASK> >>> >>> To fix this, change wait_event_interruptible() to wait_event() in >>> usbip_stop_eh(). This ensures that the teardown process strictly waits for >>> the event handler to finish its execution and drop all references to the >>> usbip_device before the memory is freed, preventing the use-after-free. >>> This change is safe from deadlocks because usbip_stop_eh() is never called >>> with locks held that the event_handler would need to acquire. >> >> Correct - usbip_stop_eh() is called without lock hold after updating >> the shutdown_busid status to true. However I am curious what happens >> to the unbinding? Should usbip_stop_eh() check the return value of >> wait_event_interruptible() and handle the error instead? > > During unbinding, we must wait for the scheduled removal event to > finish before we can safely free the device structures. From what I > see in the code, once we have reached wait_event_interruptible(), > there's no way to abort the process or somehow gracefully handle the > error. > Yes I agree with you on rewinding being hard. Care to explain the scope of this assist? Assisted-by: Gemini:gemini-3.5-flash Gemini:gemini-3.1-pro-preview syzbot thanks, -- Shuah
On Mon, Aug 10, 2026 at 9:33 PM Shuah Khan <skhan@linuxfoundation.org> wrote: > < ... > > > > > During unbinding, we must wait for the scheduled removal event to > > finish before we can safely free the device structures. From what I > > see in the code, once we have reached wait_event_interruptible(), > > there's no way to abort the process or somehow gracefully handle the > > error. > > > > Yes I agree with you on rewinding being hard. > > Care to explain the scope of this assist? > > Assisted-by: Gemini:gemini-3.5-flash Gemini:gemini-3.1-pro-preview syzbot It was generated by the syzbot's AI patch generation agent: https://github.com/google/syzkaller/blob/master/docs/syzbot_ai_patches.md We manually review the patches it suggests and upstream those we believe are fully correct. -- Aleksandr > > thanks, > -- Shuah > > >
On 8/11/26 07:07, Aleksandr Nogikh wrote: > On Mon, Aug 10, 2026 at 9:33 PM Shuah Khan <skhan@linuxfoundation.org> wrote: >> > < ... > >>> >>> During unbinding, we must wait for the scheduled removal event to >>> finish before we can safely free the device structures. From what I >>> see in the code, once we have reached wait_event_interruptible(), >>> there's no way to abort the process or somehow gracefully handle the >>> error. >>> >> >> Yes I agree with you on rewinding being hard. >> >> Care to explain the scope of this assist? >> >> Assisted-by: Gemini:gemini-3.5-flash Gemini:gemini-3.1-pro-preview syzbot > > It was generated by the syzbot's AI patch generation agent: > https://github.com/google/syzkaller/blob/master/docs/syzbot_ai_patches.md > > We manually review the patches it suggests and upstream those we > believe are fully correct. > As you are reviewing this generated code, did you look into scenarios where the driver to hang in wait_event()? thanks, -- Shuah
© 2016 - 2026 Red Hat, Inc.