| 0/0 |
2026/07/29 01:36 |
flow |
assessment-security |
9m
Results: map[DenialOfService:true Explanation:Based on the analysis of the bug report and the kernel source code, here is the
assessment of the vulnerability:
**Root Cause & Bug Mechanism:**
The bug is a Slab-Use-After-Free (UAF) in the `dummy_hcd` driver (a
software-only USB controller used for testing). The crash occurs in
`find_endpoint()` when it attempts to read `ep->desc->bEndpointAddress`.
The `ep->desc` pointer is supposed to point to a valid `usb_endpoint_descriptor`
provided by a USB gadget driver (such as `FunctionFS` or `raw_gadget`). However,
the gadget driver freed the memory containing this descriptor without properly
disabling the endpoint via `usb_ep_disable()`. As a result, `dummy_hcd` retains
a dangling pointer.
In this specific crash, the freed memory was subsequently reallocated by the
`io_uring` subsystem as an `io_kiocb` object (size 248 bytes, likely from a
merged `kmalloc-256` cache), which was then freed again. When the `dummy_timer`
softirq fires, it iterates over the endpoints and dereferences the dangling
`ep->desc` pointer, triggering the KASAN UAF report.
**Exploitability:**
**Exploitable.** An attacker who can trigger this bug can potentially exploit
it. By spraying the heap (e.g., using `io_uring` or other `kmalloc-256`
objects), the attacker can control the contents of the freed descriptor memory.
By manipulating fields like `wMaxPacketSize`, `bmAttributes`, or the endpoint
direction, the attacker can deceive the `transfer()` function in `dummy_hcd`
into performing out-of-bounds reads or writes when copying data between the URB
and the gadget's request buffers. This provides a strong primitive for privilege
escalation or information leaks.
**Accessibility & Triggers:**
* **Unprivileged / User Namespace:** **No.** Configuring USB gadgets (whether
via `configfs`, `FunctionFS`, or `raw_gadget`) requires global `CAP_SYS_ADMIN`
privileges. The USB subsystem is not user-namespace aware, meaning an
unprivileged user cannot reach this code even inside a new user namespace.
* **Peripheral / Remote Trigger:** **No.** The `dummy_hcd` driver is a virtual
host/device controller used exclusively for local testing and simulation. It
does not interface with physical hardware. Even if the underlying gadget driver
bug (failing to disable the endpoint before freeing the descriptor) applies to
real hardware UDCs (like DWC3), triggering the vulnerable state requires local
root access to configure and unbind the gadget. It cannot be triggered by simply
plugging in a malicious USB device or via network traffic.
* **Denial of Service:** **Yes.** If left unexploited, the dangling pointer
dereference will cause a kernel panic, leading to a local denial of service. Exploitable:true FilesystemTrigger:false NetworkTrigger:false PeripheralTrigger:false RemoteTrigger:false Unprivileged:false UserNamespace:false VMGuestTrigger:false VMHostTrigger:false]
|
| 1/1 |
2026/07/29 01:36 |
action |
syz-repro-to-c-repro |
0m
Results:
|
| 2/1 |
2026/07/29 01:36 |
action |
kernel-checkouter |
0m
Results: map[KernelSrc:/app/workdir/cache/src/3735123449433f9e6f9fb5b55ddd9994cbcf15f0]
|
| 3/1 |
2026/07/29 01:36 |
action |
kernel-builder |
0m
Results: map[KernelObj:/app/workdir/cache/build/36cebea7b7aa2c95c96914d8ab8316494bfc12b2]
|
| 4/1 |
2026/07/29 01:36 |
action |
codesearch-prepare |
0m
Results: map[Index:codesearch-index]
|
| 5/1 |
2026/07/29 01:37 |
agent |
expert |
8m
Model:
Results: map[DenialOfService:true Exploitable:true FilesystemTrigger:false NetworkTrigger:false PeripheralTrigger:false RemoteTrigger:false Unprivileged:false UserNamespace:false VMGuestTrigger:false VMHostTrigger:false]
Instruction: You are an experienced Linux kernel security engineer. Your task is to analyze given kernel bug report
and determine its security impact based on the following dimensions.
Use the provided tools to examine the source code, check for capability checks (e.g., capable(), ns_capable()),
and understand the nature of the bug. Analyze the given kernel build and configuration.
You can check the kernel config by grepping ".config" file; you can check kernel cmdline by grepping
".config" file for "CONFIG_CMDLINE=". Assume sysctl parameters have default values.
But analyze for the corresponding production build w/o debugging tools enabled (like KASAN, KMSAN, UBSAN).
Try different strategies when analyzing the bug:
- think of ways in which the vulnerable code is unreachable
- or the other way around: try to come up with different ideas of how an unprivileged user can reach the bug
If still unsure err on the side of the bug being non-exploitable/not-accessible.
In the final reply, provide a reasoning for your assessment.
Analysis dimensions:
* Exploitable:
Determine if the bug can result in memory corruption, elevated privileges, or an information leak.
Memory safety issues are almost always exploitable (KASAN or UBSAN reports for use-after-free, out-of-bounds;
refcounting issues, corrupted lists, etc). When kernel is crashing on a completely wild pointer access
(e.g. user-space address, or non-canonical address, but not on NULL or address corresponding to KASAN shadow
for NULL address), including both data accesses and control transfers, that also usually implies possibility
of exploitation. Such reports usually say "unable to handle kernel paging request".
Uses of uninitialized values detected by KMSAN may be exploitable b/c attacker frequently can affect uninit
values with spraying techniques. However, for these exploitability depends on how exactly the uninit value
is used in the code, and what it affects.
Information leaks are exploitable on their own and should be classified as such. A bug that copies kernel
memory contents to userspace (e.g. an out-of-bounds read whose result is returned to the caller, or
uninitialized stack/heap bytes written to a user buffer) is exploitable: it can reveal kernel pointer
values and defeat KASLR, expose sensitive data such as cryptographic keys or other processes' memory, and
serves as a necessary building block in most modern kernel privilege-escalation exploit chains. Do not classify
an information leak as non-exploitable solely because it does not directly cause a memory write or control-flow
hijack; the leak itself is the exploit primitive.
Think of what happens after the bug is triggered. Some bugs cause kernel panic and halt execution,
they are harder to exploit. For example, BUG reports halts the kernel. However, WARNING reports don't halt
execution in production builds. Debug bug detection tools (like KASAN, KMSAN, KCSAN, UBSAN) are also not enabled
in production builds, so attacker can freely exploit these bugs w/o being detected by these tools.
If you see an integer overflow, think how the overflowed value used later (if it's used as allocation size,
or an array index). If you see an out-of-bounds read, think if it's followed by an out-of-bounds write as well.
Some KCSAN data-races may be exploitable by skilled attackers as well. Think what data structures got corrupted
as the result of data races and how. However, note that kernel has lots of "benign" data races that don't lead
to any runtime misbehavior at all.
* Denial Of Service:
Determine if the bug can result in denial-of-service. Most bugs can, since they cause system crash,
hangs, deadlocks, or resource leaks. This is mostly applicable to WARNING bugs that won't cause system crash
in production. For these think what will be consequences of the violation of the kernel assumptions flagged
by the WARNING. In some cases the unexpected condition is also properly handled by the normal control flow
(e.g. with "if (WARN_ON(...))"), these won't cause denial-of-service. If the condition is not handled,
then it may or may not cause denial-of-service.
* Accessible From Unprivileged Processes:
Determine if the bug can be reached from a typical (non-root) user process that does NOT have any special capabilities
(like CAP_SYS_ADMIN, CAP_NET_ADMIN, CAP_NET_RAW, CAP_PERFMON) or access to device nodes restricted to root.
Assume that unprivileged_bpf_disabled=1, that is eBPF loading is not accessible. However, cBPF (classical BPF)
is still accessible to non-root processes.
Assume that user namespaces are not accessible, that is, the process cannot get the mentioned capabilities even
within a new user namespace (checked by ns_capable() function in the kernel sources).
* Accessible From User Namespaces:
Determine if the bug can be reached within a user-namespace where the process has all capabilities
(including CAP_SYS_ADMIN, CAP_NET_ADMIN, CAP_NET_RAW, CAP_PERFMON). Such capabilities are checked with ns_capable()
function in the kernel sources.
* VM Guest Trigger:
Determine if the bug can be triggered from the context of a typical KVM guest (e.g., set up by a QEMU VMM).
Consider accesses to standard Linux host paravirtualized features (virtio-blk, virtio-net, etc.),
and handling of VM exits in the KVM code.
* VM Host Trigger in The Confidential Computing Context:
Determine if the bug can be triggered in a confidential computing guest kernel from the context of a KVM host.
Consider access to standard Linux guest paravirtualized features (virtio-blk, virtio-net, etc.).
* Ethernet Network Trigger:
Determine if the bug can be triggered by processing ingress network Ethernet traffic, either directly (network stack)
or via drivers exposed to network data.
* Other Remote Trigger:
Determine if the bug can be triggered by processing remote traffic other than Ethernet (Wifi, Bluetooth, NFC, etc).
* Peripheral Trigger:
Determine if the bug can be triggered via an untrusted peripheral device that can be physically plugged
into a system, such as a USB device or a niche hardware driver handling external hardware inputs.
This is particularly important for mobile and desktop environments where users can plug in unknown devices.
* Malicious Filesystem Trigger:
Determine if the bug can be triggered by the kernel mounting and parsing a malicious filesystem image.
This is highly critical for Desktop and Mobile environments where external media or downloaded images
might be auto-mounted.
Don't make assumptions about the kernel source code (it may be different from what you assume it is).
Extensively use the provided code access tools (codesearch-*, git-*, grepper, etc)
to examine the actual source code, and confirm any assumptions.
Prefer calling several tools at the same time to save round-trips.
Use set-results tool to provide results of the analysis.
It must be called exactly once before the final reply.
Ignore results of this tool.
Prompt:
The kernel bug report is:
==================================================================
BUG: KASAN: slab-use-after-free in find_endpoint+0x2a3/0x2c0 drivers/usb/gadget/udc/dummy_hcd.c:1618
Read of size 1 at addr ffff8880359f2912 by task kworker/0:5/18466
CPU: 0 UID: 0 PID: 18466 Comm: kworker/0:5 Tainted: G L syzkaller #0 PREEMPT(full)
Tainted: [L]=SOFTLOCKUP
Hardware name: Google Google Compute Engine/Google Compute Engine, BIOS Google 07/16/2026
Workqueue: events request_firmware_work_func
Call Trace:
<IRQ>
__dump_stack lib/dump_stack.c:94 [inline]
dump_stack_lvl+0x100/0x190 lib/dump_stack.c:120
print_address_description mm/kasan/report.c:378 [inline]
print_report+0x13d/0x4b0 mm/kasan/report.c:482
kasan_report+0xdf/0x1c0 mm/kasan/report.c:595
find_endpoint+0x2a3/0x2c0 drivers/usb/gadget/udc/dummy_hcd.c:1618
dummy_timer+0x500/0x36f0 drivers/usb/gadget/udc/dummy_hcd.c:1890
__run_hrtimer kernel/time/hrtimer.c:2032 [inline]
__hrtimer_run_queues+0x462/0x9c0 kernel/time/hrtimer.c:2096
hrtimer_run_softirq+0x17d/0x2c0 kernel/time/hrtimer.c:2113
handle_softirqs+0x1ea/0x9b0 kernel/softirq.c:622
__do_softirq kernel/softirq.c:656 [inline]
invoke_softirq kernel/softirq.c:496 [inline]
__irq_exit_rcu+0x162/0x210 kernel/softirq.c:735
irq_exit_rcu+0x9/0x30 kernel/softirq.c:752
instr_sysvec_apic_timer_interrupt arch/x86/kernel/apic/apic.c:1062 [inline]
sysvec_apic_timer_interrupt+0xa3/0xc0 arch/x86/kernel/apic/apic.c:1062
</IRQ>
<TASK>
asm_sysvec_apic_timer_interrupt+0x1a/0x20 arch/x86/include/asm/idtentry.h:674
RIP: 0010:__orc_find+0xae/0xf0 arch/x86/kernel/unwind_orc.c:111
Code: 63 13 48 01 da 49 39 d5 73 af 4c 8d 63 fc 49 39 ec 73 b2 4d 29 f7 49 c1 ff 02 4b 8d 14 7f 48 8d 04 50 48 83 c4 08 5b 5d 41 5c <41> 5d 41 5e 41 5f e9 87 f5 05 0a 48 83 c4 08 31 c0 5b 5d 41 5c 41
RSP: 0018:ffffc900041ef628 EFLAGS: 00000292
RAX: ffffffff92308310 RBX: 0000000000000001 RCX: dffffc0000000000
RDX: 000000000000000f RSI: 0000000000000000 RDI: ffffffff91836d10
RBP: ffffc900041ef700 R08: ffffffff92308340 R09: 0000000000000007
R10: 0000000000000200 R11: 00000000000680ae R12: ffffc900041ef708
R13: ffffffff8780ae85 R14: ffffffff91836d10 R15: 0000000000000005
orc_find arch/x86/kernel/unwind_orc.c:238 [inline]
unwind_next_frame+0x2ec/0x2090 arch/x86/kernel/unwind_orc.c:510
arch_stack_walk+0x94/0xf0 arch/x86/kernel/stacktrace.c:25
stack_trace_save+0x8e/0xc0 kernel/stacktrace.c:122
kasan_save_stack+0x30/0x50 mm/kasan/common.c:57
kasan_save_track+0x14/0x30 mm/kasan/common.c:78
poison_kmalloc_redzone mm/kasan/common.c:398 [inline]
__kasan_kmalloc+0xaa/0xb0 mm/kasan/common.c:415
kasan_kmalloc include/linux/kasan.h:263 [inline]
__do_kmalloc_node mm/slub.c:5334 [inline]
__kmalloc_noprof+0x322/0x820 mm/slub.c:5359
_kmalloc_noprof include/linux/slab.h:992 [inline]
usb_alloc_urb+0x66/0xa0 drivers/usb/core/urb.c:75
ath9k_hif_usb_alloc_rx_urbs drivers/net/wireless/ath/ath9k/hif_usb.c:897 [inline]
ath9k_hif_usb_alloc_urbs+0x5e4/0xfa0 drivers/net/wireless/ath/ath9k/hif_usb.c:1034
ath9k_hif_usb_dev_init drivers/net/wireless/ath/ath9k/hif_usb.c:1124 [inline]
ath9k_hif_usb_firmware_cb+0x146/0x530 drivers/net/wireless/ath/ath9k/hif_usb.c:1252
request_firmware_work_func+0x13f/0x440 drivers/base/firmware_loader/main.c:1164
process_one_work+0xa23/0x1940 kernel/workqueue.c:3322
process_scheduled_works kernel/workqueue.c:3405 [inline]
worker_thread+0x5ef/0xe50 kernel/workqueue.c:3486
kthread+0x370/0x450 kernel/kthread.c:436
ret_from_fork+0x72b/0xd50 arch/x86/kernel/process.c:158
ret_from_fork_asm+0x1a/0x30 arch/x86/entry/entry_64.S:245
</TASK>
Allocated by task 29727:
kasan_save_stack+0x30/0x50 mm/kasan/common.c:57
kasan_save_track+0x14/0x30 mm/kasan/common.c:78
unpoison_slab_object mm/kasan/common.c:340 [inline]
__kasan_slab_alloc+0x89/0x90 mm/kasan/common.c:366
kasan_slab_alloc include/linux/kasan.h:253 [inline]
slab_post_alloc_hook mm/slub.c:4584 [inline]
kmem_cache_alloc_bulk_noprof+0x352/0x890 mm/slub.c:7435
__io_alloc_req_refill+0x9e/0x330 io_uring/io_uring.c:988
io_alloc_req io_uring/io_uring.h:536 [inline]
io_submit_sqes.cold+0xf6/0x301 io_uring/io_uring.c:2046
__do_sys_io_uring_enter+0xa88/0x1b10 io_uring/io_uring.c:2646
do_syscall_x64 arch/x86/entry/syscall_64.c:63 [inline]
do_syscall_64+0x115/0x870 arch/x86/entry/syscall_64.c:94
entry_SYSCALL_64_after_hwframe+0x77/0x7f
Freed by task 27397:
kasan_save_stack+0x30/0x50 mm/kasan/common.c:57
kasan_save_track+0x14/0x30 mm/kasan/common.c:78
kasan_save_free_info+0x3b/0x70 mm/kasan/generic.c:584
poison_slab_object mm/kasan/common.c:253 [inline]
__kasan_slab_free+0x5f/0x80 mm/kasan/common.c:285
kasan_slab_free include/linux/kasan.h:235 [inline]
slab_free_hook mm/slub.c:2677 [inline]
slab_free_after_rcu_debug+0xdf/0x170 mm/slub.c:6439
rcu_do_batch kernel/rcu/tree.c:2645 [inline]
rcu_core+0x5a2/0x10d0 kernel/rcu/tree.c:2897
handle_softirqs+0x1ea/0x9b0 kernel/softirq.c:622
do_softirq kernel/softirq.c:523 [inline]
do_softirq+0xac/0xe0 kernel/softirq.c:510
__local_bh_enable_ip+0xf8/0x120 kernel/softirq.c:450
local_bh_enable include/linux/bottom_half.h:33 [inline]
__alloc_skb+0x5db/0x710 net/core/skbuff.c:699
alloc_skb include/linux/skbuff.h:1384 [inline]
alloc_uevent_skb+0x7d/0x210 lib/kobject_uevent.c:289
uevent_net_broadcast_untagged lib/kobject_uevent.c:326 [inline]
kobject_uevent_net_broadcast lib/kobject_uevent.c:410 [inline]
kobject_uevent_env+0xd2d/0x18b0 lib/kobject_uevent.c:611
device_del+0x605/0x9b0 drivers/base/core.c:3979
device_unregister+0x1d/0xe0 drivers/base/core.c:4002
hci_conn_del_sysfs+0xb9/0x1a0 net/bluetooth/hci_sysfs.c:79
hci_conn_cleanup net/bluetooth/hci_conn.c:170 [inline]
hci_conn_del+0x506/0x1180 net/bluetooth/hci_conn.c:1306
hci_abort_conn_sync+0x7d9/0xb20 net/bluetooth/hci_sync.c:5850
abort_conn_sync+0x142/0x350 net/bluetooth/hci_conn.c:3163
hci_cmd_sync_work+0x1c0/0x470 net/bluetooth/hci_sync.c:332
process_one_work+0xa23/0x1940 kernel/workqueue.c:3322
process_scheduled_works kernel/workqueue.c:3405 [inline]
worker_thread+0x5ef/0xe50 kernel/workqueue.c:3486
kthread+0x370/0x450 kernel/kthread.c:436
ret_from_fork+0x72b/0xd50 arch/x86/kernel/process.c:158
ret_from_fork_asm+0x1a/0x30 arch/x86/entry/entry_64.S:245
Last potentially related work creation:
kasan_save_stack+0x30/0x50 mm/kasan/common.c:57
kasan_record_aux_stack+0xa7/0xc0 mm/kasan/generic.c:556
slab_free_hook mm/slub.c:2638 [inline]
slab_free mm/slub.c:6377 [inline]
kmem_cache_free+0x427/0x6b0 mm/slub.c:6504
__io_req_caches_free+0x1a6/0x220 io_uring/io_uring.c:2148
io_req_caches_free io_uring/io_uring.c:2160 [inline]
io_ring_exit_work+0x42a/0xd7a io_uring/io_uring.c:2363
process_one_work+0xa23/0x1940 kernel/workqueue.c:3322
process_scheduled_works kernel/workqueue.c:3405 [inline]
worker_thread+0x5ef/0xe50 kernel/workqueue.c:3486
kthread+0x370/0x450 kernel/kthread.c:436
ret_from_fork+0x72b/0xd50 arch/x86/kernel/process.c:158
ret_from_fork_asm+0x1a/0x30 arch/x86/entry/entry_64.S:245
The buggy address belongs to the object at ffff8880359f28c0
which belongs to the cache io_kiocb of size 248
The buggy address is located 82 bytes inside of
freed 248-byte region [ffff8880359f28c0, ffff8880359f29b8)
The buggy address belongs to the physical page:
page: refcount:0 mapcount:0 mapping:0000000000000000 index:0x0 pfn:0x359f2
memcg:ffff8880359f2f01
flags: 0xfff00000000000(node=0|zone=1|lastcpupid=0x7ff)
page_type: f5(slab)
raw: 00fff00000000000 ffff888142ef6c80 dead000000000100 dead000000000122
raw: 0000000000000000 00000008000c000c 00000000f5000000 ffff8880359f2f01
page dumped because: kasan: bad access detected
page_owner tracks the page as allocated
page last allocated via order 0, migratetype Unmovable, gfp_mask 0x52cc0(GFP_KERNEL|__GFP_NOWARN|__GFP_NORETRY|__GFP_COMP), pid 29727, tgid 29719 (syz.0.6860), ts 1428916787567, free_ts 1428553365645
set_page_owner include/linux/page_owner.h:32 [inline]
post_alloc_hook+0xfd/0x120 mm/page_alloc.c:1859
prep_new_page mm/page_alloc.c:1867 [inline]
get_page_from_freelist+0xf48/0x3530 mm/page_alloc.c:3946
__alloc_frozen_pages_noprof+0x299/0x2dc0 mm/page_alloc.c:5304
alloc_slab_page mm/slub.c:3266 [inline]
allocate_slab mm/slub.c:3380 [inline]
new_slab+0xa2/0x640 mm/slub.c:3426
refill_objects+0xe3/0x410 mm/slub.c:7310
__kmem_cache_alloc_bulk+0x3c/0x120 mm/slub.c:7346
kmem_cache_alloc_bulk_noprof+0x48b/0x890 mm/slub.c:7413
__io_alloc_req_refill+0x9e/0x330 io_uring/io_uring.c:988
io_alloc_req io_uring/io_uring.h:536 [inline]
io_submit_sqes.cold+0xf6/0x301 io_uring/io_uring.c:2046
__do_sys_io_uring_enter+0xa88/0x1b10 io_uring/io_uring.c:2646
do_syscall_x64 arch/x86/entry/syscall_64.c:63 [inline]
do_syscall_64+0x115/0x870 arch/x86/entry/syscall_64.c:94
entry_SYSCALL_64_after_hwframe+0x77/0x7f
page last free pid 29721 tgid 29719 stack trace:
reset_page_owner include/linux/page_owner.h:25 [inline]
__free_pages_prepare mm/page_alloc.c:1406 [inline]
__free_frozen_pages+0x79f/0x1090 mm/page_alloc.c:2950
__pagetable_free include/linux/mm.h:3695 [inline]
pagetable_free include/linux/mm.h:3719 [inline]
pagetable_dtor_free include/linux/mm.h:3818 [inline]
__tlb_remove_table include/asm-generic/tlb.h:221 [inline]
__tlb_remove_table_free mm/mmu_gather.c:228 [inline]
tlb_remove_table_rcu+0x2cf/0x380 mm/mmu_gather.c:291
rcu_do_batch kernel/rcu/tree.c:2645 [inline]
rcu_core+0x5a2/0x10d0 kernel/rcu/tree.c:2897
handle_softirqs+0x1ea/0x9b0 kernel/softirq.c:622
__do_softirq kernel/softirq.c:656 [inline]
invoke_softirq kernel/softirq.c:496 [inline]
__irq_exit_rcu+0x162/0x210 kernel/softirq.c:735
irq_exit_rcu+0x9/0x30 kernel/softirq.c:752
instr_sysvec_apic_timer_interrupt arch/x86/kernel/apic/apic.c:1062 [inline]
sysvec_apic_timer_interrupt+0xa3/0xc0 arch/x86/kernel/apic/apic.c:1062
asm_sysvec_apic_timer_interrupt+0x1a/0x20 arch/x86/include/asm/idtentry.h:674
Memory state around the buggy address:
ffff8880359f2800: fb fb fb fb fb fb fb fb fb fb fb fb fb fb fb fc
ffff8880359f2880: fc fc fc fc fc fc fc fc fa fb fb fb fb fb fb fb
>ffff8880359f2900: fb fb fb fb fb fb fb fb fb fb fb fb fb fb fb fb
^
ffff8880359f2980: fb fb fb fb fb fb fb fc fc fc fc fc fc fc fc fc
ffff8880359f2a00: fa fb fb fb fb fb fb fb fb fb fb fb fb fb fb fb
==================================================================
----------------
Code disassembly (best guess):
0: 63 13 movsxd (%rbx),%edx
2: 48 01 da add %rbx,%rdx
5: 49 39 d5 cmp %rdx,%r13
8: 73 af jae 0xffffffb9
a: 4c 8d 63 fc lea -0x4(%rbx),%r12
e: 49 39 ec cmp %rbp,%r12
11: 73 b2 jae 0xffffffc5
13: 4d 29 f7 sub %r14,%r15
16: 49 c1 ff 02 sar $0x2,%r15
1a: 4b 8d 14 7f lea (%r15,%r15,2),%rdx
1e: 48 8d 04 50 lea (%rax,%rdx,2),%rax
22: 48 83 c4 08 add $0x8,%rsp
26: 5b pop %rbx
27: 5d pop %rbp
28: 41 5c pop %r12
* 2a: 41 5d pop %r13 <-- trapping instruction
2c: 41 5e pop %r14
2e: 41 5f pop %r15
30: e9 87 f5 05 0a jmp 0xa05f5bc
35: 48 83 c4 08 add $0x8,%rsp
39: 31 c0 xor %eax,%eax
3b: 5b pop %rbx
3c: 5d pop %rbp
3d: 41 5c pop %r12
3f: 41 rex.B
Reply: Based on the analysis of the bug report and the kernel source code, here is the assessment of the vulnerability:
**Root Cause & Bug Mechanism:**
The bug is a Slab-Use-After-Free (UAF) in the `dummy_hcd` driver (a software-only USB controller used for testing). The crash occurs in `find_endpoint()` when it attempts to read `ep->desc->bEndpointAddress`.
The `ep->desc` pointer is supposed to point to a valid `usb_endpoint_descriptor` provided by a USB gadget driver (such as `FunctionFS` or `raw_gadget`). However, the gadget driver freed the memory containing this descriptor without properly disabling the endpoint via `usb_ep_disable()`. As a result, `dummy_hcd` retains a dangling pointer.
In this specific crash, the freed memory was subsequently reallocated by the `io_uring` subsystem as an `io_kiocb` object (size 248 bytes, likely from a merged `kmalloc-256` cache), which was then freed again. When the `dummy_timer` softirq fires, it iterates over the endpoints and dereferences the dangling `ep->desc` pointer, triggering the KASAN UAF report.
**Exploitability:**
**Exploitable.** An attacker who can trigger this bug can potentially exploit it. By spraying the heap (e.g., using `io_uring` or other `kmalloc-256` objects), the attacker can control the contents of the freed descriptor memory. By manipulating fields like `wMaxPacketSize`, `bmAttributes`, or the endpoint direction, the attacker can deceive the `transfer()` function in `dummy_hcd` into performing out-of-bounds reads or writes when copying data between the URB and the gadget's request buffers. This provides a strong primitive for privilege escalation or information leaks.
**Accessibility & Triggers:**
* **Unprivileged / User Namespace:** **No.** Configuring USB gadgets (whether via `configfs`, `FunctionFS`, or `raw_gadget`) requires global `CAP_SYS_ADMIN` privileges. The USB subsystem is not user-namespace aware, meaning an unprivileged user cannot reach this code even inside a new user namespace.
* **Peripheral / Remote Trigger:** **No.** The `dummy_hcd` driver is a virtual host/device controller used exclusively for local testing and simulation. It does not interface with physical hardware. Even if the underlying gadget driver bug (failing to disable the endpoint before freeing the descriptor) applies to real hardware UDCs (like DWC3), triggering the vulnerable state requires local root access to configure and unbind the gadget. It cannot be triggered by simply plugging in a malicious USB device or via network traffic.
* **Denial of Service:** **Yes.** If left unexploited, the dangling pointer dereference will cause a kernel panic, leading to a local denial of service.
|
| 6/2 |
2026/07/29 01:37 |
llm |
expert |
0m
Model:
Tokens:
input: 11726
output: 45
thoughts: 1022
Thoughts: **Analyzing a KASAN Slab-Use-After-Free in the Dummy USB Gadget Driver**
Okay, here's what I'm seeing. This bug report points to a KASAN slab-use-after-free vulnerability. The culprit is the `find_endpoint` function within `drivers/usb/gadget/udc/dummy_hcd.c`, specifically at line 1618. The read attempt is of size 1 at memory address `ffff8880359f2912`.
The provided stack trace gives me the usual suspects: `find_endpoint`, followed by `dummy_timer`, eventually leading to `handle_softirqs`. This is a pretty common flow for USB gadget drivers. The `hrtimer` context is interesting, indicating a timing issue possibly related to how the gadget's state changes.
Now, the object in question was initially allocated by task 29727, related to `io_uring` requests, specifically through `io_submit_sqes` and `__do_sys_io_uring_enter`, which points to the `io_kiocb` cache. My initial thought is: why would `find_endpoint` in the `dummy_hcd` driver, which is USB-specific, be poking around an `io_kiocb` object? That seems like an odd coupling, but `io_kiocb` is a slab cache, so it's possible it was re-purposed.
Then I see the allocation trace: it was allocated through `__io_alloc_req_refill`. And now the free trace: It's being freed through `slab_free_after_rcu_debug`, which is called from a RCU callback, making this a classic RCU-related use-after-free, meaning the RCU grace period might not be respected. The interesting detail is that this function then leads to `__alloc_skb`, and not directly `kmem_cache_free`. This is strange, because `__alloc_skb` allocates the `sk_buff` struct, which is generally related to networking.
Ok, wait, here's the kicker, the object was an `io_kiocb`, and the last thing potentially related is `__io_req_caches_free`. Now it makes a bit more sense. Somehow, a freed `io_kiocb` object is being accessed by a USB driver function. This is clearly a case of memory corruption across disparate kernel subsystems, with a RCU synchronization issue involved. Let me check the code for `find_endpoint` in `drivers/usb/gadget/udc/dummy_hcd.c`. I have a feeling the logic there doesn't properly handle these objects.
|
| 7/2 |
2026/07/29 01:37 |
tool |
read-file |
0m
Args: map[File:drivers/usb/gadget/udc/dummy_hcd.c FirstLine:1600 LineCount:50]
Results: map[Contents:1600: == (USB_PORT_STAT_CONNECTION | USB_PORT_STAT_ENABLE))
1601:
1602: static struct dummy_ep *find_endpoint(struct dummy *dum, u8 address)
1603: {
1604: int i;
1605:
1606: if (!is_active((dum->gadget.speed == USB_SPEED_SUPER ?
1607: dum->ss_hcd : dum->hs_hcd)))
1608: return NULL;
1609: if (!dum->ints_enabled)
1610: return NULL;
1611: if ((address & ~USB_DIR_IN) == 0)
1612: return &dum->ep[0];
1613: for (i = 1; i < DUMMY_ENDPOINTS; i++) {
1614: struct dummy_ep *ep = &dum->ep[i];
1615:
1616: if (!ep->desc)
1617: continue;
1618: if (ep->desc->bEndpointAddress == address)
1619: return ep;
1620: }
1621: return NULL;
1622: }
1623:
1624: #undef is_active
1625:
1626: #define Dev_Request (USB_TYPE_STANDARD | USB_RECIP_DEVICE)
1627: #define Dev_InRequest (Dev_Request | USB_DIR_IN)
1628: #define Intf_Request (USB_TYPE_STANDARD | USB_RECIP_INTERFACE)
1629: #define Intf_InRequest (Intf_Request | USB_DIR_IN)
1630: #define Ep_Request (USB_TYPE_STANDARD | USB_RECIP_ENDPOINT)
1631: #define Ep_InRequest (Ep_Request | USB_DIR_IN)
1632:
1633:
1634: /**
1635: * handle_control_request() - handles all control transfers
1636: * @dum_hcd: pointer to dummy (the_controller)
1637: * @urb: the urb request to handle
1638: * @setup: pointer to the setup data for a USB device control
1639: * request
1640: * @status: pointer to request handling status
1641: *
1642: * Return 0 - if the request was handled
1643: * 1 - if the request wasn't handles
1644: * error code on error
1645: */
1646: static int handle_control_request(struct dummy_hcd *dum_hcd, struct urb *urb,
1647: struct usb_ctrlrequest *setup,
1648: int *status)
1649: {
]
|
| 8/2 |
2026/07/29 01:37 |
llm |
expert |
0m
Model:
Tokens:
input: 14177
output: 70
thoughts: 82
Thoughts: **Debugging a Potential USB Endpoint Memory Issue**
Okay, here's what I'm thinking. We've got a crash, and it's happening right here, line 1618: `if (ep->desc->bEndpointAddress == address)`. The problem is likely with the read of `bEndpointAddress`. That's a single byte, so a pretty basic read, but it's clearly triggering something nasty. I see `ep->desc` is a pointer to `struct usb_endpoint_descriptor`, which is important. The alarm bells are going off... is `ep->desc` pointing to memory that's already been freed? That would explain everything. Now, the next step is crucial, I need to trace back where `ep->desc` is assigned in the code to confirm my suspicion. Let's find the source of `ep->desc`.
|
| 9/2 |
2026/07/29 01:37 |
tool |
codesearch-find-references |
0m
Error:
Args: map[ContextFile:drivers/usb/gadget/udc/dummy_hcd.c IncludeSnippetLines:1 Name:desc SourceTreePrefix:drivers/usb/gadget/udc/dummy_hcd.c]
Results: map[References:<nil> TruncatedOutput:false]
|
| 10/2 |
2026/07/29 01:37 |
llm |
expert |
0m
Model:
Tokens:
input: 14535
output: 74
thoughts: 15
Thoughts: Okay, here's my summary, tailored as if I'm an expert in the field thinking through this:
**Deep Dive on Struct Dummy_ep and the 'desc' Field**
Alright, let's see. My mind immediately flags this as a bit of low-level data structure contemplation. So, `desc` is a field within the `struct dummy_ep`. The naming convention "dummy_ep" already hints at some sort of placeholder, endpoint, or perhaps a structure used for emulation or testing. I need more context to be certain, but the "dummy" part suggests it may not be a core component.
Now, what is `desc`? The name is quite generic... "description," maybe? It could be a simple string, a pointer to a string, or even a more complex structure containing various descriptive attributes. The critical questions are: what data type is `desc`, how is it used, and importantly, is it *read* by any critical code paths? The implications could range from being a debugging aid, to essential identifying metadata. If `desc` is used for lookup or comparison, itβs vital for performance and correctness. If it's merely informational for logging or debugging, the nature of the data type is less important.
I'm now thinking about memory management; is it statically allocated, dynamically allocated, or perhaps a member of a union? Knowing this would influence how the memory for `desc` is handled, and potential pitfalls like buffer overflows or memory leaks.
Also, the size of this `struct dummy_ep` is significant, particularly if there are many instances. The size of `desc` directly contributes to that. I need to understand its impact.
Ultimately, I need more details to fully assess its implications, but this is my initial mental map of its importance.
|
| 11/2 |
2026/07/29 01:37 |
tool |
codesearch-find-references |
0m
Args: map[ContextFile:drivers/usb/gadget/udc/dummy_hcd.c IncludeSnippetLines:1 Name:dummy_ep::desc SourceTreePrefix:drivers/usb/gadget/udc/dummy_hcd.c]
Results: map[References:[map[ReferenceKind:reads ReferencingEntityKind:function ReferencingEntityName:dummy_disable SourceFile:drivers/usb/gadget/udc/dummy_hcd.c SourceLine:671 SourceSnippet: 670: ep = usb_ep_to_dummy_ep(_ep);
671: if (!_ep || !ep->desc || _ep->name == ep0name)
672: return -EINVAL;
] map[ReferenceKind:writes ReferencingEntityKind:function ReferencingEntityName:dummy_disable SourceFile:drivers/usb/gadget/udc/dummy_hcd.c SourceLine:676 SourceSnippet: 675: spin_lock_irqsave(&dum->lock, flags);
676: ep->desc = NULL;
677: ep->stream_en = 0;
] map[ReferenceKind:reads ReferencingEntityKind:function ReferencingEntityName:dummy_enable SourceFile:drivers/usb/gadget/udc/dummy_hcd.c SourceLine:537 SourceSnippet: 536: ep = usb_ep_to_dummy_ep(_ep);
537: if (!_ep || !desc || ep->desc || _ep->name == ep0name
538: || desc->bDescriptorType != USB_DT_ENDPOINT)
] map[ReferenceKind:writes ReferencingEntityKind:function ReferencingEntityName:dummy_enable SourceFile:drivers/usb/gadget/udc/dummy_hcd.c SourceLine:646 SourceSnippet: 645: }
646: ep->desc = desc;
647:
] map[ReferenceKind:reads ReferencingEntityKind:function ReferencingEntityName:dummy_queue SourceFile:drivers/usb/gadget/udc/dummy_hcd.c SourceLine:732 SourceSnippet: 731: ep = usb_ep_to_dummy_ep(_ep);
732: if (!_ep || (!ep->desc && _ep->name != ep0name))
733: return -EINVAL;
] map[ReferenceKind:reads ReferencingEntityKind:function ReferencingEntityName:dummy_queue SourceFile:drivers/usb/gadget/udc/dummy_hcd.c SourceLine:749 SourceSnippet: 748: /* implement an emulated single-request FIFO */
749: if (ep->desc && (ep->desc->bEndpointAddress & USB_DIR_IN) &&
750: !dum->fifo_req_busy &&
] map[ReferenceKind:reads ReferencingEntityKind:function ReferencingEntityName:dummy_queue SourceFile:drivers/usb/gadget/udc/dummy_hcd.c SourceLine:749 SourceSnippet: 748: /* implement an emulated single-request FIFO */
749: if (ep->desc && (ep->desc->bEndpointAddress & USB_DIR_IN) &&
750: !dum->fifo_req_busy &&
] map[ReferenceKind:reads ReferencingEntityKind:function ReferencingEntityName:dummy_set_halt_and_wedge SourceFile:drivers/usb/gadget/udc/dummy_hcd.c SourceLine:828 SourceSnippet: 827: ep->halted = ep->wedged = 0;
828: else if (ep->desc && (ep->desc->bEndpointAddress & USB_DIR_IN) &&
829: !list_empty(&ep->queue))
] map[ReferenceKind:reads ReferencingEntityKind:function ReferencingEntityName:dummy_set_halt_and_wedge SourceFile:drivers/usb/gadget/udc/dummy_hcd.c SourceLine:828 SourceSnippet: 827: ep->halted = ep->wedged = 0;
828: else if (ep->desc && (ep->desc->bEndpointAddress & USB_DIR_IN) &&
829: !list_empty(&ep->queue))
] map[ReferenceKind:reads ReferencingEntityKind:function ReferencingEntityName:find_endpoint SourceFile:drivers/usb/gadget/udc/dummy_hcd.c SourceLine:1616 SourceSnippet:1615:
1616: if (!ep->desc)
1617: continue;
] map[ReferenceKind:reads ReferencingEntityKind:function ReferencingEntityName:find_endpoint SourceFile:drivers/usb/gadget/udc/dummy_hcd.c SourceLine:1618 SourceSnippet:1617: continue;
1618: if (ep->desc->bEndpointAddress == address)
1619: return ep;
] map[ReferenceKind:writes ReferencingEntityKind:function ReferencingEntityName:init_dummy_udc_hw SourceFile:drivers/usb/gadget/udc/dummy_hcd.c SourceLine:1088 SourceSnippet:1087: ep->gadget = &dum->gadget;
1088: ep->desc = NULL;
1089: INIT_LIST_HEAD(&ep->queue);
] map[ReferenceKind:reads ReferencingEntityKind:function ReferencingEntityName:periodic_bytes SourceFile:drivers/usb/gadget/udc/dummy_hcd.c SourceLine:1575 SourceSnippet:1574: /* high bandwidth mode */
1575: tmp = usb_endpoint_maxp_mult(ep->desc);
1576: tmp *= 8 /* applies to entire frame */;
] map[ReferenceKind:reads ReferencingEntityKind:function ReferencingEntityName:periodic_bytes SourceFile:drivers/usb/gadget/udc/dummy_hcd.c SourceLine:1580 SourceSnippet:1579: if (dum->gadget.speed == USB_SPEED_SUPER) {
1580: switch (usb_endpoint_type(ep->desc)) {
1581: case USB_ENDPOINT_XFER_ISOC:
]] TruncatedOutput:false]
|
| 12/2 |
2026/07/29 01:37 |
llm |
expert |
1m
Model:
Tokens:
input: 16655
output: 43
thoughts: 54
Thoughts: Okay, here's my take on those thoughts, framed as an expert's internal monologue:
**Endpoint Descriptor Mystery**
Alright, let's break this down. In `dummy_enable`, the code assigns `ep->desc` to the value of `desc`. Simple enough, the endpoint descriptor pointer gets set. The key question, as always, is *where* `desc` actually comes from. I know `desc` is a `struct usb_endpoint_descriptor`, so I'm already visualizing that data structure in my head. But without context, the origins of `desc` in `dummy_enable` are critical to understanding the entire flow. The devil's always in the details with USB and endpoint configuration. I need to trace back from this assignment to understand how the endpoint is configured.
|
| 13/2 |
2026/07/29 01:38 |
tool |
codesearch-definition-source |
0m
Args: map[ContextFile:drivers/usb/gadget/udc/dummy_hcd.c Name:dummy_enable]
Results: map[SourceCode: 527: static int dummy_enable(struct usb_ep *_ep,
528: const struct usb_endpoint_descriptor *desc)
529: {
530: struct dummy *dum;
531: struct dummy_hcd *dum_hcd;
532: struct dummy_ep *ep;
533: unsigned max;
534: int retval;
535:
536: ep = usb_ep_to_dummy_ep(_ep);
537: if (!_ep || !desc || ep->desc || _ep->name == ep0name
538: || desc->bDescriptorType != USB_DT_ENDPOINT)
539: return -EINVAL;
540: dum = ep_to_dummy(ep);
541: if (!dum->driver)
542: return -ESHUTDOWN;
543:
544: dum_hcd = gadget_to_dummy_hcd(&dum->gadget);
545: if (!is_enabled(dum_hcd))
546: return -ESHUTDOWN;
547:
548: /*
549: * For HS/FS devices only bits 0..10 of the wMaxPacketSize represent the
550: * maximum packet size.
551: * For SS devices the wMaxPacketSize is limited by 1024.
552: */
553: max = usb_endpoint_maxp(desc);
554:
555: /* drivers must not request bad settings, since lower levels
556: * (hardware or its drivers) may not check. some endpoints
557: * can't do iso, many have maxpacket limitations, etc.
558: *
559: * since this "hardware" driver is here to help debugging, we
560: * have some extra sanity checks. (there could be more though,
561: * especially for "ep9out" style fixed function ones.)
562: */
563: retval = -EINVAL;
564: switch (usb_endpoint_type(desc)) {
565: case USB_ENDPOINT_XFER_BULK:
566: if (strstr(ep->ep.name, "-iso")
567: || strstr(ep->ep.name, "-int")) {
568: goto done;
569: }
570: switch (dum->gadget.speed) {
571: case USB_SPEED_SUPER:
572: if (max == 1024)
573: break;
574: goto done;
575: case USB_SPEED_HIGH:
576: if (max == 512)
577: break;
578: goto done;
579: case USB_SPEED_FULL:
580: if (max == 8 || max == 16 || max == 32 || max == 64)
581: /* we'll fake any legal size */
582: break;
583: /* save a return statement */
584: fallthrough;
585: default:
586: goto done;
587: }
588: break;
589: case USB_ENDPOINT_XFER_INT:
590: if (strstr(ep->ep.name, "-iso")) /* bulk is ok */
591: goto done;
592: /* real hardware might not handle all packet sizes */
593: switch (dum->gadget.speed) {
594: case USB_SPEED_SUPER:
595: case USB_SPEED_HIGH:
596: if (max <= 1024)
597: break;
598: /* save a return statement */
599: fallthrough;
600: case USB_SPEED_FULL:
601: if (max <= 64)
602: break;
603: /* save a return statement */
604: fallthrough;
605: default:
606: if (max <= 8)
607: break;
608: goto done;
609: }
610: break;
611: case USB_ENDPOINT_XFER_ISOC:
612: if (strstr(ep->ep.name, "-bulk")
613: || strstr(ep->ep.name, "-int"))
614: goto done;
615: /* real hardware might not handle all packet sizes */
616: switch (dum->gadget.speed) {
617: case USB_SPEED_SUPER:
618: case USB_SPEED_HIGH:
619: if (max <= 1024)
620: break;
621: /* save a return statement */
622: fallthrough;
623: case USB_SPEED_FULL:
624: if (max <= 1023)
625: break;
626: /* save a return statement */
627: fallthrough;
628: default:
629: goto done;
630: }
631: break;
632: default:
633: /* few chips support control except on ep0 */
634: goto done;
635: }
636:
637: _ep->maxpacket = max;
638: if (usb_ss_max_streams(_ep->comp_desc)) {
639: if (!usb_endpoint_xfer_bulk(desc)) {
640: dev_err(udc_dev(dum), "Can't enable stream support on "
641: "non-bulk ep %s\n", _ep->name);
642: return -EINVAL;
643: }
644: ep->stream_en = 1;
645: }
646: ep->desc = desc;
647:
648: dev_dbg(udc_dev(dum), "enabled %s (ep%d%s-%s) maxpacket %d stream %s\n",
649: _ep->name,
650: usb_endpoint_num(desc),
651: (desc->bEndpointAddress & USB_DIR_IN) ? "in" : "out",
652: usb_ep_type_string(usb_endpoint_type(desc)),
653: max, str_enabled_disabled(ep->stream_en));
654:
655: /* at this point real hardware should be NAKing transfers
656: * to that endpoint, until a buffer is queued to it.
657: */
658: ep->halted = ep->wedged = 0;
659: retval = 0;
660: done:
661: return retval;
662: }
SourceFile:drivers/usb/gadget/udc/dummy_hcd.c]
|
| 14/2 |
2026/07/29 01:38 |
llm |
expert |
0m
Model:
Tokens:
input: 18911
output: 19
thoughts: 2861
Thoughts: **Analyzing a USB Gadget Driver Memory Corruption**
Alright, let's break this down. `dummy_enable` is called through `usb_ep_enable`, and that `desc` pointer comes from the caller. It's usually a gadget driver. The driver either allocates or uses a static descriptor. The crux is, what happens if the driver frees `desc` while the endpoint is still active, or even disables the endpoint but `dummy_hcd` is still trying to access it? Specifically, if the driver *doesn't* call `usb_ep_disable` after freeing, `ep->desc` is left dangling.
The bug report mentions the freed memory was allocated by `io_uring` and freed by `__alloc_skb`. The allocation trace is from `__io_alloc_req_refill`, and I see the size of the `io_kiocb` as 248 bytes. The buggy address is 82 bytes into this 248-byte region. That's strange. If `ep->desc` points to this region, it suggests `ep->desc` is now pointing to a cached `io_kiocb`. But isn't `ep->desc` a `struct usb_endpoint_descriptor`, only 7 bytes long? Why would it be allocated from this particular cache?
Maybe the gadget driver allocated a larger struct containing the descriptor. Or, the pointer is completely corrupted, and `ep->desc` is just pointing at garbage. Let's check if `ep` itself is corrupted because it is just `&dum->ep[i]`. So `ep->desc` might have been overwritten by a use-after-free or out-of-bounds write *in* `dum->ep[i]`, or the gadget driver allocated the `desc` with kmalloc, and it happened to be in a slab that was later merged with the `io_kiocb` cache.
Now, `io_kiocb` is a dedicated cache made by `kmem_cache_create`. Can `kmalloc` allocate from the `io_kiocb` cache? Only if slab merging is enabled, and if sizes and flags are compatible. `io_kiocb` size is 248, `kmalloc-256` is 256. They *could* merge. I need to check if `io_kiocb` has compatible parameters. The allocation trace for the *freed* object shows it was indeed allocated using the `io_uring` cache:
```
Allocated by task 29727:
...
__io_alloc_req_refill+0x9e/0x330 io_uring/io_uring.c:988
```
Also, `usb_alloc_urb` is in the crash call trace, and the `ath9k` driver is also using `usb_alloc_urb` which is unrelated but happening in parallel.
Okay, I see the "Freed by task" trace. The RCU logic in `__alloc_skb` likely triggered the object to be freed. It goes something like this `__alloc_skb` calls `local_bh_enable`, which processes softirqs, RCU callbacks, leading to freeing via `slab_free_after_rcu_debug`. The object *was* freed by RCU. This means it *is* an `io_kiocb` object.
This is all still assuming that `ep->desc` actually points to an `io_kiocb`. Could it be that `ep` itself points to an `io_kiocb`? If the `dum` struct is in freed memory, then `dum->ep[i]` would be inside that memory.
Finally, the function `find_endpoint` is shown:
```c
for (i = 1; i < DUMMY_ENDPOINTS; i++) {
struct dummy_ep *ep = &dum->ep[i];
if (!ep->desc)
continue;
if (ep->desc->bEndpointAddress == address)
return ep;
}
```
This is where the problem lies.
|
| 15/2 |
2026/07/29 01:38 |
tool |
codesearch-struct-layout |
0m
Args:
Results: map[Fields:[map[Name:lock OffsetBits:0 SizeBits:512] map[Name:ep OffsetBits:512 SizeBits:24000] map[Name:address OffsetBits:24512 SizeBits:32] map[Name:callback_usage OffsetBits:24544 SizeBits:32] map[Name:gadget OffsetBits:24576 SizeBits:11840] map[Name:driver OffsetBits:36416 SizeBits:64] map[Name:fifo_req OffsetBits:36480 SizeBits:960] map[Name:fifo_buf OffsetBits:37440 SizeBits:512] map[Name:devstatus OffsetBits:37952 SizeBits:16] map[Name:ints_enabled OffsetBits:37968 SizeBits:1] map[Name:udc_suspended OffsetBits:37969 SizeBits:1] map[Name:pullup OffsetBits:37970 SizeBits:1] map[Name:fifo_req_busy OffsetBits:37971 SizeBits:1] map[Name:hs_hcd OffsetBits:38016 SizeBits:64] map[Name:ss_hcd OffsetBits:38080 SizeBits:64]]]
|
| 16/2 |
2026/07/29 01:38 |
llm |
expert |
0m
Model:
Tokens:
input: 23053
output: 40
thoughts: 607
Thoughts: **Debugging a Use-After-Free in a USB Gadget Driver**
Okay, this is intriguing. This `struct dummy` is massive β around 4700 bytes! And it's not coming from the `io_kiocb` cache, which is only 248 bytes. That's a key clue. I need to figure out what's going on with `ep->desc`. It's a pointer, so if it's pointing to an `io_kiocb` object, then it was assigned a pointer to one.
The bug report is saying the bad address is 82 bytes into a 248-byte freed region. The address is `ffff8880359f2912`. Okay, let's calculate: `ffff8880359f2912 - ffff8880359f28c0 = 0x52`, which is 82 bytes.
In the code, inside `find_endpoint`, we see that `ep->desc->bEndpointAddress == address`. `bEndpointAddress` is at offset 2 in `struct usb_endpoint_descriptor`. So, `ep->desc` itself must be at `ffff8880359f2910`. Let's calculate: `ffff8880359f2910 - ffff8880359f28c0 = 0x50`, which is 80 bytes.
Why would `ep->desc` point to offset 80 of an `io_kiocb`? This is starting to look like a classic use-after-free issue. Time to dig deeper. I bet this is slab merging!
If a cache of around 248 bytes was merged with `io_kiocb`, that would explain why objects from that cache would show up as `io_kiocb` in KASAN reports! I need to find something that contains a `usb_endpoint_descriptor` at offset 80 and is roughly 248 bytes in size. Gadget driver's endpoint structures or request structures are a likely candidate.
Aha! `ep->desc` is assigned in `dummy_enable`: `ep->desc = desc;` Then `desc` is passed to `usb_ep_enable(ep, desc)`. And who calls `usb_ep_enable`? Gadget drivers. Let's start by looking at common gadget drivers. `raw_gadget` is probably the prime suspect. Syzkaller probably found this.
|
| 17/2 |
2026/07/29 01:39 |
tool |
grepper |
0m
Args: map[Expression:usb_ep_enable PathPrefix:drivers/usb/gadget/legacy/raw_gadget.c]
Results: map[Output:drivers/usb/gadget/legacy/raw_gadget.c=840=static int raw_ioctl_ep_enable(struct raw_dev *dev, unsigned long value)
--
drivers/usb/gadget/legacy/raw_gadget.c-884- ep->ep->desc = desc;
drivers/usb/gadget/legacy/raw_gadget.c:885: ret = usb_ep_enable(ep->ep);
drivers/usb/gadget/legacy/raw_gadget.c-886- if (ret < 0) {
drivers/usb/gadget/legacy/raw_gadget.c-887- dev_err(&dev->gadget->dev,
drivers/usb/gadget/legacy/raw_gadget.c:888: "fail, usb_ep_enable returned %d\n", ret);
drivers/usb/gadget/legacy/raw_gadget.c-889- goto out_free;
]
|
| 18/2 |
2026/07/29 01:39 |
llm |
expert |
0m
Model:
Tokens:
input: 24511
output: 46
thoughts: 41
Thoughts: Okay, here's my interpretation of those thoughts, tailored for an expert audience, presented in the first person:
**Diving into the Raw Gadget Implementation**
Right, `raw_gadget` again. Always good to refresh on this low-level stuff. Specifically, I'm thinking about the control flow around enabling the endpoint. I should probably double-check how `raw_ioctl_ep_enable` is implemented within the `drivers/usb/gadget/legacy/raw_gadget.c` file. Gotta make sure there aren't any gotchas or unexpected interactions with the hardware initialization. I'm especially interested in how it handles resource allocation and potential race conditions in that legacy code. This is a critical piece, and it's time to confirm that it's robust.
|
| 19/2 |
2026/07/29 01:39 |
tool |
codesearch-definition-source |
0m
Args: map[ContextFile:drivers/usb/gadget/legacy/raw_gadget.c Name:raw_ioctl_ep_enable]
Results: map[SourceCode: 840: static int raw_ioctl_ep_enable(struct raw_dev *dev, unsigned long value)
841: {
842: int ret = 0, i;
843: unsigned long flags;
844: struct usb_endpoint_descriptor *desc;
845: struct raw_ep *ep;
846: bool ep_props_matched = false;
847:
848: desc = memdup_user((void __user *)value, sizeof(*desc));
849: if (IS_ERR(desc))
850: return PTR_ERR(desc);
851:
852: /*
853: * Endpoints with a maxpacket length of 0 can cause crashes in UDC
854: * drivers.
855: */
856: if (usb_endpoint_maxp(desc) == 0) {
857: dev_dbg(dev->dev, "fail, bad endpoint maxpacket\n");
858: kfree(desc);
859: return -EINVAL;
860: }
861:
862: spin_lock_irqsave(&dev->lock, flags);
863: if (dev->state != STATE_DEV_RUNNING) {
864: dev_dbg(dev->dev, "fail, device is not running\n");
865: ret = -EINVAL;
866: goto out_free;
867: }
868: if (!dev->gadget) {
869: dev_dbg(dev->dev, "fail, gadget is not bound\n");
870: ret = -EBUSY;
871: goto out_free;
872: }
873:
874: for (i = 0; i < dev->eps_num; i++) {
875: ep = &dev->eps[i];
876: if (ep->addr != usb_endpoint_num(desc) &&
877: ep->addr != USB_RAW_EP_ADDR_ANY)
878: continue;
879: if (!usb_gadget_ep_match_desc(dev->gadget, ep->ep, desc, NULL))
880: continue;
881: ep_props_matched = true;
882: if (ep->state != STATE_EP_DISABLED)
883: continue;
884: ep->ep->desc = desc;
885: ret = usb_ep_enable(ep->ep);
886: if (ret < 0) {
887: dev_err(&dev->gadget->dev,
888: "fail, usb_ep_enable returned %d\n", ret);
889: goto out_free;
890: }
891: ep->req = usb_ep_alloc_request(ep->ep, GFP_ATOMIC);
892: if (!ep->req) {
893: dev_err(&dev->gadget->dev,
894: "fail, usb_ep_alloc_request failed\n");
895: usb_ep_disable(ep->ep);
896: ret = -ENOMEM;
897: goto out_free;
898: }
899: ep->state = STATE_EP_ENABLED;
900: ep->ep->driver_data = ep;
901: ret = i;
902: goto out_unlock;
903: }
904:
905: if (!ep_props_matched) {
906: dev_dbg(&dev->gadget->dev, "fail, bad endpoint descriptor\n");
907: ret = -EINVAL;
908: } else {
909: dev_dbg(&dev->gadget->dev, "fail, no endpoints available\n");
910: ret = -EBUSY;
911: }
912:
913: out_free:
914: kfree(desc);
915: out_unlock:
916: spin_unlock_irqrestore(&dev->lock, flags);
917: return ret;
918: }
SourceFile:drivers/usb/gadget/legacy/raw_gadget.c]
|
| 20/2 |
2026/07/29 01:39 |
llm |
expert |
0m
Model:
Tokens:
input: 25984
output: 31
thoughts: 193
Thoughts: **Debugging a USB Gadget Driver Memory Issue**
Okay, so I'm looking at this code snippet: `desc = memdup_user((void __user *)value, sizeof(*desc));` and immediately I see that `sizeof(*desc)` is equivalent to `sizeof(struct usb_endpoint_descriptor)`, which is a measly 7 bytes. My first thought is that this allocation should be coming from `kmalloc-8`. But then I hit a snag. The bug report is describing a 248-byte object being freed, and `ep->desc` supposedly points to offset 80 within that larger allocation. Clearly, the `desc` I'm looking at in *this* code isn't the 7-byte allocation from `raw_ioctl_ep_enable`.
So, where *else* could `desc` be allocated? I need to trace this. Let's revisit the `raw_gadget` code β maybe I missed something in my initial review. Perhaps there's another allocation happening in a different part of that driver. Or, maybe it's not `raw_gadget` at all. I need to broaden my search. What other gadget drivers are in play here? The next step is a targeted search. I'm going to scan the `drivers/usb/gadget/` directory, looking specifically for instances of `ep->desc =`. I need to find all the places where this endpoint descriptor is being assigned, to understand where this larger allocation is actually coming from.
|
| 21/2 |
2026/07/29 01:39 |
tool |
grepper |
0m
Args: map[Expression:ep->desc = PathPrefix:drivers/usb/gadget/]
Results: map[Output:drivers/usb/gadget/composite.c=151=int config_ep_by_speed_and_alt(struct usb_gadget *g,
--
drivers/usb/gadget/composite.c-227- _ep->maxpacket = usb_endpoint_maxp(chosen_desc);
drivers/usb/gadget/composite.c:228: _ep->desc = chosen_desc;
drivers/usb/gadget/composite.c-229- _ep->comp_desc = NULL;
--
drivers/usb/gadget/epautoconf.c=63=struct usb_ep *usb_ep_autoconfig_ss(
--
drivers/usb/gadget/epautoconf.c-109- ep->address = desc->bEndpointAddress;
drivers/usb/gadget/epautoconf.c:110: ep->desc = NULL;
drivers/usb/gadget/epautoconf.c-111- ep->comp_desc = NULL;
--
drivers/usb/gadget/function/f_ecm.c=525=static int ecm_set_alt(struct usb_function *f, unsigned intf, unsigned alt)
--
drivers/usb/gadget/function/f_ecm.c-560- ecm->port.out_ep)) {
drivers/usb/gadget/function/f_ecm.c:561: ecm->port.in_ep->desc = NULL;
drivers/usb/gadget/function/f_ecm.c:562: ecm->port.out_ep->desc = NULL;
drivers/usb/gadget/function/f_ecm.c-563- goto fail;
--
drivers/usb/gadget/function/f_ecm.c=612=static void ecm_disable(struct usb_function *f)
--
drivers/usb/gadget/function/f_ecm.c-621- } else {
drivers/usb/gadget/function/f_ecm.c:622: ecm->port.in_ep->desc = NULL;
drivers/usb/gadget/function/f_ecm.c:623: ecm->port.out_ep->desc = NULL;
drivers/usb/gadget/function/f_ecm.c-624- }
--
drivers/usb/gadget/function/f_eem.c=188=static int eem_set_alt(struct usb_function *f, unsigned intf, unsigned alt)
--
drivers/usb/gadget/function/f_eem.c-207- eem->port.out_ep)) {
drivers/usb/gadget/function/f_eem.c:208: eem->port.in_ep->desc = NULL;
drivers/usb/gadget/function/f_eem.c:209: eem->port.out_ep->desc = NULL;
drivers/usb/gadget/function/f_eem.c-210- goto fail;
--
drivers/usb/gadget/function/f_ncm.c=859=static int ncm_set_alt(struct usb_function *f, unsigned intf, unsigned alt)
--
drivers/usb/gadget/function/f_ncm.c-904- ncm->port.out_ep)) {
drivers/usb/gadget/function/f_ncm.c:905: ncm->port.in_ep->desc = NULL;
drivers/usb/gadget/function/f_ncm.c:906: ncm->port.out_ep->desc = NULL;
drivers/usb/gadget/function/f_ncm.c-907- goto fail;
--
drivers/usb/gadget/function/f_phonet.c=406=static int pn_set_alt(struct usb_function *f, unsigned intf, unsigned alt)
--
drivers/usb/gadget/function/f_phonet.c-432- config_ep_by_speed(gadget, f, fp->out_ep)) {
drivers/usb/gadget/function/f_phonet.c:433: fp->in_ep->desc = NULL;
drivers/usb/gadget/function/f_phonet.c:434: fp->out_ep->desc = NULL;
drivers/usb/gadget/function/f_phonet.c-435- spin_unlock(&port->lock);
--
drivers/usb/gadget/function/f_printer.c=841=set_printer_interface(struct printer_dev *dev)
--
drivers/usb/gadget/function/f_printer.c-844-
drivers/usb/gadget/function/f_printer.c:845: dev->in_ep->desc = ep_desc(dev->gadget, &fs_ep_in_desc, &hs_ep_in_desc,
drivers/usb/gadget/function/f_printer.c-846- &ss_ep_in_desc);
--
drivers/usb/gadget/function/f_printer.c-848-
drivers/usb/gadget/function/f_printer.c:849: dev->out_ep->desc = ep_desc(dev->gadget, &fs_ep_out_desc,
drivers/usb/gadget/function/f_printer.c-850- &hs_ep_out_desc, &ss_ep_out_desc);
--
drivers/usb/gadget/function/f_printer.c-869- (void) usb_ep_disable(dev->out_ep);
drivers/usb/gadget/function/f_printer.c:870: dev->in_ep->desc = NULL;
drivers/usb/gadget/function/f_printer.c:871: dev->out_ep->desc = NULL;
drivers/usb/gadget/function/f_printer.c-872- }
--
drivers/usb/gadget/function/f_printer.c=878=static void printer_reset_interface(struct printer_dev *dev)
--
drivers/usb/gadget/function/f_printer.c-891- spin_lock_irqsave(&dev->lock, flags);
drivers/usb/gadget/function/f_printer.c:892: dev->in_ep->desc = NULL;
drivers/usb/gadget/function/f_printer.c:893: dev->out_ep->desc = NULL;
drivers/usb/gadget/function/f_printer.c-894- dev->interface = -1;
--
drivers/usb/gadget/function/f_rndis.c=529=static int rndis_set_alt(struct usb_function *f, unsigned intf, unsigned alt)
--
drivers/usb/gadget/function/f_rndis.c-560- rndis->port.out_ep)) {
drivers/usb/gadget/function/f_rndis.c:561: rndis->port.in_ep->desc = NULL;
drivers/usb/gadget/function/f_rndis.c:562: rndis->port.out_ep->desc = NULL;
drivers/usb/gadget/function/f_rndis.c-563- goto fail;
--
drivers/usb/gadget/function/f_subset.c=254=static int geth_set_alt(struct usb_function *f, unsigned intf, unsigned alt)
--
drivers/usb/gadget/function/f_subset.c-269- config_ep_by_speed(cdev->gadget, f, geth->port.out_ep)) {
drivers/usb/gadget/function/f_subset.c:270: geth->port.in_ep->desc = NULL;
drivers/usb/gadget/function/f_subset.c:271: geth->port.out_ep->desc = NULL;
drivers/usb/gadget/function/f_subset.c-272- return -EINVAL;
--
drivers/usb/gadget/function/f_uac1.c=1232=static int f_audio_bind(struct usb_configuration *c, struct usb_function *f)
--
drivers/usb/gadget/function/f_uac1.c-1415- uac1->int_ep = ep;
drivers/usb/gadget/function/f_uac1.c:1416: uac1->int_ep->desc = &ac_int_ep_desc;
drivers/usb/gadget/function/f_uac1.c-1417-
--
drivers/usb/gadget/function/f_uac1.c-1427- audio->out_ep = ep;
drivers/usb/gadget/function/f_uac1.c:1428: audio->out_ep->desc = &as_out_ep_desc;
drivers/usb/gadget/function/f_uac1.c-1429- }
--
drivers/usb/gadget/function/f_uac1.c-1436- audio->in_ep = ep;
drivers/usb/gadget/function/f_uac1.c:1437: audio->in_ep->desc = &as_in_ep_desc;
drivers/usb/gadget/function/f_uac1.c-1438- }
--
drivers/usb/gadget/function/f_uac1_legacy.c=727=f_audio_bind(struct usb_configuration *c, struct usb_function *f)
--
drivers/usb/gadget/function/f_uac1_legacy.c-781- audio->out_ep = ep;
drivers/usb/gadget/function/f_uac1_legacy.c:782: audio->out_ep->desc = &as_out_ep_desc;
drivers/usb/gadget/function/f_uac1_legacy.c-783-
--
drivers/usb/gadget/function/u_ether.c=635=static int eth_stop(struct net_device *net)
--
drivers/usb/gadget/function/u_ether.c-672- DBG(dev, "host still using in/out endpoints\n");
drivers/usb/gadget/function/u_ether.c:673: link->in_ep->desc = in;
drivers/usb/gadget/function/u_ether.c:674: link->out_ep->desc = out;
drivers/usb/gadget/function/u_ether.c-675- usb_ep_enable(link->in_ep);
--
drivers/usb/gadget/function/u_ether.c=1217=void gether_disconnect(struct gether *link)
--
drivers/usb/gadget/function/u_ether.c-1250- spin_unlock(&dev->req_lock);
drivers/usb/gadget/function/u_ether.c:1251: link->in_ep->desc = NULL;
drivers/usb/gadget/function/u_ether.c-1252-
--
drivers/usb/gadget/function/u_ether.c-1263- spin_unlock(&dev->req_lock);
drivers/usb/gadget/function/u_ether.c:1264: link->out_ep->desc = NULL;
drivers/usb/gadget/function/u_ether.c-1265-
--
drivers/usb/gadget/legacy/dbgp.c=167=static int __enable_ep(struct usb_ep *ep, struct usb_endpoint_descriptor *desc)
--
drivers/usb/gadget/legacy/dbgp.c-169- int err;
drivers/usb/gadget/legacy/dbgp.c:170: ep->desc = desc;
drivers/usb/gadget/legacy/dbgp.c-171- err = usb_ep_enable(ep);
--
drivers/usb/gadget/legacy/inode.c=726=ep_config (struct ep_data *data, const char *buf, size_t len)
--
drivers/usb/gadget/legacy/inode.c-786- case USB_SPEED_FULL:
drivers/usb/gadget/legacy/inode.c:787: ep->desc = &data->desc;
drivers/usb/gadget/legacy/inode.c-788- break;
--
drivers/usb/gadget/legacy/inode.c-790- /* fails if caller didn't provide that descriptor... */
drivers/usb/gadget/legacy/inode.c:791: ep->desc = &data->hs_desc;
drivers/usb/gadget/legacy/inode.c-792- break;
--
drivers/usb/gadget/legacy/raw_gadget.c=840=static int raw_ioctl_ep_enable(struct raw_dev *dev, unsigned long value)
--
drivers/usb/gadget/legacy/raw_gadget.c-883- continue;
drivers/usb/gadget/legacy/raw_gadget.c:884: ep->ep->desc = desc;
drivers/usb/gadget/legacy/raw_gadget.c-885- ret = usb_ep_enable(ep->ep);
--
drivers/usb/gadget/udc/aspeed_udc.c=341=static int ast_udc_ep_enable(struct usb_ep *_ep,
--
drivers/usb/gadget/udc/aspeed_udc.c-367-
drivers/usb/gadget/udc/aspeed_udc.c:368: ep->desc = desc;
drivers/usb/gadget/udc/aspeed_udc.c-369- ep->stopped = 0;
--
drivers/usb/gadget/udc/bcm63xx_udc.c=1030=static int bcm63xx_ep_enable(struct usb_ep *ep,
--
drivers/usb/gadget/udc/bcm63xx_udc.c-1058-
drivers/usb/gadget/udc/bcm63xx_udc.c:1059: ep->desc = desc;
drivers/usb/gadget/udc/bcm63xx_udc.c-1060- ep->maxpacket = usb_endpoint_maxp(desc);
--
drivers/usb/gadget/udc/bcm63xx_udc.c=1070=static int bcm63xx_ep_disable(struct usb_ep *ep)
--
drivers/usb/gadget/udc/bcm63xx_udc.c-1101- }
drivers/usb/gadget/udc/bcm63xx_udc.c:1102: ep->desc = NULL;
drivers/usb/gadget/udc/bcm63xx_udc.c-1103-
--
drivers/usb/gadget/udc/bdc/bdc_ep.c=557=int bdc_ep_disable(struct bdc_ep *ep)
--
drivers/usb/gadget/udc/bdc/bdc_ep.c-591- ep_bd_list_free(ep, ep->bd_list.num_tabs);
drivers/usb/gadget/udc/bdc/bdc_ep.c:592: ep->desc = NULL;
drivers/usb/gadget/udc/bdc/bdc_ep.c-593- ep->comp_desc = NULL;
--
drivers/usb/gadget/udc/bdc/bdc_ep.c=1860=static int bdc_gadget_ep_enable(struct usb_ep *_ep,
--
drivers/usb/gadget/udc/bdc/bdc_ep.c-1891- spin_lock_irqsave(&bdc->lock, flags);
drivers/usb/gadget/udc/bdc/bdc_ep.c:1892: ep->desc = desc;
drivers/usb/gadget/udc/bdc/bdc_ep.c-1893- ep->comp_desc = _ep->comp_desc;
--
drivers/usb/gadget/udc/cdns2/cdns2-gadget.c=1612=static int cdns2_gadget_ep_disable(struct usb_ep *ep)
--
drivers/usb/gadget/udc/cdns2/cdns2-gadget.c-1660-
drivers/usb/gadget/udc/cdns2/cdns2-gadget.c:1661: ep->desc = NULL;
drivers/usb/gadget/udc/cdns2/cdns2-gadget.c-1662- pep->ep_state &= ~EP_ENABLED;
--
drivers/usb/gadget/udc/dummy_hcd.c=527=static int dummy_enable(struct usb_ep *_ep,
--
drivers/usb/gadget/udc/dummy_hcd.c-645- }
drivers/usb/gadget/udc/dummy_hcd.c:646: ep->desc = desc;
drivers/usb/gadget/udc/dummy_hcd.c-647-
--
drivers/usb/gadget/udc/dummy_hcd.c=664=static int dummy_disable(struct usb_ep *_ep)
--
drivers/usb/gadget/udc/dummy_hcd.c-675- spin_lock_irqsave(&dum->lock, flags);
drivers/usb/gadget/udc/dummy_hcd.c:676: ep->desc = NULL;
drivers/usb/gadget/udc/dummy_hcd.c-677- ep->stream_en = 0;
--
drivers/usb/gadget/udc/dummy_hcd.c=1068=static void init_dummy_udc_hw(struct dummy *dum)
--
drivers/usb/gadget/udc/dummy_hcd.c-1087- ep->gadget = &dum->gadget;
drivers/usb/gadget/udc/dummy_hcd.c:1088: ep->desc = NULL;
drivers/usb/gadget/udc/dummy_hcd.c-1089- INIT_LIST_HEAD(&ep->queue);
--
drivers/usb/gadget/udc/net2280.c=182=net2280_enable(struct usb_ep *_ep, const struct usb_endpoint_descriptor *desc)
--
drivers/usb/gadget/udc/net2280.c-231- _ep->maxpacket = max;
drivers/usb/gadget/udc/net2280.c:232: ep->desc = desc;
drivers/usb/gadget/udc/net2280.c-233-
--
drivers/usb/gadget/udc/net2280.c=378=static void ep_reset_228x(struct net2280_regs __iomem *regs,
--
drivers/usb/gadget/udc/net2280.c-382-
drivers/usb/gadget/udc/net2280.c:383: ep->desc = NULL;
drivers/usb/gadget/udc/net2280.c-384- INIT_LIST_HEAD(&ep->queue);
--
drivers/usb/gadget/udc/net2280.c=454=static void ep_reset_338x(struct net2280_regs __iomem *regs,
--
drivers/usb/gadget/udc/net2280.c-458-
drivers/usb/gadget/udc/net2280.c:459: ep->desc = NULL;
drivers/usb/gadget/udc/net2280.c-460- INIT_LIST_HEAD(&ep->queue);
--
drivers/usb/gadget/udc/tegra-xudc.c=1706=static int __tegra_xudc_ep_disable(struct tegra_xudc_ep *ep)
--
drivers/usb/gadget/udc/tegra-xudc.c-1725-
drivers/usb/gadget/udc/tegra-xudc.c:1726: ep->desc = NULL;
drivers/usb/gadget/udc/tegra-xudc.c-1727- ep->comp_desc = NULL;
--
drivers/usb/gadget/udc/tegra-xudc.c=1787=static int __tegra_xudc_ep_enable(struct tegra_xudc_ep *ep,
--
drivers/usb/gadget/udc/tegra-xudc.c-1801-
drivers/usb/gadget/udc/tegra-xudc.c:1802: ep->desc = desc;
drivers/usb/gadget/udc/tegra-xudc.c-1803- ep->comp_desc = ep->usb_ep.comp_desc;
--
drivers/usb/gadget/udc/udc-xilinx.c=805=static int __xudc_ep_enable(struct xusb_ep *ep,
--
drivers/usb/gadget/udc/udc-xilinx.c-816- ep->epnumber = usb_endpoint_num(desc);
drivers/usb/gadget/udc/udc-xilinx.c:817: ep->desc = desc;
drivers/usb/gadget/udc/udc-xilinx.c-818- ep->ep_usb.desc = desc;
--
drivers/usb/gadget/udc/udc-xilinx.c=927=static int xudc_ep_disable(struct usb_ep *_ep)
--
drivers/usb/gadget/udc/udc-xilinx.c-946- /* Restore the endpoint's pristine config */
drivers/usb/gadget/udc/udc-xilinx.c:947: ep->desc = NULL;
drivers/usb/gadget/udc/udc-xilinx.c-948- ep->ep_usb.desc = NULL;
--
drivers/usb/gadget/udc/udc-xilinx.c=1302=static void xudc_eps_init(struct xusb_udc *udc)
--
drivers/usb/gadget/udc/udc-xilinx.c-1335- ep->epnumber = ep_number;
drivers/usb/gadget/udc/udc-xilinx.c:1336: ep->desc = NULL;
drivers/usb/gadget/udc/udc-xilinx.c-1337- /*
]
|
| 22/2 |
2026/07/29 01:39 |
llm |
expert |
0m
Model:
Tokens:
input: 27456
output: 43
thoughts: 0
|
| 23/2 |
2026/07/29 01:39 |
tool |
codesearch-definition-source |
0m
Args: map[ContextFile:drivers/usb/gadget/udc/dummy_hcd.c Name:dummy_disable]
Results: map[SourceCode: 664: static int dummy_disable(struct usb_ep *_ep)
665: {
666: struct dummy_ep *ep;
667: struct dummy *dum;
668: unsigned long flags;
669:
670: ep = usb_ep_to_dummy_ep(_ep);
671: if (!_ep || !ep->desc || _ep->name == ep0name)
672: return -EINVAL;
673: dum = ep_to_dummy(ep);
674:
675: spin_lock_irqsave(&dum->lock, flags);
676: ep->desc = NULL;
677: ep->stream_en = 0;
678: nuke(dum, ep);
679: spin_unlock_irqrestore(&dum->lock, flags);
680:
681: dev_dbg(udc_dev(dum), "disabled %s\n", _ep->name);
682: return 0;
683: }
SourceFile:drivers/usb/gadget/udc/dummy_hcd.c]
|
| 24/2 |
2026/07/29 01:39 |
llm |
expert |
1m
Model:
Tokens:
input: 32711
output: 21
thoughts: 182
Thoughts: **Analyzing a Potential Memory Corruption Vulnerability**
Okay, so I'm looking at this code and something's not quite right. `raw_gadget` is allocating `desc` with `memdup_user`, which in turn uses `kmalloc`. If `desc` is just 7 bytes, it would come from a `kmalloc-8` allocation, which makes sense. But the bug report mentions a 248-byte freed object, and `ep->desc` supposedly points 80 bytes *into* that larger block. That screams "part of a larger structure" to me.
I need to figure out what that larger structure is. Let's dig into `drivers/usb/gadget/legacy/inode.c`, specifically the `ep_config` function. Ah, here it is: `ep->desc = &data->desc;`. So `data` is a `struct ep_data`. That's the key. Now, what's the size of `struct ep_data`? Let's check that definition to see how it adds up to that 248 byte value, and how `desc` is positioned within it. This could be a good lead on a classic heap overflow.
|
| 25/2 |
2026/07/29 01:40 |
tool |
codesearch-struct-layout |
0m
Args:
Results: map[Fields:[map[Name:lock OffsetBits:0 SizeBits:1088] map[Name:state OffsetBits:1088 SizeBits:32] map[Name:count OffsetBits:1120 SizeBits:32] map[Name:dev OffsetBits:1152 SizeBits:64] map[Name:ep OffsetBits:1216 SizeBits:64] map[Name:req OffsetBits:1280 SizeBits:64] map[Name:status OffsetBits:1344 SizeBits:64] map[Name:name OffsetBits:1408 SizeBits:128] map[Name:desc OffsetBits:1536 SizeBits:72] map[Name:hs_desc OffsetBits:1608 SizeBits:72] map[Name:epfiles OffsetBits:1728 SizeBits:128] map[Name:wait OffsetBits:1856 SizeBits:640]]]
|
| 26/2 |
2026/07/29 01:40 |
llm |
expert |
0m
Model:
Tokens:
input: 33435
output: 21
thoughts: 115
Thoughts: **Analyzing USB Endpoint Data Structure Discrepancies**
Okay, so I'm looking at this `ep_data` structure and, based on the bit sizes, it should be 312 bytes. That size makes sense, and I would expect it to be allocated from `kmalloc-512`, which is typical for objects around that size. But, hold on a second... the bug report mentions a freed object size of only 248 bytes. That's a red flag. The reported freed size doesn't align with what I'd expect the `ep_data` structure to be allocated at. This discrepancy is suspicious.
Let me go take a look at `struct raw_ep` to understand the data structure involved and see if this can be understood. It looks like it's defined in `drivers/usb/gadget/legacy/raw_gadget.c` which should give me the information I need to understand this size difference. I need to figure out why we're seeing this 248 byte value when I would have expected 312 bytes to be in play. There must be something else going on.
|
| 27/2 |
2026/07/29 01:40 |
tool |
codesearch-struct-layout |
0m
Args:
Results: map[Fields:[map[Name:dev OffsetBits:0 SizeBits:64] map[Name:state OffsetBits:64 SizeBits:32] map[Name:ep OffsetBits:128 SizeBits:64] map[Name:addr OffsetBits:192 SizeBits:8] map[Name:req OffsetBits:256 SizeBits:64] map[Name:urb_queued OffsetBits:320 SizeBits:8] map[Name:disabling OffsetBits:328 SizeBits:8] map[Name:status OffsetBits:384 SizeBits:64]]]
|
| 28/2 |
2026/07/29 01:40 |
llm |
expert |
0m
Model:
Tokens:
input: 33978
output: 43
thoughts: 270
Thoughts: **Analyzing Potential Vulnerabilities in the Dummy USB Gadget Driver**
Okay, so I'm looking at this `struct raw_ep` β it seems pretty minimal, which is good from a size perspective. But hold on, a thought just struck me. What if the `ep->desc` pointer isn't actually pointing to a descriptor that the gadget driver allocated? What if, instead, the `ep` structure itself has been corrupted? That opens up a whole different can of worms.
Let's revisit how `find_endpoint` works. I'm focusing on this loop:
```c
for (i = 1; i < DUMMY_ENDPOINTS; i++) {
struct dummy_ep *ep = &dum->ep[i];
if (!ep->desc)
continue;
if (ep->desc->bEndpointAddress == address)
return ep;
}
```
If `dum`, the pointer passed to the function, is corrupted, then that `dum->ep[i]` access is reading from a completely wrong memory address. That's a classic memory corruption scenario. Now, the key is where `find_endpoint` is called from. Let's see... looking at the backtrace, it's called from `dummy_timer`, as well as a call at +0x2a3.
So `find_endpoint` gets called from `dummy_timer`, and the `dum` argument comes from there. Okay, I need to check how `dummy_timer` works now, to understand the path that leads to `dum`.
|
| 29/2 |
2026/07/29 01:40 |
tool |
codesearch-definition-source |
0m
Args: map[ContextFile:drivers/usb/gadget/udc/dummy_hcd.c Name:dummy_timer]
Results: map[SourceCode:1809: /*
1810: * Drive both sides of the transfers; looks like irq handlers to both
1811: * drivers except that the callbacks are invoked from soft interrupt
1812: * context.
1813: */
1814: static enum hrtimer_restart dummy_timer(struct hrtimer *t)
1815: {
1816: struct dummy_hcd *dum_hcd = timer_container_of(dum_hcd, t,
1817: timer);
1818: struct dummy *dum = dum_hcd->dum;
1819: struct urbp *urbp, *tmp;
1820: unsigned long flags;
1821: int limit, total;
1822: int i;
1823:
1824: /* simplistic model for one frame's bandwidth */
1825: /* FIXME: account for transaction and packet overhead */
1826: switch (dum->gadget.speed) {
1827: case USB_SPEED_LOW:
1828: total = 8/*bytes*/ * 12/*packets*/;
1829: break;
1830: case USB_SPEED_FULL:
1831: total = 64/*bytes*/ * 19/*packets*/;
1832: break;
1833: case USB_SPEED_HIGH:
1834: total = 512/*bytes*/ * 13/*packets*/ * 8/*uframes*/;
1835: break;
1836: case USB_SPEED_SUPER:
1837: /* Bus speed is 500000 bytes/ms, so use a little less */
1838: total = 490000;
1839: break;
1840: default: /* Can't happen */
1841: dev_err(dummy_dev(dum_hcd), "bogus device speed\n");
1842: total = 0;
1843: break;
1844: }
1845:
1846: /* look at each urb queued by the host side driver */
1847: spin_lock_irqsave(&dum->lock, flags);
1848: dum_hcd->timer_pending = 0;
1849:
1850: if (!dum_hcd->udev) {
1851: dev_err(dummy_dev(dum_hcd),
1852: "timer fired with no URBs pending?\n");
1853: spin_unlock_irqrestore(&dum->lock, flags);
1854: return HRTIMER_NORESTART;
1855: }
1856: dum_hcd->next_frame_urbp = NULL;
1857:
1858: for (i = 0; i < DUMMY_ENDPOINTS; i++) {
1859: if (!ep_info[i].name)
1860: break;
1861: dum->ep[i].already_seen = 0;
1862: }
1863:
1864: restart:
1865: list_for_each_entry_safe(urbp, tmp, &dum_hcd->urbp_list, urbp_list) {
1866: struct urb *urb;
1867: struct dummy_request *req;
1868: u8 address;
1869: struct dummy_ep *ep = NULL;
1870: int status = -EINPROGRESS;
1871:
1872: /* stop when we reach URBs queued after the timer interrupt */
1873: if (urbp == dum_hcd->next_frame_urbp)
1874: break;
1875:
1876: urb = urbp->urb;
1877: if (urb->unlinked)
1878: goto return_urb;
1879: else if (dum_hcd->rh_state != DUMMY_RH_RUNNING)
1880: continue;
1881:
1882: /* Used up this frame's bandwidth? */
1883: if (total <= 0)
1884: continue;
1885:
1886: /* find the gadget's ep for this request (if configured) */
1887: address = usb_pipeendpoint (urb->pipe);
1888: if (usb_urb_dir_in(urb))
1889: address |= USB_DIR_IN;
1890: ep = find_endpoint(dum, address);
1891: if (!ep) {
1892: /* set_configuration() disagreement */
1893: dev_dbg(dummy_dev(dum_hcd),
1894: "no ep configured for urb %p\n",
1895: urb);
1896: status = -EPROTO;
1897: goto return_urb;
1898: }
1899:
1900: if (ep->already_seen)
1901: continue;
1902: ep->already_seen = 1;
1903: if (ep == &dum->ep[0] && urb->error_count) {
1904: ep->setup_stage = 1; /* a new urb */
1905: urb->error_count = 0;
1906: }
1907: if (ep->halted && !ep->setup_stage) {
1908: /* NOTE: must not be iso! */
1909: dev_dbg(dummy_dev(dum_hcd), "ep %s halted, urb %p\n",
1910: ep->ep.name, urb);
1911: status = -EPIPE;
1912: goto return_urb;
1913: }
1914: /* FIXME make sure both ends agree on maxpacket */
1915:
1916: /* handle control requests */
1917: if (ep == &dum->ep[0] && ep->setup_stage) {
1918: struct usb_ctrlrequest setup;
1919: int value;
1920:
1921: setup = *(struct usb_ctrlrequest *) urb->setup_packet;
1922: /* paranoia, in case of stale queued data */
1923: list_for_each_entry(req, &ep->queue, queue) {
1924: list_del_init(&req->queue);
1925: req->req.status = -EOVERFLOW;
1926: dev_dbg(udc_dev(dum), "stale req = %p\n",
1927: req);
1928:
1929: dummy_giveback(dum, &ep->ep, req);
1930: ep->already_seen = 0;
1931: goto restart;
1932: }
1933:
1934: /* gadget driver never sees set_address or operations
1935: * on standard feature flags. some hardware doesn't
1936: * even expose them.
1937: */
1938: ep->last_io = jiffies;
1939: ep->setup_stage = 0;
1940: ep->halted = 0;
1941:
1942: value = handle_control_request(dum_hcd, urb, &setup,
1943: &status);
1944:
1945: /* gadget driver handles all other requests. block
1946: * until setup() returns; no reentrancy issues etc.
1947: */
1948: if (value > 0) {
1949: ++dum->callback_usage;
1950: spin_unlock(&dum->lock);
1951: value = dum->driver->setup(&dum->gadget,
1952: &setup);
1953: spin_lock(&dum->lock);
1954: --dum->callback_usage;
1955:
1956: if (value >= 0) {
1957: /* no delays (max 64KB data stage) */
1958: limit = 64*1024;
1959: goto treat_control_like_bulk;
1960: }
1961: /* error, see below */
1962: }
1963:
1964: if (value < 0) {
1965: if (value != -EOPNOTSUPP)
1966: dev_dbg(udc_dev(dum),
1967: "setup --> %d\n",
1968: value);
1969: status = -EPIPE;
1970: urb->actual_length = 0;
1971: }
1972:
1973: goto return_urb;
1974: }
1975:
1976: /* non-control requests */
1977: limit = total;
1978: switch (usb_pipetype(urb->pipe)) {
1979: case PIPE_ISOCHRONOUS:
1980: /*
1981: * We don't support isochronous. But if we did,
1982: * here are some of the issues we'd have to face:
1983: *
1984: * Is it urb->interval since the last xfer?
1985: * Use urb->iso_frame_desc[i].
1986: * Complete whether or not ep has requests queued.
1987: * Report random errors, to debug drivers.
1988: */
1989: limit = max(limit, periodic_bytes(dum, ep));
1990: status = -EINVAL; /* fail all xfers */
1991: break;
1992:
1993: case PIPE_INTERRUPT:
1994: /* FIXME is it urb->interval since the last xfer?
1995: * this almost certainly polls too fast.
1996: */
1997: limit = max(limit, periodic_bytes(dum, ep));
1998: fallthrough;
1999:
2000: default:
2001: treat_control_like_bulk:
2002: ep->last_io = jiffies;
2003: total -= transfer(dum_hcd, urb, ep, limit, &status);
2004: break;
2005: }
2006:
2007: /* incomplete transfer? */
2008: if (status == -EINPROGRESS)
2009: continue;
2010:
2011: return_urb:
2012: list_del(&urbp->urbp_list);
2013: kfree(urbp);
2014: if (ep)
2015: ep->already_seen = ep->setup_stage = 0;
2016:
2017: usb_hcd_unlink_urb_from_ep(dummy_hcd_to_hcd(dum_hcd), urb);
2018: spin_unlock(&dum->lock);
2019: usb_hcd_giveback_urb(dummy_hcd_to_hcd(dum_hcd), urb, status);
2020: spin_lock(&dum->lock);
2021:
2022: goto restart;
2023: }
2024:
2025: if (list_empty(&dum_hcd->urbp_list)) {
2026: usb_put_dev(dum_hcd->udev);
2027: dum_hcd->udev = NULL;
2028: } else if (!dum_hcd->timer_pending &&
2029: dum_hcd->rh_state == DUMMY_RH_RUNNING) {
2030: /* want a 1 msec delay here */
2031: dum_hcd->timer_pending = 1;
2032: hrtimer_start(&dum_hcd->timer, ns_to_ktime(DUMMY_TIMER_INT_NSECS),
2033: HRTIMER_MODE_REL_SOFT);
2034: }
2035:
2036: spin_unlock_irqrestore(&dum->lock, flags);
2037:
2038: return HRTIMER_NORESTART;
2039: }
SourceFile:drivers/usb/gadget/udc/dummy_hcd.c]
|
| 30/2 |
2026/07/29 01:40 |
llm |
expert |
0m
Model:
Tokens:
input: 38091
output: 22
thoughts: 559
Thoughts: **Debugging a Potential Memory Corruption Issue**
Okay, so I'm looking at this `dummy_timer` function and I've spotted a potential problem with memory access. The code iterates through a list of URBs and, in the process, retrieves an endpoint descriptor. Specifically, I'm concerned about how `ep->desc` is being used. I see that it's accessed like this: `ep = find_endpoint(dum, address);`, where `ep` is an endpoint and `ep->desc` is `struct usb_endpoint_descriptor`.
Now, the context is that `dum` is a member of `dum_hcd`, which itself is allocated within the `dummy_hcd` structure, initialized in the `dummy_hcd_probe` function. The key question is whether `dum` or `dum_hcd` could be freed prematurely, which would leave `ep->desc` pointing to invalid memory.
I've checked the allocation sizes, the buggy address is inside a 248-byte region, but `dum` is actually allocated with a size of 4700 bytes. So it's not `dum` that is getting freed.
My next deduction is that the freed object, the culprit, is 248 bytes in size, and the buggy address specifically involves `ep->desc->bEndpointAddress`. Thus, `ep->desc` must be pointing to this 248-byte object, which has been freed. This means a gadget driver must have allocated a 248-byte object and passed a pointer to a location within it as `desc` to `usb_ep_enable`.
I need to confirm this. I'm focusing on the offset of `ep->desc`, which is 80 bytes within the 248-byte region. This means that I should check for any structure that contains a `usb_endpoint_descriptor` at offset 80. `usb_endpoint_descriptor` is only 7 bytes, and it could be that it's embedded within a larger structure.
I'll check `struct usb_request` first because that's a common one. Nope, that doesn't fit. Now, let's explore `struct ffs_ep`, found in `drivers/usb/gadget/function/f_fs.c`, as that's a common gadget driver structure. I'll take a look there to see if I can find a match.
|
| 31/2 |
2026/07/29 01:40 |
tool |
codesearch-struct-layout |
0m
Args:
Results: map[Fields:[map[Name:ep OffsetBits:0 SizeBits:64] map[Name:req OffsetBits:64 SizeBits:64] map[Name:descs OffsetBits:128 SizeBits:192] map[Name:num OffsetBits:320 SizeBits:8]]]
|
| 32/2 |
2026/07/29 01:40 |
llm |
expert |
0m
Model:
Tokens:
input: 39284
output: 23
thoughts: 22
Thoughts: Okay, here's my take on those thoughts, framed as an expert's internal monologue:
**Quick Comparison of FFS Structures**
Alright, so `struct ffs_ep` is pretty straightforward, compact even. But now I'm thinking, what about `struct ffs_epfile`? I need to quickly compare the structures. I need to refresh my memory on what `ffs_epfile` actually holds in comparison to `ffs_ep`. Are there any overlaps? What additional metadata are contained in `ffs_epfile`, and how does this affect resource usage or access times? This quick comparison could be vital to improving performance. Perhaps `ffs_ep` is simpler, but now `ffs_epfile` is in my mind I need to think about how these two structures interact to avoid issues.
|
| 33/2 |
2026/07/29 01:40 |
tool |
codesearch-struct-layout |
0m
Args:
Results: map[Fields:[map[Name:mutex OffsetBits:0 SizeBits:1088] map[Name:ffs OffsetBits:1088 SizeBits:64] map[Name:ep OffsetBits:1152 SizeBits:64] map[Name:read_buffer OffsetBits:1216 SizeBits:64] map[Name:name OffsetBits:1280 SizeBits:40] map[Name:in OffsetBits:1320 SizeBits:8] map[Name:isoc OffsetBits:1328 SizeBits:8] map[Name:_pad OffsetBits:1336 SizeBits:8] map[Name:dmabufs_mutex OffsetBits:1344 SizeBits:1088] map[Name:dmabufs OffsetBits:2432 SizeBits:128] map[Name:seqno OffsetBits:2560 SizeBits:32]]]
|
| 34/2 |
2026/07/29 01:40 |
llm |
expert |
0m
Model:
Tokens:
input: 39750
output: 22
thoughts: 10
Thoughts: Okay, here's my interpretation of that thought process, tailored for an expert audience, and presented as if it were my own internal monologue:
**Considering `struct ffs_data` in the Context of... Something**
Alright, that thought bubbled up. The mention of `struct ffs_data`... immediately, I'm thinking about its implications. The fact that it's come to mind suggests that whatever I'm working on, or whatever problem I'm trying to solve, has a potential interface or dependency on this specific data structure. I need to now remember what context this structure even lives in. Am I considering file system operations? Fast file system implementations? Or perhaps even something *completely* unrelated, where `ffs_data` is used for an entirely different purpose (perhaps a completely different code base?). I have to consider all the various ways this could be used and which one would make the most sense. I need to make a mental note to check my documentation or previous code to re-familiarize myself with the details of this structure, especially any potential gotchas or dependencies it brings with it. This also suggests, if I can recall where this structure is used, that there is likely some level of integration that needs to be explored. Perhaps I'm on the cusp of an optimization, or maybe dealing with a subtle bug, or even just thinking about how a certain system is designed. I will explore where and how that struct might connect to the problem I am considering.
|
| 35/2 |
2026/07/29 01:40 |
tool |
codesearch-struct-layout |
0m
Args:
Results: map[Fields:[map[Name:gadget OffsetBits:0 SizeBits:64] map[Name:mutex OffsetBits:64 SizeBits:1088] map[Name:eps_lock OffsetBits:1152 SizeBits:512] map[Name:ep0req OffsetBits:1664 SizeBits:64] map[Name:ep0req_completion OffsetBits:1728 SizeBits:704] map[Name:ref OffsetBits:2432 SizeBits:32] map[Name:opened OffsetBits:2464 SizeBits:32] map[Name:state OffsetBits:2496 SizeBits:32] map[Name:setup_state OffsetBits:2528 SizeBits:32] map[Name:ev OffsetBits:2560 SizeBits:768] map[Name:flags OffsetBits:3328 SizeBits:64] map[Name:wait OffsetBits:3392 SizeBits:640] map[Name:func OffsetBits:4032 SizeBits:64] map[Name:dev_name OffsetBits:4096 SizeBits:64] map[Name:private_data OffsetBits:4160 SizeBits:64] map[Name:raw_descs_data OffsetBits:4224 SizeBits:64] map[Name:raw_descs OffsetBits:4288 SizeBits:64] map[Name:raw_descs_length OffsetBits:4352 SizeBits:32] map[Name:fs_descs_count OffsetBits:4384 SizeBits:32] map[Name:hs_descs_count OffsetBits:4416 SizeBits:32] map[Name:ss_descs_count OffsetBits:4448 SizeBits:32] map[Name:ms_os_descs_count OffsetBits:4480 SizeBits:32] map[Name:ms_os_descs_ext_prop_count OffsetBits:4512 SizeBits:32] map[Name:ms_os_descs_ext_prop_name_len OffsetBits:4544 SizeBits:32] map[Name:ms_os_descs_ext_prop_data_len OffsetBits:4576 SizeBits:32] map[Name:ms_os_descs_ext_prop_avail OffsetBits:4608 SizeBits:64] map[Name:ms_os_descs_ext_prop_name_avail OffsetBits:4672 SizeBits:64] map[Name:ms_os_descs_ext_prop_data_avail OffsetBits:4736 SizeBits:64] map[Name:user_flags OffsetBits:4800 SizeBits:32] map[Name:eps_addrmap OffsetBits:4832 SizeBits:248] map[Name:strings_count OffsetBits:5088 SizeBits:16] map[Name:interfaces_count OffsetBits:5104 SizeBits:16] map[Name:eps_count OffsetBits:5120 SizeBits:16] map[Name:_pad1 OffsetBits:5136 SizeBits:16] map[Name:raw_strings OffsetBits:5184 SizeBits:64] map[Name:stringtabs OffsetBits:5248 SizeBits:64] map[Name:sb OffsetBits:5312 SizeBits:64] map[Name:file_perms OffsetBits:5376 SizeBits:96] map[Name:ffs_eventfd OffsetBits:5504 SizeBits:64] map[Name:io_completion_wq OffsetBits:5568 SizeBits:64] map[Name:no_disconnect OffsetBits:5632 SizeBits:8] map[Name:reset_work OffsetBits:5696 SizeBits:576] map[Name:epfiles OffsetBits:6272 SizeBits:64]]]
|
| 36/2 |
2026/07/29 01:40 |
llm |
expert |
0m
Model:
Tokens:
input: 41122
output: 49
thoughts: 173
Thoughts: **Debugging a Memory Access Bug**
Okay, so I'm looking at this crash and immediately my brain hones in on `ep->desc` β that's a pointer, which means it's a potential landmine. I need to figure out where that memory is coming from. My initial thought is, did someone allocate this with something like `kmemdup` or, heaven forbid, `memdup_user`? If it *was* allocated by `memdup_user` with only 7 bytes, as I see it is, then it would be a `kmalloc-8` allocation, which means it would only be 8 bytes.
But then, the buggy address is at offset 80 *within* a 248-byte object. Hold on... that means `ep->desc` isn't just a standalone allocation. It's `base_address + 80`. That suggests `usb_endpoint_descriptor` is embedded somewhere! It *has* to be part of a larger structure. Now I need to start hunting for structures that *include* a `usb_endpoint_descriptor` as a member. Let's see what we can find.
|
| 37/2 |
2026/07/29 01:40 |
tool |
grepper |
0m
Args: map[Expression:struct usb_endpoint_descriptor[ \t]+[a-zA-Z0-9_]+; PathPrefix:drivers/usb/gadget/]
Results: map[Output:drivers/usb/gadget/function/storage_common.h=184=extern struct usb_interface_descriptor fsg_intf_desc;
drivers/usb/gadget/function/storage_common.h-185-
drivers/usb/gadget/function/storage_common.h:186:extern struct usb_endpoint_descriptor fsg_fs_bulk_in_desc;
drivers/usb/gadget/function/storage_common.h:187:extern struct usb_endpoint_descriptor fsg_fs_bulk_out_desc;
drivers/usb/gadget/function/storage_common.h-188-extern struct usb_descriptor_header *fsg_fs_function[];
drivers/usb/gadget/function/storage_common.h-189-
drivers/usb/gadget/function/storage_common.h:190:extern struct usb_endpoint_descriptor fsg_hs_bulk_in_desc;
drivers/usb/gadget/function/storage_common.h:191:extern struct usb_endpoint_descriptor fsg_hs_bulk_out_desc;
drivers/usb/gadget/function/storage_common.h-192-extern struct usb_descriptor_header *fsg_hs_function[];
drivers/usb/gadget/function/storage_common.h-193-
drivers/usb/gadget/function/storage_common.h:194:extern struct usb_endpoint_descriptor fsg_ss_bulk_in_desc;
drivers/usb/gadget/function/storage_common.h-195-extern struct usb_ss_ep_comp_descriptor fsg_ss_bulk_in_comp_desc;
drivers/usb/gadget/function/storage_common.h:196:extern struct usb_endpoint_descriptor fsg_ss_bulk_out_desc;
drivers/usb/gadget/function/storage_common.h-197-extern struct usb_ss_ep_comp_descriptor fsg_ss_bulk_out_comp_desc;
--
drivers/usb/gadget/udc/pxa27x_udc.h=305=struct udc_usb_ep {
drivers/usb/gadget/udc/pxa27x_udc.h-306- struct usb_ep usb_ep;
drivers/usb/gadget/udc/pxa27x_udc.h:307: struct usb_endpoint_descriptor desc;
drivers/usb/gadget/udc/pxa27x_udc.h-308- struct pxa_udc *dev;
]
|
| 38/2 |
2026/07/29 01:40 |
llm |
expert |
0m
Model:
Tokens:
input: 42230
output: 23
thoughts: 1417
Thoughts: **Analyzing a KASAN Bug in USB Gadget Driver - A Deep Dive**
Okay, so I'm looking at this KASAN report, and the first thing that jumps out is the mention of `struct ep_data` in `drivers/usb/gadget/legacy/inode.c`. I need to figure out how this structure relates to the problem. Let's see... `ep_data` contains endpoint descriptors, `desc` and `hs_desc`. Okay, `desc` and `hs_desc` are *embedded*! I should check the offsets of those descriptors within the structure just to be sure. Let's see: `desc` is at 192 bytes, `hs_desc` is at 201 bytes. Neither one is 80 bytes.
So, maybe `ep->desc` isn't *directly* a pointer to the descriptor, but part of a larger, allocated block? Could the gadget driver be allocating a block of descriptors for things like `usb_function` or `usb_configuration` and placing `usb_endpoint_descriptor` at offset 80? Let's check the functions that handle descriptor allocation like `usb_assign_descriptors` or `usb_copy_descriptors`. Aha, `usb_copy_descriptors` does allocate a single block. It's allocated using `kmalloc`.
The bug report says the buggy object is from the `io_kiocb` cache, size 248 bytes. If `bytes` in `usb_copy_descriptors` is around 248 bytes, `kmalloc-256` might be merged with the `io_kiocb` cache. If that happens, KASAN would report the cache name as `io_kiocb`. That's important!
And look! The bug report says the object *was* actually allocated from `io_kiocb`, even though it was used as a descriptor block. The trace is really interesting, because it shows the object was allocated by `io_uring` through the call chain `kmem_cache_alloc_bulk_noprof`, `__io_alloc_req_refill`, `io_alloc_req`, `io_submit_sqes.cold`. So, it's an `io_kiocb`! And it was freed by `io_uring` too, via RCU.
So, how can `ep->desc` possibly point to an `io_kiocb`? Did `ep->desc` get overwritten? Or is `ep` itself pointing to an `io_kiocb`? But `ep` is `&dum->ep[i]`. So `dum` would have to be pointing to the `io_kiocb` minus the offset of `ep[i]`. Wait, but `dum` is a `struct dummy` which is 4700 bytes. That's way too big for an `io_kiocb`. So, `ep->desc` must have been overwritten.
But if `dum` is a global or allocated object, how could `ep->desc` be overwritten with a pointer to an `io_kiocb`? Is there a use-after-free of `dum`? But if `dum` was freed and reallocated, since it is 4700 bytes it would be in `kmalloc-8k`. It wouldn't be overwritten by a 248-byte object.
Wait a second. What if the gadget driver didn't overwrite `ep->desc` but *passed* a pointer to an `io_kiocb` as the descriptor? How could that happen? Type confusion? A use-after-free within the gadget driver itself? Like, it allocates a request, frees it, and then it is reallocated as an `io_kiocb`, and the driver then uses it as a descriptor? But descriptors are usually static or allocated just once.
Aha! Look at the bug report again! The buggy address is 82 bytes *inside* of the freed 248-byte region. The buggy address is `ffff8880359f2912`. The code is reading `ep->desc->bEndpointAddress`. `bEndpointAddress` is at offset 2 in `struct usb_endpoint_descriptor`. So `ep->desc` is `ffff8880359f2910`, so that is offset 80 of the `io_kiocb`. So the pointer is offset 80 of an `io_kiocb`.
Why would `ep->desc` point to offset 80 of an `io_kiocb`?! I need to check the layout of
|
| 39/2 |
2026/07/29 01:41 |
tool |
codesearch-struct-layout |
0m
Args:
Results: map[Fields:[map[Name: OffsetBits:0 SizeBits:512] map[Name:opcode OffsetBits:512 SizeBits:8] map[Name:iopoll_completed OffsetBits:520 SizeBits:8] map[Name:buf_index OffsetBits:528 SizeBits:16] map[Name:flags OffsetBits:576 SizeBits:64] map[Name:cqe OffsetBits:640 SizeBits:128] map[Name:ctx OffsetBits:768 SizeBits:64] map[Name:tctx OffsetBits:832 SizeBits:64] map[Name: OffsetBits:896 SizeBits:64] map[Name: OffsetBits:960 SizeBits:64] map[Name:file_node OffsetBits:1024 SizeBits:64] map[Name:refs OffsetBits:1088 SizeBits:32] map[Name:cancel_seq_set OffsetBits:1120 SizeBits:8] map[Name: OffsetBits:1152 SizeBits:128] map[Name: OffsetBits:1280 SizeBits:128] map[Name:apoll OffsetBits:1408 SizeBits:64] map[Name:async_data OffsetBits:1472 SizeBits:64] map[Name:poll_refs OffsetBits:1536 SizeBits:32] map[Name:link OffsetBits:1600 SizeBits:64] map[Name:creds OffsetBits:1664 SizeBits:64] map[Name:work OffsetBits:1728 SizeBits:128] map[Name:big_cqe OffsetBits:1856 SizeBits:128]]]
|
| 40/2 |
2026/07/29 01:41 |
llm |
expert |
0m
Model:
Tokens:
input: 45157
output: 26
thoughts: 717
Thoughts: **Unraveling a Kernel Use-After-Free Vulnerability**
Okay, let's break this down. Offset 80 corresponds to 640 bits. At that offset, we have a `cqe`, a `struct io_uring_cqe`, which is 128 bits or 16 bytes. Wait, is `ep->desc` pointing to this `cqe`? That doesn't make intuitive sense. Why would it? Is there some kind of union at play here, or some other clever structure definition?
The immediate thought is corruption. Could `ep->desc` be the victim of a buffer overflow somewhere? If so, it could be pointing anywhere. But, it's suspiciously precise: it points *exactly* to offset 80 of an `io_kiocb`. That's not random. Then it hits me... is it possible that `ep->desc` *isn't* corrupted, but that the `io_kiocb` was allocated at the same address as a previously freed object that `ep->desc` was originally meant to point to? Bingo! This screams "Use-After-Free"!
Here's the likely scenario:
1. A gadget driver allocates a descriptor (or a block of descriptors) of roughly 248 bytes.
2. The driver passes a pointer to one of these descriptors, located at offset 80, to `usb_ep_enable`. Consequently, `ep->desc` now points to offset 80 of this block.
3. Here's the critical error: the gadget driver frees the memory block *without* disabling the endpoint, or perhaps there's a bug where the block is freed while still in use.
4. Then, `io_uring` reallocates the same memory block as an `io_kiocb`.
5. `io_uring` uses the `io_kiocb` as expected and then frees it again.
6. A timer interrupt fires, triggering `dummy_timer`, which in turn calls `find_endpoint`. This is where the problem reveals itself. `find_endpoint` tries to read `ep->desc->bEndpointAddress`.
7. KASAN, thank goodness, detects the Use-After-Free because the underlying `io_kiocb` has already been freed!
Now, the pieces fit together like a puzzle. If the block was freed, and then reallocated as `io_kiocb`, it implies allocation from a cache that's merged with `io_kiocb`. `io_kiocb` is about 248 bytes, and that's likely merged with `kmalloc-256`. So, the gadget driver allocated a block of between 193 and 256 bytes using `kmalloc`. `ep->desc` was sitting at offset 80 inside this block. The next logical step is to find *which* gadget driver allocates a block of descriptors where a `usb_endpoint_descriptor` is at offset 80.
Let me revisit `raw_gadget`. Specifically, let's examine the `raw_ioctl_ep_enable` function. I see this:
```c
desc = memdup_user((void __user *)value, sizeof(*desc));
```
Aha! `sizeof(*desc)` is 7. That's going to be allocated in `kmalloc-8`, which isn't the 256 size we expect.
What about `raw_ioctl_run`?
```c
static int raw_ioctl_run(struct raw_dev *dev, unsigned long value)
{
...
struct usb_raw_ep_descs *descs;
...
descs = memdup_user((void __user *)value, sizeof(*descs));
...
}
```
Okay, let's check `struct usb_raw_ep_descs`. That's the likely culprit. This is where I need to focus my efforts.
|
| 41/2 |
2026/07/29 01:41 |
tool |
codesearch-struct-layout |
0m
Error:
Args: map[Name:usb_raw_ep_descs]
Results:
|
| 42/2 |
2026/07/29 01:41 |
llm |
expert |
0m
Model:
Tokens:
input: 46792
output: 44
thoughts: 24
Thoughts: Okay, here's my take on those thoughts, tailored for an expert audience:
**Kernel Header Hunt**
Right, let me think... where would the raw gadget definitions *actually* be? My initial gut feeling, given the "uapi" and "gadget" keywords, is that it's going to be in the userspace-facing header files. I wouldn't expect it to be in an internal driver file, but likely within the "uapi" directory. That's where all the good stuff lives. Now, given that it's USB-related, and it specifies `raw_gadget`, I'm putting my money on `include/uapi/linux/usb/raw_gadget.h`. That's where I'd start digging, anyway. Should have all the structures, defines, and enumerations I need right there.
|
| 43/2 |
2026/07/29 01:41 |
tool |
grepper |
0m
Error:
Args: map[Expression:struct usb_raw_ep_descs PathPrefix:include/uapi/linux/usb/raw_gadget.h]
Results:
|
| 44/2 |
2026/07/29 01:41 |
llm |
expert |
1m
Model:
Tokens:
input: 47057
output: 29
thoughts: 20
Thoughts: Okay, here's my take on those thoughts, tailored for an expert audience:
**Diving into USB Descriptor Hell**
Alright, time to get my hands dirty. I need to find where the `usb_raw_ep_descs` structure is defined and, more importantly, *used* within this codebase. It's almost always a good starting point to hunt for the actual struct definition first, before digging through the code. I'm going to kick off a search for that string, across the entire project. This should uncover the header file where it lives, as well as any direct references, initialization points, and usage locations. Hopefully, this search will be efficient enough to quickly expose the relevant files and functions. From there, I can begin to piece together its role in the system.
|
| 45/2 |
2026/07/29 01:42 |
tool |
grepper |
0m
Error:
Args: map[Expression:struct usb_raw_ep_descs PathPrefix:]
Results:
|
| 46/2 |
2026/07/29 01:42 |
llm |
expert |
0m
Model:
Tokens:
input: 47286
output: 44
thoughts: 38
Thoughts: Okay, here's my take on those thoughts, tailored for an expert audience:
**Debugging a Potential Spelling Snafu**
Hmm, something's not quite clicking. I have this nagging feeling I might have miskeyed something, perhaps in a function name. Let me double-check the source code. Specifically, I should be looking at `raw_ioctl_run` within the `drivers/usb/gadget/legacy/raw_gadget.c` file. It's the usual location for that kind of low-level USB gadget interaction. Time to go spelunking. Hopefully it's just a simple typo, and I can be on my way.
|
| 47/2 |
2026/07/29 01:42 |
tool |
codesearch-definition-source |
0m
Args: map[ContextFile:drivers/usb/gadget/legacy/raw_gadget.c Name:raw_ioctl_run]
Results: map[SourceCode: 579: static int raw_ioctl_run(struct raw_dev *dev, unsigned long value)
580: {
581: int ret = 0;
582: unsigned long flags;
583:
584: if (value)
585: return -EINVAL;
586:
587: spin_lock_irqsave(&dev->lock, flags);
588: if (dev->state != STATE_DEV_INITIALIZED) {
589: dev_dbg(dev->dev, "fail, device is not initialized\n");
590: ret = -EINVAL;
591: goto out_unlock;
592: }
593: dev->state = STATE_DEV_REGISTERING;
594: spin_unlock_irqrestore(&dev->lock, flags);
595:
596: ret = usb_gadget_register_driver(&dev->driver);
597:
598: spin_lock_irqsave(&dev->lock, flags);
599: if (ret) {
600: dev_err(dev->dev,
601: "fail, usb_gadget_register_driver returned %d\n", ret);
602: dev->state = STATE_DEV_FAILED;
603: goto out_unlock;
604: }
605: dev->gadget_registered = true;
606: dev->state = STATE_DEV_RUNNING;
607: /* Matches kref_put() in raw_release(). */
608: kref_get(&dev->count);
609:
610: out_unlock:
611: spin_unlock_irqrestore(&dev->lock, flags);
612: return ret;
613: }
SourceFile:drivers/usb/gadget/legacy/raw_gadget.c]
|
| 48/2 |
2026/07/29 01:42 |
llm |
expert |
0m
Model:
Tokens:
input: 48054
output: 44
thoughts: 26
Thoughts: Okay, here's my take on those thoughts, framed as an expert's internal monologue:
**Investigating ioctl Descriptor Management**
Right, so I'm looking at this `raw_ioctl_run` function, and it's hitting me that it's *not* the one responsible for allocating the descriptors. That's a key observation. It means I need to trace back further to figure out where that crucial initialization step is happening. Okay, so `raw_ioctl_run` is clearly executing the operation, but who sets up the playing field? What is doing the work to create the right environment?
Let me pivot now. My next port of call is undoubtedly `raw_ioctl_init`. Is *that* the function that's responsible for the descriptor allocation? It's the logical place to start digging deeper. It makes sense that `init` handles the preparation, and `run` the execution. That's the architecture I'd expect. I'll need to confirm that suspicion by examining the code in greater detail. I need to understand the relationship between these functions, and how the critical resources are being managed, especially those all-important descriptors. This has got to be the key to understanding this entire ioctl interaction.
|
| 49/2 |
2026/07/29 01:42 |
tool |
codesearch-definition-source |
0m
Args: map[ContextFile:drivers/usb/gadget/legacy/raw_gadget.c Name:raw_ioctl_init]
Results: map[SourceCode: 480: static int raw_ioctl_init(struct raw_dev *dev, unsigned long value)
481: {
482: int ret = 0;
483: int driver_id_number;
484: struct usb_raw_init arg;
485: char *udc_driver_name;
486: char *udc_device_name;
487: char *driver_driver_name;
488: unsigned long flags;
489:
490: if (copy_from_user(&arg, (void __user *)value, sizeof(arg)))
491: return -EFAULT;
492:
493: switch (arg.speed) {
494: case USB_SPEED_UNKNOWN:
495: arg.speed = USB_SPEED_HIGH;
496: break;
497: case USB_SPEED_LOW:
498: case USB_SPEED_FULL:
499: case USB_SPEED_HIGH:
500: case USB_SPEED_SUPER:
501: break;
502: default:
503: return -EINVAL;
504: }
505:
506: driver_id_number = ida_alloc(&driver_id_numbers, GFP_KERNEL);
507: if (driver_id_number < 0)
508: return driver_id_number;
509:
510: driver_driver_name = kmalloc(DRIVER_DRIVER_NAME_LENGTH_MAX, GFP_KERNEL);
511: if (!driver_driver_name) {
512: ret = -ENOMEM;
513: goto out_free_driver_id_number;
514: }
515: snprintf(driver_driver_name, DRIVER_DRIVER_NAME_LENGTH_MAX,
516: DRIVER_NAME ".%d", driver_id_number);
517:
518: udc_driver_name = kmalloc(UDC_NAME_LENGTH_MAX, GFP_KERNEL);
519: if (!udc_driver_name) {
520: ret = -ENOMEM;
521: goto out_free_driver_driver_name;
522: }
523: ret = strscpy(udc_driver_name, &arg.driver_name[0],
524: UDC_NAME_LENGTH_MAX);
525: if (ret < 0)
526: goto out_free_udc_driver_name;
527: ret = 0;
528:
529: udc_device_name = kmalloc(UDC_NAME_LENGTH_MAX, GFP_KERNEL);
530: if (!udc_device_name) {
531: ret = -ENOMEM;
532: goto out_free_udc_driver_name;
533: }
534: ret = strscpy(udc_device_name, &arg.device_name[0],
535: UDC_NAME_LENGTH_MAX);
536: if (ret < 0)
537: goto out_free_udc_device_name;
538: ret = 0;
539:
540: spin_lock_irqsave(&dev->lock, flags);
541: if (dev->state != STATE_DEV_OPENED) {
542: dev_dbg(dev->dev, "fail, device is not opened\n");
543: ret = -EINVAL;
544: goto out_unlock;
545: }
546: dev->udc_name = udc_driver_name;
547:
548: dev->driver.function = DRIVER_DESC;
549: dev->driver.max_speed = arg.speed;
550: dev->driver.setup = gadget_setup;
551: dev->driver.disconnect = gadget_disconnect;
552: dev->driver.bind = gadget_bind;
553: dev->driver.unbind = gadget_unbind;
554: dev->driver.suspend = gadget_suspend;
555: dev->driver.resume = gadget_resume;
556: dev->driver.reset = gadget_reset;
557: dev->driver.driver.name = driver_driver_name;
558: dev->driver.udc_name = udc_device_name;
559: dev->driver.match_existing_only = 1;
560: dev->driver_id_number = driver_id_number;
561:
562: dev->state = STATE_DEV_INITIALIZED;
563: spin_unlock_irqrestore(&dev->lock, flags);
564: return ret;
565:
566: out_unlock:
567: spin_unlock_irqrestore(&dev->lock, flags);
568: out_free_udc_device_name:
569: kfree(udc_device_name);
570: out_free_udc_driver_name:
571: kfree(udc_driver_name);
572: out_free_driver_driver_name:
573: kfree(driver_driver_name);
574: out_free_driver_id_number:
575: ida_free(&driver_id_numbers, driver_id_number);
576: return ret;
577: }
SourceFile:drivers/usb/gadget/legacy/raw_gadget.c]
|
| 50/2 |
2026/07/29 01:42 |
llm |
expert |
0m
Model:
Tokens:
input: 49969
output: 45
thoughts: 1286
Thoughts: **Exploiting Descriptor Memory Corruption in FunctionFS**
Okay, so I've been digging into this `raw_gadget` code and it looks like it uses `memdup_user` to allocate a descriptor, which seems straightforward enough. But then I started thinking, what if the `desc` isn't allocated by `raw_gadget` directly? That got me looking at FunctionFS, `f_fs.c`. This is where it gets interesting, because FunctionFS lets userspace provide their own descriptors.
Specifically, `ffs_data_get_descs`, called from `ffs_ep0_write`, reads descriptors from userspace and allocates a block of memory, `ffs->raw_descs_data`, using `kmalloc`. This is where the potential issue lies, if I'm understanding this correctly. This `ffs->raw_descs_data` block contains *all* the descriptors, and `ffs_ep` pointers actually point *inside* this block! For example, `ep->descs[0]` points into `ffs->raw_descs_data`.
The key part is that when an endpoint is enabled, `usb_ep_enable` sets `ep->desc = desc`, where `desc` is one of the pointers inside the `ffs->raw_descs_data` block. So, if we can somehow free `ffs->raw_descs_data` and then reallocate that same memory region with something else, like an `io_kiocb`, we could potentially control where `ep->desc` points! The offset within the reallocated memory would be dictated by the user-provided descriptor layout.
The crux of the problem becomes: when is `ffs->raw_descs_data` freed? `ffs_data_clear` and `ffs_data_reset` look like the candidates. The `kfree` is in `ffs_data_clear`. The next question is, can we free this memory while an endpoint is still *enabled*? This is where the protection mechanisms come in. FunctionFS is *supposed* to disable endpoints before freeing descriptors. I need to examine `ffs_func_disable` or `ffs_func_unbind` and see if endpoints are properly disabled. `ffs_func_set_alt` does indeed disable endpoints. Crucially, `usb_ep_disable` calls `ep->desc = NULL`. Which means if the endpoint is correctly disabled before the memory is freed, there's no problem.
But what if, *somehow*, the endpoint is *not* disabled before the free? What if `usb_ep_disable` is called *after* `ffs->raw_descs_data` is freed? I need to check where `ffs_data_clear` is called from. It is called from `ffs_data_reset` and `ffs_data_put`. `ffs_data_reset` is called from `ffs_ep0_write` when the user writes to ep0. Now, I need to understand when and why `ffs_data_reset` is called in `ffs_ep0_write`. I need to figure out the flow of control and determine how the descriptors are set up and torn down.
|
| 51/2 |
2026/07/29 01:42 |
tool |
codesearch-definition-source |
0m
Args: map[ContextFile:drivers/usb/gadget/function/f_fs.c Name:ffs_ep0_write]
Results: map[SourceCode: 365: static ssize_t ffs_ep0_write(struct file *file, const char __user *buf,
366: size_t len, loff_t *ptr)
367: {
368: struct ffs_data *ffs = file->private_data;
369: ssize_t ret;
370: char *data;
371:
372: /* Fast check if setup was canceled */
373: if (ffs_setup_state_clear_cancelled(ffs) == FFS_SETUP_CANCELLED)
374: return -EIDRM;
375:
376: /* Acquire mutex */
377: ret = ffs_mutex_lock(&ffs->mutex, file->f_flags & O_NONBLOCK);
378: if (ret < 0)
379: return ret;
380:
381: /* Check state */
382: switch (ffs->state) {
383: case FFS_READ_DESCRIPTORS:
384: case FFS_READ_STRINGS:
385: /* Copy data */
386: if (len < 16) {
387: ret = -EINVAL;
388: break;
389: }
390:
391: data = ffs_prepare_buffer(buf, len);
392: if (IS_ERR(data)) {
393: ret = PTR_ERR(data);
394: break;
395: }
396:
397: /* Handle data */
398: if (ffs->state == FFS_READ_DESCRIPTORS) {
399: pr_info("read descriptors\n");
400: ret = __ffs_data_got_descs(ffs, data, len);
401: if (ret < 0)
402: break;
403:
404: ffs->state = FFS_READ_STRINGS;
405: ret = len;
406: } else {
407: pr_info("read strings\n");
408: ret = __ffs_data_got_strings(ffs, data, len);
409: if (ret < 0)
410: break;
411:
412: ret = ffs_epfiles_create(ffs);
413: if (ret) {
414: ffs->state = FFS_CLOSING;
415: break;
416: }
417:
418: ffs->state = FFS_ACTIVE;
419: mutex_unlock(&ffs->mutex);
420:
421: ret = ffs_ready(ffs);
422: if (ret < 0) {
423: ffs->state = FFS_CLOSING;
424: return ret;
425: }
426:
427: return len;
428: }
429: break;
430:
431: case FFS_ACTIVE:
432: data = NULL;
433: /*
434: * We're called from user space, we can use _irq
435: * rather then _irqsave
436: */
437: spin_lock_irq(&ffs->ev.waitq.lock);
438: switch (ffs_setup_state_clear_cancelled(ffs)) {
439: case FFS_SETUP_CANCELLED:
440: ret = -EIDRM;
441: goto done_spin;
442:
443: case FFS_NO_SETUP:
444: ret = -ESRCH;
445: goto done_spin;
446:
447: case FFS_SETUP_PENDING:
448: break;
449: }
450:
451: /* FFS_SETUP_PENDING */
452: if (!(ffs->ev.setup.bRequestType & USB_DIR_IN)) {
453: spin_unlock_irq(&ffs->ev.waitq.lock);
454: ret = __ffs_ep0_stall(ffs);
455: break;
456: }
457:
458: /* FFS_SETUP_PENDING and not stall */
459: len = min_t(size_t, len, le16_to_cpu(ffs->ev.setup.wLength));
460:
461: spin_unlock_irq(&ffs->ev.waitq.lock);
462:
463: data = ffs_prepare_buffer(buf, len);
464: if (IS_ERR(data)) {
465: ret = PTR_ERR(data);
466: break;
467: }
468:
469: spin_lock_irq(&ffs->ev.waitq.lock);
470:
471: /*
472: * We are guaranteed to be still in FFS_ACTIVE state
473: * but the state of setup could have changed from
474: * FFS_SETUP_PENDING to FFS_SETUP_CANCELLED so we need
475: * to check for that. If that happened we copied data
476: * from user space in vain but it's unlikely.
477: *
478: * For sure we are not in FFS_NO_SETUP since this is
479: * the only place FFS_SETUP_PENDING -> FFS_NO_SETUP
480: * transition can be performed and it's protected by
481: * mutex.
482: */
483: if (ffs_setup_state_clear_cancelled(ffs) ==
484: FFS_SETUP_CANCELLED) {
485: ret = -EIDRM;
486: done_spin:
487: spin_unlock_irq(&ffs->ev.waitq.lock);
488: } else {
489: /* unlocks spinlock */
490: ret = __ffs_ep0_queue_wait(ffs, data, len);
491: }
492: kfree(data);
493: break;
494:
495: default:
496: ret = -EBADFD;
497: break;
498: }
499:
500: mutex_unlock(&ffs->mutex);
501: return ret;
502: }
SourceFile:drivers/usb/gadget/function/f_fs.c]
|
| 52/2 |
2026/07/29 01:42 |
llm |
expert |
0m
Model:
Tokens:
input: 53970
output: 44
thoughts: 289
Thoughts: **Analyzing the ffs_data_reset Call Flow**
Okay, hold on a second. I just realized something. The `ffs_data_reset` function isn't directly called from `ffs_ep0_write` as I initially assumed. That's a crucial point. So, when *is* it called? Let's trace it back. Ah, here it is: it's invoked from `ffs_ep0_release`. Right, that makes sense.
```c
static int ffs_ep0_release(struct inode *inode, struct file *file)
{
struct ffs_data *ffs = file->private_data;
ffs_data_closed(ffs);
return 0;
}
```
`ffs_ep0_release` calls `ffs_data_closed`. That function is probably where the logic resides. Let's see...
```c
static void ffs_data_closed(struct ffs_data *ffs)
{
...
if (ffs->opened) {
ffs->opened = 0;
ffs_data_reset(ffs);
}
...
}
```
Yes, there it is! `ffs_data_closed` calls `ffs_data_reset` when the file descriptor is closed, and `ffs->opened` is set. So, if the user closes the ep0 file descriptor, `ffs_data_reset` gets executed. Okay, that's clear.
Now, what does `ffs_data_reset` *do*? I remember it calls `ffs_data_clear`, which I believe is responsible for freeing the `ffs->raw_descs_data` buffer. That's significant. But it raises a bigger question: If the gadget is still bound, does closing ep0 effectively unbind it? That's not the usual behaviour. I need to be sure about this. I'll need to double-check the `ffs_data_reset` function itself to be absolutely certain of the implications here. I should also check the return values of functions as they could be masking an underlying error condition.
|
| 53/2 |
2026/07/29 01:42 |
tool |
codesearch-definition-source |
0m
Args: map[ContextFile:drivers/usb/gadget/function/f_fs.c Name:ffs_data_reset]
Results: map[SourceCode:2267: static void ffs_data_reset(struct ffs_data *ffs)
2268: {
2269: ffs_data_clear(ffs);
2270:
2271: spin_lock_irq(&ffs->eps_lock);
2272: ffs->raw_descs_data = NULL;
2273: ffs->raw_descs = NULL;
2274: ffs->raw_strings = NULL;
2275: ffs->stringtabs = NULL;
2276:
2277: ffs->raw_descs_length = 0;
2278: ffs->fs_descs_count = 0;
2279: ffs->hs_descs_count = 0;
2280: ffs->ss_descs_count = 0;
2281:
2282: ffs->strings_count = 0;
2283: ffs->interfaces_count = 0;
2284: ffs->eps_count = 0;
2285:
2286: ffs->ev.count = 0;
2287:
2288: ffs->state = FFS_READ_DESCRIPTORS;
2289: ffs->setup_state = FFS_NO_SETUP;
2290: ffs->flags = 0;
2291:
2292: ffs->ms_os_descs_ext_prop_count = 0;
2293: ffs->ms_os_descs_ext_prop_name_len = 0;
2294: ffs->ms_os_descs_ext_prop_data_len = 0;
2295: spin_unlock_irq(&ffs->eps_lock);
2296: }
SourceFile:drivers/usb/gadget/function/f_fs.c]
|
| 54/2 |
2026/07/29 01:42 |
llm |
expert |
0m
Model:
Tokens:
input: 55320
output: 42
thoughts: 147
Thoughts: **Analyzing the Endpoint Deactivation Sequence**
Okay, so `ffs_data_clear` is definitely freeing `ffs->raw_descs_data`. That's important to remember. But the real question is, does this procedure effectively disable endpoints? I need to understand the full lifecycle here.
I see that `ffs_data_clear` first checks and clears the `FFS_FL_CALL_CLOSED_CALLBACK` flag. If that flag is set, it calls `ffs_closed`. Now, is `ffs_closed` the one that takes care of the endpoint deactivation, or is it another function?
My suspicion is `ffs_closed` will disable the endpoints and that it calls `ffs_func_disable` β I should confirm by checking the implementation of `ffs_closed`. Let's dig into that function and make sure this hypothesis holds true.
|
| 55/2 |
2026/07/29 01:42 |
tool |
codesearch-definition-source |
0m
Args: map[ContextFile:drivers/usb/gadget/function/f_fs.c Name:ffs_closed]
Results: map[SourceCode:4301: static void ffs_closed(struct ffs_data *ffs)
4302: {
4303: struct ffs_dev *ffs_obj;
4304: struct f_fs_opts *opts;
4305: struct config_item *ci;
4306:
4307: ffs_dev_lock();
4308:
4309: ffs_obj = ffs->private_data;
4310: if (!ffs_obj)
4311: goto done;
4312:
4313: ffs_obj->desc_ready = false;
4314:
4315: if (test_and_clear_bit(FFS_FL_CALL_CLOSED_CALLBACK, &ffs->flags) &&
4316: ffs_obj->ffs_closed_callback)
4317: ffs_obj->ffs_closed_callback(ffs);
4318:
4319: if (ffs_obj->opts)
4320: opts = ffs_obj->opts;
4321: else
4322: goto done;
4323:
4324: if (opts->no_configfs || !opts->func_inst.group.cg_item.ci_parent
4325: || !kref_read(&opts->func_inst.group.cg_item.ci_kref))
4326: goto done;
4327:
4328: ci = opts->func_inst.group.cg_item.ci_parent->ci_parent;
4329: ffs_dev_unlock();
4330:
4331: if (test_bit(FFS_FL_BOUND, &ffs->flags))
4332: unregister_gadget_item(ci);
4333: return;
4334: done:
4335: ffs_dev_unlock();
4336: }
SourceFile:drivers/usb/gadget/function/f_fs.c]
|
| 56/2 |
2026/07/29 01:43 |
llm |
expert |
0m
Model:
Tokens:
input: 56273
output: 44
thoughts: 48
Thoughts: Okay, here's my take on those thoughts, framed from my own perspective:
**Unpacking the Gadget Unregistration**
Right, `unregister_gadget_item`... that's the obvious unregistration function, but hold on a sec. I'm seeing a potential race condition here. It looks like `ffs_data_clear` is being called *before* `ffs_closed`? That's not the usual order of operations I'd expect. It's making me a little uneasy.
I need to dive deeper. `ffs_data_clear`... that's the key. I need to understand what that function does, and more importantly, what resources it releases and in what order. I'm mentally going over the possible implications: are we potentially clearing data while the connection is still actively being handled? Could this lead to corruption or inconsistencies? This calls for a closer look at that specific function's implementation and how it interacts with the lower-level file system and hardware. Let's see what's actually going on in there.
|
| 57/2 |
2026/07/29 01:43 |
tool |
codesearch-definition-source |
0m
Args: map[ContextFile:drivers/usb/gadget/function/f_fs.c Name:ffs_data_clear]
Results: map[SourceCode:2233: static void ffs_data_clear(struct ffs_data *ffs)
2234: {
2235: struct ffs_epfile *epfiles;
2236: unsigned long flags;
2237:
2238: ffs_closed(ffs);
2239:
2240: BUG_ON(ffs->gadget);
2241:
2242: spin_lock_irqsave(&ffs->eps_lock, flags);
2243: epfiles = ffs->epfiles;
2244: ffs->epfiles = NULL;
2245: spin_unlock_irqrestore(&ffs->eps_lock, flags);
2246:
2247: /*
2248: * potential race possible between ffs_func_eps_disable
2249: * & ffs_epfile_release therefore maintaining a local
2250: * copy of epfile will save us from use-after-free.
2251: */
2252: if (epfiles) {
2253: ffs_epfiles_destroy(ffs->sb, epfiles, ffs->eps_count);
2254: ffs->epfiles = NULL;
2255: }
2256:
2257: if (ffs->ffs_eventfd) {
2258: eventfd_ctx_put(ffs->ffs_eventfd);
2259: ffs->ffs_eventfd = NULL;
2260: }
2261:
2262: kfree(ffs->raw_descs_data);
2263: kfree(ffs->raw_strings);
2264: kfree(ffs->stringtabs);
2265: }
SourceFile:drivers/usb/gadget/function/f_fs.c]
|
| 58/2 |
2026/07/29 01:43 |
llm |
expert |
0m
Model:
Tokens:
input: 57117
output: 21
thoughts: 805
Thoughts: **Analyzing a USB Gadget Driver Memory Corruption Bug**
Okay, so I'm looking at this crash report, and the first thing that jumps out is that `ffs_data_clear` has a `BUG_ON(ffs->gadget);`. That means `ffs->gadget` needs to be NULL when this function is called, or we're going to have a serious problem. Where does `ffs->gadget` get set to NULL? Ah, in `ffs_func_unbind`. Makes sense, unbinding the function from the gadget. And `ffs_func_unbind` also calls `ffs_func_eps_disable`, which in turn calls `usb_ep_disable`. If `usb_ep_disable` is called, that means `ep->desc` should be set to NULL. So, we're good there. It seems normal.
But wait, what about the `raw_gadget` path? We need to consider all possibilities. Let's see... in `raw_ioctl_ep_enable`, `desc` is allocated by `memdup_user`. Okay, so `desc` should have valid data. So, what if `desc` isn't allocated by `memdup_user`? Hmmm... This looks normal. It's a 7-byte descriptor.
Right, the crash report. It mentions an address within the `io_kiocb` cache. No, `io_uring` is a framework, not a gadget driver. Could it be `gadgetfs`? The report mentions an out-of-bounds access. Let's check `drivers/usb/gadget/legacy/inode.c`.
In `ep_config`, we have descriptor allocation. It looks like it allocates `data->desc` and `data->hs_desc`, and then `ep->desc` is set to point to `data->desc`. The type of `data` is `struct ep_data`. What's the size of `struct ep_data`? Let's assume it's `kmalloc-256`. It appears there are two descriptors allocated and the allocation of `ep_data`. However, from the crash report, if `ep_data` is 312 bytes, it is allocated from `kmalloc-512`, not `kmalloc-256`.
The bug report says the offset is 82 bytes into a 248-byte region. If the `ep_data` struct size is smaller in the kernel version of the bug report and the allocation is done with a smaller size (like `kmalloc-256`) that can cause an out-of-bounds read/write. So, I have to check the size of `struct ep_data` in the relevant kernel version. That's the key. I need to get the kernel version.
|
| 59/2 |
2026/07/29 01:43 |
tool |
codesearch-struct-layout |
0m
Args:
Results: map[Fields:[map[Name:lock OffsetBits:0 SizeBits:1088] map[Name:state OffsetBits:1088 SizeBits:32] map[Name:count OffsetBits:1120 SizeBits:32] map[Name:dev OffsetBits:1152 SizeBits:64] map[Name:ep OffsetBits:1216 SizeBits:64] map[Name:req OffsetBits:1280 SizeBits:64] map[Name:status OffsetBits:1344 SizeBits:64] map[Name:name OffsetBits:1408 SizeBits:128] map[Name:desc OffsetBits:1536 SizeBits:72] map[Name:hs_desc OffsetBits:1608 SizeBits:72] map[Name:epfiles OffsetBits:1728 SizeBits:128] map[Name:wait OffsetBits:1856 SizeBits:640]]]
|
| 60/2 |
2026/07/29 01:43 |
llm |
expert |
0m
Model:
Tokens:
input: 58817
output: 33
thoughts: 1103
Thoughts: **Diving into the Bug's Root Cause**
Okay, so the `desc` offset being reported as 192 bytes (1536 bits) doesn't seem to be the issue since `ep->desc` points to the descriptor itself, not a structure containing the `ep_data`. The bug report points to `ep->desc` being at offset 80, which is where things get interesting.
I considered `struct ffs_ep`, but the `descs` member is an array of pointers to descriptors, so that's not it. This bug is about `ep->desc` which is pointing to the descriptor itself.
Aha! We're dealing with `ep->desc->bEndpointAddress`. It seems `ep->desc` is pointing to offset 80 of an `io_kiocb` structure. That sounds like an `io_uring` context, right? Could `io_uring` somehow be involved in overwriting the `ep->desc` pointer? A buffer overflow or use-after-free might overwrite the pointer, setting it to `io_kiocb + 80`. Why *exactly* 80?
Hold on. Offset 80 in `io_kiocb` lands us right at the `cqe` member, which is of type `struct io_uring_cqe`. And the very first member of `cqe`? `user_data`! If `ep->desc` points to this `cqe`, it means `ep->desc` is effectively pointing to the `user_data` field.
But why would `ep->desc` be assigned the address of `kiocb->cqe`? Maybe the pointer itself wasn't overwritten, but `ep` itself points to something in such a way that `ep->desc` is overlapping a pointer to `kiocb->cqe`? No, `ep` is `&dum->ep[i]`. `dum` is a `struct dummy`, that's not it either.
What if the gadget driver is `raw_gadget`, and the user passed `io_uring` memory as `desc`? No, `raw_ioctl_ep_enable` uses `memdup_user`, which allocates new buffer. Ah, but what if the gadget driver *is* `dummy_hcd`? Looking at the `struct dummy_ep` within `dummy_hcd`, we see the `desc` pointer.
Here's the key: `ep->desc` is a pointer stored in `struct dummy_ep`, and the value of that pointer is somehow `&kiocb->cqe`. How does this happen? What if the original `desc` was allocated by `kmalloc`, and its address happens to be `&kiocb->cqe`? No, because `kmalloc` returns the beginning of the slab object.
Unless... `desc` is embedded in a structure allocated by `kmalloc`, which is then freed and reallocated as `io_kiocb`. That's it! We're onto something. This structure must have `usb_endpoint_descriptor` at offset 80. Now, let's search for structures with that descriptor at offset 80.
|
| 61/2 |
2026/07/29 01:43 |
tool |
grepper |
0m
Args: map[Expression:struct usb_endpoint_descriptor PathPrefix:drivers/usb/gadget/]
Results: map[Output:
Full output is too long, showing 500 out of 1192 lines.
Use more precise expression if possible.
drivers/usb/gadget/composite.c=151=int config_ep_by_speed_and_alt(struct usb_gadget *g,
--
drivers/usb/gadget/composite.c-155-{
drivers/usb/gadget/composite.c:156: struct usb_endpoint_descriptor *chosen_desc = NULL;
drivers/usb/gadget/composite.c-157- struct usb_interface_descriptor *int_desc = NULL;
--
drivers/usb/gadget/composite.c-218- for_each_desc(speed_desc, d_spd, USB_DT_ENDPOINT) {
drivers/usb/gadget/composite.c:219: chosen_desc = (struct usb_endpoint_descriptor *)*d_spd;
drivers/usb/gadget/composite.c-220- if (chosen_desc->bEndpointAddress == _ep->address)
--
drivers/usb/gadget/composite.c=947=static int set_config(struct usb_composite_dev *cdev,
--
drivers/usb/gadget/composite.c-1005- for (; *descriptors; ++descriptors) {
drivers/usb/gadget/composite.c:1006: struct usb_endpoint_descriptor *ep;
drivers/usb/gadget/composite.c-1007- int addr;
--
drivers/usb/gadget/composite.c-1011-
drivers/usb/gadget/composite.c:1012: ep = (struct usb_endpoint_descriptor *)*descriptors;
drivers/usb/gadget/composite.c-1013- addr = ((ep->bEndpointAddress & 0x80) >> 3)
--
drivers/usb/gadget/epautoconf.c=63=struct usb_ep *usb_ep_autoconfig_ss(
drivers/usb/gadget/epautoconf.c-64- struct usb_gadget *gadget,
drivers/usb/gadget/epautoconf.c:65: struct usb_endpoint_descriptor *desc,
drivers/usb/gadget/epautoconf.c-66- struct usb_ss_ep_comp_descriptor *ep_comp
--
drivers/usb/gadget/epautoconf.c=149=struct usb_ep *usb_ep_autoconfig(
drivers/usb/gadget/epautoconf.c-150- struct usb_gadget *gadget,
drivers/usb/gadget/epautoconf.c:151: struct usb_endpoint_descriptor *desc
drivers/usb/gadget/epautoconf.c-152-)
--
drivers/usb/gadget/function/f_acm.c=146=static struct usb_cdc_union_desc acm_union_desc = {
--
drivers/usb/gadget/function/f_acm.c-155-
drivers/usb/gadget/function/f_acm.c:156:static struct usb_endpoint_descriptor acm_fs_notify_desc = {
drivers/usb/gadget/function/f_acm.c-157- .bLength = USB_DT_ENDPOINT_SIZE,
--
drivers/usb/gadget/function/f_acm.c-164-
drivers/usb/gadget/function/f_acm.c:165:static struct usb_endpoint_descriptor acm_fs_in_desc = {
drivers/usb/gadget/function/f_acm.c-166- .bLength = USB_DT_ENDPOINT_SIZE,
--
drivers/usb/gadget/function/f_acm.c-171-
drivers/usb/gadget/function/f_acm.c:172:static struct usb_endpoint_descriptor acm_fs_out_desc = {
drivers/usb/gadget/function/f_acm.c-173- .bLength = USB_DT_ENDPOINT_SIZE,
--
drivers/usb/gadget/function/f_acm.c=179=static struct usb_descriptor_header *acm_fs_function[] = {
--
drivers/usb/gadget/function/f_acm.c-193-/* high speed support: */
drivers/usb/gadget/function/f_acm.c:194:static struct usb_endpoint_descriptor acm_hs_notify_desc = {
drivers/usb/gadget/function/f_acm.c-195- .bLength = USB_DT_ENDPOINT_SIZE,
--
drivers/usb/gadget/function/f_acm.c-202-
drivers/usb/gadget/function/f_acm.c:203:static struct usb_endpoint_descriptor acm_hs_in_desc = {
drivers/usb/gadget/function/f_acm.c-204- .bLength = USB_DT_ENDPOINT_SIZE,
--
drivers/usb/gadget/function/f_acm.c-209-
drivers/usb/gadget/function/f_acm.c:210:static struct usb_endpoint_descriptor acm_hs_out_desc = {
drivers/usb/gadget/function/f_acm.c-211- .bLength = USB_DT_ENDPOINT_SIZE,
--
drivers/usb/gadget/function/f_acm.c=217=static struct usb_descriptor_header *acm_hs_function[] = {
--
drivers/usb/gadget/function/f_acm.c-230-
drivers/usb/gadget/function/f_acm.c:231:static struct usb_endpoint_descriptor acm_ss_in_desc = {
drivers/usb/gadget/function/f_acm.c-232- .bLength = USB_DT_ENDPOINT_SIZE,
--
drivers/usb/gadget/function/f_acm.c-237-
drivers/usb/gadget/function/f_acm.c:238:static struct usb_endpoint_descriptor acm_ss_out_desc = {
drivers/usb/gadget/function/f_acm.c-239- .bLength = USB_DT_ENDPOINT_SIZE,
--
drivers/usb/gadget/function/f_ecm.c=166=static struct usb_interface_descriptor ecm_data_intf = {
--
drivers/usb/gadget/function/f_ecm.c-180-
drivers/usb/gadget/function/f_ecm.c:181:static struct usb_endpoint_descriptor fs_ecm_notify_desc = {
drivers/usb/gadget/function/f_ecm.c-182- .bLength = USB_DT_ENDPOINT_SIZE,
--
drivers/usb/gadget/function/f_ecm.c-190-
drivers/usb/gadget/function/f_ecm.c:191:static struct usb_endpoint_descriptor fs_ecm_in_desc = {
drivers/usb/gadget/function/f_ecm.c-192- .bLength = USB_DT_ENDPOINT_SIZE,
--
drivers/usb/gadget/function/f_ecm.c-198-
drivers/usb/gadget/function/f_ecm.c:199:static struct usb_endpoint_descriptor fs_ecm_out_desc = {
drivers/usb/gadget/function/f_ecm.c-200- .bLength = USB_DT_ENDPOINT_SIZE,
--
drivers/usb/gadget/function/f_ecm.c=207=static struct usb_descriptor_header *ecm_fs_function[] = {
--
drivers/usb/gadget/function/f_ecm.c-227-
drivers/usb/gadget/function/f_ecm.c:228:static struct usb_endpoint_descriptor hs_ecm_notify_desc = {
drivers/usb/gadget/function/f_ecm.c-229- .bLength = USB_DT_ENDPOINT_SIZE,
--
drivers/usb/gadget/function/f_ecm.c-237-
drivers/usb/gadget/function/f_ecm.c:238:static struct usb_endpoint_descriptor hs_ecm_in_desc = {
drivers/usb/gadget/function/f_ecm.c-239- .bLength = USB_DT_ENDPOINT_SIZE,
--
drivers/usb/gadget/function/f_ecm.c-246-
drivers/usb/gadget/function/f_ecm.c:247:static struct usb_endpoint_descriptor hs_ecm_out_desc = {
drivers/usb/gadget/function/f_ecm.c-248- .bLength = USB_DT_ENDPOINT_SIZE,
--
drivers/usb/gadget/function/f_ecm.c=256=static struct usb_descriptor_header *ecm_hs_function[] = {
--
drivers/usb/gadget/function/f_ecm.c-276-
drivers/usb/gadget/function/f_ecm.c:277:static struct usb_endpoint_descriptor ss_ecm_notify_desc = {
drivers/usb/gadget/function/f_ecm.c-278- .bLength = USB_DT_ENDPOINT_SIZE,
--
drivers/usb/gadget/function/f_ecm.c=287=static struct usb_ss_ep_comp_descriptor ss_ecm_intr_comp_desc = {
--
drivers/usb/gadget/function/f_ecm.c-296-
drivers/usb/gadget/function/f_ecm.c:297:static struct usb_endpoint_descriptor ss_ecm_in_desc = {
drivers/usb/gadget/function/f_ecm.c-298- .bLength = USB_DT_ENDPOINT_SIZE,
--
drivers/usb/gadget/function/f_ecm.c-305-
drivers/usb/gadget/function/f_ecm.c:306:static struct usb_endpoint_descriptor ss_ecm_out_desc = {
drivers/usb/gadget/function/f_ecm.c-307- .bLength = USB_DT_ENDPOINT_SIZE,
--
drivers/usb/gadget/function/f_eem.c=48=static struct usb_interface_descriptor eem_intf = {
--
drivers/usb/gadget/function/f_eem.c-61-
drivers/usb/gadget/function/f_eem.c:62:static struct usb_endpoint_descriptor eem_fs_in_desc = {
drivers/usb/gadget/function/f_eem.c-63- .bLength = USB_DT_ENDPOINT_SIZE,
--
drivers/usb/gadget/function/f_eem.c-69-
drivers/usb/gadget/function/f_eem.c:70:static struct usb_endpoint_descriptor eem_fs_out_desc = {
drivers/usb/gadget/function/f_eem.c-71- .bLength = USB_DT_ENDPOINT_SIZE,
--
drivers/usb/gadget/function/f_eem.c=78=static struct usb_descriptor_header *eem_fs_function[] = {
--
drivers/usb/gadget/function/f_eem.c-87-
drivers/usb/gadget/function/f_eem.c:88:static struct usb_endpoint_descriptor eem_hs_in_desc = {
drivers/usb/gadget/function/f_eem.c-89- .bLength = USB_DT_ENDPOINT_SIZE,
--
drivers/usb/gadget/function/f_eem.c-96-
drivers/usb/gadget/function/f_eem.c:97:static struct usb_endpoint_descriptor eem_hs_out_desc = {
drivers/usb/gadget/function/f_eem.c-98- .bLength = USB_DT_ENDPOINT_SIZE,
--
drivers/usb/gadget/function/f_eem.c=106=static struct usb_descriptor_header *eem_hs_function[] = {
--
drivers/usb/gadget/function/f_eem.c-115-
drivers/usb/gadget/function/f_eem.c:116:static struct usb_endpoint_descriptor eem_ss_in_desc = {
drivers/usb/gadget/function/f_eem.c-117- .bLength = USB_DT_ENDPOINT_SIZE,
--
drivers/usb/gadget/function/f_eem.c-124-
drivers/usb/gadget/function/f_eem.c:125:static struct usb_endpoint_descriptor eem_ss_out_desc = {
drivers/usb/gadget/function/f_eem.c-126- .bLength = USB_DT_ENDPOINT_SIZE,
--
drivers/usb/gadget/function/f_fs.c=126=struct ffs_ep {
--
drivers/usb/gadget/function/f_fs.c-130- /* [0]: full speed, [1]: high speed, [2]: super speed */
drivers/usb/gadget/function/f_fs.c:131: struct usb_endpoint_descriptor *descs[3];
drivers/usb/gadget/function/f_fs.c-132-
--
drivers/usb/gadget/function/f_fs.c=1753=static long ffs_epfile_ioctl(struct file *file, unsigned code,
--
drivers/usb/gadget/function/f_fs.c-1824- int desc_idx;
drivers/usb/gadget/function/f_fs.c:1825: struct usb_endpoint_descriptor desc1, *desc;
drivers/usb/gadget/function/f_fs.c-1826-
--
drivers/usb/gadget/function/f_fs.c=2503=static int __must_check ffs_do_single_desc(char *data, unsigned len,
--
drivers/usb/gadget/function/f_fs.c-2566- case USB_DT_ENDPOINT: {
drivers/usb/gadget/function/f_fs.c:2567: struct usb_endpoint_descriptor *ds = (void *)_ds;
drivers/usb/gadget/function/f_fs.c-2568- pr_vdebug("endpoint descriptor\n");
--
drivers/usb/gadget/function/f_fs.c=2687=static int __ffs_data_do_entity(enum ffs_entity_type type,
--
drivers/usb/gadget/function/f_fs.c-2691- struct ffs_desc_helper *helper = priv;
drivers/usb/gadget/function/f_fs.c:2692: struct usb_endpoint_descriptor *d;
drivers/usb/gadget/function/f_fs.c-2693-
--
drivers/usb/gadget/function/f_fs.c=3281=static int __ffs_func_bind_do_descs(enum ffs_entity_type type, u8 *valuep,
--
drivers/usb/gadget/function/f_fs.c-3284-{
drivers/usb/gadget/function/f_fs.c:3285: struct usb_endpoint_descriptor *ds = (void *)desc;
drivers/usb/gadget/function/f_fs.c-3286- struct ffs_function *func = priv;
--
drivers/usb/gadget/function/f_fs.c=3381=static int __ffs_func_bind_do_nums(enum ffs_entity_type type, u8 *valuep,
--
drivers/usb/gadget/function/f_fs.c-3423- {
drivers/usb/gadget/function/f_fs.c:3424: struct usb_endpoint_descriptor **descs;
drivers/usb/gadget/function/f_fs.c-3425- descs = func->eps[idx].descs;
--
drivers/usb/gadget/function/f_hid.c=145=static struct hid_descriptor hidg_desc = {
--
drivers/usb/gadget/function/f_hid.c-156-
drivers/usb/gadget/function/f_hid.c:157:static struct usb_endpoint_descriptor hidg_ss_in_ep_desc = {
drivers/usb/gadget/function/f_hid.c-158- .bLength = USB_DT_ENDPOINT_SIZE,
--
drivers/usb/gadget/function/f_hid.c=166=static struct usb_ss_ep_comp_descriptor hidg_ss_in_comp_desc = {
--
drivers/usb/gadget/function/f_hid.c-174-
drivers/usb/gadget/function/f_hid.c:175:static struct usb_endpoint_descriptor hidg_ss_out_ep_desc = {
drivers/usb/gadget/function/f_hid.c-176- .bLength = USB_DT_ENDPOINT_SIZE,
--
drivers/usb/gadget/function/f_hid.c=203=static struct usb_descriptor_header *hidg_ss_descriptors_ssreport[] = {
--
drivers/usb/gadget/function/f_hid.c-212-
drivers/usb/gadget/function/f_hid.c:213:static struct usb_endpoint_descriptor hidg_hs_in_ep_desc = {
drivers/usb/gadget/function/f_hid.c-214- .bLength = USB_DT_ENDPOINT_SIZE,
--
drivers/usb/gadget/function/f_hid.c-221-
drivers/usb/gadget/function/f_hid.c:222:static struct usb_endpoint_descriptor hidg_hs_out_ep_desc = {
drivers/usb/gadget/function/f_hid.c-223- .bLength = USB_DT_ENDPOINT_SIZE,
--
drivers/usb/gadget/function/f_hid.c=239=static struct usb_descriptor_header *hidg_hs_descriptors_ssreport[] = {
--
drivers/usb/gadget/function/f_hid.c-247-
drivers/usb/gadget/function/f_hid.c:248:static struct usb_endpoint_descriptor hidg_fs_in_ep_desc = {
drivers/usb/gadget/function/f_hid.c-249- .bLength = USB_DT_ENDPOINT_SIZE,
--
drivers/usb/gadget/function/f_hid.c-256-
drivers/usb/gadget/function/f_hid.c:257:static struct usb_endpoint_descriptor hidg_fs_out_ep_desc = {
drivers/usb/gadget/function/f_hid.c-258- .bLength = USB_DT_ENDPOINT_SIZE,
--
drivers/usb/gadget/function/f_loopback.c=45=static struct usb_interface_descriptor loopback_intf = {
--
drivers/usb/gadget/function/f_loopback.c-55-
drivers/usb/gadget/function/f_loopback.c:56:static struct usb_endpoint_descriptor fs_loop_source_desc = {
drivers/usb/gadget/function/f_loopback.c-57- .bLength = USB_DT_ENDPOINT_SIZE,
--
drivers/usb/gadget/function/f_loopback.c-63-
drivers/usb/gadget/function/f_loopback.c:64:static struct usb_endpoint_descriptor fs_loop_sink_desc = {
drivers/usb/gadget/function/f_loopback.c-65- .bLength = USB_DT_ENDPOINT_SIZE,
--
drivers/usb/gadget/function/f_loopback.c=72=static struct usb_descriptor_header *fs_loopback_descs[] = {
--
drivers/usb/gadget/function/f_loopback.c-80-
drivers/usb/gadget/function/f_loopback.c:81:static struct usb_endpoint_descriptor hs_loop_source_desc = {
drivers/usb/gadget/function/f_loopback.c-82- .bLength = USB_DT_ENDPOINT_SIZE,
--
drivers/usb/gadget/function/f_loopback.c-88-
drivers/usb/gadget/function/f_loopback.c:89:static struct usb_endpoint_descriptor hs_loop_sink_desc = {
drivers/usb/gadget/function/f_loopback.c-90- .bLength = USB_DT_ENDPOINT_SIZE,
--
drivers/usb/gadget/function/f_loopback.c=97=static struct usb_descriptor_header *hs_loopback_descs[] = {
--
drivers/usb/gadget/function/f_loopback.c-105-
drivers/usb/gadget/function/f_loopback.c:106:static struct usb_endpoint_descriptor ss_loop_source_desc = {
drivers/usb/gadget/function/f_loopback.c-107- .bLength = USB_DT_ENDPOINT_SIZE,
--
drivers/usb/gadget/function/f_loopback.c=114=static struct usb_ss_ep_comp_descriptor ss_loop_source_comp_desc = {
--
drivers/usb/gadget/function/f_loopback.c-121-
drivers/usb/gadget/function/f_loopback.c:122:static struct usb_endpoint_descriptor ss_loop_sink_desc = {
drivers/usb/gadget/function/f_loopback.c-123- .bLength = USB_DT_ENDPOINT_SIZE,
--
drivers/usb/gadget/function/f_midi.c=153=static struct usb_ms_header_descriptor ms_header_desc = {
--
drivers/usb/gadget/function/f_midi.c-161-/* B.5.1 Standard Bulk OUT Endpoint Descriptor */
drivers/usb/gadget/function/f_midi.c:162:static struct usb_endpoint_descriptor bulk_out_desc = {
drivers/usb/gadget/function/f_midi.c-163- .bLength = USB_DT_ENDPOINT_AUDIO_SIZE,
--
drivers/usb/gadget/function/f_midi.c=177=static struct usb_ms_endpoint_descriptor_16 ms_out_desc = {
--
drivers/usb/gadget/function/f_midi.c-185-/* B.6.1 Standard Bulk IN Endpoint Descriptor */
drivers/usb/gadget/function/f_midi.c:186:static struct usb_endpoint_descriptor bulk_in_desc = {
drivers/usb/gadget/function/f_midi.c-187- .bLength = USB_DT_ENDPOINT_AUDIO_SIZE,
--
drivers/usb/gadget/function/f_midi2.c=242=static struct usb_ms_header_descriptor midi2_midi1_class_desc = {
--
drivers/usb/gadget/function/f_midi2.c-250-/* MIDI 1.0 EP OUT */
drivers/usb/gadget/function/f_midi2.c:251:static struct usb_endpoint_descriptor midi2_midi1_ep_out_desc = {
drivers/usb/gadget/function/f_midi2.c-252- .bLength = USB_DT_ENDPOINT_AUDIO_SIZE,
--
drivers/usb/gadget/function/f_midi2.c=263=static struct usb_ms_endpoint_descriptor_16 midi2_midi1_ep_out_class_desc = {
--
drivers/usb/gadget/function/f_midi2.c-271-/* MIDI 1.0 EP IN */
drivers/usb/gadget/function/f_midi2.c:272:static struct usb_endpoint_descriptor midi2_midi1_ep_in_desc = {
drivers/usb/gadget/function/f_midi2.c-273- .bLength = USB_DT_ENDPOINT_AUDIO_SIZE,
--
drivers/usb/gadget/function/f_midi2.c=305=static struct usb_ms_header_descriptor midi2_midi2_class_desc = {
--
drivers/usb/gadget/function/f_midi2.c-313-/* MIDI 2.0 EP OUT */
drivers/usb/gadget/function/f_midi2.c:314:static struct usb_endpoint_descriptor midi2_midi2_ep_out_desc[MAX_UMP_EPS];
drivers/usb/gadget/function/f_midi2.c-315-
--
drivers/usb/gadget/function/f_midi2.c=321=static struct usb_ms20_endpoint_descriptor_32 midi2_midi2_ep_out_class_desc[MAX_UMP_EPS];
--
drivers/usb/gadget/function/f_midi2.c-323-/* MIDI 2.0 EP IN */
drivers/usb/gadget/function/f_midi2.c:324:static struct usb_endpoint_descriptor midi2_midi2_ep_in_desc[MAX_UMP_EPS];
drivers/usb/gadget/function/f_midi2.c-325-
--
drivers/usb/gadget/function/f_midi2.c=1893=static int f_midi2_init_midi2_ep_in(struct f_midi2 *midi2, int index)
--
drivers/usb/gadget/function/f_midi2.c-1895- struct f_midi2_ep *ep = &midi2->midi2_eps[index];
drivers/usb/gadget/function/f_midi2.c:1896: struct usb_endpoint_descriptor *desc = &midi2_midi2_ep_in_desc[index];
drivers/usb/gadget/function/f_midi2.c-1897-
--
drivers/usb/gadget/function/f_midi2.c=1912=static int f_midi2_init_midi2_ep_out(struct f_midi2 *midi2, int index)
--
drivers/usb/gadget/function/f_midi2.c-1914- struct f_midi2_ep *ep = &midi2->midi2_eps[index];
drivers/usb/gadget/function/f_midi2.c:1915: struct usb_endpoint_descriptor *desc = &midi2_midi2_ep_out_desc[index];
drivers/usb/gadget/function/f_midi2.c-1916-
--
drivers/usb/gadget/function/f_ncm.c=228=static struct usb_interface_descriptor ncm_data_intf = {
--
drivers/usb/gadget/function/f_ncm.c-242-
drivers/usb/gadget/function/f_ncm.c:243:static struct usb_endpoint_descriptor fs_ncm_notify_desc = {
drivers/usb/gadget/function/f_ncm.c-244- .bLength = USB_DT_ENDPOINT_SIZE,
--
drivers/usb/gadget/function/f_ncm.c-252-
drivers/usb/gadget/function/f_ncm.c:253:static struct usb_endpoint_descriptor fs_ncm_in_desc = {
drivers/usb/gadget/function/f_ncm.c-254- .bLength = USB_DT_ENDPOINT_SIZE,
--
drivers/usb/gadget/function/f_ncm.c-260-
drivers/usb/gadget/function/f_ncm.c:261:static struct usb_endpoint_descriptor fs_ncm_out_desc = {
drivers/usb/gadget/function/f_ncm.c-262- .bLength = USB_DT_ENDPOINT_SIZE,
--
drivers/usb/gadget/function/f_ncm.c=269=static struct usb_descriptor_header *ncm_fs_function[] = {
--
drivers/usb/gadget/function/f_ncm.c-287-
drivers/usb/gadget/function/f_ncm.c:288:static struct usb_endpoint_descriptor hs_ncm_notify_desc = {
drivers/usb/gadget/function/f_ncm.c-289- .bLength = USB_DT_ENDPOINT_SIZE,
--
drivers/usb/gadget/function/f_ncm.c-296-};
drivers/usb/gadget/function/f_ncm.c:297:static struct usb_endpoint_descriptor hs_ncm_in_desc = {
drivers/usb/gadget/function/f_ncm.c-298- .bLength = USB_DT_ENDPOINT_SIZE,
--
drivers/usb/gadget/function/f_ncm.c-305-
drivers/usb/gadget/function/f_ncm.c:306:static struct usb_endpoint_descriptor hs_ncm_out_desc = {
drivers/usb/gadget/function/f_ncm.c-307- .bLength = USB_DT_ENDPOINT_SIZE,
--
drivers/usb/gadget/function/f_ncm.c=315=static struct usb_descriptor_header *ncm_hs_function[] = {
--
drivers/usb/gadget/function/f_ncm.c-334-
drivers/usb/gadget/function/f_ncm.c:335:static struct usb_endpoint_descriptor ss_ncm_notify_desc = {
drivers/usb/gadget/function/f_ncm.c-336- .bLength = USB_DT_ENDPOINT_SIZE,
--
drivers/usb/gadget/function/f_ncm.c=345=static struct usb_ss_ep_comp_descriptor ss_ncm_notify_comp_desc = {
--
drivers/usb/gadget/function/f_ncm.c-354-
drivers/usb/gadget/function/f_ncm.c:355:static struct usb_endpoint_descriptor ss_ncm_in_desc = {
drivers/usb/gadget/function/f_ncm.c-356- .bLength = USB_DT_ENDPOINT_SIZE,
--
drivers/usb/gadget/function/f_ncm.c-363-
drivers/usb/gadget/function/f_ncm.c:364:static struct usb_endpoint_descriptor ss_ncm_out_desc = {
drivers/usb/gadget/function/f_ncm.c-365- .bLength = USB_DT_ENDPOINT_SIZE,
--
drivers/usb/gadget/function/f_obex.c=116=static struct usb_cdc_obex_desc obex_desc = {
--
drivers/usb/gadget/function/f_obex.c-124-
drivers/usb/gadget/function/f_obex.c:125:static struct usb_endpoint_descriptor obex_hs_ep_out_desc = {
drivers/usb/gadget/function/f_obex.c-126- .bLength = USB_DT_ENDPOINT_SIZE,
--
drivers/usb/gadget/function/f_obex.c-133-
drivers/usb/gadget/function/f_obex.c:134:static struct usb_endpoint_descriptor obex_hs_ep_in_desc = {
drivers/usb/gadget/function/f_obex.c-135- .bLength = USB_DT_ENDPOINT_SIZE,
--
drivers/usb/gadget/function/f_obex.c=143=static struct usb_descriptor_header *hs_function[] = {
--
drivers/usb/gadget/function/f_obex.c-157-
drivers/usb/gadget/function/f_obex.c:158:static struct usb_endpoint_descriptor obex_fs_ep_in_desc = {
drivers/usb/gadget/function/f_obex.c-159- .bLength = USB_DT_ENDPOINT_SIZE,
--
drivers/usb/gadget/function/f_obex.c-165-
drivers/usb/gadget/function/f_obex.c:166:static struct usb_endpoint_descriptor obex_fs_ep_out_desc = {
drivers/usb/gadget/function/f_obex.c-167- .bLength = USB_DT_ENDPOINT_SIZE,
--
drivers/usb/gadget/function/f_phonet.c=114=pn_data_intf_desc = {
--
drivers/usb/gadget/function/f_phonet.c-123-
drivers/usb/gadget/function/f_phonet.c:124:static struct usb_endpoint_descriptor
drivers/usb/gadget/function/f_phonet.c-125-pn_fs_sink_desc = {
--
drivers/usb/gadget/function/f_phonet.c-132-
drivers/usb/gadget/function/f_phonet.c:133:static struct usb_endpoint_descriptor
drivers/usb/gadget/function/f_phonet.c-134-pn_hs_sink_desc = {
--
drivers/usb/gadget/function/f_phonet.c-142-
drivers/usb/gadget/function/f_phonet.c:143:static struct usb_endpoint_descriptor
drivers/usb/gadget/function/f_phonet.c-144-pn_fs_source_desc = {
--
drivers/usb/gadget/function/f_phonet.c-151-
drivers/usb/gadget/function/f_phonet.c:152:static struct usb_endpoint_descriptor
drivers/usb/gadget/function/f_phonet.c-153-pn_hs_source_desc = {
--
drivers/usb/gadget/function/f_printer.c=115=static struct usb_interface_descriptor intf_desc = {
--
drivers/usb/gadget/function/f_printer.c-124-
drivers/usb/gadget/function/f_printer.c:125:static struct usb_endpoint_descriptor fs_ep_in_desc = {
drivers/usb/gadget/function/f_printer.c-126- .bLength = USB_DT_ENDPOINT_SIZE,
--
drivers/usb/gadget/function/f_printer.c-131-
drivers/usb/gadget/function/f_printer.c:132:static struct usb_endpoint_descriptor fs_ep_out_desc = {
drivers/usb/gadget/function/f_printer.c-133- .bLength = USB_DT_ENDPOINT_SIZE,
--
drivers/usb/gadget/function/f_printer.c=139=static struct usb_descriptor_header *fs_printer_function[] = {
--
drivers/usb/gadget/function/f_printer.c-150-
drivers/usb/gadget/function/f_printer.c:151:static struct usb_endpoint_descriptor hs_ep_in_desc = {
drivers/usb/gadget/function/f_printer.c-152- .bLength = USB_DT_ENDPOINT_SIZE,
--
drivers/usb/gadget/function/f_printer.c-157-
drivers/usb/gadget/function/f_printer.c:158:static struct usb_endpoint_descriptor hs_ep_out_desc = {
drivers/usb/gadget/function/f_printer.c-159- .bLength = USB_DT_ENDPOINT_SIZE,
--
drivers/usb/gadget/function/f_printer.c=165=static struct usb_descriptor_header *hs_printer_function[] = {
--
drivers/usb/gadget/function/f_printer.c-175-
drivers/usb/gadget/function/f_printer.c:176:static struct usb_endpoint_descriptor ss_ep_in_desc = {
drivers/usb/gadget/function/f_printer.c-177- .bLength = USB_DT_ENDPOINT_SIZE,
--
drivers/usb/gadget/function/f_printer.c=183=static struct usb_ss_ep_comp_descriptor ss_ep_in_comp_desc = {
--
drivers/usb/gadget/function/f_printer.c-187-
drivers/usb/gadget/function/f_printer.c:188:static struct usb_endpoint_descriptor ss_ep_out_desc = {
drivers/usb/gadget/function/f_printer.c-189- .bLength = USB_DT_ENDPOINT_SIZE,
--
drivers/usb/gadget/function/f_printer.c=200=static struct usb_descriptor_header *ss_printer_function[] = {
--
drivers/usb/gadget/function/f_printer.c-209-/* maxpacket and other transfer characteristics vary by speed. */
drivers/usb/gadget/function/f_printer.c:210:static inline struct usb_endpoint_descriptor *ep_desc(struct usb_gadget *gadget,
drivers/usb/gadget/function/f_printer.c:211: struct usb_endpoint_descriptor *fs,
drivers/usb/gadget/function/f_printer.c:212: struct usb_endpoint_descriptor *hs,
drivers/usb/gadget/function/f_printer.c:213: struct usb_endpoint_descriptor *ss)
drivers/usb/gadget/function/f_printer.c-214-{
--
drivers/usb/gadget/function/f_rndis.c=163=rndis_iad_descriptor = {
--
drivers/usb/gadget/function/f_rndis.c-176-
drivers/usb/gadget/function/f_rndis.c:177:static struct usb_endpoint_descriptor fs_notify_desc = {
drivers/usb/gadget/function/f_rndis.c-178- .bLength = USB_DT_ENDPOINT_SIZE,
--
drivers/usb/gadget/function/f_rndis.c-186-
drivers/usb/gadget/function/f_rndis.c:187:static struct usb_endpoint_descriptor fs_in_desc = {
drivers/usb/gadget/function/f_rndis.c-188- .bLength = USB_DT_ENDPOINT_SIZE,
--
drivers/usb/gadget/function/f_rndis.c-194-
drivers/usb/gadget/function/f_rndis.c:195:static struct usb_endpoint_descriptor fs_out_desc = {
drivers/usb/gadget/function/f_rndis.c-196- .bLength = USB_DT_ENDPOINT_SIZE,
--
drivers/usb/gadget/function/f_rndis.c=203=static struct usb_descriptor_header *eth_fs_function[] = {
--
drivers/usb/gadget/function/f_rndis.c-222-
drivers/usb/gadget/function/f_rndis.c:223:static struct usb_endpoint_descriptor hs_notify_desc = {
drivers/usb/gadget/function/f_rndis.c-224- .bLength = USB_DT_ENDPOINT_SIZE,
--
drivers/usb/gadget/function/f_rndis.c-232-
drivers/usb/gadget/function/f_rndis.c:233:static struct usb_endpoint_descriptor hs_in_desc = {
drivers/usb/gadget/function/f_rndis.c-234- .bLength = USB_DT_ENDPOINT_SIZE,
--
drivers/usb/gadget/function/f_rndis.c-241-
drivers/usb/gadget/function/f_rndis.c:242:static struct usb_endpoint_descriptor hs_out_desc = {
drivers/usb/gadget/function/f_rndis.c-243- .bLength = USB_DT_ENDPOINT_SIZE,
--
drivers/usb/gadget/function/f_rndis.c=251=static struct usb_descriptor_header *eth_hs_function[] = {
--
drivers/usb/gadget/function/f_rndis.c-270-
drivers/usb/gadget/function/f_rndis.c:271:static struct usb_endpoint_descriptor ss_notify_desc = {
drivers/usb/gadget/function/f_rndis.c-272- .bLength = USB_DT_ENDPOINT_SIZE,
--
drivers/usb/gadget/function/f_rndis.c=281=static struct usb_ss_ep_comp_descriptor ss_intr_comp_desc = {
--
drivers/usb/gadget/function/f_rndis.c-290-
drivers/usb/gadget/function/f_rndis.c:291:static struct usb_endpoint_descriptor ss_in_desc = {
drivers/usb/gadget/function/f_rndis.c-292- .bLength = USB_DT_ENDPOINT_SIZE,
--
drivers/usb/gadget/function/f_rndis.c-299-
drivers/usb/gadget/function/f_rndis.c:300:static struct usb_endpoint_descriptor ss_out_desc = {
drivers/usb/gadget/function/f_rndis.c-301- .bLength = USB_DT_ENDPOINT_SIZE,
--
drivers/usb/gadget/function/f_serial.c=42=static struct usb_interface_descriptor gser_interface_desc = {
--
drivers/usb/gadget/function/f_serial.c-54-
drivers/usb/gadget/function/f_serial.c:55:static struct usb_endpoint_descriptor gser_fs_in_desc = {
drivers/usb/gadget/function/f_serial.c-56- .bLength = USB_DT_ENDPOINT_SIZE,
--
drivers/usb/gadget/function/f_serial.c-61-
drivers/usb/gadget/function/f_serial.c:62:static struct usb_endpoint_descriptor gser_fs_out_desc = {
drivers/usb/gadget/function/f_serial.c-63- .bLength = USB_DT_ENDPOINT_SIZE,
--
drivers/usb/gadget/function/f_serial.c=69=static struct usb_descriptor_header *gser_fs_function[] = {
--
drivers/usb/gadget/function/f_serial.c-77-
drivers/usb/gadget/function/f_serial.c:78:static struct usb_endpoint_descriptor gser_hs_in_desc = {
drivers/usb/gadget/function/f_serial.c-79- .bLength = USB_DT_ENDPOINT_SIZE,
--
drivers/usb/gadget/function/f_serial.c-84-
drivers/usb/gadget/function/f_serial.c:85:static struct usb_endpoint_descriptor gser_hs_out_desc = {
drivers/usb/gadget/function/f_serial.c-86- .bLength = USB_DT_ENDPOINT_SIZE,
--
drivers/usb/gadget/function/f_serial.c=92=static struct usb_descriptor_header *gser_hs_function[] = {
--
drivers/usb/gadget/function/f_serial.c-98-
drivers/usb/gadget/function/f_serial.c:99:static struct usb_endpoint_descriptor gser_ss_in_desc = {
drivers/usb/gadget/function/f_serial.c-100- .bLength = USB_DT_ENDPOINT_SIZE,
--
drivers/usb/gadget/function/f_serial.c-105-
drivers/usb/gadget/function/f_serial.c:106:static struct usb_endpoint_descriptor gser_ss_out_desc = {
drivers/usb/gadget/function/f_serial.c-107- .bLength = USB_DT_ENDPOINT_SIZE,
--
drivers/usb/gadget/function/f_sourcesink.c=71=static struct usb_interface_descriptor source_sink_intf_alt1 = {
--
drivers/usb/gadget/function/f_sourcesink.c-82-
drivers/usb/gadget/function/f_sourcesink.c:83:static struct usb_endpoint_descriptor fs_source_desc = {
drivers/usb/gadget/function/f_sourcesink.c-84- .bLength = USB_DT_ENDPOINT_SIZE,
--
drivers/usb/gadget/function/f_sourcesink.c-90-
drivers/usb/gadget/function/f_sourcesink.c:91:static struct usb_endpoint_descriptor fs_sink_desc = {
drivers/usb/gadget/function/f_sourcesink.c-92- .bLength = USB_DT_ENDPOINT_SIZE,
--
drivers/usb/gadget/function/f_sourcesink.c-98-
drivers/usb/gadget/function/f_sourcesink.c:99:static struct usb_endpoint_descriptor fs_iso_source_desc = {
drivers/usb/gadget/function/f_sourcesink.c-100- .bLength = USB_DT_ENDPOINT_SIZE,
]
|
| 62/2 |
2026/07/29 01:43 |
llm |
expert |
0m
Model:
Tokens:
input: 72159
output: 40
thoughts: 9099
Thoughts: **Analyzing a USB Gadget Driver Use-After-Free Vulnerability**
Okay, I'm diving into this KASAN report, and it looks like we have a use-after-free issue, and I believe the core of the problem lies with the interaction between a gadget driver and the USB core, specifically how endpoint descriptors are handled.
Initially, I see the offset of `struct usb_endpoint_descriptor` and recognize that it's 7 bytes. The key is that the buggy address (offset 80 in a freed region of size 248) is pointing inside of freed memory, and is reading `bEndpointAddress`. I begin tracing the size of `struct ep_data`, which seems relevant, but ultimately leads to a red herring with its configuration-dependent size (`struct mutex`). I need to be more mindful of where `struct usb_endpoint_descriptor` is being allocated and freed.
I then focus on understanding the memory layout. I need to figure out where this endpoint descriptor is actually being allocated, what the size of the allocation is, and how it is being freed, especially considering the constraints mentioned in the crash report. I am trying to determine what memory chunk the endpoint descriptor belongs to, by examining drivers that could be responsible.
I try to exclude different possibilities, but I have already examined `raw_gadget` and other candidates. The memory size of `248` bytes and the offset of `80` bytes inside the allocation, strongly suggests this has to be a user controlled buffer with an `usb_endpoint_descriptor` located at the specified offset.
Next I review `FunctionFS` (`f_fs.c`). I start to realize that the key lies in the fact that the FunctionFS driver allows userspace to provide their own descriptors. This is where I start to see the possibility of a user-controlled descriptor at an arbitrary offset in a block of memory. I note that this driver allocates a `kmalloc` of length `len` for descriptors, which is critical.
I consider the sizes involved. If the user provides an endpoint descriptor at offset 80, the allocation size will be near 248 bytes to satisfy the layout. This is where `ffs->raw_descs_data` is allocated and it could be reallocated from `io_kiocb`. Then, based on the crash report, I deduce that a use-after-free situation must be involved.
I examine the `ffs_data_clear` function, the `ffs_func_unbind` and related functions. I then trace the calls of `usb_ep_disable`, noting the point that `usb_ep_disable` sets `ep->desc = NULL`. I trace to confirm this is how the descriptor is disabled. I understand, if there is a use-after-free, it is very likely this flow has been disrupted.
I go back over the execution and the potential race conditions, considering that `dummy_timer` has access to `ep->desc` during execution. If `usb_ep_disable` is set, `ep->desc` should be set to NULL, preventing the UAF. Therefore, the problem would stem from the flow of `usb_ep_disable` *not* happening at all.
Finally, I understand how the memory is allocated, and the bug scenario:
1. User writes descriptors to ep0. `ffs->raw_descs_data` is allocated.
2. Gadget is bound, endpoints are enabled. `ep->desc` points to offset 80 of `ffs->raw_descs_data`.
3. User closes ep0 or resets the configuration.
4. `ffs_data_clear` is called, which frees `ffs->raw_descs_data`. This is the problematic step if `ffs->gadget` is not NULL.
5. There must be something that causes `ffs->gadget` to not be NULL, which is the crux of the issue.
I now focus my efforts on `f_fs.c` and try to find a possible scenario to cause the free without the gadget unbinding. I consider multiple possibilities, including the different failure scenarios. After a thorough review, I now focus my attention to the sequence where `ffs_func_unbind` is not being called, and the endpoint is left active. Thus, the conditions for the use-after-free are satisfied. The key is in the timing of unbinding and when the memory is being re-used.
|
| 63/2 |
2026/07/29 01:44 |
tool |
grepper |
0m
Args: map[Expression:raw_descs_data PathPrefix:drivers/usb/gadget/function/f_fs.c]
Results: map[Output:drivers/usb/gadget/function/f_fs.c=2233=static void ffs_data_clear(struct ffs_data *ffs)
--
drivers/usb/gadget/function/f_fs.c-2261-
drivers/usb/gadget/function/f_fs.c:2262: kfree(ffs->raw_descs_data);
drivers/usb/gadget/function/f_fs.c-2263- kfree(ffs->raw_strings);
--
drivers/usb/gadget/function/f_fs.c=2267=static void ffs_data_reset(struct ffs_data *ffs)
--
drivers/usb/gadget/function/f_fs.c-2271- spin_lock_irq(&ffs->eps_lock);
drivers/usb/gadget/function/f_fs.c:2272: ffs->raw_descs_data = NULL;
drivers/usb/gadget/function/f_fs.c-2273- ffs->raw_descs = NULL;
--
drivers/usb/gadget/function/f_fs.c=2928=static int __ffs_data_got_descs(struct ffs_data *ffs,
--
drivers/usb/gadget/function/f_fs.c-3042-
drivers/usb/gadget/function/f_fs.c:3043: ffs->raw_descs_data = _data;
drivers/usb/gadget/function/f_fs.c-3044- ffs->raw_descs = raw_descs;
]
|
| 64/2 |
2026/07/29 01:44 |
llm |
expert |
0m
Model:
Tokens:
input: 82683
output: 39
thoughts: 2380
Thoughts: **Analyzing a Potential Use-After-Free Vulnerability**
Okay, so I'm digging into this code, trying to figure out if there's a use-after-free situation in the `dummy_hcd` driver. My initial thought was that a race between `dummy_timer` and `ffs_func_unbind` might be the cause, because `ffs_func_unbind` unbinds a function, disabling endpoints, freeing descriptors, and ultimately freeing data that the timer might be using. The critical path is: `ffs_data_clear` frees data, but `dummy_timer` might still have a pointer.
However, a closer look suggests this isn't a likely scenario. `ffs_func_unbind` calls `ffs_func_eps_disable`, which in turn calls `usb_ep_disable`. `usb_ep_disable` calls `dummy_disable`, which *does* set `ep->desc = NULL`. The critical part, as `dummy_timer` drops the lock to call the `setup` function, which *could* permit a race, this only happens for the control endpoint. And for the control endpoint, the descriptor is a static one, so there's no dangling pointer risk. Also, `dummy_timer` synchronizes with `usb_ep_disable` through `dum->lock`, preventing concurrent access after the lock has been reacquired, ensuring `ep->desc` is `NULL` if `usb_ep_disable` was called before the lock was reacquired.
So, it's not the timer itself racing with the `ffs_func_unbind` process. The `transfer` routine *does* dereference `ep->desc`, making it a potential use-after-free. But, as noted, this dereference is safe because the control endpoint's descriptor is static.
But this got me thinking β what if a *different* process were the source of the use-after-free, not the timer? If `usb_ep_disable` is not called before `kfree` of the descriptor, that's a problem. The function `find_endpoint` is only called from the `dummy_timer` and `dummy_urb_enqueue` functions, and because `dummy_urb_enqueue` acquires the lock, it synchronizes with `usb_ep_disable`.
So, the issue *must* arise from situations where `usb_ep_disable` is *not* called. My suspicion is on the gadget driver's code. I've checked the standard flow with `raw_gadget`, `usb_ep_enable` (and failures), `usb_ep_alloc_request`, `raw_ioctl_ep_disable` and `raw_release` and the issue is not there. `raw_gadget` is not the culprit. The code calls `usb_ep_disable` before `kfree` everywhere.
If it's *not* raw gadget, what about `gadgetfs`? The inode destruction flow in gadgetfs looked promising, but it doesn't call `usb_ep_disable`. If the file is closed, the endpoint may get disabled. So there could be a case where `gadgetfs` closes the file, and then the descriptor for the endpoint is freed. But is there a use-after-free here? I need to check `ep_release` to confirm.
|
| 65/2 |
2026/07/29 01:44 |
tool |
codesearch-definition-source |
0m
Args: map[ContextFile:drivers/usb/gadget/legacy/inode.c Name:ep_release]
Results: map[SourceCode: 380: static int
381: ep_release (struct inode *inode, struct file *fd)
382: {
383: struct ep_data *data = fd->private_data;
384: int value;
385:
386: value = mutex_lock_interruptible(&data->lock);
387: if (value < 0)
388: return value;
389:
390: /* clean up if this can be reopened */
391: if (data->state != STATE_EP_UNBOUND) {
392: data->state = STATE_EP_DISABLED;
393: data->desc.bDescriptorType = 0;
394: data->hs_desc.bDescriptorType = 0;
395: usb_ep_disable(data->ep);
396: }
397: mutex_unlock(&data->lock);
398: put_ep (data);
399: return 0;
400: }
SourceFile:drivers/usb/gadget/legacy/inode.c]
|
| 66/2 |
2026/07/29 01:44 |
llm |
expert |
0m
Model:
Tokens:
input: 86154
output: 44
thoughts: 419
Thoughts: **Analyzing USB Endpoint Disabling in FunctionFS**
Okay, so I see it's calling `usb_ep_disable(data->ep)` β that should be safe, no issues there. But what about FunctionFS (`f_fs.c`)? I'm thinking about the freeing of resources. We already verified `ffs_data_clear` and that it frees `ffs->raw_descs_data`. It has a `BUG_ON(ffs->gadget);` in it. So, that means `ffs->gadget` *must* be NULL at that point. Makes sense. Now, `ffs->gadget` is set to NULL in `ffs_func_unbind`.
`ffs_func_unbind` calls `ffs_func_eps_disable`, which in turn calls `usb_ep_disable`. But hold on, the critical point is that `ffs_func_eps_disable` is ONLY called *if* `ffs->func == func`. Okay, so:
```c
if (ffs->func == func) {
ffs_func_eps_disable(func);
ffs->func = NULL;
}
```
Could there be a scenario where `ffs->func != func`? When is `ffs_func_unbind` called in the first place? Ah, it's triggered by the composite framework when the gadget is unbound. So, if `ffs->func != func`, then something has overwritten or cleared `ffs->func`. When does that happen? Only in `ffs_func_unbind`!
Wait a second⦠what if `ffs_func_bind` fails? Let's refresh our memory of the code.
```c
static int ffs_func_bind(struct usb_configuration *c,
struct usb_function *f)
{
...
ffs->func = func;
...
ret = ffs_epfiles_create(ffs);
if (ret)
goto error;
...
error:
/* wait, does it clear ffs->func? */
}
```
Right, it sets `ffs->func` *before* the potentially failing `ffs_epfiles_create`. And the crucial question here is whether the error path clears `ffs->func`? I need to check the `ffs_func_bind` error path to be absolutely sure.
|
| 67/2 |
2026/07/29 01:44 |
tool |
codesearch-definition-source |
0m
Args: map[ContextFile:drivers/usb/gadget/function/f_fs.c Name:ffs_func_bind]
Results: map[SourceCode:3723: static int ffs_func_bind(struct usb_configuration *c,
3724: struct usb_function *f)
3725: {
3726: struct f_fs_opts *ffs_opts = ffs_do_functionfs_bind(f, c);
3727: struct ffs_function *func = ffs_func_from_usb(f);
3728: int ret;
3729:
3730: if (IS_ERR(ffs_opts))
3731: return PTR_ERR(ffs_opts);
3732:
3733: ret = _ffs_func_bind(c, f);
3734: if (ret && !--ffs_opts->refcnt)
3735: functionfs_unbind(func->ffs);
3736:
3737: return ret;
3738: }
SourceFile:drivers/usb/gadget/function/f_fs.c]
|
| 68/2 |
2026/07/29 01:44 |
llm |
expert |
0m
Model:
Tokens:
input: 87439
output: 45
thoughts: 16
Thoughts: Okay, here's my take on those thoughts, tailored for an expert audience:
**The Binding Revelation**
Alright, let me just parse this... Right, so I was initially focused on the general binding process, thinking about how it all comes together in this particular context. And then, bam! It hits me: the actual heavy lifting, the nitty-gritty of the binding, isn't happening where I initially thought. It's that little function, `_ffs_func_bind`, that's really doing the magic. I was approaching this from a high-level perspective, and I missed the critical point that this function is the linchpin. It's the one actually responsible for the detailed, concrete, operation. Suddenly, the system's structure is much clearer.
|
| 69/2 |
2026/07/29 01:44 |
tool |
codesearch-definition-source |
0m
Args: map[ContextFile:drivers/usb/gadget/function/f_fs.c Name:_ffs_func_bind]
Results: map[SourceCode:3561: static int _ffs_func_bind(struct usb_configuration *c,
3562: struct usb_function *f)
3563: {
3564: struct ffs_function *func = ffs_func_from_usb(f);
3565: struct ffs_data *ffs = func->ffs;
3566:
3567: const int full = !!func->ffs->fs_descs_count;
3568: const int high = !!func->ffs->hs_descs_count;
3569: const int super = !!func->ffs->ss_descs_count;
3570:
3571: int fs_len, hs_len, ss_len, ret, i;
3572: struct ffs_ep *eps_ptr;
3573:
3574: /* Make it a single chunk, less management later on */
3575: vla_group(d);
3576: vla_item_with_sz(d, struct ffs_ep, eps, ffs->eps_count);
3577: vla_item_with_sz(d, struct usb_descriptor_header *, fs_descs,
3578: full ? ffs->fs_descs_count + 1 : 0);
3579: vla_item_with_sz(d, struct usb_descriptor_header *, hs_descs,
3580: high ? ffs->hs_descs_count + 1 : 0);
3581: vla_item_with_sz(d, struct usb_descriptor_header *, ss_descs,
3582: super ? ffs->ss_descs_count + 1 : 0);
3583: vla_item_with_sz(d, short, inums, ffs->interfaces_count);
3584: vla_item_with_sz(d, struct usb_os_desc_table, os_desc_table,
3585: c->cdev->use_os_string ? ffs->interfaces_count : 0);
3586: vla_item_with_sz(d, char[16], ext_compat,
3587: c->cdev->use_os_string ? ffs->interfaces_count : 0);
3588: vla_item_with_sz(d, struct usb_os_desc, os_desc,
3589: c->cdev->use_os_string ? ffs->interfaces_count : 0);
3590: vla_item_with_sz(d, struct usb_os_desc_ext_prop, ext_prop,
3591: ffs->ms_os_descs_ext_prop_count);
3592: vla_item_with_sz(d, char, ext_prop_name,
3593: ffs->ms_os_descs_ext_prop_name_len);
3594: vla_item_with_sz(d, char, ext_prop_data,
3595: ffs->ms_os_descs_ext_prop_data_len);
3596: vla_item_with_sz(d, char, raw_descs, ffs->raw_descs_length);
3597: char *vlabuf;
3598:
3599: /* Has descriptors only for speeds gadget does not support */
3600: if (!(full | high | super))
3601: return -ENOTSUPP;
3602:
3603: /* Allocate a single chunk, less management later on */
3604: vlabuf = kzalloc(vla_group_size(d), GFP_KERNEL);
3605: if (!vlabuf)
3606: return -ENOMEM;
3607:
3608: ffs->ms_os_descs_ext_prop_avail = vla_ptr(vlabuf, d, ext_prop);
3609: ffs->ms_os_descs_ext_prop_name_avail =
3610: vla_ptr(vlabuf, d, ext_prop_name);
3611: ffs->ms_os_descs_ext_prop_data_avail =
3612: vla_ptr(vlabuf, d, ext_prop_data);
3613:
3614: /* Copy descriptors */
3615: memcpy(vla_ptr(vlabuf, d, raw_descs), ffs->raw_descs,
3616: ffs->raw_descs_length);
3617:
3618: memset(vla_ptr(vlabuf, d, inums), 0xff, d_inums__sz);
3619: eps_ptr = vla_ptr(vlabuf, d, eps);
3620: for (i = 0; i < ffs->eps_count; i++)
3621: eps_ptr[i].num = -1;
3622:
3623: /* Save pointers
3624: * d_eps == vlabuf, func->eps used to kfree vlabuf later
3625: */
3626: func->eps = vla_ptr(vlabuf, d, eps);
3627: func->interfaces_nums = vla_ptr(vlabuf, d, inums);
3628:
3629: /*
3630: * Go through all the endpoint descriptors and allocate
3631: * endpoints first, so that later we can rewrite the endpoint
3632: * numbers without worrying that it may be described later on.
3633: */
3634: if (full) {
3635: func->function.fs_descriptors = vla_ptr(vlabuf, d, fs_descs);
3636: fs_len = ffs_do_descs(ffs->fs_descs_count,
3637: vla_ptr(vlabuf, d, raw_descs),
3638: d_raw_descs__sz,
3639: __ffs_func_bind_do_descs, func);
3640: if (fs_len < 0) {
3641: ret = fs_len;
3642: goto error;
3643: }
3644: } else {
3645: fs_len = 0;
3646: }
3647:
3648: if (high) {
3649: func->function.hs_descriptors = vla_ptr(vlabuf, d, hs_descs);
3650: hs_len = ffs_do_descs(ffs->hs_descs_count,
3651: vla_ptr(vlabuf, d, raw_descs) + fs_len,
3652: d_raw_descs__sz - fs_len,
3653: __ffs_func_bind_do_descs, func);
3654: if (hs_len < 0) {
3655: ret = hs_len;
3656: goto error;
3657: }
3658: } else {
3659: hs_len = 0;
3660: }
3661:
3662: if (super) {
3663: func->function.ss_descriptors = func->function.ssp_descriptors =
3664: vla_ptr(vlabuf, d, ss_descs);
3665: ss_len = ffs_do_descs(ffs->ss_descs_count,
3666: vla_ptr(vlabuf, d, raw_descs) + fs_len + hs_len,
3667: d_raw_descs__sz - fs_len - hs_len,
3668: __ffs_func_bind_do_descs, func);
3669: if (ss_len < 0) {
3670: ret = ss_len;
3671: goto error;
3672: }
3673: } else {
3674: ss_len = 0;
3675: }
3676:
3677: /*
3678: * Now handle interface numbers allocation and interface and
3679: * endpoint numbers rewriting. We can do that in one go
3680: * now.
3681: */
3682: ret = ffs_do_descs(ffs->fs_descs_count +
3683: (high ? ffs->hs_descs_count : 0) +
3684: (super ? ffs->ss_descs_count : 0),
3685: vla_ptr(vlabuf, d, raw_descs), d_raw_descs__sz,
3686: __ffs_func_bind_do_nums, func);
3687: if (ret < 0)
3688: goto error;
3689:
3690: func->function.os_desc_table = vla_ptr(vlabuf, d, os_desc_table);
3691: if (c->cdev->use_os_string) {
3692: for (i = 0; i < ffs->interfaces_count; ++i) {
3693: struct usb_os_desc *desc;
3694:
3695: desc = func->function.os_desc_table[i].os_desc =
3696: vla_ptr(vlabuf, d, os_desc) +
3697: i * sizeof(struct usb_os_desc);
3698: desc->ext_compat_id =
3699: vla_ptr(vlabuf, d, ext_compat) + i * 16;
3700: INIT_LIST_HEAD(&desc->ext_prop);
3701: }
3702: ret = ffs_do_os_descs(ffs->ms_os_descs_count,
3703: vla_ptr(vlabuf, d, raw_descs) +
3704: fs_len + hs_len + ss_len,
3705: d_raw_descs__sz - fs_len - hs_len -
3706: ss_len,
3707: __ffs_func_bind_do_os_desc, func);
3708: if (ret < 0)
3709: goto error;
3710: }
3711: func->function.os_desc_n =
3712: c->cdev->use_os_string ? ffs->interfaces_count : 0;
3713:
3714: /* And we're done */
3715: ffs_event_add(ffs, FUNCTIONFS_BIND);
3716: return 0;
3717:
3718: error:
3719: /* XXX Do we need to release all claimed endpoints here? */
3720: return ret;
3721: }
SourceFile:drivers/usb/gadget/function/f_fs.c]
|
| 70/2 |
2026/07/29 01:44 |
llm |
expert |
1m
Model:
Tokens:
input: 90702
output: 66
thoughts: 12542
Thoughts: **Analysis of a USB Gadget Driver Use-After-Free Vulnerability**
Okay, here's what I've pieced together about this crash. We're looking at a Use-After-Free (UAF) in a USB gadget driver, possibly triggered when endpoints are reconfigured or freed incorrectly. The key seems to be a dangling pointer to an endpoint descriptor (`ep->desc`).
My first thought was that the error handling in `_ffs_func_bind` (and related functions) was crucial. If these functions fail during endpoint binding, `vlabuf` (which holds endpoint descriptors) might not be properly freed, and `ffs->func` may already be set. This leads to a scenario where endpoint descriptors are allocated, endpoints may be enabled, but when cleanup occurs, the function tries to free a memory location after it has already been freed. This may be because of a failure on enabling the endpoint. This will cause the endpoint descriptors to have a double free. The root cause is the `ffs_func_set_alt` does not properly handle the error and the endpoints will be enabled even if an error is triggered, leading to the use after free. This is the root cause. This causes an attacker to possibly cause an OOB read or write.
The issue lies in the flow when the function is unbound (either due to an error, or normal unbinding). `ffs_func_unbind` is key here. It calls `ffs_func_eps_disable`, which should disable and clean up the endpoints. I had to determine if `ffs_func_eps_disable` was correctly called in all error paths. Crucially, if `usb_ep_enable` (which assigns the descriptor) fails in `ffs_func_set_alt` or another function, it doesn't always properly disable endpoints, and there is a possibility that a dangling pointer could exist in `ep->desc`. I also looked at whether `usb_ep_disable` was called in all the right places, and in particular to determine if it had been called before freeing `vlabuf`, which holds the descriptors and the `dummy_ep::desc` pointer.
There's a lot of intricate pointer manipulation and memory management going on, with multiple allocation steps (`kzalloc`, `vla_group_size`), descriptor assignments, and endpoint enabling/disabling. It is quite easy for the code to make a mistake in this complex and often error prone pointer manipulation.
The actual crash, however, points to the `dummy_hcd` driver. This suggests that the issue is NOT directly in `f_fs.c`, but that something there, or a different gadget driver (like `raw_gadget`), is causing `dummy_hcd` to dereference a freed descriptor. This happens in the `dummy_timer` function.
What I believe is happening, is that there's a race condition. I am suspicious of the fact that the `dummy_timer` function is running and that the `usb_ep_disable` or `kfree` routines are not being called in the proper order. It seems that the descriptors are being freed, but the `dummy_hcd` driver is still accessing them later. Because it only occurs in `dummy_hcd`, the race condition is something which is only seen in `dummy_hcd` and not necessarily other drivers, but is something that all drivers are vulnerable to if a similar error is made.
The crash report confirms a Slab-Use-After-Free. The memory previously held a page table, then it was reallocated to `io_kiocb`. This means the `ep->desc` pointer somehow ended up pointing to a freed page that was then re-used. Specifically, a gadget driver probably allocates memory, sets a pointer to the memory into `ep->desc`, frees the memory, and somehow, the page gets reallocated and then dereferenced.
I also looked at other places in USB drivers that might exhibit this kind of use after free. The most suspect place is `raw_gadget`, because it has very little error handling.
This UAF could allow an attacker to control the contents of `ep->desc`, potentially leading to out-of-bounds reads or writes in the `transfer` function of the USB stack. This is exploitable, even if it requires root privileges to configure the USB gadget. The UAF is exploitable if the attacker can control some of the contents of the freed `io_kiocb`, and can then control the endpoint descriptor, to modify transfer related pointers. This might lead to data corruption or arbitrary code execution.
Exploitability: **Yes**
Denial of Service: **Yes**
Remote Trigger: **No**
Peripheral Trigger: **No**
Network Trigger: **No**
VM Host Trigger: **No**
VM Guest Trigger:
|
| 71/2 |
2026/07/29 01:46 |
tool |
set-results |
0m
Args: map[DenialOfService:true Exploitable:true FilesystemTrigger:false NetworkTrigger:false PeripheralTrigger:false RemoteTrigger:false Unprivileged:false UserNamespace:false VMGuestTrigger:false VMHostTrigger:false]
Results: map[DenialOfService:true Exploitable:true FilesystemTrigger:false NetworkTrigger:false PeripheralTrigger:false RemoteTrigger:false Unprivileged:false UserNamespace:false VMGuestTrigger:false VMHostTrigger:false]
|
| 72/2 |
2026/07/29 01:46 |
llm |
expert |
0m
Model:
Tokens:
input: 104399
output: 624
thoughts: 316
Thoughts: **Final Reasoning on the Use-After-Free Vulnerability**
Okay, the results are in, and now it's time to lay out the complete rationale. My analysis points definitively to a Use-After-Free (UAF) vulnerability within `dummy_hcd.c`. This arises specifically when the code accesses `ep->desc->bEndpointAddress`. The core of the problem stems from a scenario where a gadget driver, and I'm leaning towards either `FunctionFS` or `raw_gadget` being the culprit, frees the memory containing the endpoint descriptor. This happens without first properly disabling the associated endpoint within the `dummy_hcd` Universal Device Controller (UDC).
The trigger is the `dummy_timer` firing. When this timer's interrupt occurs, it iterates over the endpoints, and unfortunately, it dereferences that now-dangling `ep->desc` pointer. The freed memory, as KASAN helpfully pointed out, was subsequently reallocated, this time as an `io_kiocb` object within `io_uring`. That object was, in turn, freed, which is what generated the KASAN report.
The potential for exploitation is definitely present. An attacker with the ability to spray the heap and control the contents of the reallocated descriptor could potentially manipulate the endpoint attributes. Parameters like `wMaxPacketSize` and direction become attack vectors, and that could open the door to out-of-bounds reads or writes during USB data transfers.
However, and this is crucial, the configuration of USB gadgets using methods like configfs, FunctionFS, or raw_gadget necessitates global root privileges β specifically, `CAP_SYS_ADMIN`. These methods aren't accessible to unprivileged users or within user namespaces. Furthermore, keep in mind that `dummy_hcd` is a purely software-based testing driver. This means that, by definition, it's not going to be triggered by any physical peripherals. Even if the underlying bug affecting the gadget driver applied to real UDCs, triggering it would still mandate local root access to configure the gadget. Therefore, there are, in my assessment, no remote, peripheral, or unprivileged trigger paths to exploit this vulnerability.
|
| 73/1 |
2026/07/29 01:46 |
action |
format-explanation |
0m
Results: map[Explanation:Based on the analysis of the bug report and the kernel source code, here is the
assessment of the vulnerability:
**Root Cause & Bug Mechanism:**
The bug is a Slab-Use-After-Free (UAF) in the `dummy_hcd` driver (a
software-only USB controller used for testing). The crash occurs in
`find_endpoint()` when it attempts to read `ep->desc->bEndpointAddress`.
The `ep->desc` pointer is supposed to point to a valid `usb_endpoint_descriptor`
provided by a USB gadget driver (such as `FunctionFS` or `raw_gadget`). However,
the gadget driver freed the memory containing this descriptor without properly
disabling the endpoint via `usb_ep_disable()`. As a result, `dummy_hcd` retains
a dangling pointer.
In this specific crash, the freed memory was subsequently reallocated by the
`io_uring` subsystem as an `io_kiocb` object (size 248 bytes, likely from a
merged `kmalloc-256` cache), which was then freed again. When the `dummy_timer`
softirq fires, it iterates over the endpoints and dereferences the dangling
`ep->desc` pointer, triggering the KASAN UAF report.
**Exploitability:**
**Exploitable.** An attacker who can trigger this bug can potentially exploit
it. By spraying the heap (e.g., using `io_uring` or other `kmalloc-256`
objects), the attacker can control the contents of the freed descriptor memory.
By manipulating fields like `wMaxPacketSize`, `bmAttributes`, or the endpoint
direction, the attacker can deceive the `transfer()` function in `dummy_hcd`
into performing out-of-bounds reads or writes when copying data between the URB
and the gadget's request buffers. This provides a strong primitive for privilege
escalation or information leaks.
**Accessibility & Triggers:**
* **Unprivileged / User Namespace:** **No.** Configuring USB gadgets (whether
via `configfs`, `FunctionFS`, or `raw_gadget`) requires global `CAP_SYS_ADMIN`
privileges. The USB subsystem is not user-namespace aware, meaning an
unprivileged user cannot reach this code even inside a new user namespace.
* **Peripheral / Remote Trigger:** **No.** The `dummy_hcd` driver is a virtual
host/device controller used exclusively for local testing and simulation. It
does not interface with physical hardware. Even if the underlying gadget driver
bug (failing to disable the endpoint before freeing the descriptor) applies to
real hardware UDCs (like DWC3), triggering the vulnerable state requires local
root access to configure and unbind the gadget. It cannot be triggered by simply
plugging in a malicious USB device or via network traffic.
* **Denial of Service:** **Yes.** If left unexploited, the dangling pointer
dereference will cause a kernel panic, leading to a local denial of service.]
|