* [syzbot] [usb?] WARNING in dummy_free_request (2)
@ 2026-07-29 0:10 syzbot
2026-08-02 0:10 ` syzbot
` (2 more replies)
0 siblings, 3 replies; 7+ messages in thread
From: syzbot @ 2026-07-29 0:10 UTC (permalink / raw)
To: gregkh, linux-kernel, linux-usb, syzkaller-bugs
Hello,
syzbot found the following issue on:
HEAD commit: cc2b5f627e8c Add linux-next specific files for 20260714
git tree: linux-next
console output: https://syzkaller.appspot.com/x/log.txt?x=159922a9580000
kernel config: https://syzkaller.appspot.com/x/.config?x=2290ccbf984c524f
dashboard link: https://syzkaller.appspot.com/bug?extid=c73e1942197af9f54ca3
compiler: Debian clang version 22.1.8 (++20260613092233+e80beda6e255-1~exp1~20260613092250.77), Debian LLD 22.1.8
Unfortunately, I don't have any reproducer for this issue yet.
Downloadable assets:
disk image: https://storage.googleapis.com/syzbot-assets/6836f8efb1da/disk-cc2b5f62.raw.xz
vmlinux: https://storage.googleapis.com/syzbot-assets/0109d3477cc7/vmlinux-cc2b5f62.xz
kernel image: https://storage.googleapis.com/syzbot-assets/c2e49e350bbf/bzImage-cc2b5f62.xz
IMPORTANT: if you fix the issue, please add the following tag to the commit:
Reported-by: syzbot+c73e1942197af9f54ca3@syzkaller.appspotmail.com
configfs-gadget.g1 gadget.0: uvc: uvc_function_bind()
configfs-gadget.g1 gadget.0: uvc: Unable to allocate streaming EP
------------[ cut here ]------------
!list_empty(&req->queue)
WARNING: drivers/usb/gadget/udc/dummy_hcd.c:691 at dummy_free_request+0x6f/0x80 drivers/usb/gadget/udc/dummy_hcd.c:691, CPU#1: syz.3.795/7609
Modules linked in:
CPU: 1 UID: 0 PID: 7609 Comm: syz.3.795 Tainted: G L syzkaller #0 PREEMPT_{RT,(full)}
Tainted: [L]=SOFTLOCKUP
Hardware name: Google Google Compute Engine/Google Compute Engine, BIOS Google 07/16/2026
RIP: 0010:dummy_free_request+0x6f/0x80 drivers/usb/gadget/udc/dummy_hcd.c:691
Code: 75 22 e8 14 e0 8b fa 48 89 df 5b 41 5e e9 89 5c ec fa e8 04 e0 8b fa 90 0f 0b 90 5b 41 5e c3 cc cc cc cc cc e8 f2 df 8b fa 90 <0f> 0b 90 eb d8 66 66 66 2e 0f 1f 84 00 00 00 00 00 90 90 90 90 90
RSP: 0018:ffffc900087cf730 EFLAGS: 00010287
RAX: ffffffff8739c3be RBX: ffff88803a585ff0 RCX: 0000000000080000
RDX: ffffc9000759a000 RSI: 00000000000009d6 RDI: 00000000000009d7
RBP: 00000000ffffffea R08: 0000000000000000 R09: 0000000000000000
R10: dffffc0000000000 R11: ffffffff8739c350 R12: ffff88805f2f0520
R13: ffff888021b808d8 R14: ffff8880295e00a8 R15: ffff8880295e0c98
FS: 00007f8a2e8b66c0(0000) GS:ffff888125af2000(0000) knlGS:0000000000000000
CS: 0010 DS: 0000 ES: 0000 CR0: 0000000080050033
CR2: 00007fc44f5ab050 CR3: 0000000024f34000 CR4: 00000000003526f0
Call Trace:
<TASK>
uvc_function_bind+0xe5d/0x15a0 drivers/usb/gadget/function/f_uvc.c:893
usb_add_function+0x290/0x930 drivers/usb/gadget/composite.c:333
configfs_composite_bind+0xde0/0x1410 drivers/usb/gadget/configfs.c:1802
gadget_bind_driver+0x2ca/0x9e0 drivers/usb/gadget/udc/core.c:1662
call_driver_probe drivers/base/dd.c:-1 [inline]
really_probe+0x254/0xae0 drivers/base/dd.c:706
__driver_probe_device+0x1e8/0x360 drivers/base/dd.c:868
driver_probe_device+0x4f/0x240 drivers/base/dd.c:898
__driver_attach+0x339/0x600 drivers/base/dd.c:1292
bus_for_each_dev+0x23e/0x2c0 drivers/base/bus.c:383
bus_add_driver+0x348/0x670 drivers/base/bus.c:763
driver_register+0x23a/0x320 drivers/base/driver.c:174
usb_gadget_register_driver_owner+0xf9/0x270 drivers/usb/gadget/udc/core.c:1752
gadget_dev_desc_UDC_store+0x1c9/0x2f0 drivers/usb/gadget/configfs.c:300
flush_write_buffer fs/configfs/file.c:207 [inline]
configfs_write_iter+0x33a/0x430 fs/configfs/file.c:229
new_sync_write fs/read_write.c:595 [inline]
vfs_write+0x61e/0xbb0 fs/read_write.c:687
ksys_write+0x156/0x270 fs/read_write.c:739
do_syscall_x64 arch/x86/entry/syscall_64.c:61 [inline]
do_syscall_64+0x17b/0x530 arch/x86/entry/syscall_64.c:85
entry_SYSCALL_64_after_hwframe+0x77/0x7f
RIP: 0033:0x7f8a3065de99
Code: ff c3 66 2e 0f 1f 84 00 00 00 00 00 0f 1f 44 00 00 48 89 f8 48 89 f7 48 89 d6 48 89 ca 4d 89 c2 4d 89 c8 4c 8b 4c 24 08 0f 05 <48> 3d 01 f0 ff ff 73 01 c3 48 c7 c1 e8 ff ff ff f7 d8 64 89 01 48
RSP: 002b:00007f8a2e8b6028 EFLAGS: 00000246 ORIG_RAX: 0000000000000001
RAX: ffffffffffffffda RBX: 00007f8a308e5fa0 RCX: 00007f8a3065de99
RDX: 000000000000000b RSI: 0000200000000880 RDI: 0000000000000003
RBP: 00007f8a306f3eaf R08: 0000000000000000 R09: 0000000000000000
R10: 0000000000000000 R11: 0000000000000246 R12: 0000000000000000
R13: 00007f8a308e6038 R14: 00007f8a308e5fa0 R15: 00007ffcfa6f54e8
</TASK>
---
This report is generated by a bot. It may contain errors.
See https://goo.gl/tpsmEJ for more information about syzbot.
syzbot engineers can be reached at syzkaller@googlegroups.com.
syzbot will keep track of this issue. See:
https://goo.gl/tpsmEJ#status for how to communicate with syzbot.
If the report is already addressed, let syzbot know by replying with:
#syz fix: exact-commit-title
If you want to overwrite report's subsystems, reply with:
#syz set subsystems: new-subsystem
(See the list of subsystem names on the web dashboard)
If the report is a duplicate of another one, reply with:
#syz dup: exact-subject-of-another-report
If you want to undo deduplication, reply with:
#syz undup
^ permalink raw reply [flat|nested] 7+ messages in thread
* Re: [syzbot] [usb?] WARNING in dummy_free_request (2)
2026-07-29 0:10 [syzbot] [usb?] WARNING in dummy_free_request (2) syzbot
@ 2026-08-02 0:10 ` syzbot
2026-11-06 14:55 ` Jing Lin
2026-11-06 14:55 ` Forwarded: [PATCH RFC] usb: gadget: gadgetfs: dequeue pending ep0 request before freeing syzbot
2 siblings, 0 replies; 7+ messages in thread
From: syzbot @ 2026-08-02 0:10 UTC (permalink / raw)
To: gregkh, linux-kernel, linux-usb, syzkaller-bugs
syzbot has found a reproducer for the following issue on:
HEAD commit: e2d5e23967d0 Merge branch 'for-next/core' into for-kernelci
git tree: git://git.kernel.org/pub/scm/linux/kernel/git/arm64/linux.git for-kernelci
console output: https://syzkaller.appspot.com/x/log.txt?x=179d8649580000
kernel config: https://syzkaller.appspot.com/x/.config?x=72c97575381cec47
dashboard link: https://syzkaller.appspot.com/bug?extid=c73e1942197af9f54ca3
compiler: Debian clang version 22.1.8 (++20260613092233+e80beda6e255-1~exp1~20260613092250.77), Debian LLD 22.1.8
userspace arch: arm64
syz repro: https://syzkaller.appspot.com/x/repro.syz?x=13b62db9580000
C reproducer: https://syzkaller.appspot.com/x/repro.c?x=17a12c9e580000
Downloadable assets:
disk image: https://storage.googleapis.com/syzbot-assets/294cdababe04/disk-e2d5e239.raw.xz
vmlinux: https://storage.googleapis.com/syzbot-assets/3e828c9fa51b/vmlinux-e2d5e239.xz
kernel image: https://storage.googleapis.com/syzbot-assets/ffd7614ad310/Image-e2d5e239.gz.xz
IMPORTANT: if you fix the issue, please add the following tag to the commit:
Reported-by: syzbot+c73e1942197af9f54ca3@syzkaller.appspotmail.com
gadgetfs: suspended from state 3
gadgetfs: disconnected
------------[ cut here ]------------
WARNING: drivers/usb/gadget/udc/dummy_hcd.c:710 at dummy_free_request+0x7c/0x84 drivers/usb/gadget/udc/dummy_hcd.c:710, CPU#0: syz.0.32/4939
Modules linked in:
CPU: 0 UID: 0 PID: 4939 Comm: syz.0.32 Not tainted syzkaller #0 PREEMPT
Hardware name: Google Google Compute Engine/Google Compute Engine, BIOS Google 07/02/2026
pstate: 83400005 (Nzcv daif +PAN -UAO +TCO +DIT -SSBS BTYPE=--)
pc : dummy_free_request+0x7c/0x84 drivers/usb/gadget/udc/dummy_hcd.c:710
lr : dummy_free_request+0x7c/0x84 drivers/usb/gadget/udc/dummy_hcd.c:710
sp : ffff8000962579d0
x29: ffff8000962579d0 x28: 1fffe000195c4a01 x27: ffff0000cae25008
x26: 1fffe0001a0e2408 x25: 1fffe0001952998b x24: 1fffe000195299aa
x23: dfff800000000000 x22: ffff800088b07eb8 x21: ffff800087332200
x20: ffff0000ca94c068 x19: ffff0000d866f900 x18: 0000000000000000
x17: 0000000000000000 x16: 0000000000000000 x15: 0000000000000000
x14: 00000000868e5610 x13: 0000000000000001 x12: 0000000000000000
x11: ffff800080156ee8 x10: 0000000000000003 x9 : 0000000000000000
x8 : 0000000000000000 x7 : 0000000000000000 x6 : 0000000000000000
x5 : 0000000000000001 x4 : 0000000000000008 x3 : ffff8000835a7208
x2 : 0000000000000001 x1 : ffff0000c82cba80 x0 : 0000000000000000
Call trace:
dummy_free_request+0x7c/0x84 drivers/usb/gadget/udc/dummy_hcd.c:710 (P)
usb_ep_free_request+0x108/0x3fc drivers/usb/gadget/udc/core.c:221
gadgetfs_unbind+0x19c/0x1f0 drivers/usb/gadget/legacy/inode.c:1661
gadget_unbind_driver+0x188/0x790 drivers/usb/gadget/udc/core.c:1724
device_remove+0xc4/0x134 drivers/base/dd.c:616
__device_release_driver drivers/base/dd.c:1349 [inline]
device_release_driver_internal+0x124/0x20c drivers/base/dd.c:1372
driver_detach+0x124/0x1b4 drivers/base/dd.c:1435
bus_remove_driver+0x124/0x22c drivers/base/bus.c:832
driver_unregister+0x78/0xac drivers/base/driver.c:202
usb_gadget_unregister_driver+0x50/0x78 drivers/usb/gadget/udc/core.c:1784
dev_release+0x8c/0x15c drivers/usb/gadget/legacy/inode.c:1198
__fput+0x338/0x74c fs/file_table.c:512
____fput+0x20/0x30 fs/file_table.c:540
task_work_run+0x1cc/0x25c kernel/task_work.c:233
resume_user_mode_work include/linux/resume_user_mode.h:50 [inline]
__exit_to_user_mode_loop kernel/entry/common.c:70 [inline]
exit_to_user_mode_loop+0x10c/0x17c kernel/entry/common.c:101
__exit_to_user_mode_prepare include/linux/irq-entry-common.h:207 [inline]
syscall_exit_to_user_mode_prepare include/linux/irq-entry-common.h:230 [inline]
arm64_syscall_exit_to_user_mode arch/arm64/kernel/entry-common.c:88 [inline]
el0_svc+0x18c/0x260 arch/arm64/kernel/entry-common.c:737
el0t_64_sync_handler+0x48/0x148 arch/arm64/kernel/entry-common.c:755
el0t_64_sync+0x198/0x19c arch/arm64/kernel/entry.S:594
irq event stamp: 2192
hardirqs last enabled at (2191): [<ffff80008690a7dc>] __raw_spin_unlock_irq include/linux/spinlock_api_smp.h:187 [inline]
hardirqs last enabled at (2191): [<ffff80008690a7dc>] _raw_spin_unlock_irq+0x30/0x80 kernel/locking/spinlock.c:206
hardirqs last disabled at (2192): [<ffff8000868e5428>] el1_brk64+0x20/0x54 arch/arm64/kernel/entry-common.c:425
softirqs last enabled at (1170): [<ffff80008030ff18>] softirq_handle_end kernel/softirq.c:468 [inline]
softirqs last enabled at (1170): [<ffff80008030ff18>] handle_softirqs+0xc28/0xd98 kernel/softirq.c:650
softirqs last disabled at (1161): [<ffff8000800204c0>] __do_softirq+0x14/0x20 kernel/softirq.c:656
---[ end trace 0000000000000000 ]---
==================================================================
BUG: KASAN: slab-use-after-free in __list_del_entry_valid_or_report+0x3c/0x1b8 lib/list_debug.c:49
Read of size 8 at addr ffff0000d866f908 by task syz.0.32/4939
CPU: 1 UID: 0 PID: 4939 Comm: syz.0.32 Tainted: G W syzkaller #0 PREEMPT
Tainted: [W]=WARN
Hardware name: Google Google Compute Engine/Google Compute Engine, BIOS Google 07/02/2026
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+0xd8/0x12c lib/dump_stack.c:120
print_address_description+0xb0/0x238 mm/kasan/report.c:378
print_report+0x68/0x84 mm/kasan/report.c:482
kasan_report+0x8c/0xc4 mm/kasan/report.c:595
__asan_report_load8_noabort+0x20/0x2c mm/kasan/report_generic.c:381
__list_del_entry_valid_or_report+0x3c/0x1b8 lib/list_debug.c:49
__list_del_entry_valid include/linux/list.h:132 [inline]
__list_del_entry include/linux/list.h:246 [inline]
list_del_init include/linux/list.h:318 [inline]
nuke+0x60/0x194 drivers/usb/gadget/udc/dummy_hcd.c:360
stop_activity drivers/usb/gadget/udc/dummy_hcd.c:379 [inline]
dummy_udc_stop+0xe4/0x180 drivers/usb/gadget/udc/dummy_hcd.c:1057
usb_gadget_udc_stop_locked drivers/usb/gadget/udc/core.c:1265 [inline]
gadget_unbind_driver+0x204/0x790 drivers/usb/gadget/udc/core.c:1727
device_remove+0xc4/0x134 drivers/base/dd.c:616
__device_release_driver drivers/base/dd.c:1349 [inline]
device_release_driver_internal+0x124/0x20c drivers/base/dd.c:1372
driver_detach+0x124/0x1b4 drivers/base/dd.c:1435
bus_remove_driver+0x124/0x22c drivers/base/bus.c:832
driver_unregister+0x78/0xac drivers/base/driver.c:202
usb_gadget_unregister_driver+0x50/0x78 drivers/usb/gadget/udc/core.c:1784
dev_release+0x8c/0x15c drivers/usb/gadget/legacy/inode.c:1198
__fput+0x338/0x74c fs/file_table.c:512
____fput+0x20/0x30 fs/file_table.c:540
task_work_run+0x1cc/0x25c kernel/task_work.c:233
resume_user_mode_work include/linux/resume_user_mode.h:50 [inline]
__exit_to_user_mode_loop kernel/entry/common.c:70 [inline]
exit_to_user_mode_loop+0x10c/0x17c kernel/entry/common.c:101
__exit_to_user_mode_prepare include/linux/irq-entry-common.h:207 [inline]
syscall_exit_to_user_mode_prepare include/linux/irq-entry-common.h:230 [inline]
arm64_syscall_exit_to_user_mode arch/arm64/kernel/entry-common.c:88 [inline]
el0_svc+0x18c/0x260 arch/arm64/kernel/entry-common.c:737
el0t_64_sync_handler+0x48/0x148 arch/arm64/kernel/entry-common.c:755
el0t_64_sync+0x198/0x19c arch/arm64/kernel/entry.S:594
Allocated by task 4939:
kasan_save_stack mm/kasan/common.c:57 [inline]
kasan_save_track+0x40/0x78 mm/kasan/common.c:78
kasan_save_alloc_info+0x44/0x54 mm/kasan/generic.c:570
poison_kmalloc_redzone mm/kasan/common.c:398 [inline]
__kasan_kmalloc+0x9c/0xb4 mm/kasan/common.c:415
kasan_kmalloc include/linux/kasan.h:263 [inline]
__kmalloc_cache_noprof+0x2e8/0x5ec mm/slub.c:5489
_kmalloc_noprof include/linux/slab.h:988 [inline]
_kzalloc_noprof include/linux/slab.h:1309 [inline]
dummy_alloc_request+0x88/0x128 drivers/usb/gadget/udc/dummy_hcd.c:693
usb_ep_alloc_request+0x74/0x480 drivers/usb/gadget/udc/core.c:197
gadgetfs_bind+0x104/0x75c drivers/usb/gadget/legacy/inode.c:1686
gadget_bind_driver+0x274/0x834 drivers/usb/gadget/udc/core.c:1662
call_driver_probe drivers/base/dd.c:-1 [inline]
really_probe+0x29c/0x800 drivers/base/dd.c:706
__driver_probe_device+0x1e0/0x350 drivers/base/dd.c:868
driver_probe_device+0x6c/0x19c drivers/base/dd.c:898
__driver_attach+0x168/0x384 drivers/base/dd.c:1292
bus_for_each_dev+0x128/0x1b4 drivers/base/bus.c:383
driver_attach+0x50/0x60 drivers/base/dd.c:1310
bus_add_driver+0x208/0x500 drivers/base/bus.c:763
driver_register+0x220/0x310 drivers/base/driver.c:174
usb_gadget_register_driver_owner+0xf0/0x25c drivers/usb/gadget/udc/core.c:1752
dev_config+0x91c/0xcc0 drivers/usb/gadget/legacy/inode.c:1883
vfs_write+0x2a4/0xa0c fs/read_write.c:685
ksys_write+0x12c/0x228 fs/read_write.c:739
__do_sys_write fs/read_write.c:750 [inline]
__se_sys_write fs/read_write.c:747 [inline]
__arm64_sys_write+0x7c/0x90 fs/read_write.c:747
__invoke_syscall arch/arm64/kernel/syscall.c:35 [inline]
invoke_syscall+0x98/0x244 arch/arm64/kernel/syscall.c:49
el0_svc_common+0xec/0x23c arch/arm64/kernel/syscall.c:121
do_el0_svc+0x4c/0x5c arch/arm64/kernel/syscall.c:140
el0_svc+0x64/0x260 arch/arm64/kernel/entry-common.c:736
el0t_64_sync_handler+0x48/0x148 arch/arm64/kernel/entry-common.c:755
el0t_64_sync+0x198/0x19c arch/arm64/kernel/entry.S:594
Freed by task 4939:
kasan_save_stack mm/kasan/common.c:57 [inline]
kasan_save_track+0x40/0x78 mm/kasan/common.c:78
kasan_save_free_info+0x58/0x70 mm/kasan/generic.c:584
poison_slab_object mm/kasan/common.c:253 [inline]
__kasan_slab_free+0x74/0xa4 mm/kasan/common.c:285
kasan_slab_free include/linux/kasan.h:235 [inline]
slab_free_hook mm/slub.c:2677 [inline]
slab_free mm/slub.c:6377 [inline]
kfree+0x188/0x690 mm/slub.c:6692
dummy_free_request+0x5c/0x84 drivers/usb/gadget/udc/dummy_hcd.c:711
usb_ep_free_request+0x108/0x3fc drivers/usb/gadget/udc/core.c:221
gadgetfs_unbind+0x19c/0x1f0 drivers/usb/gadget/legacy/inode.c:1661
gadget_unbind_driver+0x188/0x790 drivers/usb/gadget/udc/core.c:1724
device_remove+0xc4/0x134 drivers/base/dd.c:616
__device_release_driver drivers/base/dd.c:1349 [inline]
device_release_driver_internal+0x124/0x20c drivers/base/dd.c:1372
driver_detach+0x124/0x1b4 drivers/base/dd.c:1435
bus_remove_driver+0x124/0x22c drivers/base/bus.c:832
driver_unregister+0x78/0xac drivers/base/driver.c:202
usb_gadget_unregister_driver+0x50/0x78 drivers/usb/gadget/udc/core.c:1784
dev_release+0x8c/0x15c drivers/usb/gadget/legacy/inode.c:1198
__fput+0x338/0x74c fs/file_table.c:512
____fput+0x20/0x30 fs/file_table.c:540
task_work_run+0x1cc/0x25c kernel/task_work.c:233
resume_user_mode_work include/linux/resume_user_mode.h:50 [inline]
__exit_to_user_mode_loop kernel/entry/common.c:70 [inline]
exit_to_user_mode_loop+0x10c/0x17c kernel/entry/common.c:101
__exit_to_user_mode_prepare include/linux/irq-entry-common.h:207 [inline]
syscall_exit_to_user_mode_prepare include/linux/irq-entry-common.h:230 [inline]
arm64_syscall_exit_to_user_mode arch/arm64/kernel/entry-common.c:88 [inline]
el0_svc+0x18c/0x260 arch/arm64/kernel/entry-common.c:737
el0t_64_sync_handler+0x48/0x148 arch/arm64/kernel/entry-common.c:755
el0t_64_sync+0x198/0x19c arch/arm64/kernel/entry.S:594
The buggy address belongs to the object at ffff0000d866f900
which belongs to the cache kmalloc-128 of size 128
The buggy address is located 8 bytes inside of
freed 128-byte region [ffff0000d866f900, ffff0000d866f980)
The buggy address belongs to the physical page:
page: refcount:0 mapcount:0 mapping:0000000000000000 index:0x0 pfn:0x11866f
flags: 0x5ffc00000000000(node=0|zone=2|lastcpupid=0x7ff)
page_type: f5(slab)
raw: 05ffc00000000000 ffff0000c0001a00 dead000000000100 dead000000000122
raw: 0000000000000000 0000000800100010 00000000f5000000 0000000000000000
page dumped because: kasan: bad access detected
Memory state around the buggy address:
ffff0000d866f800: fa fb fb fb fb fb fb fb fb fb fb fb fb fb fb fb
ffff0000d866f880: fc fc fc fc fc fc fc fc fc fc fc fc fc fc fc fc
>ffff0000d866f900: fa fb fb fb fb fb fb fb fb fb fb fb fb fb fb fb
^
ffff0000d866f980: fc fc fc fc fc fc fc fc fc fc fc fc fc fc fc fc
ffff0000d866fa00: fa fb fb fb fb fb fb fb fb fb fb fb fb fb fb fb
==================================================================
list_del corruption, ffff0000d866f900->next is NULL
------------[ cut here ]------------
kernel BUG at lib/list_debug.c:53!
Internal error: Oops - BUG: 00000000f2000800 [#1] SMP
Modules linked in:
CPU: 1 UID: 0 PID: 4939 Comm: syz.0.32 Tainted: G B W syzkaller #0 PREEMPT
Tainted: [B]=BAD_PAGE, [W]=WARN
Hardware name: Google Google Compute Engine/Google Compute Engine, BIOS Google 07/02/2026
pstate: 634000c5 (nZCv daIF +PAN -UAO +TCO +DIT -SSBS BTYPE=--)
pc : __list_del_entry_valid_or_report+0xec/0x1b8 lib/list_debug.c:52
lr : __list_del_entry_valid_or_report+0xec/0x1b8 lib/list_debug.c:52
sp : ffff8000962579c0
x29: ffff8000962579c0 x28: 1fffe000195c4a01 x27: ffff0000cae25008
x26: 1fffe000195c4a00 x25: 00000000ffffff94 x24: 1fffe00019529808
x23: dfff800000000000 x22: dfff800000000000 x21: 0338032e0000134b
x20: 0000000000000000 x19: ffff0000d866f900 x18: 0000000000000000
x17: 3d3d3d3d3d3d3d3d x16: 3d3d3d3d3d3d3d3d x15: 3d3d3d3d3d3d3d3d
x14: 3d3d3d3d3d3d3d3d x13: 0000000000000001 x12: 0000000000000000
x11: 0000000000000000 x10: 0000000000000002 x9 : bfb6b4227df68700
x8 : bfb6b4227df68700 x7 : 0000000000000001 x6 : ffff80008048de94
x5 : 0000000000000000 x4 : 0000000000000000 x3 : ffff800080498bc8
x2 : 0000000100000001 x1 : ffff0000c82cba80 x0 : 0000000000000033
Call trace:
__list_del_entry_valid_or_report+0xec/0x1b8 lib/list_debug.c:52 (P)
__list_del_entry_valid include/linux/list.h:132 [inline]
__list_del_entry include/linux/list.h:246 [inline]
list_del_init include/linux/list.h:318 [inline]
nuke+0x60/0x194 drivers/usb/gadget/udc/dummy_hcd.c:360
stop_activity drivers/usb/gadget/udc/dummy_hcd.c:379 [inline]
dummy_udc_stop+0xe4/0x180 drivers/usb/gadget/udc/dummy_hcd.c:1057
usb_gadget_udc_stop_locked drivers/usb/gadget/udc/core.c:1265 [inline]
gadget_unbind_driver+0x204/0x790 drivers/usb/gadget/udc/core.c:1727
device_remove+0xc4/0x134 drivers/base/dd.c:616
__device_release_driver drivers/base/dd.c:1349 [inline]
device_release_driver_internal+0x124/0x20c drivers/base/dd.c:1372
driver_detach+0x124/0x1b4 drivers/base/dd.c:1435
bus_remove_driver+0x124/0x22c drivers/base/bus.c:832
driver_unregister+0x78/0xac drivers/base/driver.c:202
usb_gadget_unregister_driver+0x50/0x78 drivers/usb/gadget/udc/core.c:1784
dev_release+0x8c/0x15c drivers/usb/gadget/legacy/inode.c:1198
__fput+0x338/0x74c fs/file_table.c:512
____fput+0x20/0x30 fs/file_table.c:540
task_work_run+0x1cc/0x25c kernel/task_work.c:233
resume_user_mode_work include/linux/resume_user_mode.h:50 [inline]
__exit_to_user_mode_loop kernel/entry/common.c:70 [inline]
exit_to_user_mode_loop+0x10c/0x17c kernel/entry/common.c:101
__exit_to_user_mode_prepare include/linux/irq-entry-common.h:207 [inline]
syscall_exit_to_user_mode_prepare include/linux/irq-entry-common.h:230 [inline]
arm64_syscall_exit_to_user_mode arch/arm64/kernel/entry-common.c:88 [inline]
el0_svc+0x18c/0x260 arch/arm64/kernel/entry-common.c:737
el0t_64_sync_handler+0x48/0x148 arch/arm64/kernel/entry-common.c:755
el0t_64_sync+0x198/0x19c arch/arm64/kernel/entry.S:594
Code: f0029ac0 91328000 aa1303e1 979c21eb (d4210000)
---[ end trace 0000000000000000 ]---
---
If you want syzbot to run the reproducer, reply with:
#syz test: git://repo/address.git branch-or-commit-hash
If you attach or paste a git patch, syzbot will apply it before testing.
^ permalink raw reply [flat|nested] 7+ messages in thread
* [PATCH RFC] usb: gadget: gadgetfs: dequeue pending ep0 request before freeing
2026-07-29 0:10 [syzbot] [usb?] WARNING in dummy_free_request (2) syzbot
@ 2026-11-06 14:55 ` Jing Lin
2026-11-06 14:55 ` Jing Lin
2026-11-06 14:55 ` Forwarded: [PATCH RFC] usb: gadget: gadgetfs: dequeue pending ep0 request before freeing syzbot
2 siblings, 0 replies; 7+ messages in thread
From: Jing Lin via B4 Relay @ 2026-11-06 14:55 UTC (permalink / raw)
To: syzbot+c73e1942197af9f54ca3; +Cc: linux-usb, syzkaller-bugs, kayano050408
From: Jing Lin <kayano050408@gmail.com>
Please test this gadgetfs ep0 request lifetime fix
The unpatched base reproduces the warning and use-after-free locally
on arm64 QEMU with dummy_hcd and KASAN
#syz test: https://git.kernel.org/pub/scm/linux/kernel/git/torvalds/linux.git f66469d9905b39d2c260a15d7d6db450f4db67c1
gadgetfs_unbind() frees the preallocated ep0 request after disconnecting
the gadget, assuming that no I/O remains active
With dummy_hcd, disconnecting does not necessarily drain ep0, so a delayed
userspace response can leave dev->req queued when it is freed
This triggers the warning in dummy_free_request() and a use-after-free
when dummy_udc_stop() subsequently drains the endpoint queue
Track dev->req from submission through completion under dev->lock and
reject reuse while it is busy
After waiting for active UDC users to finish, dequeue a pending request
before freeing it and clearing ep0->driver_data
Drop dev->lock around dequeue because the completion callback takes it
Only dequeue pending requests to avoid errors from UDCs that reject
cancellation of an unqueued request
Fixes: 1da177e4c3f4 ("Linux-2.6.12-rc2")
Reported-by: syzbot+c73e1942197af9f54ca3@syzkaller.appspotmail.com
Closes: https://syzkaller.appspot.com/bug?extid=c73e1942197af9f54ca3
Signed-off-by: Jing Lin <kayano050408@gmail.com>
---
Local validation on arm64 QEMU with dummy_hcd, KASAN and lockdep:
- The unpatched kernel reproduces the warning and use-after-free
- The patched kernel runs the C reproducer and rebind/concurrency workload
without the warning, a KASAN report or a lockdep report
- W=1 compilation of inode.o and the Image build completed successfully
AI tools assisted with patch development, the commit description, review
and local QEMU validation
Related syzbot RFC:
https://lore.kernel.org/all/161c70c4-a363-469d-8665-64f71763f84b@mail.kernel.org/
This version tracks pending requests and guards reuse while they are busy
---
drivers/usb/gadget/legacy/inode.c | 55 ++++++++++++++++++++++++++++++++-------
1 file changed, 46 insertions(+), 9 deletions(-)
diff --git a/drivers/usb/gadget/legacy/inode.c b/drivers/usb/gadget/legacy/inode.c
index 7f6d84601..d60df4ad3 100644
--- a/drivers/usb/gadget/legacy/inode.c
+++ b/drivers/usb/gadget/legacy/inode.c
@@ -140,6 +140,7 @@ struct dev_data {
setup_abort : 1,
gadget_registered : 1;
unsigned setup_wLength;
+ bool ep0_req_queued;
/* the rest is basically write-once */
struct usb_config_descriptor *config, *hs_config;
@@ -984,6 +985,16 @@ static inline void ep0_readable (struct dev_data *dev)
kill_fasync (&dev->fasync, SIGIO, POLL_IN);
}
+static void dev_req_complete(struct usb_ep *ep, struct usb_request *req)
+{
+ struct dev_data *dev = ep->driver_data;
+ unsigned long flags;
+
+ spin_lock_irqsave(&dev->lock, flags);
+ dev->ep0_req_queued = false;
+ spin_unlock_irqrestore(&dev->lock, flags);
+}
+
static void clean_req (struct usb_ep *ep, struct usb_request *req)
{
struct dev_data *dev = ep->driver_data;
@@ -992,7 +1003,7 @@ static void clean_req (struct usb_ep *ep, struct usb_request *req)
kfree(req->buf);
req->buf = dev->rbuf;
}
- req->complete = epio_complete;
+ req->complete = dev_req_complete;
dev->setup_out_ready = 0;
}
@@ -1004,6 +1015,7 @@ static void ep0_complete (struct usb_ep *ep, struct usb_request *req)
/* for control OUT, data must still get to userspace */
spin_lock_irqsave(&dev->lock, flags);
+ dev->ep0_req_queued = false;
if (!dev->setup_in) {
dev->setup_out_error = (req->status != 0);
if (!dev->setup_out_error)
@@ -1015,7 +1027,7 @@ static void ep0_complete (struct usb_ep *ep, struct usb_request *req)
/* clean up as appropriate */
if (free && req->buf != &dev->rbuf)
clean_req (ep, req);
- req->complete = epio_complete;
+ req->complete = dev_req_complete;
spin_unlock_irqrestore(&dev->lock, flags);
}
@@ -1023,7 +1035,7 @@ static int setup_req (struct usb_ep *ep, struct usb_request *req, u16 len)
{
struct dev_data *dev = ep->driver_data;
- if (dev->setup_out_ready) {
+ if (dev->setup_out_ready || dev->ep0_req_queued) {
DBG (dev, "ep0 request busy!\n");
return -EBUSY;
}
@@ -1073,13 +1085,20 @@ ep0_read (struct file *fd, char __user *buf, size_t len, loff_t *ptr)
struct usb_request *req = dev->req;
if ((retval = setup_req (ep, req, 0)) == 0) {
+ dev->state = STATE_DEV_CONNECTED;
+ dev->ep0_req_queued = true;
++dev->udc_usage;
spin_unlock_irq (&dev->lock);
retval = usb_ep_queue (ep, req, GFP_KERNEL);
spin_lock_irq (&dev->lock);
--dev->udc_usage;
+ if (retval < 0) {
+ dev->ep0_req_queued = false;
+ clean_req(ep, req);
+ }
+ } else {
+ dev->state = STATE_DEV_CONNECTED;
}
- dev->state = STATE_DEV_CONNECTED;
/* assume that was SET_CONFIGURATION */
if (dev->current_config) {
@@ -1270,6 +1289,7 @@ ep0_write (struct file *fd, const char __user *buf, size_t len, loff_t *ptr)
retval = setup_req (dev->gadget->ep0, dev->req, len);
if (retval == 0) {
dev->state = STATE_DEV_CONNECTED;
+ dev->ep0_req_queued = true;
++dev->udc_usage;
spin_unlock_irq (&dev->lock);
if (copy_from_user (dev->req->buf, buf, len))
@@ -1284,6 +1304,7 @@ ep0_write (struct file *fd, const char __user *buf, size_t len, loff_t *ptr)
spin_lock_irq(&dev->lock);
--dev->udc_usage;
if (retval < 0) {
+ dev->ep0_req_queued = false;
clean_req (dev->gadget->ep0, dev->req);
} else
retval = len;
@@ -1508,6 +1529,11 @@ gadgetfs_setup (struct usb_gadget *gadget, const struct usb_ctrlrequest *ctrl)
} else if (dev->state == STATE_DEV_SETUP)
dev->setup_abort = 1;
+ if (dev->ep0_req_queued) {
+ spin_unlock(&dev->lock);
+ return -EBUSY;
+ }
+
req->buf = dev->rbuf;
req->context = NULL;
switch (ctrl->bRequest) {
@@ -1626,6 +1652,7 @@ gadgetfs_setup (struct usb_gadget *gadget, const struct usb_ctrlrequest *ctrl)
if (value < 0)
break;
+ dev->ep0_req_queued = true;
++dev->udc_usage;
spin_unlock (&dev->lock);
value = usb_ep_queue (gadget->ep0, dev->req,
@@ -1633,6 +1660,7 @@ gadgetfs_setup (struct usb_gadget *gadget, const struct usb_ctrlrequest *ctrl)
spin_lock (&dev->lock);
--dev->udc_usage;
if (value < 0) {
+ dev->ep0_req_queued = false;
clean_req (gadget->ep0, dev->req);
break;
}
@@ -1664,16 +1692,18 @@ gadgetfs_setup (struct usb_gadget *gadget, const struct usb_ctrlrequest *ctrl)
req->length = value;
req->zero = value < w_length;
+ dev->ep0_req_queued = true;
++dev->udc_usage;
spin_unlock (&dev->lock);
value = usb_ep_queue (gadget->ep0, req, GFP_KERNEL);
spin_lock(&dev->lock);
--dev->udc_usage;
- spin_unlock(&dev->lock);
if (value < 0) {
+ dev->ep0_req_queued = false;
DBG (dev, "ep_queue --> %d\n", value);
req->status = 0;
}
+ spin_unlock(&dev->lock);
return value;
}
@@ -1771,6 +1801,7 @@ static void
gadgetfs_unbind (struct usb_gadget *gadget)
{
struct dev_data *dev = get_gadget_data (gadget);
+ bool queued;
DBG (dev, "%s\n", __func__);
@@ -1781,15 +1812,20 @@ gadgetfs_unbind (struct usb_gadget *gadget)
usleep_range(1000, 2000);
spin_lock_irq(&dev->lock);
}
+ queued = dev->ep0_req_queued;
spin_unlock_irq (&dev->lock);
+ if (dev->req) {
+ if (queued)
+ usb_ep_dequeue(gadget->ep0, dev->req);
+ usb_ep_free_request(gadget->ep0, dev->req);
+ dev->req = NULL;
+ }
+
destroy_ep_files (dev);
gadget->ep0->driver_data = NULL;
set_gadget_data (gadget, NULL);
- /* we've already been disconnected ... no i/o is active */
- if (dev->req)
- usb_ep_free_request (gadget->ep0, dev->req);
DBG (dev, "%s done\n", __func__);
put_dev (dev);
}
@@ -1816,11 +1852,12 @@ static int gadgetfs_bind(struct usb_gadget *gadget,
get_dev (dev);
/* preallocate control response and buffer */
+ dev->ep0_req_queued = false;
dev->req = usb_ep_alloc_request (gadget->ep0, GFP_KERNEL);
if (!dev->req)
goto enomem;
dev->req->context = NULL;
- dev->req->complete = epio_complete;
+ dev->req->complete = dev_req_complete;
if (activate_ep_files (dev) < 0)
goto enomem;
---
base-commit: f66469d9905b39d2c260a15d7d6db450f4db67c1
change-id: 20261106-b4-gadgetfs-ep0-f814c14717a9
Best regards,
--
Jing Lin <kayano050408@gmail.com>
^ permalink raw reply related [flat|nested] 7+ messages in thread
* [PATCH RFC] usb: gadget: gadgetfs: dequeue pending ep0 request before freeing
@ 2026-11-06 14:55 ` Jing Lin
0 siblings, 0 replies; 7+ messages in thread
From: Jing Lin @ 2026-11-06 14:55 UTC (permalink / raw)
To: syzbot+c73e1942197af9f54ca3; +Cc: linux-usb, syzkaller-bugs, kayano050408
Please test this gadgetfs ep0 request lifetime fix
The unpatched base reproduces the warning and use-after-free locally
on arm64 QEMU with dummy_hcd and KASAN
#syz test: https://git.kernel.org/pub/scm/linux/kernel/git/torvalds/linux.git f66469d9905b39d2c260a15d7d6db450f4db67c1
gadgetfs_unbind() frees the preallocated ep0 request after disconnecting
the gadget, assuming that no I/O remains active
With dummy_hcd, disconnecting does not necessarily drain ep0, so a delayed
userspace response can leave dev->req queued when it is freed
This triggers the warning in dummy_free_request() and a use-after-free
when dummy_udc_stop() subsequently drains the endpoint queue
Track dev->req from submission through completion under dev->lock and
reject reuse while it is busy
After waiting for active UDC users to finish, dequeue a pending request
before freeing it and clearing ep0->driver_data
Drop dev->lock around dequeue because the completion callback takes it
Only dequeue pending requests to avoid errors from UDCs that reject
cancellation of an unqueued request
Fixes: 1da177e4c3f4 ("Linux-2.6.12-rc2")
Reported-by: syzbot+c73e1942197af9f54ca3@syzkaller.appspotmail.com
Closes: https://syzkaller.appspot.com/bug?extid=c73e1942197af9f54ca3
Signed-off-by: Jing Lin <kayano050408@gmail.com>
---
Local validation on arm64 QEMU with dummy_hcd, KASAN and lockdep:
- The unpatched kernel reproduces the warning and use-after-free
- The patched kernel runs the C reproducer and rebind/concurrency workload
without the warning, a KASAN report or a lockdep report
- W=1 compilation of inode.o and the Image build completed successfully
AI tools assisted with patch development, the commit description, review
and local QEMU validation
Related syzbot RFC:
https://lore.kernel.org/all/161c70c4-a363-469d-8665-64f71763f84b@mail.kernel.org/
This version tracks pending requests and guards reuse while they are busy
---
drivers/usb/gadget/legacy/inode.c | 55 ++++++++++++++++++++++++++++++++-------
1 file changed, 46 insertions(+), 9 deletions(-)
diff --git a/drivers/usb/gadget/legacy/inode.c b/drivers/usb/gadget/legacy/inode.c
index 7f6d84601..d60df4ad3 100644
--- a/drivers/usb/gadget/legacy/inode.c
+++ b/drivers/usb/gadget/legacy/inode.c
@@ -140,6 +140,7 @@ struct dev_data {
setup_abort : 1,
gadget_registered : 1;
unsigned setup_wLength;
+ bool ep0_req_queued;
/* the rest is basically write-once */
struct usb_config_descriptor *config, *hs_config;
@@ -984,6 +985,16 @@ static inline void ep0_readable (struct dev_data *dev)
kill_fasync (&dev->fasync, SIGIO, POLL_IN);
}
+static void dev_req_complete(struct usb_ep *ep, struct usb_request *req)
+{
+ struct dev_data *dev = ep->driver_data;
+ unsigned long flags;
+
+ spin_lock_irqsave(&dev->lock, flags);
+ dev->ep0_req_queued = false;
+ spin_unlock_irqrestore(&dev->lock, flags);
+}
+
static void clean_req (struct usb_ep *ep, struct usb_request *req)
{
struct dev_data *dev = ep->driver_data;
@@ -992,7 +1003,7 @@ static void clean_req (struct usb_ep *ep, struct usb_request *req)
kfree(req->buf);
req->buf = dev->rbuf;
}
- req->complete = epio_complete;
+ req->complete = dev_req_complete;
dev->setup_out_ready = 0;
}
@@ -1004,6 +1015,7 @@ static void ep0_complete (struct usb_ep *ep, struct usb_request *req)
/* for control OUT, data must still get to userspace */
spin_lock_irqsave(&dev->lock, flags);
+ dev->ep0_req_queued = false;
if (!dev->setup_in) {
dev->setup_out_error = (req->status != 0);
if (!dev->setup_out_error)
@@ -1015,7 +1027,7 @@ static void ep0_complete (struct usb_ep *ep, struct usb_request *req)
/* clean up as appropriate */
if (free && req->buf != &dev->rbuf)
clean_req (ep, req);
- req->complete = epio_complete;
+ req->complete = dev_req_complete;
spin_unlock_irqrestore(&dev->lock, flags);
}
@@ -1023,7 +1035,7 @@ static int setup_req (struct usb_ep *ep, struct usb_request *req, u16 len)
{
struct dev_data *dev = ep->driver_data;
- if (dev->setup_out_ready) {
+ if (dev->setup_out_ready || dev->ep0_req_queued) {
DBG (dev, "ep0 request busy!\n");
return -EBUSY;
}
@@ -1073,13 +1085,20 @@ ep0_read (struct file *fd, char __user *buf, size_t len, loff_t *ptr)
struct usb_request *req = dev->req;
if ((retval = setup_req (ep, req, 0)) == 0) {
+ dev->state = STATE_DEV_CONNECTED;
+ dev->ep0_req_queued = true;
++dev->udc_usage;
spin_unlock_irq (&dev->lock);
retval = usb_ep_queue (ep, req, GFP_KERNEL);
spin_lock_irq (&dev->lock);
--dev->udc_usage;
+ if (retval < 0) {
+ dev->ep0_req_queued = false;
+ clean_req(ep, req);
+ }
+ } else {
+ dev->state = STATE_DEV_CONNECTED;
}
- dev->state = STATE_DEV_CONNECTED;
/* assume that was SET_CONFIGURATION */
if (dev->current_config) {
@@ -1270,6 +1289,7 @@ ep0_write (struct file *fd, const char __user *buf, size_t len, loff_t *ptr)
retval = setup_req (dev->gadget->ep0, dev->req, len);
if (retval == 0) {
dev->state = STATE_DEV_CONNECTED;
+ dev->ep0_req_queued = true;
++dev->udc_usage;
spin_unlock_irq (&dev->lock);
if (copy_from_user (dev->req->buf, buf, len))
@@ -1284,6 +1304,7 @@ ep0_write (struct file *fd, const char __user *buf, size_t len, loff_t *ptr)
spin_lock_irq(&dev->lock);
--dev->udc_usage;
if (retval < 0) {
+ dev->ep0_req_queued = false;
clean_req (dev->gadget->ep0, dev->req);
} else
retval = len;
@@ -1508,6 +1529,11 @@ gadgetfs_setup (struct usb_gadget *gadget, const struct usb_ctrlrequest *ctrl)
} else if (dev->state == STATE_DEV_SETUP)
dev->setup_abort = 1;
+ if (dev->ep0_req_queued) {
+ spin_unlock(&dev->lock);
+ return -EBUSY;
+ }
+
req->buf = dev->rbuf;
req->context = NULL;
switch (ctrl->bRequest) {
@@ -1626,6 +1652,7 @@ gadgetfs_setup (struct usb_gadget *gadget, const struct usb_ctrlrequest *ctrl)
if (value < 0)
break;
+ dev->ep0_req_queued = true;
++dev->udc_usage;
spin_unlock (&dev->lock);
value = usb_ep_queue (gadget->ep0, dev->req,
@@ -1633,6 +1660,7 @@ gadgetfs_setup (struct usb_gadget *gadget, const struct usb_ctrlrequest *ctrl)
spin_lock (&dev->lock);
--dev->udc_usage;
if (value < 0) {
+ dev->ep0_req_queued = false;
clean_req (gadget->ep0, dev->req);
break;
}
@@ -1664,16 +1692,18 @@ gadgetfs_setup (struct usb_gadget *gadget, const struct usb_ctrlrequest *ctrl)
req->length = value;
req->zero = value < w_length;
+ dev->ep0_req_queued = true;
++dev->udc_usage;
spin_unlock (&dev->lock);
value = usb_ep_queue (gadget->ep0, req, GFP_KERNEL);
spin_lock(&dev->lock);
--dev->udc_usage;
- spin_unlock(&dev->lock);
if (value < 0) {
+ dev->ep0_req_queued = false;
DBG (dev, "ep_queue --> %d\n", value);
req->status = 0;
}
+ spin_unlock(&dev->lock);
return value;
}
@@ -1771,6 +1801,7 @@ static void
gadgetfs_unbind (struct usb_gadget *gadget)
{
struct dev_data *dev = get_gadget_data (gadget);
+ bool queued;
DBG (dev, "%s\n", __func__);
@@ -1781,15 +1812,20 @@ gadgetfs_unbind (struct usb_gadget *gadget)
usleep_range(1000, 2000);
spin_lock_irq(&dev->lock);
}
+ queued = dev->ep0_req_queued;
spin_unlock_irq (&dev->lock);
+ if (dev->req) {
+ if (queued)
+ usb_ep_dequeue(gadget->ep0, dev->req);
+ usb_ep_free_request(gadget->ep0, dev->req);
+ dev->req = NULL;
+ }
+
destroy_ep_files (dev);
gadget->ep0->driver_data = NULL;
set_gadget_data (gadget, NULL);
- /* we've already been disconnected ... no i/o is active */
- if (dev->req)
- usb_ep_free_request (gadget->ep0, dev->req);
DBG (dev, "%s done\n", __func__);
put_dev (dev);
}
@@ -1816,11 +1852,12 @@ static int gadgetfs_bind(struct usb_gadget *gadget,
get_dev (dev);
/* preallocate control response and buffer */
+ dev->ep0_req_queued = false;
dev->req = usb_ep_alloc_request (gadget->ep0, GFP_KERNEL);
if (!dev->req)
goto enomem;
dev->req->context = NULL;
- dev->req->complete = epio_complete;
+ dev->req->complete = dev_req_complete;
if (activate_ep_files (dev) < 0)
goto enomem;
---
base-commit: f66469d9905b39d2c260a15d7d6db450f4db67c1
change-id: 20261106-b4-gadgetfs-ep0-f814c14717a9
Best regards,
--
Jing Lin <kayano050408@gmail.com>
^ permalink raw reply related [flat|nested] 7+ messages in thread
* Forwarded: [PATCH RFC] usb: gadget: gadgetfs: dequeue pending ep0 request before freeing
2026-07-29 0:10 [syzbot] [usb?] WARNING in dummy_free_request (2) syzbot
2026-08-02 0:10 ` syzbot
2026-11-06 14:55 ` Jing Lin
@ 2026-11-06 14:55 ` syzbot
2 siblings, 0 replies; 7+ messages in thread
From: syzbot @ 2026-11-06 14:55 UTC (permalink / raw)
To: linux-kernel
For archival purposes, forwarding an incoming command email to
linux-kernel@vger.kernel.org.
***
Subject: [PATCH RFC] usb: gadget: gadgetfs: dequeue pending ep0 request before freeing
Author: devnull@kernel.org
From: Jing Lin <kayano050408@gmail.com>
Please test this gadgetfs ep0 request lifetime fix
The unpatched base reproduces the warning and use-after-free locally
on arm64 QEMU with dummy_hcd and KASAN
#syz test: https://git.kernel.org/pub/scm/linux/kernel/git/torvalds/linux.git f66469d9905b39d2c260a15d7d6db450f4db67c1
gadgetfs_unbind() frees the preallocated ep0 request after disconnecting
the gadget, assuming that no I/O remains active
With dummy_hcd, disconnecting does not necessarily drain ep0, so a delayed
userspace response can leave dev->req queued when it is freed
This triggers the warning in dummy_free_request() and a use-after-free
when dummy_udc_stop() subsequently drains the endpoint queue
Track dev->req from submission through completion under dev->lock and
reject reuse while it is busy
After waiting for active UDC users to finish, dequeue a pending request
before freeing it and clearing ep0->driver_data
Drop dev->lock around dequeue because the completion callback takes it
Only dequeue pending requests to avoid errors from UDCs that reject
cancellation of an unqueued request
Fixes: 1da177e4c3f4 ("Linux-2.6.12-rc2")
Reported-by: syzbot+c73e1942197af9f54ca3@syzkaller.appspotmail.com
Closes: https://syzkaller.appspot.com/bug?extid=c73e1942197af9f54ca3
Signed-off-by: Jing Lin <kayano050408@gmail.com>
---
Local validation on arm64 QEMU with dummy_hcd, KASAN and lockdep:
- The unpatched kernel reproduces the warning and use-after-free
- The patched kernel runs the C reproducer and rebind/concurrency workload
without the warning, a KASAN report or a lockdep report
- W=1 compilation of inode.o and the Image build completed successfully
AI tools assisted with patch development, the commit description, review
and local QEMU validation
Related syzbot RFC:
https://lore.kernel.org/all/161c70c4-a363-469d-8665-64f71763f84b@mail.kernel.org/
This version tracks pending requests and guards reuse while they are busy
---
drivers/usb/gadget/legacy/inode.c | 55 ++++++++++++++++++++++++++++++++-------
1 file changed, 46 insertions(+), 9 deletions(-)
diff --git a/drivers/usb/gadget/legacy/inode.c b/drivers/usb/gadget/legacy/inode.c
index 7f6d84601..d60df4ad3 100644
--- a/drivers/usb/gadget/legacy/inode.c
+++ b/drivers/usb/gadget/legacy/inode.c
@@ -140,6 +140,7 @@ struct dev_data {
setup_abort : 1,
gadget_registered : 1;
unsigned setup_wLength;
+ bool ep0_req_queued;
/* the rest is basically write-once */
struct usb_config_descriptor *config, *hs_config;
@@ -984,6 +985,16 @@ static inline void ep0_readable (struct dev_data *dev)
kill_fasync (&dev->fasync, SIGIO, POLL_IN);
}
+static void dev_req_complete(struct usb_ep *ep, struct usb_request *req)
+{
+ struct dev_data *dev = ep->driver_data;
+ unsigned long flags;
+
+ spin_lock_irqsave(&dev->lock, flags);
+ dev->ep0_req_queued = false;
+ spin_unlock_irqrestore(&dev->lock, flags);
+}
+
static void clean_req (struct usb_ep *ep, struct usb_request *req)
{
struct dev_data *dev = ep->driver_data;
@@ -992,7 +1003,7 @@ static void clean_req (struct usb_ep *ep, struct usb_request *req)
kfree(req->buf);
req->buf = dev->rbuf;
}
- req->complete = epio_complete;
+ req->complete = dev_req_complete;
dev->setup_out_ready = 0;
}
@@ -1004,6 +1015,7 @@ static void ep0_complete (struct usb_ep *ep, struct usb_request *req)
/* for control OUT, data must still get to userspace */
spin_lock_irqsave(&dev->lock, flags);
+ dev->ep0_req_queued = false;
if (!dev->setup_in) {
dev->setup_out_error = (req->status != 0);
if (!dev->setup_out_error)
@@ -1015,7 +1027,7 @@ static void ep0_complete (struct usb_ep *ep, struct usb_request *req)
/* clean up as appropriate */
if (free && req->buf != &dev->rbuf)
clean_req (ep, req);
- req->complete = epio_complete;
+ req->complete = dev_req_complete;
spin_unlock_irqrestore(&dev->lock, flags);
}
@@ -1023,7 +1035,7 @@ static int setup_req (struct usb_ep *ep, struct usb_request *req, u16 len)
{
struct dev_data *dev = ep->driver_data;
- if (dev->setup_out_ready) {
+ if (dev->setup_out_ready || dev->ep0_req_queued) {
DBG (dev, "ep0 request busy!\n");
return -EBUSY;
}
@@ -1073,13 +1085,20 @@ ep0_read (struct file *fd, char __user *buf, size_t len, loff_t *ptr)
struct usb_request *req = dev->req;
if ((retval = setup_req (ep, req, 0)) == 0) {
+ dev->state = STATE_DEV_CONNECTED;
+ dev->ep0_req_queued = true;
++dev->udc_usage;
spin_unlock_irq (&dev->lock);
retval = usb_ep_queue (ep, req, GFP_KERNEL);
spin_lock_irq (&dev->lock);
--dev->udc_usage;
+ if (retval < 0) {
+ dev->ep0_req_queued = false;
+ clean_req(ep, req);
+ }
+ } else {
+ dev->state = STATE_DEV_CONNECTED;
}
- dev->state = STATE_DEV_CONNECTED;
/* assume that was SET_CONFIGURATION */
if (dev->current_config) {
@@ -1270,6 +1289,7 @@ ep0_write (struct file *fd, const char __user *buf, size_t len, loff_t *ptr)
retval = setup_req (dev->gadget->ep0, dev->req, len);
if (retval == 0) {
dev->state = STATE_DEV_CONNECTED;
+ dev->ep0_req_queued = true;
++dev->udc_usage;
spin_unlock_irq (&dev->lock);
if (copy_from_user (dev->req->buf, buf, len))
@@ -1284,6 +1304,7 @@ ep0_write (struct file *fd, const char __user *buf, size_t len, loff_t *ptr)
spin_lock_irq(&dev->lock);
--dev->udc_usage;
if (retval < 0) {
+ dev->ep0_req_queued = false;
clean_req (dev->gadget->ep0, dev->req);
} else
retval = len;
@@ -1508,6 +1529,11 @@ gadgetfs_setup (struct usb_gadget *gadget, const struct usb_ctrlrequest *ctrl)
} else if (dev->state == STATE_DEV_SETUP)
dev->setup_abort = 1;
+ if (dev->ep0_req_queued) {
+ spin_unlock(&dev->lock);
+ return -EBUSY;
+ }
+
req->buf = dev->rbuf;
req->context = NULL;
switch (ctrl->bRequest) {
@@ -1626,6 +1652,7 @@ gadgetfs_setup (struct usb_gadget *gadget, const struct usb_ctrlrequest *ctrl)
if (value < 0)
break;
+ dev->ep0_req_queued = true;
++dev->udc_usage;
spin_unlock (&dev->lock);
value = usb_ep_queue (gadget->ep0, dev->req,
@@ -1633,6 +1660,7 @@ gadgetfs_setup (struct usb_gadget *gadget, const struct usb_ctrlrequest *ctrl)
spin_lock (&dev->lock);
--dev->udc_usage;
if (value < 0) {
+ dev->ep0_req_queued = false;
clean_req (gadget->ep0, dev->req);
break;
}
@@ -1664,16 +1692,18 @@ gadgetfs_setup (struct usb_gadget *gadget, const struct usb_ctrlrequest *ctrl)
req->length = value;
req->zero = value < w_length;
+ dev->ep0_req_queued = true;
++dev->udc_usage;
spin_unlock (&dev->lock);
value = usb_ep_queue (gadget->ep0, req, GFP_KERNEL);
spin_lock(&dev->lock);
--dev->udc_usage;
- spin_unlock(&dev->lock);
if (value < 0) {
+ dev->ep0_req_queued = false;
DBG (dev, "ep_queue --> %d\n", value);
req->status = 0;
}
+ spin_unlock(&dev->lock);
return value;
}
@@ -1771,6 +1801,7 @@ static void
gadgetfs_unbind (struct usb_gadget *gadget)
{
struct dev_data *dev = get_gadget_data (gadget);
+ bool queued;
DBG (dev, "%s\n", __func__);
@@ -1781,15 +1812,20 @@ gadgetfs_unbind (struct usb_gadget *gadget)
usleep_range(1000, 2000);
spin_lock_irq(&dev->lock);
}
+ queued = dev->ep0_req_queued;
spin_unlock_irq (&dev->lock);
+ if (dev->req) {
+ if (queued)
+ usb_ep_dequeue(gadget->ep0, dev->req);
+ usb_ep_free_request(gadget->ep0, dev->req);
+ dev->req = NULL;
+ }
+
destroy_ep_files (dev);
gadget->ep0->driver_data = NULL;
set_gadget_data (gadget, NULL);
- /* we've already been disconnected ... no i/o is active */
- if (dev->req)
- usb_ep_free_request (gadget->ep0, dev->req);
DBG (dev, "%s done\n", __func__);
put_dev (dev);
}
@@ -1816,11 +1852,12 @@ static int gadgetfs_bind(struct usb_gadget *gadget,
get_dev (dev);
/* preallocate control response and buffer */
+ dev->ep0_req_queued = false;
dev->req = usb_ep_alloc_request (gadget->ep0, GFP_KERNEL);
if (!dev->req)
goto enomem;
dev->req->context = NULL;
- dev->req->complete = epio_complete;
+ dev->req->complete = dev_req_complete;
if (activate_ep_files (dev) < 0)
goto enomem;
---
base-commit: f66469d9905b39d2c260a15d7d6db450f4db67c1
change-id: 20261106-b4-gadgetfs-ep0-f814c14717a9
Best regards,
--
Jing Lin <kayano050408@gmail.com>
^ permalink raw reply related [flat|nested] 7+ messages in thread
* Re: [PATCH RFC] usb: gadget: gadgetfs: dequeue pending ep0 request before freeing
2026-11-06 14:55 ` Jing Lin
(?)
@ 2026-11-06 15:26 ` sashiko-bot
-1 siblings, 0 replies; 7+ messages in thread
From: sashiko-bot @ 2026-11-06 15:26 UTC (permalink / raw)
To: Jing Lin; +Cc: linux-usb
Thank you for your contribution! Sashiko AI review found 2 potential
issues to consider:
- [High] usb: gadget: gadgetfs: gadgetfs_setup() reuses req while
setup_out_ready
- [Low] usb: gadget: gadgetfs: invalid Fixes SHA and syzbot command in
changelog
--
commit 30f3f2dca823922d4eaa362244e46b92716f82d8
Author: Jing Lin <kayano050408@gmail.com>
> usb: gadget: gadgetfs: dequeue pending ep0 request before freeing
>
> Please test this gadgetfs ep0 request lifetime fix
> The unpatched base reproduces the warning and use-after-free locally
> on arm64 QEMU with dummy_hcd and KASAN
>
> #syz test: https://git.kernel.org/pub/scm/linux/kernel/git/torvalds/linux.git f66469d9905b39d2c260a15d7d6db450f4db67c1
>
> gadgetfs_unbind() frees the preallocated ep0 request after disconnecting
> the gadget, assuming that no I/O remains active
>
> With dummy_hcd, disconnecting does not necessarily drain ep0, so a delayed
> userspace response can leave dev->req queued when it is freed
> This triggers the warning in dummy_free_request() and a use-after-free
> when dummy_udc_stop() subsequently drains the endpoint queue
>
> Track dev->req from submission through completion under dev->lock and
> reject reuse while it is busy
> After waiting for active UDC users to finish, dequeue a pending request
> before freeing it and clearing ep0->driver_data
> Drop dev->lock around dequeue because the completion callback takes it
> Only dequeue pending requests to avoid errors from UDCs that reject
> cancellation of an unqueued request
>
> Fixes: 1da177e4c3f4 ("Linux-2.6.12-rc2")
> Reported-by: syzbot+c73e1942197af9f54ca3@syzkaller.appspotmail.com
> Closes: https://syzkaller.appspot.com/bug?extid=c73e1942197af9f54ca3
> Signed-off-by: Jing Lin <kayano050408@gmail.com>
[Severity: Low]
The conversational testing notes and the "#syz test:" command at the top of
the commit message should be removed before applying. Also, the 12-character
prefix 1da177e4c3f4 in the Fixes: trailer cannot be uniquely resolved in git
history.
> diff --git a/drivers/usb/gadget/legacy/inode.c b/drivers/usb/gadget/legacy/inode.c
> index 7f6d8460152f9..d60df4ad38a3e 100644
> --- a/drivers/usb/gadget/legacy/inode.c
> +++ b/drivers/usb/gadget/legacy/inode.c
[ ... ]
> @@ -1508,6 +1529,11 @@ gadgetfs_setup (struct usb_gadget *gadget, const struct usb_ctrlrequest *ctrl)
> } else if (dev->state == STATE_DEV_SETUP)
> dev->setup_abort = 1;
>
> + if (dev->ep0_req_queued) {
> + spin_unlock(&dev->lock);
> + return -EBUSY;
> + }
> +
> req->buf = dev->rbuf;
> req->context = NULL;
> switch (ctrl->bRequest) {
[Severity: High]
Can gadgetfs_setup() reuse dev->req while ep0_read() is still copying out a
completed control OUT transfer?
When a delegated control OUT transfer completes, ep0_complete() clears
dev->ep0_req_queued and sets dev->setup_out_ready = 1, leaving the payload in
dev->req->buf until ep0_read() copies it to userspace and calls clean_req():
drivers/usb/gadget/legacy/inode.c:ep0_read() {
...
len = min (len, (size_t)dev->req->actual);
++dev->udc_usage;
spin_unlock_irq(&dev->lock);
if (copy_to_user (buf, dev->req->buf, len))
retval = -EFAULT;
else
retval = len;
spin_lock_irq(&dev->lock);
--dev->udc_usage;
clean_req (dev->gadget->ep0, dev->req);
...
}
Because gadgetfs_setup() only checks dev->ep0_req_queued (and clears
dev->setup_out_ready = 0 at the delegate label before calling setup_req()), a
new SETUP packet arriving while ep0_read() has dropped dev->lock for
copy_to_user() will reuse dev->req:
CPU0 CPU1
ep0_read()
spin_unlock_irq(&dev->lock)
gadgetfs_setup()
/* USB_REQ_GET_DESCRIPTOR */
req->buf = dev->config;
usb_ep_queue(gadget->ep0, req, ...)
copy_to_user(buf, dev->req->buf, len)
spin_lock_irq(&dev->lock)
clean_req(gadget->ep0, dev->req)
kfree(req->buf) <-- frees dev->config / interior pointer
If the new SETUP request is USB_REQ_GET_DESCRIPTOR, req->buf is pointed at
dev->dev (the 18-byte descriptor at the tail of dev->buf), dev->hs_config (an
interior pointer into dev->buf), or dev->config (dev->buf itself).
copy_to_user() can then read up to 238 bytes past the end of dev->buf when
req->buf is dev->dev, and clean_req() then passes req->buf to kfree(),
either crashing SLUB on an interior pointer or freeing dev->buf while it is
queued in the UDC and before dev_release() frees it again. If the new SETUP
request is another delegated control OUT transfer, gadgetfs_setup() clears
dev->setup_out_ready = 0 and re-queues dev->req, after which ep0_read()'s
clean_req() frees the new req->buf while queued and resets req->complete.
Checking dev->setup_out_ready alongside dev->ep0_req_queued in
gadgetfs_setup() would prevent reusing dev->req until ep0_read() finishes
with it.
--
Sashiko AI review · https://sashiko.dev/#/patchset/179397691694.27761.9543494907127681190.gadgetfs-ep0-patch-test@gmail.com?part=1
^ permalink raw reply [flat|nested] 7+ messages in thread
* Re: [syzbot] [usb?] WARNING in dummy_free_request (2)
2026-11-06 14:55 ` Jing Lin
(?)
(?)
@ 2026-11-06 15:39 ` syzbot
-1 siblings, 0 replies; 7+ messages in thread
From: syzbot @ 2026-11-06 15:39 UTC (permalink / raw)
To: devnull, kayano050408, linux-kernel, linux-usb, syzkaller-bugs
Hello,
syzbot has tested the proposed patch and the reproducer did not trigger any issue:
Reported-by: syzbot+c73e1942197af9f54ca3@syzkaller.appspotmail.com
Tested-by: syzbot+c73e1942197af9f54ca3@syzkaller.appspotmail.com
Tested on:
commit: f66469d9 Merge tag 'net-7.4-rc2' of git://git.kernel.o..
git tree: https://git.kernel.org/pub/scm/linux/kernel/git/torvalds/linux.git
console output: https://syzkaller.appspot.com/x/log.txt?x=15bae369580000
kernel config: https://syzkaller.appspot.com/x/.config?x=664189a7d8e350d3
dashboard link: https://syzkaller.appspot.com/bug?extid=c73e1942197af9f54ca3
compiler: Debian clang version 22.1.8 (1~deb13u4), Debian LLD 22.1.8
patch: https://syzkaller.appspot.com/x/patch.diff?x=17acf2fd580000
Note: testing is done by a robot and is best-effort only.
^ permalink raw reply [flat|nested] 7+ messages in thread
end of thread, other threads:[~2026-11-06 15:39 UTC | newest]
Thread overview: 7+ messages (download: mbox.gz follow: Atom feed
-- links below jump to the message on this page --
2026-07-29 0:10 [syzbot] [usb?] WARNING in dummy_free_request (2) syzbot
2026-08-02 0:10 ` syzbot
2026-11-06 14:55 ` [PATCH RFC] usb: gadget: gadgetfs: dequeue pending ep0 request before freeing Jing Lin via B4 Relay
2026-11-06 14:55 ` Jing Lin
2026-11-06 15:26 ` sashiko-bot
2026-11-06 15:39 ` [syzbot] [usb?] WARNING in dummy_free_request (2) syzbot
2026-11-06 14:55 ` Forwarded: [PATCH RFC] usb: gadget: gadgetfs: dequeue pending ep0 request before freeing syzbot
This is an external index of several public inboxes,
see mirroring instructions on how to clone and mirror
all data and code used by this external index.