| 0/0 |
2026/08/07 05:35 |
flow |
patching |
50m
Results: map[AckedBy:[] Fixes:map[AuthorEmail:torvalds@ppc970.osdl.org AuthorName:Linus Torvalds Hash:1da177e4c3f41524e886b7f1b8a0c1fc7321cac2 Title:Linux-2.6.12-rc2] KernelBranch:master KernelCommit:075b74841bd0065a3bda3440873c747938e69b68 KernelRepo:git://git.kernel.org/pub/scm/linux/kernel/git/torvalds/linux.git PatchDescription:usb: gadgetfs: fix reference count underflow in gadgetfs_bind()
A KASAN slab-use-after-free bug was found in gadgetfs_kill_sb():
BUG: KASAN: slab-use-after-free in put_dev
drivers/usb/gadget/legacy/inode.c:165 [inline]
BUG: KASAN: slab-use-after-free in gadgetfs_kill_sb+0x46/0x110
drivers/usb/gadget/legacy/inode.c:2104
Write of size 4 at addr ffff88811638c080 by task syz-executor169/5848
CPU: 0 UID: 0 PID: 5848 Comm: syz-executor169 Not tainted
Hardware name: QEMU Standard PC (Q35 + ICH9, 2009), BIOS
1.16.3-debian-1.16.3-2 04/01/2014
Call Trace:
<TASK>
dump_stack_lvl+0xe8/0x150 lib/dump_stack.c:120
print_address_description+0x55/0x1e0 mm/kasan/report.c:378
print_report+0x58/0x70 mm/kasan/report.c:482
kasan_report+0x117/0x150 mm/kasan/report.c:595
check_region_inline mm/kasan/generic.c:-1 [inline]
kasan_check_range+0x264/0x2c0 mm/kasan/generic.c:200
instrument_atomic_read_write include/linux/instrumented.h:112 [inline]
atomic_fetch_sub_release include/linux/atomic/atomic-instrumented.h:400
[inline]
__refcount_sub_and_test include/linux/refcount.h:389 [inline]
__refcount_dec_and_test include/linux/refcount.h:432 [inline]
refcount_dec_and_test include/linux/refcount.h:450 [inline]
put_dev drivers/usb/gadget/legacy/inode.c:165 [inline]
gadgetfs_kill_sb+0x46/0x110 drivers/usb/gadget/legacy/inode.c:2104
deactivate_locked_super+0xbc/0x130 fs/super.c:477
cleanup_mnt+0x3d3/0x460 fs/namespace.c:1317
task_work_run+0x1d9/0x270 kernel/task_work.c:233
The root cause is a reference count underflow in the error handling path of
gadgetfs_bind(). When gadgetfs_bind() encounters an error (e.g., memory
allocation failure), it jumps to the enomem label and calls
gadgetfs_unbind(). However, gadgetfs_unbind() is designed to tear down a
fully initialized and bound gadget, and unconditionally drops a device
reference by calling put_dev(dev). Since gadgetfs_bind() only acquires this
reference (get_dev(dev)) at the very end of the function upon complete
success, calling gadgetfs_unbind() in the error path drops a reference that
was never taken.
This leads to a premature free of the dev structure when the ep0 file is
closed, and subsequently a use-after-free when the filesystem is unmounted
and gadgetfs_kill_sb() tries to drop the initial reference.
To fix this, stop calling the full teardown function gadgetfs_unbind() from
the gadgetfs_bind() error path. Instead, manually and locally clean up only
the specific state that was successfully altered before the failure
occurred. PatchDiff:diff --git a/drivers/usb/gadget/legacy/inode.c b/drivers/usb/gadget/legacy/inode.c
index d87a8ab51..365f9ab4d 100644
--- a/drivers/usb/gadget/legacy/inode.c
+++ b/drivers/usb/gadget/legacy/inode.c
@@ -1700,7 +1700,13 @@ static int gadgetfs_bind(struct usb_gadget *gadget,
return 0;
enomem:
- gadgetfs_unbind (gadget);
+ if (dev->req) {
+ usb_ep_free_request(gadget->ep0, dev->req);
+ dev->req = NULL;
+ }
+ gadget->ep0->driver_data = NULL;
+ dev->gadget = NULL;
+ set_gadget_data(gadget, NULL);
return -ENOMEM;
}
Recipients:[map[Email:brauner@kernel.org Name:Christian Brauner To:false] map[Email:gregkh@linuxfoundation.org Name:Greg Kroah-Hartman To:true] map[Email:jack@suse.cz Name:Jan Kara To:false] map[Email:kees@kernel.org Name:Kees Cook To:false] map[Email:linux-kernel@vger.kernel.org Name: To:false] map[Email:linux-usb@vger.kernel.org Name: To:true] map[Email:mjguzik@gmail.com Name:Mateusz Guzik To:false] map[Email:viro@zeniv.linux.org.uk Name:Al Viro To:false] map[Email:torvalds@ppc970.osdl.org Name:Linus Torvalds To:true]] ReportedBy:[] ReviewedBy:[] SuggestedBy:[] TestedBy:[]]
|
| 1/1 |
2026/08/07 05:35 |
action |
base-commit-picker |
0m
Results: map[KernelBranch:master KernelCommit:075b74841bd0065a3bda3440873c747938e69b68 KernelRepo:git://git.kernel.org/pub/scm/linux/kernel/git/torvalds/linux.git]
|
| 2/1 |
2026/08/07 05:35 |
action |
syz-repro-to-c-repro |
0m
Results: map[SimplifiedCRepro:#define _GNU_SOURCE
#include <stdio.h>
#include <stdlib.h>
#include <string.h>
#include <unistd.h>
#include <fcntl.h>
#include <sys/mount.h>
#include <sys/stat.h>
#include <sys/types.h>
#include <sys/wait.h>
#include <sys/syscall.h>
#include <stdint.h>
#include <stdbool.h>
#include <dirent.h>
#include <errno.h>
#include <sched.h>
void write_file(const char *path, const char *val) {
int fd = open(path, O_WRONLY);
if (fd < 0) {
// We don't exit here because failslab might not be available in all kernels,
// but we print the error.
printf("[-] Failed to open %s: %s\n", path, strerror(errno));
return;
}
if (write(fd, val, strlen(val)) < 0) {
printf("[-] Failed to write to %s: %s\n", path, strerror(errno));
}
close(fd);
}
struct usb_config_descriptor {
uint8_t bLength;
uint8_t bDescriptorType;
uint16_t wTotalLength;
uint8_t bNumInterfaces;
uint8_t bConfigurationValue;
uint8_t iConfiguration;
uint8_t bmAttributes;
uint8_t bMaxPower;
} __attribute__ ((packed));
struct usb_device_descriptor {
uint8_t bLength;
uint8_t bDescriptorType;
uint16_t bcdUSB;
uint8_t bDeviceClass;
uint8_t bDeviceSubClass;
uint8_t bDeviceProtocol;
uint8_t bMaxPacketSize0;
uint16_t idVendor;
uint16_t idProduct;
uint16_t bcdDevice;
uint8_t iManufacturer;
uint8_t iProduct;
uint8_t iSerialNumber;
uint8_t bNumConfigurations;
} __attribute__ ((packed));
struct config_data {
uint32_t tag;
struct usb_config_descriptor config;
struct usb_device_descriptor device;
} __attribute__ ((packed));
void trigger_bug(int nth) {
int res;
res = unshare(CLONE_NEWNS);
if (res < 0) {
printf("[-] Failed to unshare: %s\n", strerror(errno));
exit(1);
}
printf("[+] unshare successful.\n");
res = mount(NULL, "/", NULL, MS_PRIVATE | MS_REC, NULL);
if (res < 0) {
printf("[-] Failed to mount / private: %s\n", strerror(errno));
exit(1);
}
printf("[+] mount / private successful.\n");
res = mkdir("/tmp/gadgetfs", 0777);
if (res < 0 && errno != EEXIST) {
printf("[-] Failed to mkdir /tmp/gadgetfs: %s\n", strerror(errno));
exit(1);
}
printf("[+] mkdir /tmp/gadgetfs successful.\n");
res = mount("gadgetfs", "/tmp/gadgetfs", "gadgetfs", 0, NULL);
if (res < 0) {
printf("[-] Failed to mount gadgetfs: %s\n", strerror(errno));
exit(1);
}
printf("[+] mount gadgetfs successful.\n");
char ep0_name[256] = {0};
DIR *dir = opendir("/tmp/gadgetfs");
if (!dir) {
printf("[-] Failed to opendir /tmp/gadgetfs: %s\n", strerror(errno));
exit(1);
}
printf("[+] opendir /tmp/gadgetfs successful.\n");
struct dirent *ent;
while ((ent = readdir(dir)) != NULL) {
if (ent->d_name[0] != '.') {
strncpy(ep0_name, ent->d_name, sizeof(ep0_name) - 1);
break;
}
}
closedir(dir);
if (ep0_name[0] == '\0') {
printf("[-] Failed to find ep0 file\n");
umount("/tmp/gadgetfs");
exit(1);
}
printf("[+] Found ep0 file: %s\n", ep0_name);
char ep0_path[512];
snprintf(ep0_path, sizeof(ep0_path), "/tmp/gadgetfs/%s", ep0_name);
int fd = open(ep0_path, O_RDWR);
if (fd < 0) {
printf("[-] Failed to open %s: %s\n", ep0_path, strerror(errno));
umount("/tmp/gadgetfs");
exit(1);
}
printf("[+] open %s successful.\n", ep0_path);
struct config_data data;
memset(&data, 0, sizeof(data));
data.tag = 0;
data.config.bLength = 9;
data.config.bDescriptorType = 2; // USB_DT_CONFIG
data.config.wTotalLength = 9;
data.config.bConfigurationValue = 1;
data.config.bmAttributes = 0x80; // USB_CONFIG_ATT_ONE
data.device.bLength = 18;
data.device.bDescriptorType = 1; // USB_DT_DEVICE
data.device.bNumConfigurations = 1;
write_file("/sys/kernel/debug/failslab/ignore-gfp-wait", "N");
write_file("/sys/kernel/debug/failslab/task-filter", "Y");
write_file("/sys/kernel/debug/failslab/probability", "100");
write_file("/sys/kernel/debug/failslab/times", "1");
write_file("/proc/self/make-it-fail", "1");
char nth_str[32];
snprintf(nth_str, sizeof(nth_str), "%d", nth);
char path[256];
snprintf(path, sizeof(path), "/proc/self/task/%ld/fail-nth", syscall(SYS_gettid));
write_file(path, nth_str);
printf("[*] Triggering bug with fail-nth=%d...\n", nth);
// Expected to fail due to fault injection
if (write(fd, &data, sizeof(data)) < 0) {
printf("[+] write to ep0 failed as expected (fault injection).\n");
} else {
printf("[-] write to ep0 succeeded (fault injection missed).\n");
}
write_file("/sys/kernel/debug/failslab/probability", "0");
write_file("/sys/kernel/debug/failslab/times", "0");
write_file("/proc/self/make-it-fail", "0");
write_file(path, "0");
res = close(fd);
if (res < 0) {
printf("[-] Failed to close fd: %s\n", strerror(errno));
} else {
printf("[+] close fd successful.\n");
}
res = umount("/tmp/gadgetfs");
if (res < 0) {
printf("[-] Failed to umount gadgetfs: %s\n", strerror(errno));
} else {
printf("[+] umount gadgetfs successful.\n");
}
}
int main() {
printf("[*] Starting bug reproduction...\n");
int res = mount("debugfs", "/sys/kernel/debug", "debugfs", 0, NULL);
if (res < 0 && errno != EBUSY) {
printf("[-] Failed to mount debugfs: %s\n", strerror(errno));
} else {
printf("[+] debugfs mounted or already present.\n");
}
for (int i = 1; i <= 50; i++) {
pid_t pid = fork();
if (pid < 0) {
printf("[-] Failed to fork: %s\n", strerror(errno));
exit(1);
}
if (pid == 0) {
trigger_bug(i);
exit(0);
}
wait(NULL);
}
printf("[+] Bug reproduction finished.\n");
return 0;
}
]
|
| 3/1 |
2026/08/07 05:35 |
action |
kernel-checkouter |
0m
Results: map[KernelSrc:/app/workdir/cache/src/fda57abbaa89268f1be2087d652398a1a507dfa0]
|
| 4/1 |
2026/08/07 05:35 |
action |
kernel-builder |
0m
Results: map[KernelObj:/app/workdir/cache/build/9514ae972f608b949e6f94b00f42cd8b515fc5bf]
|
| 5/1 |
2026/08/07 05:35 |
action |
crash-reproducer |
1m
Results: map[OtherCrashReports:<nil> ReproducedBugTitle:KASAN: slab-use-after-free Write in gadgetfs_kill_sb ReproducedCrashReport:udc dummy_udc.0: failed to start USB Gadget filesystem: -12
gadgetfs gadget.0: probe with driver gadgetfs failed with error -12
gadgetfs: bound to dummy_udc driver
==================================================================
BUG: KASAN: slab-use-after-free in instrument_atomic_read_write include/linux/instrumented.h:112 [inline]
BUG: KASAN: slab-use-after-free in atomic_fetch_sub_release include/linux/atomic/atomic-instrumented.h:400 [inline]
BUG: KASAN: slab-use-after-free in __refcount_sub_and_test include/linux/refcount.h:389 [inline]
BUG: KASAN: slab-use-after-free in __refcount_dec_and_test include/linux/refcount.h:432 [inline]
BUG: KASAN: slab-use-after-free in refcount_dec_and_test include/linux/refcount.h:450 [inline]
BUG: KASAN: slab-use-after-free in put_dev drivers/usb/gadget/legacy/inode.c:165 [inline]
BUG: KASAN: slab-use-after-free in gadgetfs_kill_sb+0x46/0x110 drivers/usb/gadget/legacy/inode.c:2104
Write of size 4 at addr ffff88811638c080 by task syz-executor169/5848
CPU: 0 UID: 0 PID: 5848 Comm: syz-executor169 Not tainted syzkaller #1 PREEMPT_{RT,(full)}
Hardware name: QEMU Standard PC (Q35 + ICH9, 2009), BIOS 1.16.3-debian-1.16.3-2 04/01/2014
Call Trace:
<TASK>
dump_stack_lvl+0xe8/0x150 lib/dump_stack.c:120
print_address_description+0x55/0x1e0 mm/kasan/report.c:378
print_report+0x58/0x70 mm/kasan/report.c:482
kasan_report+0x117/0x150 mm/kasan/report.c:595
check_region_inline mm/kasan/generic.c:-1 [inline]
kasan_check_range+0x264/0x2c0 mm/kasan/generic.c:200
instrument_atomic_read_write include/linux/instrumented.h:112 [inline]
atomic_fetch_sub_release include/linux/atomic/atomic-instrumented.h:400 [inline]
__refcount_sub_and_test include/linux/refcount.h:389 [inline]
__refcount_dec_and_test include/linux/refcount.h:432 [inline]
refcount_dec_and_test include/linux/refcount.h:450 [inline]
put_dev drivers/usb/gadget/legacy/inode.c:165 [inline]
gadgetfs_kill_sb+0x46/0x110 drivers/usb/gadget/legacy/inode.c:2104
deactivate_locked_super+0xbc/0x130 fs/super.c:477
cleanup_mnt+0x3d3/0x460 fs/namespace.c:1317
task_work_run+0x1d9/0x270 kernel/task_work.c:233
resume_user_mode_work include/linux/resume_user_mode.h:50 [inline]
__exit_to_user_mode_loop kernel/entry/common.c:70 [inline]
exit_to_user_mode_loop+0x1fa/0x730 kernel/entry/common.c:101
__exit_to_user_mode_prepare include/linux/irq-entry-common.h:207 [inline]
syscall_exit_to_user_mode_prepare include/linux/irq-entry-common.h:230 [inline]
syscall_exit_to_user_mode include/linux/entry-common.h:318 [inline]
do_syscall_64+0x353/0x580 arch/x86/entry/syscall_64.c:100
entry_SYSCALL_64_after_hwframe+0x77/0x7f
RIP: 0033:0x7f5f0e505027
Code: a2 c7 05 54 b0 08 00 00 00 00 00 eb 96 e8 a1 08 00 00 90 31 f6 e9 09 00 00 00 66 0f 1f 84 00 00 00 00 00 b8 a6 00 00 00 0f 05 <48> 3d 00 f0 ff ff 77 01 c3 48 c7 c2 d8 ff ff ff f7 d8 64 89 02 b8
RSP: 002b:00007ffcdf2b0288 EFLAGS: 00000246 ORIG_RAX: 00000000000000a6
RAX: 0000000000000000 RBX: 0000000000000003 RCX: 00007f5f0e505027
RDX: 0000000000000000 RSI: 0000000000000000 RDI: 00007f5f0e55f05e
RBP: 00007f5f0e55f05e R08: 0000000000000000 R09: 0000000000000000
R10: 0000000000000000 R11: 0000000000000246 R12: 00007f5f0e55f623
R13: 00007ffcdf2b03e0 R14: 00007f5f0e561510 R15: 00007f5f0e5614e8
</TASK>
Allocated by task 5848:
kasan_save_stack mm/kasan/common.c:57 [inline]
kasan_save_track+0x3e/0x80 mm/kasan/common.c:78
poison_kmalloc_redzone mm/kasan/common.c:398 [inline]
__kasan_kmalloc+0x93/0xb0 mm/kasan/common.c:415
kasan_kmalloc include/linux/kasan.h:263 [inline]
__kmalloc_cache_noprof+0x3d2/0x6b0 mm/slub.c:5489
_kmalloc_noprof include/linux/slab.h:988 [inline]
_kzalloc_noprof include/linux/slab.h:1309 [inline]
dev_new drivers/usb/gadget/legacy/inode.c:176 [inline]
gadgetfs_fill_super+0x27b/0x7a0 drivers/usb/gadget/legacy/inode.c:2054
vfs_get_super fs/super.c:1273 [inline]
get_tree_single+0xc0/0x150 fs/super.c:1300
vfs_get_tree+0x92/0x2a0 fs/super.c:1700
fc_mount fs/namespace.c:1198 [inline]
do_new_mount_fc fs/namespace.c:3765 [inline]
do_new_mount+0x319/0xdc0 fs/namespace.c:3841
do_mount fs/namespace.c:4174 [inline]
__do_sys_mount fs/namespace.c:4390 [inline]
__se_sys_mount+0x31d/0x420 fs/namespace.c:4367
do_syscall_x64 arch/x86/entry/syscall_64.c:63 [inline]
do_syscall_64+0x174/0x580 arch/x86/entry/syscall_64.c:94
entry_SYSCALL_64_after_hwframe+0x77/0x7f
Freed by task 5848:
kasan_save_stack mm/kasan/common.c:57 [inline]
kasan_save_track+0x3e/0x80 mm/kasan/common.c:78
kasan_save_free_info+0x40/0x50 mm/kasan/generic.c:584
poison_slab_object mm/kasan/common.c:253 [inline]
__kasan_slab_free+0x5c/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 mm/slub.c:6377 [inline]
kfree+0x1c5/0x6c0 mm/slub.c:6692
put_dev drivers/usb/gadget/legacy/inode.c:169 [inline]
dev_release+0x173/0x210 drivers/usb/gadget/legacy/inode.c:1215
__fput+0x42a/0xa80 fs/file_table.c:512
fput_close_sync+0x11f/0x240 fs/file_table.c:617
__do_sys_close fs/open.c:1511 [inline]
__se_sys_close fs/open.c:1496 [inline]
__x64_sys_close+0x7e/0x110 fs/open.c:1496
do_syscall_x64 arch/x86/entry/syscall_64.c:63 [inline]
do_syscall_64+0x174/0x580 arch/x86/entry/syscall_64.c:94
entry_SYSCALL_64_after_hwframe+0x77/0x7f
The buggy address belongs to the object at ffff88811638c000
which belongs to the cache kmalloc-1k of size 1024
The buggy address is located 128 bytes inside of
freed 1024-byte region [ffff88811638c000, ffff88811638c400)
The buggy address belongs to the physical page:
page: refcount:0 mapcount:0 mapping:0000000000000000 index:0xffff88811638d000 pfn:0x116388
head: order:3 mapcount:0 entire_mapcount:0 nr_pages_mapped:0 pincount:0
flags: 0x100000000000240(workingset|head|node=0|zone=2)
page_type: f5(slab)
raw: 0100000000000240 ffff888100041dc0 ffffea00044a8a10 ffffea00043da810
raw: ffff88811638d000 000000080010000f 00000000f5000000 0000000000000000
head: 0100000000000240 ffff888100041dc0 ffffea00044a8a10 ffffea00043da810
head: ffff88811638d000 000000080010000f 00000000f5000000 0000000000000000
head: 0100000000000003 fffffffffffffe01 00000000ffffffff 00000000ffffffff
head: 0000000000000000 0000000000000000 00000000ffffffff 0000000000000008
page dumped because: kasan: bad access detected
page_owner tracks the page as allocated
page last allocated via order 3, migratetype Unmovable, gfp_mask 0xd20c0(__GFP_IO|__GFP_FS|__GFP_NOWARN|__GFP_NORETRY|__GFP_COMP|__GFP_NOMEMALLOC), pid 5581, tgid 5581 (syz-executor), ts 49160475111, free_ts 0
set_page_owner include/linux/page_owner.h:32 [inline]
post_alloc_hook+0x1f9/0x250 mm/page_alloc.c:1859
prep_new_page mm/page_alloc.c:1867 [inline]
get_page_from_freelist+0x262a/0x26a0 mm/page_alloc.c:3946
__alloc_frozen_pages_noprof+0x18d/0x380 mm/page_alloc.c:5304
alloc_slab_page mm/slub.c:3266 [inline]
allocate_slab+0x79/0x5e0 mm/slub.c:3380
new_slab mm/slub.c:3426 [inline]
refill_objects+0x2d8/0x350 mm/slub.c:7310
refill_sheaf mm/slub.c:2804 [inline]
__pcs_replace_empty_main+0x330/0x690 mm/slub.c:4675
alloc_from_pcs mm/slub.c:4773 [inline]
slab_alloc_node mm/slub.c:4905 [inline]
__kmalloc_cache_noprof+0x468/0x6b0 mm/slub.c:5485
_kmalloc_noprof include/linux/slab.h:988 [inline]
_kzalloc_noprof include/linux/slab.h:1309 [inline]
rxrpc_service_prealloc+0x9f/0x130 net/rxrpc/call_accept.c:167
rxrpc_listen+0x1b7/0x360 net/rxrpc/af_rxrpc.c:237
afs_open_socket+0x2fa/0x3f0 fs/afs/rxrpc.c:107
afs_net_init+0x6ce/0x8f0 fs/afs/main.c:116
ops_init+0x35d/0x5d0 net/core/net_namespace.c:137
setup_net+0x118/0x350 net/core/net_namespace.c:446
copy_net_ns+0x4f9/0x720 net/core/net_namespace.c:579
create_new_namespaces+0x3f0/0x6b0 kernel/nsproxy.c:132
unshare_nsproxy_namespaces+0x149/0x190 kernel/nsproxy.c:234
page_owner free stack trace missing
Memory state around the buggy address:
ffff88811638bf80: fc fc fc fc fc fc fc fc fc fc fc fc fc fc fc fc
ffff88811638c000: fa fb fb fb fb fb fb fb fb fb fb fb fb fb fb fb
>ffff88811638c080: fb fb fb fb fb fb fb fb fb fb fb fb fb fb fb fb
^
ffff88811638c100: fb fb fb fb fb fb fb fb fb fb fb fb fb fb fb fb
ffff88811638c180: fb fb fb fb fb fb fb fb fb fb fb fb fb fb fb fb
==================================================================
ReproducedFaultInjection:FAULT_INJECTION: forcing a failure.
name fail_usercopy, interval 1, probability 0, space 0, times 1
Call Trace:
dump_stack_lvl+0xe8/0x150 lib/dump_stack.c:120
fail_dump lib/fault-inject.c:73 [inline]
should_fail_ex+0x467/0x600 lib/fault-inject.c:174
_inline_copy_from_user include/linux/uaccess.h:170 [inline]
_copy_from_user+0x2d/0xb0 lib/usercopy.c:18
copy_from_user include/linux/uaccess.h:222 [inline]
dev_config+0x2ab/0x12c0 drivers/usb/gadget/legacy/inode.c:1822
vfs_write+0x29f/0xbb0 fs/read_write.c:685
ksys_write+0x156/0x270 fs/read_write.c:739
do_syscall_x64 arch/x86/entry/syscall_64.c:63 [inline]
do_syscall_64+0x174/0x580 arch/x86/entry/syscall_64.c:94
entry_SYSCALL_64_after_hwframe+0x77/0x7f
FAULT_INJECTION: forcing a failure.
name fail_usercopy, interval 1, probability 0, space 0, times 0
Call Trace:
dump_stack_lvl+0xe8/0x150 lib/dump_stack.c:120
fail_dump lib/fault-inject.c:73 [inline]
should_fail_ex+0x467/0x600 lib/fault-inject.c:174
_inline_copy_from_user include/linux/uaccess.h:170 [inline]
_copy_from_user+0x2d/0xb0 lib/usercopy.c:18
copy_from_user include/linux/uaccess.h:222 [inline]
memdup_user+0x5e/0xd0 mm/util.c:225
dev_config+0x4d8/0x12c0 drivers/usb/gadget/legacy/inode.c:1829
vfs_write+0x29f/0xbb0 fs/read_write.c:685
ksys_write+0x156/0x270 fs/read_write.c:739
do_syscall_x64 arch/x86/entry/syscall_64.c:63 [inline]
do_syscall_64+0x174/0x580 arch/x86/entry/syscall_64.c:94
entry_SYSCALL_64_after_hwframe+0x77/0x7f
FAULT_INJECTION: forcing a failure.
name failslab, interval 1, probability 100, space 0, times 0
Call Trace:
dump_stack_lvl+0xe8/0x150 lib/dump_stack.c:120
fail_dump lib/fault-inject.c:73 [inline]
should_fail_ex+0x467/0x600 lib/fault-inject.c:174
should_failslab+0xa8/0x100 mm/failslab.c:46
slab_pre_alloc_hook mm/slub.c:4539 [inline]
slab_alloc_node mm/slub.c:4897 [inline]
__kmalloc_cache_noprof+0xa0/0x6b0 mm/slub.c:5485
_kmalloc_noprof include/linux/slab.h:988 [inline]
_kzalloc_noprof include/linux/slab.h:1309 [inline]
bus_add_driver+0x165/0x670 drivers/base/bus.c:747
driver_register+0x23a/0x320 drivers/base/driver.c:174
usb_gadget_register_driver_owner+0xf9/0x270 drivers/usb/gadget/udc/core.c:1752
dev_config+0xe4e/0x12c0 drivers/usb/gadget/legacy/inode.c:1883
vfs_write+0x29f/0xbb0 fs/read_write.c:685
ksys_write+0x156/0x270 fs/read_write.c:739
do_syscall_x64 arch/x86/entry/syscall_64.c:63 [inline]
do_syscall_64+0x174/0x580 arch/x86/entry/syscall_64.c:94
entry_SYSCALL_64_after_hwframe+0x77/0x7f
FAULT_INJECTION: forcing a failure.
name failslab, interval 1, probability 100, space 0, times 0
Call Trace:
dump_stack_lvl+0xe8/0x150 lib/dump_stack.c:120
fail_dump lib/fault-inject.c:73 [inline]
should_fail_ex+0x467/0x600 lib/fault-inject.c:174
should_failslab+0xa8/0x100 mm/failslab.c:46
slab_pre_alloc_hook mm/slub.c:4539 [inline]
slab_alloc_node mm/slub.c:4897 [inline]
kmem_cache_alloc_noprof+0xa4/0x650 mm/slub.c:4931
__kernfs_new_node+0xe7/0xa70 fs/kernfs/dir.c:665
kernfs_new_node fs/kernfs/dir.c:751 [inline]
kernfs_create_dir_ns+0xfe/0x230 fs/kernfs/dir.c:1120
sysfs_create_dir_ns+0x12f/0x2a0 fs/sysfs/dir.c:59
create_dir lib/kobject.c:73 [inline]
kobject_add_internal+0x62c/0xce0 lib/kobject.c:240
kobject_add_varg lib/kobject.c:374 [inline]
kobject_init_and_add+0x12b/0x1a0 lib/kobject.c:457
bus_add_driver+0x258/0x670 drivers/base/bus.c:756
driver_register+0x23a/0x320 drivers/base/driver.c:174
usb_gadget_register_driver_owner+0xf9/0x270 drivers/usb/gadget/udc/core.c:1752
dev_config+0xe4e/0x12c0 drivers/usb/gadget/legacy/inode.c:1883
vfs_write+0x29f/0xbb0 fs/read_write.c:685
ksys_write+0x156/0x270 fs/read_write.c:739
do_syscall_x64 arch/x86/entry/syscall_64.c:63 [inline]
do_syscall_64+0x174/0x580 arch/x86/entry/syscall_64.c:94
entry_SYSCALL_64_after_hwframe+0x77/0x7f
FAULT_INJECTION: forcing a failure.
name failslab, interval 1, probability 100, space 0, times 0
Call Trace:
dump_stack_lvl+0xe8/0x150 lib/dump_stack.c:120
fail_dump lib/fault-inject.c:73 [inline]
should_fail_ex+0x467/0x600 lib/fault-inject.c:174
should_failslab+0xa8/0x100 mm/failslab.c:46
slab_pre_alloc_hook mm/slub.c:4539 [inline]
slab_alloc_node mm/slub.c:4897 [inline]
__do_kmalloc_node mm/slub.c:5333 [inline]
__kmalloc_node_track_caller_noprof+0x102/0x7c0 mm/slub.c:5471
__kmemdup_nul mm/util.c:64 [inline]
kstrdup+0x42/0x100 mm/util.c:84
__kernfs_new_node+0xac/0xa70 fs/kernfs/dir.c:661
kernfs_new_node+0xea/0x140 fs/kernfs/dir.c:751
kernfs_create_link+0xa7/0x200 fs/kernfs/symlink.c:39
sysfs_do_create_link_sd+0x83/0x110 fs/sysfs/symlink.c:44
driver_sysfs_add+0x89/0x210 drivers/base/dd.c:489
really_probe+0x142/0xae0 drivers/base/dd.c:694
__driver_probe_device+0x1e8/0x360 drivers/base/dd.c:868
driver_probe_device+0x4f/0x240 drivers/base/dd.c:898
__driver_attach+0x339/0x600 drivers/base/dd.c:1292
bus_for_each_dev+0x23e/0x2c0 drivers/base/bus.c:383
bus_add_driver+0x348/0x670 drivers/base/bus.c:763
driver_register+0x23a/0x320 drivers/base/driver.c:174
usb_gadget_register_driver_owner+0xf9/0x270 drivers/usb/gadget/udc/core.c:1752
dev_config+0xe4e/0x12c0 drivers/usb/gadget/legacy/inode.c:1883
vfs_write+0x29f/0xbb0 fs/read_write.c:685
ksys_write+0x156/0x270 fs/read_write.c:739
do_syscall_x64 arch/x86/entry/syscall_64.c:63 [inline]
do_syscall_64+0x174/0x580 arch/x86/entry/syscall_64.c:94
entry_SYSCALL_64_after_hwframe+0x77/0x7f]
|
| 6/1 |
2026/08/07 05:36 |
action |
codesearch-prepare |
0m
Results: map[Index:codesearch-index]
|
| 7/1 |
2026/08/07 05:37 |
agent |
debugger |
6m
Model:
Instruction:
You are an experienced Linux kernel developer tasked with debugging a kernel crash root cause.
You need to provide a detailed explanation of the root cause for another developer to be
able to write a fix for the bug based on your explanation. Include all relevant details
into the response: function/struct/field/etc names, code snippets, line numbers,
macro/enum values, etc.
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.
Prompt:
The crash is:
udc dummy_udc.0: failed to start USB Gadget filesystem: -12
gadgetfs gadget.0: probe with driver gadgetfs failed with error -12
gadgetfs: bound to dummy_udc driver
==================================================================
BUG: KASAN: slab-use-after-free in instrument_atomic_read_write include/linux/instrumented.h:112 [inline]
BUG: KASAN: slab-use-after-free in atomic_fetch_sub_release include/linux/atomic/atomic-instrumented.h:400 [inline]
BUG: KASAN: slab-use-after-free in __refcount_sub_and_test include/linux/refcount.h:389 [inline]
BUG: KASAN: slab-use-after-free in __refcount_dec_and_test include/linux/refcount.h:432 [inline]
BUG: KASAN: slab-use-after-free in refcount_dec_and_test include/linux/refcount.h:450 [inline]
BUG: KASAN: slab-use-after-free in put_dev drivers/usb/gadget/legacy/inode.c:165 [inline]
BUG: KASAN: slab-use-after-free in gadgetfs_kill_sb+0x46/0x110 drivers/usb/gadget/legacy/inode.c:2104
Write of size 4 at addr ffff88811638c080 by task syz-executor169/5848
CPU: 0 UID: 0 PID: 5848 Comm: syz-executor169 Not tainted syzkaller #1 PREEMPT_{RT,(full)}
Hardware name: QEMU Standard PC (Q35 + ICH9, 2009), BIOS 1.16.3-debian-1.16.3-2 04/01/2014
Call Trace:
<TASK>
dump_stack_lvl+0xe8/0x150 lib/dump_stack.c:120
print_address_description+0x55/0x1e0 mm/kasan/report.c:378
print_report+0x58/0x70 mm/kasan/report.c:482
kasan_report+0x117/0x150 mm/kasan/report.c:595
check_region_inline mm/kasan/generic.c:-1 [inline]
kasan_check_range+0x264/0x2c0 mm/kasan/generic.c:200
instrument_atomic_read_write include/linux/instrumented.h:112 [inline]
atomic_fetch_sub_release include/linux/atomic/atomic-instrumented.h:400 [inline]
__refcount_sub_and_test include/linux/refcount.h:389 [inline]
__refcount_dec_and_test include/linux/refcount.h:432 [inline]
refcount_dec_and_test include/linux/refcount.h:450 [inline]
put_dev drivers/usb/gadget/legacy/inode.c:165 [inline]
gadgetfs_kill_sb+0x46/0x110 drivers/usb/gadget/legacy/inode.c:2104
deactivate_locked_super+0xbc/0x130 fs/super.c:477
cleanup_mnt+0x3d3/0x460 fs/namespace.c:1317
task_work_run+0x1d9/0x270 kernel/task_work.c:233
resume_user_mode_work include/linux/resume_user_mode.h:50 [inline]
__exit_to_user_mode_loop kernel/entry/common.c:70 [inline]
exit_to_user_mode_loop+0x1fa/0x730 kernel/entry/common.c:101
__exit_to_user_mode_prepare include/linux/irq-entry-common.h:207 [inline]
syscall_exit_to_user_mode_prepare include/linux/irq-entry-common.h:230 [inline]
syscall_exit_to_user_mode include/linux/entry-common.h:318 [inline]
do_syscall_64+0x353/0x580 arch/x86/entry/syscall_64.c:100
entry_SYSCALL_64_after_hwframe+0x77/0x7f
RIP: 0033:0x7f5f0e505027
Code: a2 c7 05 54 b0 08 00 00 00 00 00 eb 96 e8 a1 08 00 00 90 31 f6 e9 09 00 00 00 66 0f 1f 84 00 00 00 00 00 b8 a6 00 00 00 0f 05 <48> 3d 00 f0 ff ff 77 01 c3 48 c7 c2 d8 ff ff ff f7 d8 64 89 02 b8
RSP: 002b:00007ffcdf2b0288 EFLAGS: 00000246 ORIG_RAX: 00000000000000a6
RAX: 0000000000000000 RBX: 0000000000000003 RCX: 00007f5f0e505027
RDX: 0000000000000000 RSI: 0000000000000000 RDI: 00007f5f0e55f05e
RBP: 00007f5f0e55f05e R08: 0000000000000000 R09: 0000000000000000
R10: 0000000000000000 R11: 0000000000000246 R12: 00007f5f0e55f623
R13: 00007ffcdf2b03e0 R14: 00007f5f0e561510 R15: 00007f5f0e5614e8
</TASK>
Allocated by task 5848:
kasan_save_stack mm/kasan/common.c:57 [inline]
kasan_save_track+0x3e/0x80 mm/kasan/common.c:78
poison_kmalloc_redzone mm/kasan/common.c:398 [inline]
__kasan_kmalloc+0x93/0xb0 mm/kasan/common.c:415
kasan_kmalloc include/linux/kasan.h:263 [inline]
__kmalloc_cache_noprof+0x3d2/0x6b0 mm/slub.c:5489
_kmalloc_noprof include/linux/slab.h:988 [inline]
_kzalloc_noprof include/linux/slab.h:1309 [inline]
dev_new drivers/usb/gadget/legacy/inode.c:176 [inline]
gadgetfs_fill_super+0x27b/0x7a0 drivers/usb/gadget/legacy/inode.c:2054
vfs_get_super fs/super.c:1273 [inline]
get_tree_single+0xc0/0x150 fs/super.c:1300
vfs_get_tree+0x92/0x2a0 fs/super.c:1700
fc_mount fs/namespace.c:1198 [inline]
do_new_mount_fc fs/namespace.c:3765 [inline]
do_new_mount+0x319/0xdc0 fs/namespace.c:3841
do_mount fs/namespace.c:4174 [inline]
__do_sys_mount fs/namespace.c:4390 [inline]
__se_sys_mount+0x31d/0x420 fs/namespace.c:4367
do_syscall_x64 arch/x86/entry/syscall_64.c:63 [inline]
do_syscall_64+0x174/0x580 arch/x86/entry/syscall_64.c:94
entry_SYSCALL_64_after_hwframe+0x77/0x7f
Freed by task 5848:
kasan_save_stack mm/kasan/common.c:57 [inline]
kasan_save_track+0x3e/0x80 mm/kasan/common.c:78
kasan_save_free_info+0x40/0x50 mm/kasan/generic.c:584
poison_slab_object mm/kasan/common.c:253 [inline]
__kasan_slab_free+0x5c/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 mm/slub.c:6377 [inline]
kfree+0x1c5/0x6c0 mm/slub.c:6692
put_dev drivers/usb/gadget/legacy/inode.c:169 [inline]
dev_release+0x173/0x210 drivers/usb/gadget/legacy/inode.c:1215
__fput+0x42a/0xa80 fs/file_table.c:512
fput_close_sync+0x11f/0x240 fs/file_table.c:617
__do_sys_close fs/open.c:1511 [inline]
__se_sys_close fs/open.c:1496 [inline]
__x64_sys_close+0x7e/0x110 fs/open.c:1496
do_syscall_x64 arch/x86/entry/syscall_64.c:63 [inline]
do_syscall_64+0x174/0x580 arch/x86/entry/syscall_64.c:94
entry_SYSCALL_64_after_hwframe+0x77/0x7f
The buggy address belongs to the object at ffff88811638c000
which belongs to the cache kmalloc-1k of size 1024
The buggy address is located 128 bytes inside of
freed 1024-byte region [ffff88811638c000, ffff88811638c400)
The buggy address belongs to the physical page:
page: refcount:0 mapcount:0 mapping:0000000000000000 index:0xffff88811638d000 pfn:0x116388
head: order:3 mapcount:0 entire_mapcount:0 nr_pages_mapped:0 pincount:0
flags: 0x100000000000240(workingset|head|node=0|zone=2)
page_type: f5(slab)
raw: 0100000000000240 ffff888100041dc0 ffffea00044a8a10 ffffea00043da810
raw: ffff88811638d000 000000080010000f 00000000f5000000 0000000000000000
head: 0100000000000240 ffff888100041dc0 ffffea00044a8a10 ffffea00043da810
head: ffff88811638d000 000000080010000f 00000000f5000000 0000000000000000
head: 0100000000000003 fffffffffffffe01 00000000ffffffff 00000000ffffffff
head: 0000000000000000 0000000000000000 00000000ffffffff 0000000000000008
page dumped because: kasan: bad access detected
page_owner tracks the page as allocated
page last allocated via order 3, migratetype Unmovable, gfp_mask 0xd20c0(__GFP_IO|__GFP_FS|__GFP_NOWARN|__GFP_NORETRY|__GFP_COMP|__GFP_NOMEMALLOC), pid 5581, tgid 5581 (syz-executor), ts 49160475111, free_ts 0
set_page_owner include/linux/page_owner.h:32 [inline]
post_alloc_hook+0x1f9/0x250 mm/page_alloc.c:1859
prep_new_page mm/page_alloc.c:1867 [inline]
get_page_from_freelist+0x262a/0x26a0 mm/page_alloc.c:3946
__alloc_frozen_pages_noprof+0x18d/0x380 mm/page_alloc.c:5304
alloc_slab_page mm/slub.c:3266 [inline]
allocate_slab+0x79/0x5e0 mm/slub.c:3380
new_slab mm/slub.c:3426 [inline]
refill_objects+0x2d8/0x350 mm/slub.c:7310
refill_sheaf mm/slub.c:2804 [inline]
__pcs_replace_empty_main+0x330/0x690 mm/slub.c:4675
alloc_from_pcs mm/slub.c:4773 [inline]
slab_alloc_node mm/slub.c:4905 [inline]
__kmalloc_cache_noprof+0x468/0x6b0 mm/slub.c:5485
_kmalloc_noprof include/linux/slab.h:988 [inline]
_kzalloc_noprof include/linux/slab.h:1309 [inline]
rxrpc_service_prealloc+0x9f/0x130 net/rxrpc/call_accept.c:167
rxrpc_listen+0x1b7/0x360 net/rxrpc/af_rxrpc.c:237
afs_open_socket+0x2fa/0x3f0 fs/afs/rxrpc.c:107
afs_net_init+0x6ce/0x8f0 fs/afs/main.c:116
ops_init+0x35d/0x5d0 net/core/net_namespace.c:137
setup_net+0x118/0x350 net/core/net_namespace.c:446
copy_net_ns+0x4f9/0x720 net/core/net_namespace.c:579
create_new_namespaces+0x3f0/0x6b0 kernel/nsproxy.c:132
unshare_nsproxy_namespaces+0x149/0x190 kernel/nsproxy.c:234
page_owner free stack trace missing
Memory state around the buggy address:
ffff88811638bf80: fc fc fc fc fc fc fc fc fc fc fc fc fc fc fc fc
ffff88811638c000: fa fb fb fb fb fb fb fb fb fb fb fb fb fb fb fb
>ffff88811638c080: fb fb fb fb fb fb fb fb fb fb fb fb fb fb fb fb
^
ffff88811638c100: fb fb fb fb fb fb fb fb fb fb fb fb fb fb fb fb
ffff88811638c180: fb fb fb fb fb fb fb fb fb fb fb fb fb fb fb fb
==================================================================
The following C code is a draft of the vulnerable syscall sequence. Keep in mind that
it may lack the precise threading, sandboxing, and some arguments of a working reproducer:
#define _GNU_SOURCE
#include <stdio.h>
#include <stdlib.h>
#include <string.h>
#include <unistd.h>
#include <fcntl.h>
#include <sys/mount.h>
#include <sys/stat.h>
#include <sys/types.h>
#include <sys/wait.h>
#include <sys/syscall.h>
#include <stdint.h>
#include <stdbool.h>
#include <dirent.h>
#include <errno.h>
#include <sched.h>
void write_file(const char *path, const char *val) {
int fd = open(path, O_WRONLY);
if (fd < 0) {
// We don't exit here because failslab might not be available in all kernels,
// but we print the error.
printf("[-] Failed to open %s: %s\n", path, strerror(errno));
return;
}
if (write(fd, val, strlen(val)) < 0) {
printf("[-] Failed to write to %s: %s\n", path, strerror(errno));
}
close(fd);
}
struct usb_config_descriptor {
uint8_t bLength;
uint8_t bDescriptorType;
uint16_t wTotalLength;
uint8_t bNumInterfaces;
uint8_t bConfigurationValue;
uint8_t iConfiguration;
uint8_t bmAttributes;
uint8_t bMaxPower;
} __attribute__ ((packed));
struct usb_device_descriptor {
uint8_t bLength;
uint8_t bDescriptorType;
uint16_t bcdUSB;
uint8_t bDeviceClass;
uint8_t bDeviceSubClass;
uint8_t bDeviceProtocol;
uint8_t bMaxPacketSize0;
uint16_t idVendor;
uint16_t idProduct;
uint16_t bcdDevice;
uint8_t iManufacturer;
uint8_t iProduct;
uint8_t iSerialNumber;
uint8_t bNumConfigurations;
} __attribute__ ((packed));
struct config_data {
uint32_t tag;
struct usb_config_descriptor config;
struct usb_device_descriptor device;
} __attribute__ ((packed));
void trigger_bug(int nth) {
int res;
res = unshare(CLONE_NEWNS);
if (res < 0) {
printf("[-] Failed to unshare: %s\n", strerror(errno));
exit(1);
}
printf("[+] unshare successful.\n");
res = mount(NULL, "/", NULL, MS_PRIVATE | MS_REC, NULL);
if (res < 0) {
printf("[-] Failed to mount / private: %s\n", strerror(errno));
exit(1);
}
printf("[+] mount / private successful.\n");
res = mkdir("/tmp/gadgetfs", 0777);
if (res < 0 && errno != EEXIST) {
printf("[-] Failed to mkdir /tmp/gadgetfs: %s\n", strerror(errno));
exit(1);
}
printf("[+] mkdir /tmp/gadgetfs successful.\n");
res = mount("gadgetfs", "/tmp/gadgetfs", "gadgetfs", 0, NULL);
if (res < 0) {
printf("[-] Failed to mount gadgetfs: %s\n", strerror(errno));
exit(1);
}
printf("[+] mount gadgetfs successful.\n");
char ep0_name[256] = {0};
DIR *dir = opendir("/tmp/gadgetfs");
if (!dir) {
printf("[-] Failed to opendir /tmp/gadgetfs: %s\n", strerror(errno));
exit(1);
}
printf("[+] opendir /tmp/gadgetfs successful.\n");
struct dirent *ent;
while ((ent = readdir(dir)) != NULL) {
if (ent->d_name[0] != '.') {
strncpy(ep0_name, ent->d_name, sizeof(ep0_name) - 1);
break;
}
}
closedir(dir);
if (ep0_name[0] == '\0') {
printf("[-] Failed to find ep0 file\n");
umount("/tmp/gadgetfs");
exit(1);
}
printf("[+] Found ep0 file: %s\n", ep0_name);
char ep0_path[512];
snprintf(ep0_path, sizeof(ep0_path), "/tmp/gadgetfs/%s", ep0_name);
int fd = open(ep0_path, O_RDWR);
if (fd < 0) {
printf("[-] Failed to open %s: %s\n", ep0_path, strerror(errno));
umount("/tmp/gadgetfs");
exit(1);
}
printf("[+] open %s successful.\n", ep0_path);
struct config_data data;
memset(&data, 0, sizeof(data));
data.tag = 0;
data.config.bLength = 9;
data.config.bDescriptorType = 2; // USB_DT_CONFIG
data.config.wTotalLength = 9;
data.config.bConfigurationValue = 1;
data.config.bmAttributes = 0x80; // USB_CONFIG_ATT_ONE
data.device.bLength = 18;
data.device.bDescriptorType = 1; // USB_DT_DEVICE
data.device.bNumConfigurations = 1;
write_file("/sys/kernel/debug/failslab/ignore-gfp-wait", "N");
write_file("/sys/kernel/debug/failslab/task-filter", "Y");
write_file("/sys/kernel/debug/failslab/probability", "100");
write_file("/sys/kernel/debug/failslab/times", "1");
write_file("/proc/self/make-it-fail", "1");
char nth_str[32];
snprintf(nth_str, sizeof(nth_str), "%d", nth);
char path[256];
snprintf(path, sizeof(path), "/proc/self/task/%ld/fail-nth", syscall(SYS_gettid));
write_file(path, nth_str);
printf("[*] Triggering bug with fail-nth=%d...\n", nth);
// Expected to fail due to fault injection
if (write(fd, &data, sizeof(data)) < 0) {
printf("[+] write to ep0 failed as expected (fault injection).\n");
} else {
printf("[-] write to ep0 succeeded (fault injection missed).\n");
}
write_file("/sys/kernel/debug/failslab/probability", "0");
write_file("/sys/kernel/debug/failslab/times", "0");
write_file("/proc/self/make-it-fail", "0");
write_file(path, "0");
res = close(fd);
if (res < 0) {
printf("[-] Failed to close fd: %s\n", strerror(errno));
} else {
printf("[+] close fd successful.\n");
}
res = umount("/tmp/gadgetfs");
if (res < 0) {
printf("[-] Failed to umount gadgetfs: %s\n", strerror(errno));
} else {
printf("[+] umount gadgetfs successful.\n");
}
}
int main() {
printf("[*] Starting bug reproduction...\n");
int res = mount("debugfs", "/sys/kernel/debug", "debugfs", 0, NULL);
if (res < 0 && errno != EBUSY) {
printf("[-] Failed to mount debugfs: %s\n", strerror(errno));
} else {
printf("[+] debugfs mounted or already present.\n");
}
for (int i = 1; i <= 50; i++) {
pid_t pid = fork();
if (pid < 0) {
printf("[-] Failed to fork: %s\n", strerror(errno));
exit(1);
}
if (pid == 0) {
trigger_bug(i);
exit(0);
}
wait(NULL);
}
printf("[+] Bug reproduction finished.\n");
return 0;
}
The reproducer uses fault injection to force allocation failure at a specific point.
These injected failures often exercise rarely used error-handling paths,
so the bug is frequently in that error handling.
The following fault injection report(s) show what was injected:
FAULT_INJECTION: forcing a failure.
name fail_usercopy, interval 1, probability 0, space 0, times 1
Call Trace:
dump_stack_lvl+0xe8/0x150 lib/dump_stack.c:120
fail_dump lib/fault-inject.c:73 [inline]
should_fail_ex+0x467/0x600 lib/fault-inject.c:174
_inline_copy_from_user include/linux/uaccess.h:170 [inline]
_copy_from_user+0x2d/0xb0 lib/usercopy.c:18
copy_from_user include/linux/uaccess.h:222 [inline]
dev_config+0x2ab/0x12c0 drivers/usb/gadget/legacy/inode.c:1822
vfs_write+0x29f/0xbb0 fs/read_write.c:685
ksys_write+0x156/0x270 fs/read_write.c:739
do_syscall_x64 arch/x86/entry/syscall_64.c:63 [inline]
do_syscall_64+0x174/0x580 arch/x86/entry/syscall_64.c:94
entry_SYSCALL_64_after_hwframe+0x77/0x7f
FAULT_INJECTION: forcing a failure.
name fail_usercopy, interval 1, probability 0, space 0, times 0
Call Trace:
dump_stack_lvl+0xe8/0x150 lib/dump_stack.c:120
fail_dump lib/fault-inject.c:73 [inline]
should_fail_ex+0x467/0x600 lib/fault-inject.c:174
_inline_copy_from_user include/linux/uaccess.h:170 [inline]
_copy_from_user+0x2d/0xb0 lib/usercopy.c:18
copy_from_user include/linux/uaccess.h:222 [inline]
memdup_user+0x5e/0xd0 mm/util.c:225
dev_config+0x4d8/0x12c0 drivers/usb/gadget/legacy/inode.c:1829
vfs_write+0x29f/0xbb0 fs/read_write.c:685
ksys_write+0x156/0x270 fs/read_write.c:739
do_syscall_x64 arch/x86/entry/syscall_64.c:63 [inline]
do_syscall_64+0x174/0x580 arch/x86/entry/syscall_64.c:94
entry_SYSCALL_64_after_hwframe+0x77/0x7f
FAULT_INJECTION: forcing a failure.
name failslab, interval 1, probability 100, space 0, times 0
Call Trace:
dump_stack_lvl+0xe8/0x150 lib/dump_stack.c:120
fail_dump lib/fault-inject.c:73 [inline]
should_fail_ex+0x467/0x600 lib/fault-inject.c:174
should_failslab+0xa8/0x100 mm/failslab.c:46
slab_pre_alloc_hook mm/slub.c:4539 [inline]
slab_alloc_node mm/slub.c:4897 [inline]
__kmalloc_cache_noprof+0xa0/0x6b0 mm/slub.c:5485
_kmalloc_noprof include/linux/slab.h:988 [inline]
_kzalloc_noprof include/linux/slab.h:1309 [inline]
bus_add_driver+0x165/0x670 drivers/base/bus.c:747
driver_register+0x23a/0x320 drivers/base/driver.c:174
usb_gadget_register_driver_owner+0xf9/0x270 drivers/usb/gadget/udc/core.c:1752
dev_config+0xe4e/0x12c0 drivers/usb/gadget/legacy/inode.c:1883
vfs_write+0x29f/0xbb0 fs/read_write.c:685
ksys_write+0x156/0x270 fs/read_write.c:739
do_syscall_x64 arch/x86/entry/syscall_64.c:63 [inline]
do_syscall_64+0x174/0x580 arch/x86/entry/syscall_64.c:94
entry_SYSCALL_64_after_hwframe+0x77/0x7f
FAULT_INJECTION: forcing a failure.
name failslab, interval 1, probability 100, space 0, times 0
Call Trace:
dump_stack_lvl+0xe8/0x150 lib/dump_stack.c:120
fail_dump lib/fault-inject.c:73 [inline]
should_fail_ex+0x467/0x600 lib/fault-inject.c:174
should_failslab+0xa8/0x100 mm/failslab.c:46
slab_pre_alloc_hook mm/slub.c:4539 [inline]
slab_alloc_node mm/slub.c:4897 [inline]
kmem_cache_alloc_noprof+0xa4/0x650 mm/slub.c:4931
__kernfs_new_node+0xe7/0xa70 fs/kernfs/dir.c:665
kernfs_new_node fs/kernfs/dir.c:751 [inline]
kernfs_create_dir_ns+0xfe/0x230 fs/kernfs/dir.c:1120
sysfs_create_dir_ns+0x12f/0x2a0 fs/sysfs/dir.c:59
create_dir lib/kobject.c:73 [inline]
kobject_add_internal+0x62c/0xce0 lib/kobject.c:240
kobject_add_varg lib/kobject.c:374 [inline]
kobject_init_and_add+0x12b/0x1a0 lib/kobject.c:457
bus_add_driver+0x258/0x670 drivers/base/bus.c:756
driver_register+0x23a/0x320 drivers/base/driver.c:174
usb_gadget_register_driver_owner+0xf9/0x270 drivers/usb/gadget/udc/core.c:1752
dev_config+0xe4e/0x12c0 drivers/usb/gadget/legacy/inode.c:1883
vfs_write+0x29f/0xbb0 fs/read_write.c:685
ksys_write+0x156/0x270 fs/read_write.c:739
do_syscall_x64 arch/x86/entry/syscall_64.c:63 [inline]
do_syscall_64+0x174/0x580 arch/x86/entry/syscall_64.c:94
entry_SYSCALL_64_after_hwframe+0x77/0x7f
FAULT_INJECTION: forcing a failure.
name failslab, interval 1, probability 100, space 0, times 0
Call Trace:
dump_stack_lvl+0xe8/0x150 lib/dump_stack.c:120
fail_dump lib/fault-inject.c:73 [inline]
should_fail_ex+0x467/0x600 lib/fault-inject.c:174
should_failslab+0xa8/0x100 mm/failslab.c:46
slab_pre_alloc_hook mm/slub.c:4539 [inline]
slab_alloc_node mm/slub.c:4897 [inline]
__do_kmalloc_node mm/slub.c:5333 [inline]
__kmalloc_node_track_caller_noprof+0x102/0x7c0 mm/slub.c:5471
__kmemdup_nul mm/util.c:64 [inline]
kstrdup+0x42/0x100 mm/util.c:84
__kernfs_new_node+0xac/0xa70 fs/kernfs/dir.c:661
kernfs_new_node+0xea/0x140 fs/kernfs/dir.c:751
kernfs_create_link+0xa7/0x200 fs/kernfs/symlink.c:39
sysfs_do_create_link_sd+0x83/0x110 fs/sysfs/symlink.c:44
driver_sysfs_add+0x89/0x210 drivers/base/dd.c:489
really_probe+0x142/0xae0 drivers/base/dd.c:694
__driver_probe_device+0x1e8/0x360 drivers/base/dd.c:868
driver_probe_device+0x4f/0x240 drivers/base/dd.c:898
__driver_attach+0x339/0x600 drivers/base/dd.c:1292
bus_for_each_dev+0x23e/0x2c0 drivers/base/bus.c:383
bus_add_driver+0x348/0x670 drivers/base/bus.c:763
driver_register+0x23a/0x320 drivers/base/driver.c:174
usb_gadget_register_driver_owner+0xf9/0x270 drivers/usb/gadget/udc/core.c:1752
dev_config+0xe4e/0x12c0 drivers/usb/gadget/legacy/inode.c:1883
vfs_write+0x29f/0xbb0 fs/read_write.c:685
ksys_write+0x156/0x270 fs/read_write.c:739
do_syscall_x64 arch/x86/entry/syscall_64.c:63 [inline]
do_syscall_64+0x174/0x580 arch/x86/entry/syscall_64.c:94
entry_SYSCALL_64_after_hwframe+0x77/0x7f
Reply: The root cause of this KASAN slab-use-after-free is a reference count underflow in the error handling path of `gadgetfs_bind()`.
When fault injection forces a memory allocation failure during the binding process, `gadgetfs_bind()` incorrectly calls `gadgetfs_unbind()`, which drops a reference to the device (`dev`) that was never actually taken.
### Detailed Sequence of Events
1. **Initialization and Open:**
When the gadgetfs superblock is created, `dev_new()` allocates the `dev_data` structure (`dev`) and initializes its refcount to 1. When the user opens the `/tmp/gadgetfs/ep0` file, `gadget_dev_open()` is called, which increments the refcount to 2 via `get_dev(dev)`.
2. **Driver Registration and Binding:**
The user writes configuration data to `ep0`, invoking `dev_config()`. This function calls `usb_gadget_register_driver(&gadgetfs_driver)`, which registers the driver and attempts to bind it to the UDC, eventually calling `gadgetfs_bind()`.
3. **Fault Injection and Refcount Underflow:**
In `gadgetfs_bind()`, memory allocations are performed (e.g., `usb_ep_alloc_request()` or inside `activate_ep_files()`). If fault injection causes one of these to fail, the code jumps to the `enomem` label:
```c
// drivers/usb/gadget/legacy/inode.c : gadgetfs_bind()
dev->req = usb_ep_alloc_request (gadget->ep0, GFP_KERNEL);
if (!dev->req)
goto enomem;
...
if (activate_ep_files (dev) < 0)
goto enomem;
...
get_dev (dev); // <--- Reference is only taken on SUCCESS
return 0;
enomem:
gadgetfs_unbind (gadget);
return -ENOMEM;
```
The `enomem` path calls `gadgetfs_unbind()`, which performs cleanup and unconditionally calls `put_dev(dev)`. Because `get_dev(dev)` is only called at the very end of `gadgetfs_bind()` on success, `gadgetfs_unbind()` drops a reference that was never acquired. The refcount incorrectly drops from 2 to 1.
4. **Probe Failure Ignored:**
The `-ENOMEM` error propagates up to the driver core. However, `__driver_attach()` intentionally ignores probe failures and returns `0`. Because `gadgetfs_driver` does not set `match_existing_only = true`, `usb_gadget_register_driver_owner()` also returns `0` (success).
5. **Closing ep0 and Freeing:**
Seeing a success return value, `dev_config()` sets `dev->gadget_registered = true`.
Later, the reproducer closes the `ep0` file descriptor, triggering `dev_release()`. `dev_release()` cleans up and calls `put_dev(dev)`. The refcount drops from 1 to 0, and `dev` is prematurely freed.
6. **The Use-After-Free:**
Finally, the reproducer unmounts the filesystem, which invokes `gadgetfs_kill_sb()`.
```c
// drivers/usb/gadget/legacy/inode.c : gadgetfs_kill_sb()
if (the_device) {
put_dev (the_device); // <--- UAF here
the_device = NULL;
}
```
`gadgetfs_kill_sb()` attempts to drop the initial reference by calling `put_dev(the_device)`. Since `the_device` points to the `dev` structure that was already freed in step 5, this triggers the KASAN slab-use-after-free.
### Suggested Fix
`gadgetfs_bind()` should not call `gadgetfs_unbind()` in its error path, as `gadgetfs_unbind()` is designed to tear down a fully bound gadget (and thus expects the extra reference to exist). Instead, `gadgetfs_bind()` should manually clean up the specific state it altered before the failure occurred.
```c
enomem:
- gadgetfs_unbind (gadget);
+ if (dev->req) {
+ usb_ep_free_request (gadget->ep0, dev->req);
+ dev->req = NULL;
+ }
+ gadget->ep0->driver_data = NULL;
+ set_gadget_data (gadget, NULL);
+ dev->gadget = NULL;
return -ENOMEM;
```
*(Note: `activate_ep_files()` already perfectly cleans up after itself on failure, so no additional teardown is needed for it in `gadgetfs_bind()`'s error path).*
|
| 8/2 |
2026/08/07 05:37 |
llm |
debugger |
0m
Model:
Tokens:
input: 13029
output: 39
thoughts: 147
Thoughts: **Analyzing a KASAN Slab-Use-After-Free in Gadgetfs**
Okay, so I'm looking at this KASAN slab-use-after-free bug that's popping up, specifically in the `gadgetfs_kill_sb` function. It looks like the chain is: `gadgetfs_kill_sb` -> `put_dev` -> `refcount_dec_and_test`. The crash itself occurs during a write operation to the `ep0` file within gadgetfs – interesting, I wonder what the context of that write is.
Digging a bit deeper, this write to the `ep0` file is handled by the `dev_config` function located in `drivers/usb/gadget/legacy/inode.c`. That's a key piece of information. Now, the freed object, the culprit of this whole use-after-free issue, was originally allocated in `dev_new` within `gadgetfs_fill_super`. And then, the object is freed in `dev_release` which, if I recall correctly, is triggered when the associated file is closed.
Alright, so the freed object is being used after the file handle is closed, and that's the vulnerability. The crash happens when we write to the ep0 file, which is handled by dev_config.
Let's start by carefully examining that `drivers/usb/gadget/legacy/inode.c` file; that's where the heart of the issue is likely hiding. I need to trace the object's lifecycle and figure out where the refcount is going wrong and which paths are accessing the memory after `dev_release` has been called.
|
| 9/2 |
2026/08/07 05:37 |
tool |
codesearch-definition-source |
0m
Args: map[ContextFile:drivers/usb/gadget/legacy/inode.c Name:put_dev]
Results: map[SourceCode: 163: static void put_dev (struct dev_data *data)
164: {
165: if (likely (!refcount_dec_and_test (&data->count)))
166: return;
167: /* needs no more cleanup */
168: BUG_ON (waitqueue_active (&data->wait));
169: kfree (data);
170: }
SourceFile:drivers/usb/gadget/legacy/inode.c]
|
| 10/2 |
2026/08/07 05:37 |
llm |
debugger |
0m
Model:
Tokens:
input: 13726
output: 43
thoughts: 13
Thoughts: Okay, let's dive into this. Here's my thought process on this:
**Analyzing `gadgetfs_kill_sb`**
Alright, the next thing on my plate is checking out `gadgetfs_kill_sb`. Let me mentally walk through what's likely happening here. Knowing the usual structure of filesystems, a `_kill_sb` function is probably responsible for unmounting a gadgetfs filesystem. I'm thinking about the standard shutdown sequence: freeing superblocks, detaching the filesystem from the mount point, and making sure everything is cleaned up properly so there aren't any lingering resources that could cause problems. I'll need to look at how this specifically handles the gadgetfs context. Are there specific gadget-related data structures that need special handling? Are there any device-specific considerations that impact this function? I'll be looking for potential race conditions or resource leaks. Let's see how well it's implemented. Time to delve into the code.
|
| 11/2 |
2026/08/07 05:37 |
tool |
codesearch-definition-source |
0m
Args: map[ContextFile:drivers/usb/gadget/legacy/inode.c Name:gadgetfs_kill_sb]
Results: map[SourceCode:2098: static void
2099: gadgetfs_kill_sb (struct super_block *sb)
2100: {
2101: mutex_lock(&sb_mutex);
2102: kill_anon_super (sb);
2103: if (the_device) {
2104: put_dev (the_device);
2105: the_device = NULL;
2106: }
2107: kfree(CHIP);
2108: CHIP = NULL;
2109: mutex_unlock(&sb_mutex);
2110: }
SourceFile:drivers/usb/gadget/legacy/inode.c]
|
| 12/2 |
2026/08/07 05:37 |
llm |
debugger |
0m
Model:
Tokens:
input: 14192
output: 39
thoughts: 9
Thoughts: Okay, here's my attempt at summarizing that thought process, keeping in mind I'm speaking to a fellow expert:
**Checking the Release Pipeline**
Alright, let's just get straight to it. First things first, I need to take a look at `dev_release`. No need to beat around the bush; I've got a specific purpose. I need to take a quick peek at the pipeline to assess its current state. I need to make sure everything looks as expected before I move on.
|
| 13/2 |
2026/08/07 05:37 |
tool |
codesearch-definition-source |
0m
Args: map[ContextFile:drivers/usb/gadget/legacy/inode.c Name:dev_release]
Results: map[SourceCode:1190: static int
1191: dev_release (struct inode *inode, struct file *fd)
1192: {
1193: struct dev_data *dev = fd->private_data;
1194:
1195: /* closing ep0 === shutdown all */
1196:
1197: if (dev->gadget_registered) {
1198: usb_gadget_unregister_driver (&gadgetfs_driver);
1199: dev->gadget_registered = false;
1200: }
1201:
1202: /* at this point "good" hardware has disconnected the
1203: * device from USB; the host won't see it any more.
1204: * alternatively, all host requests will time out.
1205: */
1206:
1207: kfree (dev->buf);
1208: dev->buf = NULL;
1209:
1210: /* other endpoints were all decoupled from this device */
1211: spin_lock_irq(&dev->lock);
1212: dev->state = STATE_DEV_DISABLED;
1213: spin_unlock_irq(&dev->lock);
1214:
1215: put_dev (dev);
1216: return 0;
1217: }
SourceFile:drivers/usb/gadget/legacy/inode.c]
|
| 14/2 |
2026/08/07 05:37 |
llm |
debugger |
0m
Model:
Tokens:
input: 14755
output: 39
thoughts: 9
Thoughts: Okay, here's my interpretation of that thought process, tailored for an expert audience, and written in the first person:
**Initial Assessment: A Focused Examination**
Alright, the directive is clear. My immediate instinct, given the context, is to zero in on `dev_new`. It's likely a branch, directory, or perhaps even a specific configuration file related to a development environment. Based on the naming convention it is also likely that this is new and so needs special consideration. The fact that this is the first thing referenced suggests a critical task is about to occur.
My primary concern now is to determine the scope and nature of the evaluation required in this case and to determine the current state of `dev_new`.
|
| 15/2 |
2026/08/07 05:37 |
tool |
codesearch-definition-source |
0m
Args: map[ContextFile:drivers/usb/gadget/legacy/inode.c Name:dev_new]
Results: map[SourceCode: 172: static struct dev_data *dev_new (void)
173: {
174: struct dev_data *dev;
175:
176: dev = kzalloc_obj(*dev);
177: if (!dev)
178: return NULL;
179: dev->state = STATE_DEV_DISABLED;
180: refcount_set (&dev->count, 1);
181: spin_lock_init (&dev->lock);
182: INIT_LIST_HEAD (&dev->epfiles);
183: init_waitqueue_head (&dev->wait);
184: return dev;
185: }
SourceFile:drivers/usb/gadget/legacy/inode.c]
|
| 16/2 |
2026/08/07 05:37 |
llm |
debugger |
0m
Model:
Tokens:
input: 15182
output: 43
thoughts: 13
Thoughts: Okay, let's dive into this. Here's my thought process on this:
**Analyzing `gadgetfs_fill_super`**
Alright, the objective is pretty clear. I need to take a look at the function `gadgetfs_fill_super`. It's a standard practice whenever I'm dealing with file system internals. My experience tells me that this function is going to be the heart of initializing the gadgetfs file system. It's the point where the kernel sets up the superblock, root inode, and generally gets the file system ready to handle I/O requests.
I'm expecting to see calls to `sget` or similar functions to get the `super_block` structure, initializing the file system type, setting up filesystem-specific data, creating the root inode with `new_inode` or its equivalent, and linking it to the `super_block`. Depending on the design, I might find checks for options passed during mount, perhaps some locking mechanisms, and probably error handling throughout. Understanding the sequence of operations here is crucial for comprehending how the file system's structure is built. I'll need to pay close attention to the data structures involved, the allocations being done, and how the kernel is setting up the overall framework. Essentially, it's about making sure everything is in place to provide a usable file system interface to the userspace.
|
| 17/2 |
2026/08/07 05:37 |
tool |
codesearch-definition-source |
0m
Args: map[ContextFile:drivers/usb/gadget/legacy/inode.c Name:gadgetfs_fill_super]
Results: map[SourceCode:2014: static int
2015: gadgetfs_fill_super (struct super_block *sb, struct fs_context *fc)
2016: {
2017: struct inode *inode;
2018: struct dev_data *dev;
2019: int rc;
2020:
2021: mutex_lock(&sb_mutex);
2022:
2023: if (the_device) {
2024: rc = -ESRCH;
2025: goto Done;
2026: }
2027:
2028: CHIP = usb_get_gadget_udc_name();
2029: if (!CHIP) {
2030: rc = -ENODEV;
2031: goto Done;
2032: }
2033:
2034: /* superblock */
2035: sb->s_blocksize = PAGE_SIZE;
2036: sb->s_blocksize_bits = PAGE_SHIFT;
2037: sb->s_magic = GADGETFS_MAGIC;
2038: sb->s_op = &gadget_fs_operations;
2039: sb->s_time_gran = 1;
2040:
2041: /* root inode */
2042: inode = gadgetfs_make_inode (sb,
2043: NULL, &simple_dir_operations,
2044: S_IFDIR | S_IRUGO | S_IXUGO);
2045: if (!inode)
2046: goto Enomem;
2047: inode->i_op = &simple_dir_inode_operations;
2048: if (!(sb->s_root = d_make_root (inode)))
2049: goto Enomem;
2050:
2051: /* the ep0 file is named after the controller we expect;
2052: * user mode code can use it for sanity checks, like we do.
2053: */
2054: dev = dev_new ();
2055: if (!dev)
2056: goto Enomem;
2057:
2058: dev->sb = sb;
2059: rc = gadgetfs_create_file(sb, CHIP, dev, &ep0_operations);
2060: if (rc) {
2061: put_dev(dev);
2062: goto Enomem;
2063: }
2064:
2065: /* other endpoint files are available after hardware setup,
2066: * from binding to a controller.
2067: */
2068: the_device = dev;
2069: rc = 0;
2070: goto Done;
2071:
2072: Enomem:
2073: kfree(CHIP);
2074: CHIP = NULL;
2075: rc = -ENOMEM;
2076:
2077: Done:
2078: mutex_unlock(&sb_mutex);
2079: return rc;
2080: }
SourceFile:drivers/usb/gadget/legacy/inode.c]
|
| 18/2 |
2026/08/07 05:37 |
llm |
debugger |
0m
Model:
Tokens:
input: 16441
output: 43
thoughts: 111
Thoughts: **Analyzing the Device Structure and Inode Creation**
Okay, so I'm looking at this code, and the first thing that jumps out is that `the_device` is a global variable. Interesting. It's a `static struct dev_data *`, so it's only visible within this compilation unit. Good, keeps things somewhat localized. Let me trace how it's used.
Right, `gadgetfs_fill_super` is where it gets initialized. It calls `dev_new()`, which presumably allocates and sets up the device data, and then *crucially* assigns `the_device = dev`. So, the newly created device data is accessible globally through `the_device`. I assume the refcount is set to 1 after the call to `dev_new()`. Seems standard so far.
Now, the concern. `gadgetfs_create_file`. This function's purpose is clear: to create the inode and dentry for `CHIP`, which I assume is a pre-defined device. But... does it take a reference? I need to know *if* and *how* `gadgetfs_create_file` handles the reference counting for `the_device` or the newly created device. Does it increment the refcount of `the_device`? If not, we're in trouble – we'll have a race. If it *does* take a reference, we need to understand the corresponding release path. Understanding the refcounting is key here. That's the crux of the issue; I need to verify that the device's lifecycle is correctly managed.
|
| 19/2 |
2026/08/07 05:37 |
tool |
codesearch-definition-source |
0m
Args: map[ContextFile:drivers/usb/gadget/legacy/inode.c Name:gadgetfs_create_file]
Results: map[SourceCode:1983: /* creates in fs root directory, so non-renamable and non-linkable.
1984: * so inode and dentry are paired, until device reconfig.
1985: */
1986: static int gadgetfs_create_file (struct super_block *sb, char const *name,
1987: void *data, const struct file_operations *fops)
1988: {
1989: struct dentry *dentry;
1990: struct inode *inode;
1991:
1992: inode = gadgetfs_make_inode (sb, data, fops,
1993: S_IFREG | (default_perm & S_IRWXUGO));
1994: if (!inode)
1995: return -ENOMEM;
1996:
1997: dentry = simple_start_creating(sb->s_root, name);
1998: if (IS_ERR(dentry)) {
1999: iput(inode);
2000: return PTR_ERR(dentry);
2001: }
2002:
2003: d_make_persistent(dentry, inode);
2004:
2005: simple_done_creating(dentry);
2006: return 0;
2007: }
SourceFile:drivers/usb/gadget/legacy/inode.c]
|
| 20/2 |
2026/08/07 05:37 |
llm |
debugger |
0m
Model:
Tokens:
input: 17338
output: 43
thoughts: 29
Thoughts: Here's my understanding of the situation, tailored for an expert like yourself:
**Analyzing the `gadgetfs_create_file` Process**
Okay, so I'm looking at `gadgetfs_create_file`. The key thing here is that it’s passing the `data` pointer – which I recognize as `dev` in this context, right? – straight into `gadgetfs_make_inode`. This immediately flags a couple of thoughts. Firstly, I have to consider the implications of passing `dev` (likely device-specific information, or perhaps some sort of context related to the gadget itself) directly into the inode creation function. Is `gadgetfs_make_inode` designed to handle this directly, or is it a bit of a shortcut?
Is this the typical pattern, or is it an instance of implicit state management? Does it have implications for concurrency?
I need to confirm if that direct transfer is intentional or if there is something specific about `dev`, perhaps an internal representation used in the inode, that `gadgetfs_make_inode` needs to correctly initialize the inode. I'll need to dig into the source of `gadgetfs_make_inode` and see how it's actually using the `data` pointer to fully understand the design and any potential risks, especially in a concurrent environment where multiple devices might be interacting. Is `dev` properly protected? Are there mutexes or other synchronization primitives in place?
|
| 21/2 |
2026/08/07 05:37 |
tool |
codesearch-definition-source |
0m
Args: map[ContextFile:drivers/usb/gadget/legacy/inode.c Name:gadgetfs_make_inode]
Results: map[SourceCode:1964: static struct inode *
1965: gadgetfs_make_inode (struct super_block *sb,
1966: void *data, const struct file_operations *fops,
1967: int mode)
1968: {
1969: struct inode *inode = new_inode (sb);
1970:
1971: if (inode) {
1972: inode->i_ino = get_next_ino();
1973: inode->i_mode = mode;
1974: inode->i_uid = make_kuid(&init_user_ns, default_uid);
1975: inode->i_gid = make_kgid(&init_user_ns, default_gid);
1976: simple_inode_init_ts(inode);
1977: inode->i_private = data;
1978: inode->i_fop = fops;
1979: }
1980: return inode;
1981: }
SourceFile:drivers/usb/gadget/legacy/inode.c]
|
| 22/2 |
2026/08/07 05:37 |
llm |
debugger |
0m
Model:
Tokens:
input: 18024
output: 40
thoughts: 50
Thoughts: Okay, here's my interpretation of those thoughts, tailored for an expert audience, as if they were my own mental process:
**Diving Deep into `gadgetfs_make_inode`**
Alright, hold on a second. I'm focusing on `gadgetfs_make_inode` here, and something's just pinging my radar. It doesn't appear that this function takes a reference to the `dev` struct, right? That's immediately crucial. If that's the case, and I'm pretty sure it's the case, then at that point, `dev` would have a reference count of 1. Okay, that's important to bear in mind. Later, when we start talking about the `ep0` file being opened, and the associated operations, we'll see that `ep0_operations` are used. This all is related to how the endpoints and the device itself are managed.
|
| 23/2 |
2026/08/07 05:37 |
tool |
codesearch-definition-source |
0m
Args: map[ContextFile:drivers/usb/gadget/legacy/inode.c Name:ep0_operations]
Results: map[SourceCode:1931: static const struct file_operations ep0_operations = {
1932:
1933: .open = gadget_dev_open,
1934: .read = ep0_read,
1935: .write = dev_config,
1936: .fasync = ep0_fasync,
1937: .poll = ep0_poll,
1938: .unlocked_ioctl = gadget_dev_ioctl,
1939: .release = dev_release,
1940: };
SourceFile:drivers/usb/gadget/legacy/inode.c]
|
| 24/2 |
2026/08/07 05:37 |
llm |
debugger |
0m
Model:
Tokens:
input: 18500
output: 42
thoughts: 12
Thoughts: Okay, let's dive into this. Here's my take on those thoughts, as if they're my own:
**Analyzing `gadget_dev_open`**
Alright, `gadget_dev_open`... that immediately suggests we're dealing with device driver code, specifically within a "gadget" framework. My initial thought process involves a few key areas:
* **Context:** What kind of "gadget" are we talking about? USB? Something else? The specific context drastically changes my interpretation. I need to figure out the environment.
* **Purpose:** The name itself strongly indicates the opening of a device file. But why is this relevant? Is it for configuration? Data transfer? Debugging? Knowing the use-case is vital to understanding the implications.
* **Abstraction Layer:** The term "gadget" makes me think it's likely a relatively high-level abstraction. I want to look into how far removed from the hardware this function is, and consider how to interface it.
* **Error Handling:** "Open" functions are notorious for error handling. I'll need to immediately think about what errors can occur and what the function is expected to do in each case. Bad arguments, resource exhaustion, hardware failures... I want to see if this function handles them well. I'm looking for robustness.
* **Concurrency:** Is this function thread-safe? Does it need synchronization? If multiple users can call this concurrently, that opens up a whole can of worms.
* **Resource Management:** What resources does this function allocate? File descriptors, memory, locks? Proper cleanup is crucial, so I want to understand how the resources are managed, particularly on close or when errors occur.
* **Security:** Device drivers are a common attack surface. So, I need to look for any potential security vulnerabilities, such as privilege escalation or data exposure. Input validation, proper access control, and preventing race conditions are all things I'll be looking out for.
Ultimately, I need to quickly get a sense of its place within the broader system. I will start by seeing how it's called and by what, and what it does internally.
|
| 25/2 |
2026/08/07 05:37 |
tool |
codesearch-definition-source |
0m
Args: map[ContextFile:drivers/usb/gadget/legacy/inode.c Name:gadget_dev_open]
Results: map[SourceCode:1913: static int
1914: gadget_dev_open (struct inode *inode, struct file *fd)
1915: {
1916: struct dev_data *dev = inode->i_private;
1917: int value = -EBUSY;
1918:
1919: spin_lock_irq(&dev->lock);
1920: if (dev->state == STATE_DEV_DISABLED) {
1921: dev->ev_next = 0;
1922: dev->state = STATE_DEV_OPENED;
1923: fd->private_data = dev;
1924: get_dev (dev);
1925: value = 0;
1926: }
1927: spin_unlock_irq(&dev->lock);
1928: return value;
1929: }
SourceFile:drivers/usb/gadget/legacy/inode.c]
|
| 26/2 |
2026/08/07 05:37 |
llm |
debugger |
0m
Model:
Tokens:
input: 19289
output: 39
thoughts: 175
Thoughts: **Debugging a UAF in gadgetfs_kill_sb**
Okay, let's break this down. `gadget_dev_open` kicks things off by calling `get_dev(dev)`, which bumps the reference count to 2, as expected. Then, when `ep0` closes, `dev_release` is invoked, and that in turn calls `put_dev(dev)`, which decrements the refcount to 1. So far, so good. Everything seems logical.
Now, here's where things get interesting. The UAF is in `gadgetfs_kill_sb`, specifically when it calls `put_dev(the_device)`. The implication is that `the_device` has *already* been freed before `gadgetfs_kill_sb` even gets around to calling `put_dev`. How is that possible? Something is clearly wrong with the device lifecycle management. We've got a classic use-after-free scenario. The device should still be alive until `gadgetfs_kill_sb` has run. But apparently, something freed it before. We need to identify the code path that might be doing this. Let's delve into the `dev_config` structure and its handling.
|
| 27/2 |
2026/08/07 05:37 |
tool |
codesearch-definition-source |
0m
Args: map[ContextFile:drivers/usb/gadget/legacy/inode.c Name:dev_config]
Results: map[SourceCode:1800: static ssize_t
1801: dev_config (struct file *fd, const char __user *buf, size_t len, loff_t *ptr)
1802: {
1803: struct dev_data *dev = fd->private_data;
1804: ssize_t value, length = len;
1805: unsigned total;
1806: u32 tag;
1807: char *kbuf;
1808:
1809: spin_lock_irq(&dev->lock);
1810: if (dev->state > STATE_DEV_OPENED) {
1811: value = ep0_write(fd, buf, len, ptr);
1812: spin_unlock_irq(&dev->lock);
1813: return value;
1814: }
1815: spin_unlock_irq(&dev->lock);
1816:
1817: if ((len < (USB_DT_CONFIG_SIZE + USB_DT_DEVICE_SIZE + 4)) ||
1818: (len > PAGE_SIZE * 4))
1819: return -EINVAL;
1820:
1821: /* we might need to change message format someday */
1822: if (copy_from_user (&tag, buf, 4))
1823: return -EFAULT;
1824: if (tag != 0)
1825: return -EINVAL;
1826: buf += 4;
1827: length -= 4;
1828:
1829: kbuf = memdup_user(buf, length);
1830: if (IS_ERR(kbuf))
1831: return PTR_ERR(kbuf);
1832:
1833: spin_lock_irq (&dev->lock);
1834: value = -EINVAL;
1835: if (dev->buf) {
1836: spin_unlock_irq(&dev->lock);
1837: kfree(kbuf);
1838: return value;
1839: }
1840: dev->buf = kbuf;
1841:
1842: /* full or low speed config */
1843: dev->config = (void *) kbuf;
1844: total = le16_to_cpu(dev->config->wTotalLength);
1845: if (!is_valid_config(dev->config, total) ||
1846: total > length - USB_DT_DEVICE_SIZE)
1847: goto fail;
1848: kbuf += total;
1849: length -= total;
1850:
1851: /* optional high speed config */
1852: if (kbuf [1] == USB_DT_CONFIG) {
1853: dev->hs_config = (void *) kbuf;
1854: total = le16_to_cpu(dev->hs_config->wTotalLength);
1855: if (!is_valid_config(dev->hs_config, total) ||
1856: total > length - USB_DT_DEVICE_SIZE)
1857: goto fail;
1858: kbuf += total;
1859: length -= total;
1860: } else {
1861: dev->hs_config = NULL;
1862: }
1863:
1864: /* could support multiple configs, using another encoding! */
1865:
1866: /* device descriptor (tweaked for paranoia) */
1867: if (length != USB_DT_DEVICE_SIZE)
1868: goto fail;
1869: dev->dev = (void *)kbuf;
1870: if (dev->dev->bLength != USB_DT_DEVICE_SIZE
1871: || dev->dev->bDescriptorType != USB_DT_DEVICE
1872: || dev->dev->bNumConfigurations != 1)
1873: goto fail;
1874: dev->dev->bcdUSB = cpu_to_le16 (0x0200);
1875:
1876: /* triggers gadgetfs_bind(); then we can enumerate. */
1877: spin_unlock_irq (&dev->lock);
1878: if (dev->hs_config)
1879: gadgetfs_driver.max_speed = USB_SPEED_HIGH;
1880: else
1881: gadgetfs_driver.max_speed = USB_SPEED_FULL;
1882:
1883: value = usb_gadget_register_driver(&gadgetfs_driver);
1884: if (value != 0) {
1885: spin_lock_irq(&dev->lock);
1886: goto fail;
1887: } else {
1888: /* at this point "good" hardware has for the first time
1889: * let the USB the host see us. alternatively, if users
1890: * unplug/replug that will clear all the error state.
1891: *
1892: * note: everything running before here was guaranteed
1893: * to choke driver model style diagnostics. from here
1894: * on, they can work ... except in cleanup paths that
1895: * kick in after the ep0 descriptor is closed.
1896: */
1897: value = len;
1898: dev->gadget_registered = true;
1899: }
1900: return value;
1901:
1902: fail:
1903: dev->config = NULL;
1904: dev->hs_config = NULL;
1905: dev->dev = NULL;
1906: spin_unlock_irq (&dev->lock);
1907: pr_debug ("%s: %s fail %zd, %p\n", shortname, __func__, value, dev);
1908: kfree (dev->buf);
1909: dev->buf = NULL;
1910: return value;
1911: }
SourceFile:drivers/usb/gadget/legacy/inode.c]
|
| 28/2 |
2026/08/07 05:37 |
llm |
debugger |
0m
Model:
Tokens:
input: 21481
output: 41
thoughts: 525
Thoughts: **Analyzing a Potential USB Gadget Driver Failure**
Okay, so I'm looking at this `dev_config` function and the way it handles `usb_gadget_register_driver`. My first thought is: "Wait, this *can* fail, can't it?" And if it fails, it branches to `fail` and returns `value`, which represents the error code. The question then becomes, does `usb_gadget_register_driver` actually free `dev` if it fails? No, it doesn't.
Now, I'm reviewing this fault injection report. It indicates that the system is being forced to fail, presumably during memory allocation. The call trace reveals the error is happening within `usb_gadget_register_driver_owner`, and further down the stack, specifically within `__kmalloc_cache_noprof` and eventually in slab allocation. This implies a potential memory allocation issue, specifically within the driver registration process. So, it looks like if `usb_gadget_register_driver` fails, then `dev_config` returns an error, and the failure is linked to a slab allocation issue.
The next question I ask myself is: If `usb_gadget_register_driver` fails, does it unregister `gadgetfs_driver`? Looking at the code, it does not. The `gadgetfs_driver` itself stays registered. And wait, I have to remember that `gadgetfs_driver` is a global variable. This could have some important implications for resource management in an error scenario. This whole thing makes me realize I need to dive deeper into this error path, especially around memory allocation, and the overall reliability of the `dev_config` function. I will need to verify whether the global nature of `gadgetfs_driver` has an impact on the error recovery.
|
| 29/2 |
2026/08/07 05:37 |
tool |
codesearch-definition-source |
0m
Args: map[ContextFile:drivers/usb/gadget/legacy/inode.c Name:gadgetfs_driver]
Results: map[SourceCode:1746: static struct usb_gadget_driver gadgetfs_driver = {
1747: .function = (char *) driver_desc,
1748: .bind = gadgetfs_bind,
1749: .unbind = gadgetfs_unbind,
1750: .setup = gadgetfs_setup,
1751: .reset = gadgetfs_disconnect,
1752: .disconnect = gadgetfs_disconnect,
1753: .suspend = gadgetfs_suspend,
1754:
1755: .driver = {
1756: .name = shortname,
1757: },
1758: };
SourceFile:drivers/usb/gadget/legacy/inode.c]
|
| 30/2 |
2026/08/07 05:37 |
llm |
debugger |
0m
Model:
Tokens:
input: 22659
output: 47
thoughts: 196
Thoughts: **Analyzing Gadget Driver Registration and Unregistration**
Okay, so I'm tracing the logic around the `gadgetfs_driver` registration and unregistration, and I need to be absolutely sure I'm not overlooking anything. The core assumption here is that if `usb_gadget_register_driver` fails, the `gadgetfs_driver` isn't registered. That's a given. However, the sequence of events is what's critical.
My first thought: what happens if `gadgetfs_driver` registers successfully, *then* we hit a failure when `ep0` is closed? Ah, then `dev_release` gets called, and this block of code is executed:
```c
if (dev->gadget_registered) {
usb_gadget_unregister_driver (&gadgetfs_driver);
dev->gadget_registered = false;
}
```
Okay, makes sense. But, if `usb_gadget_register_driver` *fails* initially, I need to double-check that `dev->gadget_registered` is *not* set to `true`. And in that case, the `dev_release` function is not going to unregister `gadgetfs_driver` because that conditional isn't met.
Then, the nagging question: if `usb_gadget_register_driver` fails, does it leave something behind, some sort of dangling state? I need to know. Time to dig into the `usb_gadget_register_driver_owner` function to see what it's really doing under the hood. I need to understand all the failure paths and any potential side effects. The goal here is to ensure clean up in all scenarios.
|
| 31/2 |
2026/08/07 05:38 |
tool |
codesearch-definition-source |
0m
Args: map[ContextFile:drivers/usb/gadget/udc/core.c Name:usb_gadget_register_driver_owner]
Results: map[SourceCode:1740: int usb_gadget_register_driver_owner(struct usb_gadget_driver *driver,
1741: struct module *owner, const char *mod_name)
1742: {
1743: int ret;
1744:
1745: if (!driver || !driver->bind || !driver->setup)
1746: return -EINVAL;
1747:
1748: driver->driver.bus = &gadget_bus_type;
1749: driver->driver.owner = owner;
1750: driver->driver.mod_name = mod_name;
1751: driver->driver.probe_type = PROBE_FORCE_SYNCHRONOUS;
1752: ret = driver_register(&driver->driver);
1753: if (ret) {
1754: pr_warn("%s: driver registration failed: %d\n",
1755: driver->function, ret);
1756: return ret;
1757: }
1758:
1759: mutex_lock(&udc_lock);
1760: if (!driver->is_bound) {
1761: if (driver->match_existing_only) {
1762: pr_warn("%s: couldn't find an available UDC or it's busy\n",
1763: driver->function);
1764: ret = -EBUSY;
1765: } else {
1766: pr_info("%s: couldn't find an available UDC\n",
1767: driver->function);
1768: ret = 0;
1769: }
1770: }
1771: mutex_unlock(&udc_lock);
1772:
1773: if (ret)
1774: driver_unregister(&driver->driver);
1775: return ret;
1776: }
SourceFile:drivers/usb/gadget/udc/core.c]
|
| 32/2 |
2026/08/07 05:38 |
llm |
debugger |
0m
Model:
Tokens:
input: 23837
output: 41
thoughts: 213
Thoughts: **Dissecting the USB Gadget Driver Registration Flow**
Okay, let's break this down. It looks like `usb_gadget_register_driver_owner` kicks things off with a call to `driver_register`. Standard stuff so far. But, hang on, after a successful `driver_register`, it checks `driver->is_bound`. That's interesting.
If `driver->is_bound` is *false* and the driver is marked with `driver->match_existing_only` true, it immediately hits the brakes, returning `-EBUSY` and unregistering itself. Makes sense, that's expected behaviour. It doesn't want to bind.
But what if `driver_register` *does* succeed, and `driver->is_bound` is actually *true*? In that case, the function just returns 0. No problems, right? The driver is already bound, or can be.
Now, the important question: how does `driver->is_bound` get flipped to true in the first place? Let's trace it back. We have `driver_register` -> `bus_add_driver` -> `driver_attach` -> `__driver_attach` -> `driver_probe_device` -> `really_probe` -> `udc_bind_to_driver` -> `gadget_bind_driver` -> finally `driver->bind(gadget, driver)`. Aha! The `bind` method is the key. For `gadgetfs`, I remember, that's `gadgetfs_bind`. That's where the magic happens.
|
| 33/2 |
2026/08/07 05:38 |
tool |
codesearch-definition-source |
0m
Args: map[ContextFile:drivers/usb/gadget/legacy/inode.c Name:gadgetfs_bind]
Results: map[SourceCode:1668: static int gadgetfs_bind(struct usb_gadget *gadget,
1669: struct usb_gadget_driver *driver)
1670: {
1671: struct dev_data *dev = the_device;
1672:
1673: if (!dev)
1674: return -ESRCH;
1675: if (0 != strcmp (CHIP, gadget->name)) {
1676: pr_err("%s expected %s controller not %s\n",
1677: shortname, CHIP, gadget->name);
1678: return -ENODEV;
1679: }
1680:
1681: set_gadget_data (gadget, dev);
1682: dev->gadget = gadget;
1683: gadget->ep0->driver_data = dev;
1684:
1685: /* preallocate control response and buffer */
1686: dev->req = usb_ep_alloc_request (gadget->ep0, GFP_KERNEL);
1687: if (!dev->req)
1688: goto enomem;
1689: dev->req->context = NULL;
1690: dev->req->complete = epio_complete;
1691:
1692: if (activate_ep_files (dev) < 0)
1693: goto enomem;
1694:
1695: INFO (dev, "bound to %s driver\n", gadget->name);
1696: spin_lock_irq(&dev->lock);
1697: dev->state = STATE_DEV_UNCONNECTED;
1698: spin_unlock_irq(&dev->lock);
1699: get_dev (dev);
1700: return 0;
1701:
1702: enomem:
1703: gadgetfs_unbind (gadget);
1704: return -ENOMEM;
1705: }
SourceFile:drivers/usb/gadget/legacy/inode.c]
|
| 34/2 |
2026/08/07 05:38 |
llm |
debugger |
0m
Model:
Tokens:
input: 25023
output: 34
thoughts: 114
Thoughts: **Reflecting on `gadgetfs_bind` and `driver_register`**
Okay, so I'm digging into this code and it's hitting me... `gadgetfs_bind` calls `get_dev(dev)`. That means if `gadgetfs_bind` is successful, the reference count of `dev` gets bumped up. Alright, makes sense. But then a thought hits me - what if `gadgetfs_bind` *succeeds* but then, *after* that point, `driver_register` fails? We've got a reference count issue right there! That feels like a subtle race condition that's just waiting to bite us.
Wait a second... I need to remember exactly when things are called... ah, right, `gadgetfs_bind` is actually called DURING `driver_register`! So, if `driver_register` bails out *after* that `gadgetfs_bind` call... It all hinges on what happens when `driver_register` has a problem after a successful `gadgetfs_bind`. This is important, better dig into the implementation of `driver_register` to check how it handles these scenarios.
|
| 35/2 |
2026/08/07 05:38 |
tool |
codesearch-definition-source |
0m
Args: map[ContextFile:drivers/base/driver.c Name:driver_register]
Results: map[SourceCode: 142: /**
143: * driver_register - register driver with bus
144: * @drv: driver to register
145: *
146: * We pass off most of the work to the bus_add_driver() call,
147: * since most of the things we have to do deal with the bus
148: * structures.
149: */
150: int driver_register(struct device_driver *drv)
151: {
152: int ret;
153: struct device_driver *other;
154:
155: if (!bus_is_registered(drv->bus)) {
156: pr_err("Driver '%s' was unable to register with bus_type '%s' because the bus was not initialized.\n",
157: drv->name, drv->bus->name);
158: return -EINVAL;
159: }
160:
161: if ((drv->bus->probe && drv->probe) ||
162: (drv->bus->remove && drv->remove) ||
163: (drv->bus->shutdown && drv->shutdown))
164: pr_warn("Driver '%s' needs updating - please use "
165: "bus_type methods\n", drv->name);
166:
167: other = driver_find(drv->name, drv->bus);
168: if (other) {
169: pr_err("Error: Driver '%s' is already registered, "
170: "aborting...\n", drv->name);
171: return -EBUSY;
172: }
173:
174: ret = bus_add_driver(drv);
175: if (ret)
176: return ret;
177: ret = driver_add_groups(drv, drv->groups);
178: if (ret) {
179: bus_remove_driver(drv);
180: return ret;
181: }
182: kobject_uevent(&drv->p->kobj, KOBJ_ADD);
183: deferred_probe_extend_timeout();
184:
185: return ret;
186: }
SourceFile:drivers/base/driver.c]
|
| 36/2 |
2026/08/07 05:38 |
llm |
debugger |
0m
Model:
Tokens:
input: 26106
output: 34
thoughts: 712
Thoughts: **Analyzing a Fault Injection Scenario**
Okay, so I'm looking at this code and trying to understand a failure scenario. My initial thought process was: if `bus_add_driver` succeeds, it subsequently calls `driver_add_groups`. If, however, *that* call fails, then `bus_remove_driver` gets called to clean up. But the fault injection trace is showing `bus_add_driver` failing directly! That's not the intended path.
Looking closer at the trace, the fault injection is clearly indicated. I see the failure is forced within `failslab`, likely related to memory allocation within the kernel. The stack trace clearly shows where the failure occurs within the `bus_add_driver` flow. I'm seeing it's happening when trying to create a sysfs link inside `driver_sysfs_add`, which is called from `really_probe`. Wait a minute, I see `driver_sysfs_add` is called *after* a successful `probe` call! So, the failure is happening after the driver is supposed to be working, it's occurring on the setup side.
Now, my focus is on `really_probe`. This is where the core interaction with the driver is happening, and it’s where this unexpected failure is occurring after the driver has nominally succeeded in its `probe` function. I need to understand what `really_probe` is doing and *why* it's failing here after an apparent initial success. This is key to pinpointing the root cause.
|
| 37/2 |
2026/08/07 05:38 |
tool |
codesearch-definition-source |
0m
Args: map[ContextFile:drivers/base/dd.c Name:really_probe]
Results: map[SourceCode: 652: static int really_probe(struct device *dev, const struct device_driver *drv)
653: {
654: bool test_remove = IS_ENABLED(CONFIG_DEBUG_TEST_DRIVER_REMOVE) &&
655: !drv->suppress_bind_attrs;
656: int ret, link_ret;
657:
658: if (defer_all_probes) {
659: /*
660: * Value of defer_all_probes can be set only by
661: * device_block_probing() which, in turn, will call
662: * wait_for_device_probe() right after that to avoid any races.
663: */
664: dev_dbg(dev, "Driver %s force probe deferral\n", drv->name);
665: return -EPROBE_DEFER;
666: }
667:
668: link_ret = device_links_check_suppliers(dev);
669: if (link_ret == -EPROBE_DEFER)
670: return link_ret;
671:
672: dev_dbg(dev, "bus: '%s': %s: probing driver %s with device\n",
673: drv->bus->name, __func__, drv->name);
674: if (!list_empty(&dev->devres_head)) {
675: dev_crit(dev, "Resources present before probing\n");
676: ret = -EBUSY;
677: goto done;
678: }
679:
680: re_probe:
681: device_set_driver(dev, drv);
682:
683: /* If using pinctrl, bind pins now before probing */
684: ret = pinctrl_bind_pins(dev);
685: if (ret)
686: goto pinctrl_bind_failed;
687:
688: if (dev->bus->dma_configure) {
689: ret = dev->bus->dma_configure(dev);
690: if (ret)
691: goto pinctrl_bind_failed;
692: }
693:
694: ret = driver_sysfs_add(dev);
695: if (ret) {
696: dev_err(dev, "%s: driver_sysfs_add failed\n", __func__);
697: goto sysfs_failed;
698: }
699:
700: if (dev->pm_domain && dev->pm_domain->activate) {
701: ret = dev->pm_domain->activate(dev);
702: if (ret)
703: goto probe_failed;
704: }
705:
706: ret = call_driver_probe(dev, drv);
707: if (ret) {
708: /*
709: * If fw_devlink_best_effort is active (denoted by -EAGAIN), the
710: * device might actually probe properly once some of its missing
711: * suppliers have probed. So, treat this as if the driver
712: * returned -EPROBE_DEFER.
713: */
714: if (link_ret == -EAGAIN)
715: ret = -EPROBE_DEFER;
716:
717: /*
718: * Return probe errors as positive values so that the callers
719: * can distinguish them from other errors.
720: */
721: ret = -ret;
722: goto probe_failed;
723: }
724:
725: ret = device_add_groups(dev, drv->dev_groups);
726: if (ret) {
727: dev_err(dev, "device_add_groups() failed\n");
728: goto dev_groups_failed;
729: }
730:
731: if (dev_has_sync_state(dev)) {
732: ret = device_create_file(dev, &dev_attr_state_synced);
733: if (ret) {
734: dev_err(dev, "state_synced sysfs add failed\n");
735: goto dev_sysfs_state_synced_failed;
736: }
737: }
738:
739: if (test_remove) {
740: test_remove = false;
741:
742: device_remove(dev);
743: driver_sysfs_remove(dev);
744: if (dev->bus && dev->bus->dma_cleanup)
745: dev->bus->dma_cleanup(dev);
746: device_unbind_cleanup(dev);
747:
748: goto re_probe;
749: }
750:
751: pinctrl_init_done(dev);
752:
753: if (dev->pm_domain && dev->pm_domain->sync)
754: dev->pm_domain->sync(dev);
755:
756: driver_bound(dev);
757: dev_dbg(dev, "bus: '%s': %s: bound device to driver %s\n",
758: drv->bus->name, __func__, drv->name);
759: goto done;
760:
761: dev_sysfs_state_synced_failed:
762: dev_groups_failed:
763: device_remove(dev);
764: probe_failed:
765: driver_sysfs_remove(dev);
766: sysfs_failed:
767: bus_notify(dev, BUS_NOTIFY_DRIVER_NOT_BOUND);
768: if (dev->bus && dev->bus->dma_cleanup)
769: dev->bus->dma_cleanup(dev);
770: pinctrl_bind_failed:
771: device_links_no_driver(dev);
772: device_unbind_cleanup(dev);
773: done:
774: return ret;
775: }
SourceFile:drivers/base/dd.c]
|
| 38/2 |
2026/08/07 05:38 |
llm |
debugger |
0m
Model:
Tokens:
input: 29022
output: 36
thoughts: 135
Thoughts: **My Immediate Assessment of the Driver Probe Sequence**
Okay, so I'm tracing the driver probe flow here and I've got a key insight: `driver_sysfs_add` is definitely called *before* `call_driver_probe` – that's crucial. Now, if `driver_sysfs_add` hits a snag and fails, *bam*, `really_probe` returns an error. And if that happens, `call_driver_probe`, which is the function that actually calls `gadgetfs_bind`, is *never* called.
Wait a second... if `gadgetfs_bind` is skipped entirely, the crucial increment of the `dev` refcount in that function never happens! That's a potential leak point if we're not careful. But hang on, the failure of `driver_sysfs_add` *does* mean `really_probe` bails out with an error, so there's probably some cleanup happening higher up. The question is, does `driver_register` itself then also return a failure? I need to check the calling sequence. Let me go look at `bus_add_driver` to see how the failure is handled.
|
| 39/2 |
2026/08/07 05:38 |
tool |
codesearch-definition-source |
0m
Args: map[ContextFile:drivers/base/bus.c Name:bus_add_driver]
Results: map[SourceCode: 728: /**
729: * bus_add_driver - Add a driver to the bus.
730: * @drv: driver.
731: */
732: int bus_add_driver(struct device_driver *drv)
733: {
734: struct subsys_private *sp = bus_to_subsys(drv->bus);
735: struct driver_private *priv;
736: int error = 0;
737:
738: if (!sp)
739: return -EINVAL;
740:
741: /*
742: * Reference in sp is now incremented and will be dropped when
743: * the driver is removed from the bus
744: */
745: pr_debug("bus: '%s': add driver %s\n", sp->bus->name, drv->name);
746:
747: priv = kzalloc_obj(*priv);
748: if (!priv) {
749: error = -ENOMEM;
750: goto out_put_bus;
751: }
752: klist_init(&priv->klist_devices, NULL, NULL);
753: priv->driver = drv;
754: drv->p = priv;
755: priv->kobj.kset = sp->drivers_kset;
756: error = kobject_init_and_add(&priv->kobj, &driver_ktype, NULL,
757: "%s", drv->name);
758: if (error)
759: goto out_unregister;
760:
761: klist_add_tail(&priv->knode_bus, &sp->klist_drivers);
762: if (sp->drivers_autoprobe) {
763: error = driver_attach(drv);
764: if (error)
765: goto out_del_list;
766: }
767: error = module_add_driver(drv->owner, drv);
768: if (error) {
769: printk(KERN_ERR "%s: failed to create module links for %s\n",
770: __func__, drv->name);
771: goto out_detach;
772: }
773:
774: error = driver_create_file(drv, &driver_attr_uevent);
775: if (error) {
776: printk(KERN_ERR "%s: uevent attr (%s) failed\n",
777: __func__, drv->name);
778: }
779: error = driver_add_groups(drv, sp->bus->drv_groups);
780: if (error) {
781: /* How the hell do we get out of this pickle? Give up */
782: printk(KERN_ERR "%s: driver_add_groups(%s) failed\n",
783: __func__, drv->name);
784: }
785:
786: if (!drv->suppress_bind_attrs) {
787: error = add_bind_files(drv);
788: if (error) {
789: /* Ditto */
790: printk(KERN_ERR "%s: add_bind_files(%s) failed\n",
791: __func__, drv->name);
792: }
793: }
794:
795: return 0;
796:
797: out_detach:
798: driver_detach(drv);
799: out_del_list:
800: klist_del(&priv->knode_bus);
801: out_unregister:
802: kobject_put(&priv->kobj);
803: /* drv->p is freed in driver_release() */
804: drv->p = NULL;
805: out_put_bus:
806: subsys_put(sp);
807: return error;
808: }
SourceFile:drivers/base/bus.c]
|
| 40/2 |
2026/08/07 05:38 |
llm |
debugger |
0m
Model:
Tokens:
input: 30653
output: 34
thoughts: 19
Thoughts: Here's my take on the situation:
**Unexpected `driver_attach` Behavior**
Alright, hang on a second. I was just reviewing the recent logs and I've stumbled upon something that has me rethinking my initial assessment. I thought `driver_attach` was handling the failure of `really_probe` correctly, but now I'm seeing it consistently returning 0 even when `really_probe` fails. That's... not what I expected at all. I was under the impression that the return value of `driver_attach` would provide a clearer indication of a complete failure from probe to attach, but now I need to dive deeper. I need to figure out why this discrepancy exists and how to account for it in my error handling. This could have broader implications for the stability checks I've put in place and will have to revisit those. Looks like I have some debugging ahead of me.
|
| 41/2 |
2026/08/07 05:38 |
tool |
codesearch-definition-source |
0m
Args: map[ContextFile:drivers/base/dd.c Name:driver_attach]
Results: map[SourceCode:1298: /**
1299: * driver_attach - try to bind driver to devices.
1300: * @drv: driver.
1301: *
1302: * Walk the list of devices that the bus has on it and try to
1303: * match the driver with each one. If driver_probe_device()
1304: * returns 0 and the @dev->driver is set, we've found a
1305: * compatible pair.
1306: */
1307: int driver_attach(const struct device_driver *drv)
1308: {
1309: /* The (void *) will be put back to const * in __driver_attach() */
1310: return bus_for_each_dev(drv->bus, NULL, (void *)drv, __driver_attach);
1311: }
SourceFile:drivers/base/dd.c]
|
| 42/2 |
2026/08/07 05:38 |
llm |
debugger |
0m
Model:
Tokens:
input: 31156
output: 35
thoughts: 7
Thoughts: Okay, here's my attempt at summarizing the thought process, tailored for an expert audience, and written in the first person:
**Navigating the `__driver_attach` Landscape**
Alright, let's break down this `__driver_attach` concept. We're talking low-level here, kernel land, where a lot of magic happens – or, as we well know, *doesn't* happen without careful planning. My immediate focus is on the *why* of this function. It's the hook, the entry point, the first place where the rubber meets the road between the kernel and the new driver.
My initial mental checklist starts running:
* **Resource Allocation:** What resources are we dealing with? I'm thinking memory, interrupts, DMA channels – anything the driver needs to operate. The first step is *probably* figuring out how to acquire and manage those resources. That means checking for availability and potentially some kind of negotiation if there's contention. I'm already anticipating potential deadlocks.
* **Device Probe:** The probe stage is where the driver actually *sees* the device. We're looking for Vendor ID, Device ID, and maybe other identifying information to verify that we're talking to the right piece of hardware. I might need to deal with multiple devices of the same type or, perhaps, even handle multiple devices of different types, if the driver is designed to work with those. This is where I'd start looking at how reliable my probing logic is, and if I need to back out cleanly if a problem is detected.
* **Driver Initialization:** Once the hardware is identified, the driver has to get set up. This is where I'd be looking at registering interrupt handlers, allocating any private data structures, setting up queues, and configuring the hardware registers. This includes enabling the device, setting up any necessary clocks, and ensuring it can communicate with the rest of the system.
* **Error Handling:** Okay, this is *critical*. I'm thinking *what can go wrong*? What failure modes can I anticipate? Is it a device missing or not responding? Is the device responding with unexpected data? Bad memory allocation? Bad register configuration? And, how am I going to handle these errors gracefully? Does it return a specific error code? Can it be recovered from? Or does it need to signal a critical failure back up the call chain? This entire stage is my bread and butter. I'm not happy if I'm not thinking about error handling.
* **Driver Registration:** At some point, once the device is configured and the driver is ready, it needs to register itself with the kernel's device management system. This is often the final step, signaling that the driver is ready to accept I/O requests.
My main focus at this stage is to make sure every single point is covered; every step has a logical continuation. I’ll make sure that error codes and states are clear, with every possible problem, accounted for, and that the code does as much as is possible, while giving up as little as is absolutely necessary.
|
| 43/2 |
2026/08/07 05:38 |
tool |
codesearch-definition-source |
0m
Args: map[ContextFile:drivers/base/dd.c Name:__driver_attach]
Results: map[SourceCode:1232: static int __driver_attach(struct device *dev, void *data)
1233: {
1234: const struct device_driver *drv = data;
1235: bool async = false;
1236: int ret;
1237:
1238: /*
1239: * Lock device and try to bind to it. We drop the error
1240: * here and always return 0, because we need to keep trying
1241: * to bind to devices and some drivers will return an error
1242: * simply if it didn't support the device.
1243: *
1244: * driver_probe_device() will spit a warning if there
1245: * is an error.
1246: */
1247:
1248: ret = driver_match_device(drv, dev);
1249: if (ret == 0) {
1250: /* no match */
1251: return 0;
1252: } else if (ret == -EPROBE_DEFER) {
1253: dev_dbg(dev, "Device match requests probe deferral\n");
1254: dev_set_can_match(dev);
1255: driver_deferred_probe_add(dev);
1256: /*
1257: * Driver could not match with device, but may match with
1258: * another device on the bus.
1259: */
1260: return 0;
1261: } else if (ret < 0) {
1262: dev_dbg(dev, "Bus failed to match device: %d\n", ret);
1263: /*
1264: * Driver could not match with device, but may match with
1265: * another device on the bus.
1266: */
1267: return 0;
1268: } /* ret > 0 means positive match */
1269:
1270: if (driver_allows_async_probing(drv)) {
1271: /*
1272: * Instead of probing the device synchronously we will
1273: * probe it asynchronously to allow for more parallelism.
1274: *
1275: * We only take the device lock here in order to guarantee
1276: * that the dev->driver and async_driver fields are protected
1277: */
1278: dev_dbg(dev, "probing driver %s asynchronously\n", drv->name);
1279: device_lock(dev);
1280: if (!dev->driver && !dev->p->async_driver) {
1281: get_device(dev);
1282: dev->p->async_driver = drv;
1283: async = true;
1284: }
1285: device_unlock(dev);
1286: if (async)
1287: async_schedule_dev(__driver_attach_async_helper, dev);
1288: return 0;
1289: }
1290:
1291: __device_driver_lock(dev, dev->parent);
1292: driver_probe_device(drv, dev);
1293: __device_driver_unlock(dev, dev->parent);
1294:
1295: return 0;
1296: }
SourceFile:drivers/base/dd.c]
|
| 44/2 |
2026/08/07 05:38 |
llm |
debugger |
0m
Model:
Tokens:
input: 32806
output: 41
thoughts: 124
Thoughts: Okay, let me break this down.
**My Analysis of the Driver Registration Process**
Right, so `__driver_attach` *always* returns 0. That's the baseline, the foundation. Therefore, `driver_attach` also returns 0. That's a success, it means `bus_add_driver` is going to succeed! Following that, `driver_register` also succeeding. But hang on, a successful `driver_register` means `usb_gadget_register_driver_owner` is being called. And within that, it specifically checks the `driver->is_bound` flag.
Now, here's where it gets interesting: If `driver->is_bound` is false, it's going to return `-EBUSY`. That's the key. And *that* only happens if `match_existing_only` is true. But is that `match_existing_only` flag set for the `gadgetfs_driver`? I need to go back and check the definition of `gadgetfs_driver` to confirm whether that's true or not. Otherwise the whole thing is off.
|
| 45/2 |
2026/08/07 05:38 |
tool |
codesearch-definition-source |
0m
Args: map[ContextFile:drivers/usb/gadget/legacy/inode.c Name:gadgetfs_driver]
Results: map[SourceCode:1746: static struct usb_gadget_driver gadgetfs_driver = {
1747: .function = (char *) driver_desc,
1748: .bind = gadgetfs_bind,
1749: .unbind = gadgetfs_unbind,
1750: .setup = gadgetfs_setup,
1751: .reset = gadgetfs_disconnect,
1752: .disconnect = gadgetfs_disconnect,
1753: .suspend = gadgetfs_suspend,
1754:
1755: .driver = {
1756: .name = shortname,
1757: },
1758: };
SourceFile:drivers/usb/gadget/legacy/inode.c]
|
| 46/2 |
2026/08/07 05:38 |
llm |
debugger |
0m
Model:
Tokens:
input: 33437
output: 47
thoughts: 67
Thoughts: **Analyzing GadgetFS Driver Registration**
Okay, so I'm looking at this `gadgetfs_driver` code, and I see that `match_existing_only` isn't set. That's a key detail. What this means, based on my understanding of how `usb_gadget_register_driver_owner` works, is that the function will happily return a success code (zero) *even* if the driver isn't actually bound yet. That's assuming `driver->is_bound` is false, which is the crux of the problem. This could lead to a situation that looks like a success but isn't. I need to be absolutely certain of this behavior, so let me go back and re-examine the implementation of `usb_gadget_register_driver_owner` and how it handles the driver's binding state. I need to confirm this assumption.
|
| 47/2 |
2026/08/07 05:38 |
tool |
codesearch-definition-source |
0m
Args: map[ContextFile:drivers/usb/gadget/udc/core.c Name:usb_gadget_register_driver_owner]
Results: map[SourceCode:1740: int usb_gadget_register_driver_owner(struct usb_gadget_driver *driver,
1741: struct module *owner, const char *mod_name)
1742: {
1743: int ret;
1744:
1745: if (!driver || !driver->bind || !driver->setup)
1746: return -EINVAL;
1747:
1748: driver->driver.bus = &gadget_bus_type;
1749: driver->driver.owner = owner;
1750: driver->driver.mod_name = mod_name;
1751: driver->driver.probe_type = PROBE_FORCE_SYNCHRONOUS;
1752: ret = driver_register(&driver->driver);
1753: if (ret) {
1754: pr_warn("%s: driver registration failed: %d\n",
1755: driver->function, ret);
1756: return ret;
1757: }
1758:
1759: mutex_lock(&udc_lock);
1760: if (!driver->is_bound) {
1761: if (driver->match_existing_only) {
1762: pr_warn("%s: couldn't find an available UDC or it's busy\n",
1763: driver->function);
1764: ret = -EBUSY;
1765: } else {
1766: pr_info("%s: couldn't find an available UDC\n",
1767: driver->function);
1768: ret = 0;
1769: }
1770: }
1771: mutex_unlock(&udc_lock);
1772:
1773: if (ret)
1774: driver_unregister(&driver->driver);
1775: return ret;
1776: }
SourceFile:drivers/usb/gadget/udc/core.c]
|
| 48/2 |
2026/08/07 05:38 |
llm |
debugger |
0m
Model:
Tokens:
input: 34304
output: 45
thoughts: 125
Thoughts: **Analyzing the USB Gadget Driver Registration Flow**
Okay, yes! I've confirmed it: If the driver isn't bound already (`driver->is_bound` is false) and we're not only trying to match existing devices (`driver->match_existing_only` is also false), the function will return zero! So, that means `usb_gadget_register_driver` is returning 0 in this scenario. Got it.
And then, I see that within the `dev_config` function, we're setting `dev->gadget_registered = true`. Ah-ha! That's the crucial flag. Now, if this flag is set to true, it means that when `ep0` is closed, the `dev_release` function is going to be called, which then in turn, calls `usb_gadget_unregister_driver(&gadgetfs_driver)`. Okay, so it looks like I need to dig into `usb_gadget_unregister_driver` now to see what actually happens there. Let's see...
|
| 49/2 |
2026/08/07 05:38 |
tool |
codesearch-definition-source |
0m
Args: map[ContextFile:drivers/usb/gadget/udc/core.c Name:usb_gadget_unregister_driver]
Results: map[SourceCode:1779: int usb_gadget_unregister_driver(struct usb_gadget_driver *driver)
1780: {
1781: if (!driver || !driver->unbind)
1782: return -EINVAL;
1783:
1784: driver_unregister(&driver->driver);
1785: return 0;
1786: }
SourceFile:drivers/usb/gadget/udc/core.c]
|
| 50/2 |
2026/08/07 05:38 |
llm |
debugger |
0m
Model:
Tokens:
input: 34841
output: 36
thoughts: 832
Thoughts: **Debugging a USB Driver UAF with Fault Injection**
Okay, so I'm staring at this potential use-after-free in a USB driver, and it's got me scratching my head. The core of it seems to be in the device unbinding sequence, specifically after a driver fails to bind. I'm seeing that `driver_unregister` is called, which should remove the driver from the bus.
My initial thought was, if `really_probe` fails, then `gadgetfs_unbind` *should* never be called. And if `gadgetfs_unbind` isn't called, then neither is `gadgetfs_bind`, which means the device's reference count shouldn't have been incremented! If `gadgetfs_bind` wasn't called, the refcount should remain at 2 (1 from the device's allocation in `dev_new`, and 1 from `gadget_dev_open`). The `ep0` is then closed, `dev_release` calls `put_dev(dev)` and the refcount goes to 1, as expected. Following this `gadgetfs_kill_sb` calls `put_dev(the_device)`, dropping the refcount to 0, and the device is freed. It looks perfectly balanced, so why the UAF?
Time to re-examine the fault injection trace. Aha! It looks like there's an injected failure happening. Let's trace that.
Okay, so the fault is injected during the call to `driver_sysfs_add`, which is called within `really_probe`. If `driver_sysfs_add` fails, `really_probe` then goes to `sysfs_failed`. Now let's see what happens during `sysfs_failed`
Ah! So, if `really_probe` fails, `driver_probe_device` returns an error. The key is in `sysfs_failed`: I need to look at how `device_unbind_cleanup` interacts with the reference counts of the device after a failure. This could be where the refcount is mismanaged, leading to the UAF. Now to go check `driver_probe_device` and the surrounding code, and see how the reference counts are handled in the error paths.
|
| 51/2 |
2026/08/07 05:38 |
tool |
codesearch-definition-source |
0m
Args: map[ContextFile:drivers/base/dd.c Name:driver_probe_device]
Results: map[SourceCode: 878: /**
879: * driver_probe_device - attempt to bind device & driver together
880: * @drv: driver to bind a device to
881: * @dev: device to try to bind to the driver
882: *
883: * This function returns -ENODEV if the device is not registered, -EBUSY if it
884: * already has a driver, 0 if the device is bound successfully and a positive
885: * (inverted) error code for failures from the ->probe method.
886: *
887: * This function must be called with @dev lock held. When called for a
888: * USB interface, @dev->parent lock must be held as well.
889: *
890: * If the device has a parent, runtime-resume the parent before driver probing.
891: */
892: static int driver_probe_device(const struct device_driver *drv, struct device *dev)
893: {
894: int trigger_count = atomic_read(&deferred_trigger_count);
895: int ret;
896:
897: atomic_inc(&probe_count);
898: ret = __driver_probe_device(drv, dev);
899: if (ret == -EPROBE_DEFER || ret == EPROBE_DEFER) {
900: driver_deferred_probe_add(dev);
901:
902: /*
903: * Did a trigger occur while probing? Need to re-trigger if yes
904: */
905: if (trigger_count != atomic_read(&deferred_trigger_count) &&
906: !defer_all_probes)
907: driver_deferred_probe_trigger();
908: }
909: atomic_dec(&probe_count);
910: wake_up_all(&probe_waitqueue);
911: return ret;
912: }
SourceFile:drivers/base/dd.c]
|
| 52/2 |
2026/08/07 05:38 |
llm |
debugger |
0m
Model:
Tokens:
input: 36780
output: 37
thoughts: 9
Thoughts: Okay, here's my interpretation of that thought process, tailored for an expert audience, and written in the first person:
**Decoding the `__driver_probe_device` Function**
Alright, let's break this down. When I see `__driver_probe_device`, my initial thought is immediately drawn to the core of the device driver lifecycle within the kernel. This isn't just a generic "probe" function; the double underscore signals an internal function, likely the fundamental workhorse responsible for the critical action of *proactively attempting* to bind a driver to a device. It's a low-level operation, dealing with the nitty-gritty details of matching drivers with hardware and, crucially, managing the potential conflicts or errors that can arise during this critical "binding" phase.
I'm thinking about the arguments it likely takes: a pointer to the device structure itself, a pointer to the driver structure, perhaps some hints regarding the bus type or device ID. My mental model conjures the typical sequence: checking device and driver compatibility, calling the driver's specific probe function, and handling potential failures. It's the point where everything hangs in the balance: a successful probe, and the device is ready to go, the driver takes over. A failure, and the system needs to gracefully handle it – potentially looking for alternative drivers, logging an error, or simply leaving the device unmanaged.
I'm also anticipating how it deals with resource allocation: requesting the necessary memory regions, IRQs, DMA channels, and then the critical step of reporting these resources to the system. It would need to consider the order of operations, the locking mechanisms, and the potential race conditions that can occur. I'm wondering if it involves any of the hotplug or ACPI subsystems. I'd need to consider how this core functionality interacts with power management, device-specific quirks and error handling. I'm imagining the conditional compilation, platform-specific code and potential for architecture-dependent variations. The complexity lies in its generality; it needs to be flexible enough to handle a vast array of devices across different hardware architectures.
So, when I see `__driver_probe_device`, I instantly start visualizing the entire driver initialization workflow – the core logic that orchestrates the crucial binding of a driver to a device, and all the inherent challenges that come with making that happen correctly and safely. It's a fundamental piece, and understanding it is key to understanding how devices integrate with the kernel.
|
| 53/2 |
2026/08/07 05:39 |
tool |
codesearch-definition-source |
0m
Args: map[ContextFile:drivers/base/dd.c Name:__driver_probe_device]
Results: map[SourceCode: 827: static int __driver_probe_device(const struct device_driver *drv, struct device *dev)
828: {
829: int ret = 0;
830:
831: if (dev->p->dead || !device_is_registered(dev))
832: return -ENODEV;
833: if (dev->driver)
834: return -EBUSY;
835:
836: /*
837: * In device_add(), the "struct device" gets linked into the subsystem's
838: * list of devices and broadcast to userspace (via uevent) before we're
839: * quite ready to probe. Those open pathways to driver probe before
840: * we've finished enough of device_add() to reliably support probe.
841: * Detect this and tell other pathways to try again later. device_add()
842: * itself will also try to probe immediately after setting
843: * "ready_to_probe".
844: */
845: if (!dev_ready_to_probe(dev))
846: return dev_err_probe(dev, -EPROBE_DEFER, "Device not ready to probe\n");
847:
848: /*
849: * Call dev_set_can_match() after calling dev_ready_to_probe(), so
850: * driver_deferred_probe_add() won't actually add the device to the
851: * deferred probe list when dev_ready_to_probe() returns false.
852: *
853: * When dev_ready_to_probe() returns false, it means that device_add()
854: * will do another probe() attempt for us.
855: */
856: dev_set_can_match(dev);
857: dev_dbg(dev, "bus: '%s': %s: matched device with driver %s\n",
858: drv->bus->name, __func__, drv->name);
859:
860: pm_runtime_get_suppliers(dev);
861: if (dev->parent)
862: pm_runtime_get_sync(dev->parent);
863:
864: pm_runtime_barrier(dev);
865: if (initcall_debug)
866: ret = really_probe_debug(dev, drv);
867: else
868: ret = really_probe(dev, drv);
869: pm_request_idle(dev);
870:
871: if (dev->parent)
872: pm_runtime_put(dev->parent);
873:
874: pm_runtime_put_suppliers(dev);
875: return ret;
876: }
SourceFile:drivers/base/dd.c]
|
| 54/2 |
2026/08/07 05:39 |
llm |
debugger |
0m
Model:
Tokens:
input: 38166
output: 39
thoughts: 573
Thoughts: **Analyzing a USB Gadget Driver Registration Issue**
Okay, let me break this down. So, `__driver_probe_device` just returns the result of `really_probe`, which seems straightforward. But then I see that `__driver_attach` blithely *ignores* whatever `driver_probe_device` returns. That's immediately suspect. It just returns 0 after probing, which makes `driver_attach` return 0, then `bus_add_driver` also returns 0, and so on, all the way up to the registration functions. The return values don't seem to indicate much of a problem. But `dev_config` *does* set `dev->gadget_registered` to true.
Now, here's the kicker. If `really_probe` fails, then `gadgetfs_bind` is *never* called. This means the device reference count (`dev` refcount) isn't incremented beyond the initial counts (one from `dev_new` and another from `gadget_dev_open`). The catch is, `dev->gadget_registered` is *still* set to true, which is a real problem.
When the endpoint 0 (ep0) is closed, `dev_release` is called. It checks `dev->gadget_registered`. If it's true, it unregisters the driver using `usb_gadget_unregister_driver`, which then calls `driver_unregister`. But here's the trap - because `gadgetfs_bind` wasn't called (due to the probe failure), the refcount is 2 at the beginning. `dev_release` calls `put_dev(dev)`, dropping the refcount to 1. Then, `gadgetfs_kill_sb` calls `put_dev(the_device)`, dropping the refcount to 0, and *boom* the device is freed.
Now, if the device is freed inside `gadgetfs_kill_sb`, why is it causing a use-after-free (UAF) later on? That's what's got my attention. Looking at the crash trace again...
The trace indicates a slab-use-after-free in `gadgetfs_kill_sb`, meaning `the_device` was *already* freed when `gadgetfs_kill_sb` was called. That's the real problem! I need to trace this back.
Let's go back and examine `dev_release` again because that's the point where `the_device` and `dev` are handled. I need to figure out exactly why it thinks the device is registered but the bind never happened. Is there an issue with the ref counting? Where else could this device be getting released? I have to trace the state of `the_device` and `dev` throughout the device lifecycle when probe fails and `gadgetfs_bind` is not called!
|
| 55/2 |
2026/08/07 05:39 |
tool |
codesearch-definition-source |
0m
Args: map[ContextFile:drivers/usb/gadget/legacy/inode.c Name:dev_release]
Results: map[SourceCode:1190: static int
1191: dev_release (struct inode *inode, struct file *fd)
1192: {
1193: struct dev_data *dev = fd->private_data;
1194:
1195: /* closing ep0 === shutdown all */
1196:
1197: if (dev->gadget_registered) {
1198: usb_gadget_unregister_driver (&gadgetfs_driver);
1199: dev->gadget_registered = false;
1200: }
1201:
1202: /* at this point "good" hardware has disconnected the
1203: * device from USB; the host won't see it any more.
1204: * alternatively, all host requests will time out.
1205: */
1206:
1207: kfree (dev->buf);
1208: dev->buf = NULL;
1209:
1210: /* other endpoints were all decoupled from this device */
1211: spin_lock_irq(&dev->lock);
1212: dev->state = STATE_DEV_DISABLED;
1213: spin_unlock_irq(&dev->lock);
1214:
1215: put_dev (dev);
1216: return 0;
1217: }
SourceFile:drivers/usb/gadget/legacy/inode.c]
|
| 56/2 |
2026/08/07 05:39 |
llm |
debugger |
0m
Model:
Tokens:
input: 39813
output: 36
thoughts: 543
Thoughts: **Debugging a USB Gadget Driver Unregistration Scenario**
Okay, so I'm trying to figure out this `usb_gadget_unregister_driver` sequence, and specifically, whether it's correctly unbinding the `gadgetfs` driver. My initial concern is whether `gadgetfs_unbind` is always called when it should be. The crucial question is: does `usb_gadget_unregister_driver` reliably trigger the `gadgetfs_unbind` function?
I started with the assumption that if `usb_gadget_unregister_driver` is called, it *should* call `gadgetfs_unbind`. But then I realized, what if `gadgetfs_bind` was *never* called in the first place? If that's the case, then `gadgetfs_unbind` shouldn't be called either, right? That makes sense.
But what about the edge case? What if `gadgetfs_bind` *was* called, but something else went wrong during the probe process, say if `driver_sysfs_add` failed? That’s the real crux of the issue.
Let's dive back into `really_probe`. I need to re-examine the code flow:
```c
ret = driver_sysfs_add(dev);
if (ret) {
dev_err(dev, "%s: driver_sysfs_add failed\n", __func__);
goto sysfs_failed;
}
if (dev->pm_domain && dev->pm_domain->activate) {
ret = dev->pm_domain->activate(dev);
if (ret)
goto probe_failed;
}
ret = call_driver_probe(dev, drv);
```
Aha! It's clear now. `driver_sysfs_add` is *before* `call_driver_probe`, which calls `gadgetfs_bind`. So, if `driver_sysfs_add` fails, the `call_driver_probe` (and, crucially, `gadgetfs_bind`) is never executed! That’s key. `gadgetfs_bind` would NOT be called, and the refcount of `dev` would *not* be incremented.
If that's the case, and assuming the device is correctly setup, the refcount remains at 2. When `ep0` is closed, `dev_release` calls `put_dev(dev)`. The refcount then goes down to 1. Then, `gadgetfs_kill_sb` calls `put_dev(the_device)`, dropping the refcount to 0. The device will then be freed. But, where is the extra `put_dev`?
Now, what if `dev_config` fails *after* `usb_gadget_register_driver`? Let's check:
```c
value = usb_gadget_register_driver(&gadgetfs_driver);
if (value != 0) {
spin_lock_irq(&dev->lock);
goto fail;
} else {
value = len;
dev->gadget_registered = true;
}
return value;
```
Nope, the code clearly doesn’t fail after the registration.
So, what if `gadgetfs_bind` *is* called, but *something else* fails within the probe? Let me check `call_driver_probe` again to make sure I haven't missed any edge cases there.
|
| 57/2 |
2026/08/07 05:39 |
tool |
codesearch-definition-source |
0m
Args: map[ContextFile:drivers/base/dd.c Name:call_driver_probe]
Results: map[SourceCode: 621: static int call_driver_probe(struct device *dev, const struct device_driver *drv)
622: {
623: int ret = 0;
624:
625: if (dev->bus->probe)
626: ret = dev->bus->probe(dev);
627: else if (drv->probe)
628: ret = drv->probe(dev);
629:
630: switch (ret) {
631: case 0:
632: break;
633: case -EPROBE_DEFER:
634: /* Driver requested deferred probing */
635: dev_dbg(dev, "Driver %s requests probe deferral\n", drv->name);
636: break;
637: case -ENODEV:
638: case -ENXIO:
639: dev_dbg(dev, "probe with driver %s rejects match %d\n",
640: drv->name, ret);
641: break;
642: default:
643: /* driver matched but the probe failed */
644: dev_err(dev, "probe with driver %s failed with error %d\n",
645: drv->name, ret);
646: break;
647: }
648:
649: return ret;
650: }
SourceFile:drivers/base/dd.c]
|
| 58/2 |
2026/08/07 05:39 |
llm |
debugger |
0m
Model:
Tokens:
input: 41602
output: 45
thoughts: 21
Thoughts: Okay, here's my interpretation of that thought process, assuming I'm an embedded systems expert familiar with the Linux kernel and USB driver architecture:
**Gadget Driver Binding: A Familiar Path**
Right, so, thinking about gadgets and driver binding, specifically, when the system's probing for the correct driver, that `dev->bus->probe` pointer—that's the key. That function pointer, the one that the USB core calls to see if a driver can handle a device... in the gadget context, that probe function is going to be `udc_bind_to_driver`. That's the function that takes the USB device controller (UDC) information and associates it with the gadget driver. It's a fundamental part of the USB gadget driver initialization process. This is the mechanism that links the USB peripheral with the correct gadget driver.
|
| 59/2 |
2026/08/07 05:39 |
tool |
codesearch-definition-source |
0m
Error:
Args: map[ContextFile:drivers/usb/gadget/udc/core.c Name:udc_bind_to_driver]
Results: map[SourceCode: SourceFile:]
|
| 60/2 |
2026/08/07 05:39 |
llm |
debugger |
0m
Model:
Tokens:
input: 41868
output: 38
thoughts: 24
Thoughts: Okay, here's my interpretation of that thought process, as if I were having it:
**Digging into the USB Gadget Driver Core**
Alright, time to dive in. I need to understand something related to the USB gadget driver framework, and the term that jumps to mind is `probe`. Seems like that's often a key function during device initialization. I know the general location of the relevant code; `drivers/usb/gadget/udc/core.c` is usually where the core Unidirectional Data Channel (UDC) logic resides, which makes sense. So, let's start there. I'll search for `probe` specifically within that file. That should give me a solid entry point to trace the initialization sequence and understand how the system discovers and configures USB gadgets. From there, I can likely understand how my target device interacts within the UDC framework.
|
| 61/2 |
2026/08/07 05:39 |
tool |
grepper |
0m
Args: map[Expression:\.probe\s*= PathPrefix:drivers/usb/gadget/udc/core.c]
Results: map[Output:drivers/usb/gadget/udc/core.c=1951=static const struct bus_type gadget_bus_type = {
drivers/usb/gadget/udc/core.c-1952- .name = "gadget",
drivers/usb/gadget/udc/core.c:1953: .probe = gadget_bind_driver,
drivers/usb/gadget/udc/core.c-1954- .remove = gadget_unbind_driver,
]
|
| 62/2 |
2026/08/07 05:39 |
llm |
debugger |
0m
Model:
Tokens:
input: 42249
output: 43
thoughts: 10
Thoughts: Okay, here's my interpretation of that thought process, tailored for an expert audience, and written in the first person:
**Navigating the `gadget_bind_driver` Landscape**
Right, `gadget_bind_driver`. Just the mention of it sparks a familiar set of considerations. I'm immediately thinking about the intricacies of the USB gadget subsystem in the Linux kernel and the crucial role this function plays within it.
I'm mentally running through the driver binding process. Is this referring to a new driver trying to attach to a gadget, or a reconfiguration of an existing relationship? What specific circumstances triggered this thought? Are we talking about a standard gadget like a USB Ethernet device, or something more specialized? The devil, as always, is in the details, and the `gadget_bind_driver` function is the point of a great deal of the configuration.
My mind is jumping to the potential issues. Is there a race condition? Is the driver properly registering its supported functions and configurations? Are the endpoints correctly allocated and configured? Are there resource conflicts with other drivers or gadgets already attached to the bus? I need to consider all the different USB classes involved, the different device descriptors, and their interactions, and whether the bind is being requested at the correct time.
The thought then rolls on to the error handling... if the bind fails, what's the expected return value, and how is that propagated up the call stack? How does the system handle a partially bound driver? What debugging tools are available to pinpoint the root cause of the problem? The `gadget_bind_driver` function is the gatekeeper, and the surrounding processes are all that matters.
I am also considering what platform the discussion relates to. Are there any hardware-specific quirks I need to keep in mind? Are we talking about a system-on-chip with a tightly integrated USB controller, or a more traditional PCI-based setup? The underlying hardware architecture can have a significant impact on how `gadget_bind_driver` behaves.
In short, `gadget_bind_driver` is a nexus point – a starting point for thinking through the potential issues, complexities, and opportunities within a USB gadget driver context. It's a reminder to keep the broader system context in mind and consider all the possible failure points. It's time to dig deeper.
|
| 63/2 |
2026/08/07 05:39 |
tool |
codesearch-definition-source |
0m
Args: map[ContextFile:drivers/usb/gadget/udc/core.c Name:gadget_bind_driver]
Results: map[SourceCode:1641: static int gadget_bind_driver(struct device *dev)
1642: {
1643: struct usb_gadget *gadget = dev_to_usb_gadget(dev);
1644: struct usb_udc *udc = gadget->udc;
1645: struct usb_gadget_driver *driver = container_of(dev->driver,
1646: struct usb_gadget_driver, driver);
1647: int ret = 0;
1648:
1649: mutex_lock(&udc_lock);
1650: if (driver->is_bound) {
1651: mutex_unlock(&udc_lock);
1652: return -ENXIO; /* Driver binds to only one gadget */
1653: }
1654: driver->is_bound = true;
1655: udc->driver = driver;
1656: mutex_unlock(&udc_lock);
1657:
1658: dev_dbg(&udc->dev, "binding gadget driver [%s]\n", driver->function);
1659:
1660: usb_gadget_udc_set_speed(udc, driver->max_speed);
1661:
1662: ret = driver->bind(udc->gadget, driver);
1663: if (ret)
1664: goto err_bind;
1665:
1666: mutex_lock(&udc->connect_lock);
1667: ret = usb_gadget_udc_start_locked(udc);
1668: if (ret) {
1669: mutex_unlock(&udc->connect_lock);
1670: goto err_start;
1671: }
1672: usb_gadget_enable_async_callbacks(udc);
1673: udc->allow_connect = true;
1674: ret = usb_udc_connect_control_locked(udc);
1675: if (ret)
1676: goto err_connect_control;
1677:
1678: mutex_unlock(&udc->connect_lock);
1679:
1680: kobject_uevent(&udc->dev.kobj, KOBJ_CHANGE);
1681: return 0;
1682:
1683: err_connect_control:
1684: udc->allow_connect = false;
1685: usb_gadget_disable_async_callbacks(udc);
1686: if (gadget->irq)
1687: synchronize_irq(gadget->irq);
1688: usb_gadget_udc_stop_locked(udc);
1689: mutex_unlock(&udc->connect_lock);
1690:
1691: err_start:
1692: driver->unbind(udc->gadget);
1693:
1694: err_bind:
1695: if (ret != -EISNAM)
1696: dev_err(&udc->dev, "failed to start %s: %d\n",
1697: driver->function, ret);
1698:
1699: mutex_lock(&udc_lock);
1700: udc->driver = NULL;
1701: driver->is_bound = false;
1702: mutex_unlock(&udc_lock);
1703:
1704: return ret;
1705: }
SourceFile:drivers/usb/gadget/udc/core.c]
|
| 64/2 |
2026/08/07 05:39 |
llm |
debugger |
0m
Model:
Tokens:
input: 43794
output: 41
thoughts: 48
Thoughts: Okay, here's my interpretation of those thoughts, framed as my own:
**Unraveling a USB Gadget Failure**
Right, so I'm digging into this `usb_gadget_udc_start_locked` function. It looks like the critical path here. Aha! If *that* thing fails, it triggers the `driver->unbind(udc->gadget)` call, interesting. I need to be aware of this potential failure point when debugging.
And hang on, let me just confirm, `driver->unbind`… yes, that's defined as `gadgetfs_unbind`. Good. That confirms my understanding of how gadgetfs handles unbinding from the UDC. Knowing the precise unbinding function, helps me to correlate potential error messages with the specific unbinding procedure. This is key for efficient debugging.
|
| 65/2 |
2026/08/07 05:39 |
tool |
codesearch-definition-source |
0m
Args: map[ContextFile:drivers/usb/gadget/legacy/inode.c Name:gadgetfs_unbind]
Results: map[SourceCode:1639: static void
1640: gadgetfs_unbind (struct usb_gadget *gadget)
1641: {
1642: struct dev_data *dev = get_gadget_data (gadget);
1643:
1644: DBG (dev, "%s\n", __func__);
1645:
1646: spin_lock_irq (&dev->lock);
1647: dev->state = STATE_DEV_UNBOUND;
1648: while (dev->udc_usage > 0) {
1649: spin_unlock_irq(&dev->lock);
1650: usleep_range(1000, 2000);
1651: spin_lock_irq(&dev->lock);
1652: }
1653: spin_unlock_irq (&dev->lock);
1654:
1655: destroy_ep_files (dev);
1656: gadget->ep0->driver_data = NULL;
1657: set_gadget_data (gadget, NULL);
1658:
1659: /* we've already been disconnected ... no i/o is active */
1660: if (dev->req)
1661: usb_ep_free_request (gadget->ep0, dev->req);
1662: DBG (dev, "%s done\n", __func__);
1663: put_dev (dev);
1664: }
SourceFile:drivers/usb/gadget/legacy/inode.c]
|
| 66/2 |
2026/08/07 05:39 |
llm |
debugger |
0m
Model:
Tokens:
input: 44501
output: 45
thoughts: 1272
Thoughts: **Analyzing the GadgetFS Driver Registration and Unregistration Flow**
Okay, let's break this down. My initial thought was: Yes! `gadgetfs_unbind` correctly calls `put_dev(dev)`! This makes sense: if `gadget_bind_driver` *fails* after the driver's `bind` function is called, the system properly calls `driver->unbind`, which *should* call `put_dev(dev)`. This seems balanced.
But hold on, let's trace this carefully. If `gadget_bind_driver` fails, we get an error back. This cascades up through `call_driver_probe`, `really_probe`, `__driver_probe_device`, `driver_probe_device`. And then, *strangely*, `__driver_attach` *ignores* the error and returns 0. So, `driver_attach` returns 0, and then `bus_add_driver` also returns 0, and then `driver_register` returns 0, which means `usb_gadget_register_driver_owner` returns 0!
Now, this is where things get tricky. Since `usb_gadget_register_driver_owner` returns 0, it means that `dev_config` is able to set `dev->gadget_registered = true`! But, wait... in the `err_bind` case, `driver->is_bound` is explicitly set to `false`:
```c
err_bind:
if (ret != -EISNAM)
dev_err(&udc->dev, "failed to start %s: %d\n",
driver->function, ret);
mutex_lock(&udc_lock);
udc->driver = NULL;
driver->is_bound = false;
mutex_unlock(&udc_lock);
```
But `usb_gadget_register_driver_owner` *checks* `driver->is_bound`. Because this is `false`, and `driver->match_existing_only` is false, this function will still return 0, leading to `dev_config` succeeding anyway:
```c
mutex_lock(&udc_lock);
if (!driver->is_bound) {
if (driver->match_existing_only) {
pr_warn("%s: couldn't find an available UDC or it's busy\n",
driver->function);
ret = -EBUSY;
} else {
pr_info("%s: couldn't find an available UDC\n",
driver->function);
ret = 0;
}
}
mutex_unlock(&udc_lock);
```
So, even if `gadget_bind_driver` *failed*, `dev->gadget_registered` is *still* set to `true`. This feels like a potential leak...
Okay, let's step back. If `gadget_bind_driver` failed *before* the registration was complete, `gadgetfs_unbind` was already called, and `put_dev(dev)` was already executed. Therefore, `dev` has refcount 1 (from `dev_new`). Now, consider this: `gadget_dev_open` was called, so refcount was 2. Then, `gadgetfs_bind` calls `get_dev(dev)`, refcount is 3. `gadgetfs_unbind` calls `put_dev(dev)`, refcount becomes 2. Finally, `ep0` is closed, and `dev_release` is called.
Inside `dev_release`, because `dev->gadget_registered` is `true`, we call `usb_gadget_unregister_driver(&gadgetfs_driver)`, which will eventually call `driver_unregister`. Then `dev_release` calls `put_dev(dev)`, refcount is 1. Then, `gadgetfs_kill_sb` calls `put_dev(the_device)`, so the refcount becomes 0, and `dev` is freed. This should be a balanced sequence, but where did that extra `put_dev` go? It looks balanced.
Okay, let's consider another scenario: what if `driver_sysfs_add` fails? If that happens, then `really_probe` jumps to `sysfs_failed`.
```c
ret = driver_sysfs_add(dev);
if (ret) {
dev_err(dev, "%s: driver_sysfs_add failed\n", __func__);
goto sysfs_failed;
}
```
If
|
| 67/2 |
2026/08/07 05:39 |
tool |
codesearch-definition-source |
0m
Args: map[ContextFile:drivers/usb/gadget/udc/core.c Name:usb_gadget_unregister_driver]
Results: map[SourceCode:1779: int usb_gadget_unregister_driver(struct usb_gadget_driver *driver)
1780: {
1781: if (!driver || !driver->unbind)
1782: return -EINVAL;
1783:
1784: driver_unregister(&driver->driver);
1785: return 0;
1786: }
SourceFile:drivers/usb/gadget/udc/core.c]
|
| 68/2 |
2026/08/07 05:39 |
llm |
debugger |
0m
Model:
Tokens:
input: 46983
output: 34
thoughts: 17
Thoughts: Okay, here's my interpretation of that thought process, tailored for an expert audience, and written in the first person:
**Unraveling Driver Unregistration**
Hmm, this code is calling `driver_unregister`. That's interesting, and I need to immediately understand the implications. The immediate question burning in my mind is, what exactly does `driver_unregister` *do*? I'm familiar with the concept, of course – it's the kernel's mechanism for cleanly removing a device driver from the system. But the specifics are always critical. I need to refresh my understanding of the exact sequence of actions it undertakes. Does it handle resource cleanup, unbinding from devices, deregistering the driver from the core subsystems? Is it robust in handling potential errors during unregistration? More importantly, what are the implicit assumptions the driver has to satisfy to make `driver_unregister` successful? Are there any potential race conditions I need to be aware of? Are there any potential deadlocks I need to think about?
|
| 69/2 |
2026/08/07 05:39 |
tool |
codesearch-definition-source |
0m
Args: map[ContextFile:drivers/base/driver.c Name:driver_unregister]
Results: map[SourceCode: 189: /**
190: * driver_unregister - remove driver from system.
191: * @drv: driver.
192: *
193: * Again, we pass off most of the work to the bus-level call.
194: */
195: void driver_unregister(struct device_driver *drv)
196: {
197: if (!drv || !drv->p) {
198: WARN(1, "Unexpected driver unregister!\n");
199: return;
200: }
201: driver_remove_groups(drv, drv->groups);
202: bus_remove_driver(drv);
203: }
SourceFile:drivers/base/driver.c]
|
| 70/2 |
2026/08/07 05:39 |
llm |
debugger |
0m
Model:
Tokens:
input: 47477
output: 36
thoughts: 8
Thoughts: Okay, here's my interpretation of that thought process, summarized as you requested:
**Deconstructing `bus_remove_driver`**
Alright, let's break this down. The implication here is that we're dealing with a system, probably an operating system kernel or some sort of device management framework, that has a concept of "buses" – virtual or physical connections that devices plug into. The function `bus_remove_driver` is specifically designed to handle the removal of a driver from one of those buses.
My initial questions would be: what triggers this removal? Is it a graceful un-plug, a forced unload, or an error condition? Understanding the *why* is crucial. Then, what are the key steps involved? I'm thinking:
* **Safety Checks:** We'd definitely need to start with some sanity checks. Is the bus valid? Is the driver actually attached to this bus? Are there any pending I/O operations or references to the driver that need to be handled before we proceed? This is critical to prevent crashes or memory corruption.
* **Device Disconnect:** Next, there's the device itself. Any devices controlled by the driver on this bus need to be disconnected, de-initialized, or otherwise properly handled. We probably need a systematic approach to that to avoid any resource leaks.
* **Driver De-registration:** We'll likely need to "unhook" the driver from the bus's internal structures. This involves removing it from any lists or data structures that track which drivers are associated with which buses. This operation should be thread safe.
* **Cleanup:** Lastly, we must free any resources specifically allocated for this driver, and perform any necessary final cleanup steps.
This process seems straightforward, but is fraught with peril if it isn't properly designed and executed. Thread safety, resource management, and error handling are absolutely paramount. This function needs to be robust. I'll need to know the specific architecture and framework involved to offer more precise insights, but at a high level, that's what I'm thinking.
|
| 71/2 |
2026/08/07 05:40 |
tool |
codesearch-definition-source |
0m
Args: map[ContextFile:drivers/base/bus.c Name:bus_remove_driver]
Results: map[SourceCode: 810: /**
811: * bus_remove_driver - delete driver from bus's knowledge.
812: * @drv: driver.
813: *
814: * Detach the driver from the devices it controls, and remove
815: * it from its bus's list of drivers. Finally, we drop the reference
816: * to the bus we took in bus_add_driver().
817: */
818: void bus_remove_driver(struct device_driver *drv)
819: {
820: struct subsys_private *sp = bus_to_subsys(drv->bus);
821:
822: if (!sp)
823: return;
824:
825: pr_debug("bus: '%s': remove driver %s\n", sp->bus->name, drv->name);
826:
827: if (!drv->suppress_bind_attrs)
828: remove_bind_files(drv);
829: driver_remove_groups(drv, sp->bus->drv_groups);
830: driver_remove_file(drv, &driver_attr_uevent);
831: klist_remove(&drv->p->knode_bus);
832: driver_detach(drv);
833: module_remove_driver(drv);
834: kobject_put(&drv->p->kobj);
835:
836: /*
837: * Decrement the reference count twice, once for the bus_to_subsys()
838: * call in the start of this function, and the second one from the
839: * reference increment in bus_add_driver()
840: */
841: subsys_put(sp);
842: subsys_put(sp);
843: }
SourceFile:drivers/base/bus.c]
|
| 72/2 |
2026/08/07 05:40 |
llm |
debugger |
0m
Model:
Tokens:
input: 48511
output: 34
thoughts: 10
Thoughts: Okay, let's see. My mind's buzzing... I'm going over the execution flow here. Ah, right, I see it. **The `driver_detach` call is happening!** That's the key moment. What implications does that have, precisely? Now I need to unpack what's been happening before, and what will happen after this specific line of code. What state is the system in at this point? What resources are being freed? And crucially, what's *supposed* to happen versus what *could* happen, given potential race conditions or resource conflicts. I need to quickly recall the memory management strategy in this module to anticipate potential issues. Right, the detach… is that clean? Are we handling all the edge cases correctly? Am I missing a lock that might be needed, or conversely, leaving one held too long? I've got to ensure the driver's memory is properly cleaned up to prevent leaks. The order of operations at this stage is absolutely critical. Let me mentally step through the surrounding code again... I need to consider all possible failure paths, of course. Time to dive deeper into the surrounding context now.
|
| 73/2 |
2026/08/07 05:40 |
tool |
codesearch-definition-source |
0m
Args: map[ContextFile:drivers/base/dd.c Name:driver_detach]
Results: map[SourceCode:1411: /**
1412: * driver_detach - detach driver from all devices it controls.
1413: * @drv: driver.
1414: */
1415: void driver_detach(const struct device_driver *drv)
1416: {
1417: struct device_private *dev_prv;
1418: struct device *dev;
1419:
1420: if (driver_allows_async_probing(drv))
1421: async_synchronize_full();
1422:
1423: for (;;) {
1424: spin_lock(&drv->p->klist_devices.k_lock);
1425: if (list_empty(&drv->p->klist_devices.k_list)) {
1426: spin_unlock(&drv->p->klist_devices.k_lock);
1427: break;
1428: }
1429: dev_prv = list_last_entry(&drv->p->klist_devices.k_list,
1430: struct device_private,
1431: knode_driver.n_node);
1432: dev = dev_prv->device;
1433: get_device(dev);
1434: spin_unlock(&drv->p->klist_devices.k_lock);
1435: device_release_driver_internal(dev, drv, dev->parent);
1436: put_device(dev);
1437: }
1438: }
SourceFile:drivers/base/dd.c]
|
| 74/2 |
2026/08/07 05:40 |
llm |
debugger |
0m
Model:
Tokens:
input: 49250
output: 36
thoughts: 345
Thoughts: **Debugging a Driver Binding Issue**
Okay, so I'm trying to figure out why a driver isn't binding correctly, and it's got me thinking. If `gadgetfs_bind` was *never* called, then `dev->driver` should *not* be set to `gadgetfs_driver` – that seems straightforward. But, hold on... let's retrace my steps. The call to `device_set_driver` occurs in `really_probe` *before* the call to `driver_sysfs_add`! That's interesting, let me double-check the `really_probe` function again...
Okay, here it is: `device_set_driver(dev, drv);` then a few other functions are called. After that, we have `ret = driver_sysfs_add(dev);`.
If `driver_sysfs_add` fails, the `BUS_NOTIFY_DRIVER_NOT_BOUND` is called, and `device_unbind_cleanup` is run. Okay, this is getting closer. What does `device_unbind_cleanup` *actually* do? Let's go look that up.
|
| 75/2 |
2026/08/07 05:40 |
tool |
codesearch-definition-source |
0m
Args: map[ContextFile:drivers/base/dd.c Name:device_unbind_cleanup]
Results: map[SourceCode: 593: static void device_unbind_cleanup(struct device *dev)
594: {
595: if (dev->driver->p_cb.post_unbind_rust)
596: dev->driver->p_cb.post_unbind_rust(dev);
597: devres_release_all(dev);
598: arch_teardown_dma_ops(dev);
599: kfree(dev->dma_range_map);
600: dev->dma_range_map = NULL;
601: device_set_driver(dev, NULL);
602: dev_set_drvdata(dev, NULL);
603: dev_pm_domain_detach(dev, dev->power.detach_power_off);
604: if (dev->pm_domain && dev->pm_domain->dismiss)
605: dev->pm_domain->dismiss(dev);
606: pm_runtime_reinit(dev);
607: dev_pm_set_driver_flags(dev, 0);
608: }
SourceFile:drivers/base/dd.c]
|
| 76/2 |
2026/08/07 05:40 |
llm |
debugger |
0m
Model:
Tokens:
input: 50209
output: 34
thoughts: 574
Thoughts: **My Analysis of the Driver Unbinding Sequence**
Okay, so I'm tracing this `device_set_driver(dev, NULL)` call. The first thing that jumps out is that `dev->driver` becomes NULL. That means `drv->p->klist_devices` is empty, right? Which means `driver_detach` does absolutely *nothing*. And, consequently, `usb_gadget_unregister_driver` does nothing too. Therefore, `gadgetfs_unbind` is *never* called. And if `gadgetfs_unbind` isn't called, then the all-important `put_dev(dev)` call that *should* be happening from within it, is *also* never happening.
Hold on, if `put_dev(dev)` isn't called from `gadgetfs_unbind`, then the device refcount is currently at 2. It started at 1 from `dev_new` and increments to 2 after `gadget_dev_open`. Now, when `ep0` closes, `dev_release` gets called which *does* call `put_dev(dev)`. The refcount drops to 1. Then, `gadgetfs_kill_sb` calls `put_dev(the_device)` and the refcount goes to 0, which triggers a free of `dev`. So, where's the use-after-free vulnerability that I'm supposed to be seeing? It seems like everything is cleaned up in this case.
Okay, let's play devil's advocate and consider the scenario where `gadgetfs_bind` *does* succeed. The refcount is now 3 (1 from `dev_new`, 1 from `gadget_dev_open`, and 1 from `gadgetfs_bind`).
So, `driver_sysfs_add` succeeds, and `call_driver_probe` also succeeds. Then, the critical point: `device_add_groups` fails. Now what? The code branches to `dev_groups_failed`, which leads to:
```c
dev_sysfs_state_synced_failed:
dev_groups_failed:
device_remove(dev);
probe_failed:
driver_sysfs_remove(dev);
sysfs_failed:
bus_notify(dev, BUS_NOTIFY_DRIVER_NOT_BOUND);
if (dev->bus && dev->bus->dma_cleanup)
dev->bus->dma_cleanup(dev);
pinctrl_bind_failed:
device_links_no_driver(dev);
device_unbind_cleanup(dev);
```
Wait a second... if `call_driver_probe` *did* succeed, does the failure of `device_add_groups` trigger a call to the driver's `unbind` function?
*No!* It doesn't look like `really_probe` calls `driver->remove` *or* `driver->unbind` if `device_add_groups` fails. I need to be absolutely sure about this. Let's dig deeper into the `really_probe` function to confirm.
|
| 77/2 |
2026/08/07 05:40 |
tool |
codesearch-definition-source |
0m
Args: map[ContextFile:drivers/base/dd.c Name:really_probe]
Results: map[SourceCode: 652: static int really_probe(struct device *dev, const struct device_driver *drv)
653: {
654: bool test_remove = IS_ENABLED(CONFIG_DEBUG_TEST_DRIVER_REMOVE) &&
655: !drv->suppress_bind_attrs;
656: int ret, link_ret;
657:
658: if (defer_all_probes) {
659: /*
660: * Value of defer_all_probes can be set only by
661: * device_block_probing() which, in turn, will call
662: * wait_for_device_probe() right after that to avoid any races.
663: */
664: dev_dbg(dev, "Driver %s force probe deferral\n", drv->name);
665: return -EPROBE_DEFER;
666: }
667:
668: link_ret = device_links_check_suppliers(dev);
669: if (link_ret == -EPROBE_DEFER)
670: return link_ret;
671:
672: dev_dbg(dev, "bus: '%s': %s: probing driver %s with device\n",
673: drv->bus->name, __func__, drv->name);
674: if (!list_empty(&dev->devres_head)) {
675: dev_crit(dev, "Resources present before probing\n");
676: ret = -EBUSY;
677: goto done;
678: }
679:
680: re_probe:
681: device_set_driver(dev, drv);
682:
683: /* If using pinctrl, bind pins now before probing */
684: ret = pinctrl_bind_pins(dev);
685: if (ret)
686: goto pinctrl_bind_failed;
687:
688: if (dev->bus->dma_configure) {
689: ret = dev->bus->dma_configure(dev);
690: if (ret)
691: goto pinctrl_bind_failed;
692: }
693:
694: ret = driver_sysfs_add(dev);
695: if (ret) {
696: dev_err(dev, "%s: driver_sysfs_add failed\n", __func__);
697: goto sysfs_failed;
698: }
699:
700: if (dev->pm_domain && dev->pm_domain->activate) {
701: ret = dev->pm_domain->activate(dev);
702: if (ret)
703: goto probe_failed;
704: }
705:
706: ret = call_driver_probe(dev, drv);
707: if (ret) {
708: /*
709: * If fw_devlink_best_effort is active (denoted by -EAGAIN), the
710: * device might actually probe properly once some of its missing
711: * suppliers have probed. So, treat this as if the driver
712: * returned -EPROBE_DEFER.
713: */
714: if (link_ret == -EAGAIN)
715: ret = -EPROBE_DEFER;
716:
717: /*
718: * Return probe errors as positive values so that the callers
719: * can distinguish them from other errors.
720: */
721: ret = -ret;
722: goto probe_failed;
723: }
724:
725: ret = device_add_groups(dev, drv->dev_groups);
726: if (ret) {
727: dev_err(dev, "device_add_groups() failed\n");
728: goto dev_groups_failed;
729: }
730:
731: if (dev_has_sync_state(dev)) {
732: ret = device_create_file(dev, &dev_attr_state_synced);
733: if (ret) {
734: dev_err(dev, "state_synced sysfs add failed\n");
735: goto dev_sysfs_state_synced_failed;
736: }
737: }
738:
739: if (test_remove) {
740: test_remove = false;
741:
742: device_remove(dev);
743: driver_sysfs_remove(dev);
744: if (dev->bus && dev->bus->dma_cleanup)
745: dev->bus->dma_cleanup(dev);
746: device_unbind_cleanup(dev);
747:
748: goto re_probe;
749: }
750:
751: pinctrl_init_done(dev);
752:
753: if (dev->pm_domain && dev->pm_domain->sync)
754: dev->pm_domain->sync(dev);
755:
756: driver_bound(dev);
757: dev_dbg(dev, "bus: '%s': %s: bound device to driver %s\n",
758: drv->bus->name, __func__, drv->name);
759: goto done;
760:
761: dev_sysfs_state_synced_failed:
762: dev_groups_failed:
763: device_remove(dev);
764: probe_failed:
765: driver_sysfs_remove(dev);
766: sysfs_failed:
767: bus_notify(dev, BUS_NOTIFY_DRIVER_NOT_BOUND);
768: if (dev->bus && dev->bus->dma_cleanup)
769: dev->bus->dma_cleanup(dev);
770: pinctrl_bind_failed:
771: device_links_no_driver(dev);
772: device_unbind_cleanup(dev);
773: done:
774: return ret;
775: }
SourceFile:drivers/base/dd.c]
|
| 78/2 |
2026/08/07 05:40 |
llm |
debugger |
0m
Model:
Tokens:
input: 53351
output: 34
thoughts: 165
Thoughts: **My Analysis of Device Removal in the Kernel**
Okay, let me break this down. My initial assumption was wrong; `device_remove(dev)` doesn't trigger the driver's `remove` method directly. Instead, it appears to handle a cleanup that's more focused on removing groups and attributes with calls to `device_remove_groups` and `device_remove_attrs`. This changes my understanding.
Now, if `device_add_groups` fails during the probe sequence, `really_probe` then calls `device_remove(dev)`, `driver_sysfs_remove(dev)`, and `device_unbind_cleanup(dev)`. And it's apparent that at no point in that sequence do we see `dev->bus->remove(dev)` or the driver's own `drv->remove(dev)` function being called. This is a crucial oversight!
This implies a scenario where the driver could remain bound to the device even if `really_probe` returns an error due to the failure in `device_add_groups`! The driver is technically still associated, but the probe has been considered unsuccessful, which is a potentially nasty state to leave things in.
I need to confirm this understanding, so let me pull up the source code for `device_remove` in `drivers/base/core.c` and take a closer look at its exact behavior. Understanding its precise actions is key to comprehending this potential issue.
|
| 79/2 |
2026/08/07 05:40 |
tool |
codesearch-definition-source |
0m
Args: map[ContextFile:drivers/base/core.c Name:device_remove]
Results: map[SourceCode: 610: static void device_remove(struct device *dev)
611: {
612: device_remove_file(dev, &dev_attr_state_synced);
613: device_remove_groups(dev, dev->driver->dev_groups);
614:
615: if (dev->bus && dev->bus->remove)
616: dev->bus->remove(dev);
617: else if (dev->driver->remove)
618: dev->driver->remove(dev);
619: }
SourceFile:drivers/base/dd.c]
|
| 80/2 |
2026/08/07 05:40 |
llm |
debugger |
0m
Model:
Tokens:
input: 54039
output: 34
thoughts: 3780
Thoughts: **Analyzing the USB Gadget Driver Lifecycle and Potential UAF**
Okay, so I've been tracing the execution path here, trying to understand how this USB gadget driver unloads and where a potential use-after-free (UAF) could arise. Initially, it seemed pretty straightforward: `device_remove` calls `dev->bus->remove(dev)`, which then cascades through `gadget_unbind_driver`, `gadgetfs_unbind`, and finally `put_dev(dev)`. If `device_add_groups` fails early on, we end up with a clean unbind and `put_dev` call. And if all succeeds, then at the end, `dev` is freed with refcount 0. So far, so good, everything is balanced.
I thought about the `driver_sysfs_add` failing. If *that* happens, then `gadgetfs_bind` is never called, so `dev`'s refcount could be off by one (2). Again, the refcount ends up becoming 0 by the end of `gadgetfs_kill_sb`. All still seemed balanced.
Then there was the possibility of `driver_register` failing, which also looked safe because `dev` is eventually freed. But what if `usb_gadget_register_driver_owner` fails *after* `driver_register` succeeds? No, that can't happen, there's a check for that scenario that prevents the race.
The thought then turned to whether the driver could be registered twice. However, `dev->state` is checked, preventing double registrations. But then, what if `gadgetfs_bind` is never called because `driver_sysfs_add` failed? If `dev_config` is called *again*, it should fail with `-EINVAL` because `dev->buf` is already set.
But what if `dev_config` fails *after* `dev->buf` is set? That looks safer because `dev->buf` is freed. And the logic says `dev->buf` is freed if `usb_gadget_register_driver` fails. But, what if the gadget registers fine, but `gadgetfs_bind` is *never* called? The ref count gets a little squirrely again... but still, `dev` seems to be freed safely. No UAF there either.
The fault injection trace is where it got interesting. It's not one single error, it is a *loop* of fault injections at various points. Then I had a thought... `driver_sysfs_add` might fail. This is where I think there is the bug. If `driver_sysfs_add` fails, then `really_probe` returns an error, but the error is ignored in the `__driver_attach` and it still registers! Meaning, `dev->gadget_registered` becomes true, but `gadgetfs_bind` is *never* called. This means the refcount is off. Then when the device is unbound, we end up with `put_dev` calls that should make `dev` be freed at the end. Then, the `gadgetfs_kill_sb` code is hit: the device refcount becomes 0 and `dev` is freed. But is there a UAF there?
In this case, since the repro is running multiple times, I'm thinking the UAF has to be somewhere within `gadgetfs_kill_sb` or the stack of calls that it invokes. I need to dig further into `kill_anon_super`, `generic_shutdown_super` and what calls it.
|
| 81/2 |
2026/08/07 05:40 |
tool |
codesearch-definition-source |
0m
Args: map[ContextFile:fs/super.c Name:kill_anon_super]
Results: map[SourceCode:1235: void kill_anon_super(struct super_block *sb)
1236: {
1237: dev_t dev = sb->s_dev;
1238: generic_shutdown_super(sb);
1239: kill_super_notify(sb);
1240: free_anon_bdev(dev);
1241: }
SourceFile:fs/super.c]
|
| 82/2 |
2026/08/07 05:40 |
llm |
debugger |
0m
Model:
Tokens:
input: 58756
output: 41
thoughts: 1147
Thoughts: **UAF Hunting: A Deep Dive into GadgetFS Unbinding**
Okay, so I'm tracking down this potential UAF in GadgetFS, right? The key lies in the shutdown sequence. I see `generic_shutdown_super` calls `shrink_dcache_for_umount`, which is expected. But I'm fixated on `ep0` and the device's release. I have to figure out the timing.
First thought: does freeing the `ep0` inode trigger `dev_release`? No, that's not it. `dev_release` is a file operation, called when the `struct file` is closed. Right! Now I'm getting somewhere. When is that `struct file` closed? Aha, the reproducer calls `close(fd)` BEFORE the `umount("/tmp/gadgetfs")`!
So, `dev_release` gets called BEFORE `gadgetfs_kill_sb`. Okay, this changes things: if `dev_release` happens first, the `dev` refcount goes to 1. Then, `gadgetfs_kill_sb` calls `put_dev(the_device)`, dropping the refcount to 0. The `dev` is freed. Perfectly normal, no UAF yet.
What if `dev_release` *doesn't* happen first? Impossible, `close(fd)` comes before `umount`. This must be a different code path. The real question then is what triggers `dev_release` if the normal umount isn't happening. I have to chase down the `usb_gadget_unregister_driver` calls in the shutdown process.
What if `usb_gadget_unregister_driver` calls `gadgetfs_unbind`? If `gadgetfs_bind` *succeeded*, and it did, the refcount should start at 3. The `gadget_registered` flag is set. Then, `driver_sysfs_add` succeeds, `call_driver_probe` also succeeds, but then `device_add_groups` FAILS. If THAT happens, `really_probe` then calls `device_remove(dev)`. This calls `dev->bus->remove(dev)`, and then calls `gadget_unbind_driver`, which then calls `gadgetfs_unbind`. `gadgetfs_unbind` calls `put_dev(dev)`, dropping the refcount to 2. `really_probe` returns an error. `__driver_attach` ignores the error and returns 0. That means `driver_register` returns 0. Therefore `usb_gadget_register_driver_owner` returns 0. The flag `dev->gadget_registered` is set. So, if `ep0` is closed, then `dev_release` is called.
```c
if (dev->gadget_registered) {
usb_gadget_unregister_driver (&gadgetfs_driver);
dev->gadget_registered = false;
}
```
`usb_gadget_unregister_driver` then calls `driver_unregister`. `driver_unregister` calls `bus_remove_driver`. That then calls `driver_detach`. BUT, `dev->driver` is NULL, so `driver_detach` doesn't actually DO anything. `dev_release` then calls `put_dev(dev)`, dropping the refcount to 1. Finally, `gadgetfs_kill_sb` calls `put_dev(the_device)`, dropping the refcount to 0. Still no UAF! It's all balanced.
Wait, what if `gadgetfs_bind` *fails*? Let's trace that path.
```c
static int gadgetfs_bind(struct usb_gadget *gadget,
struct usb_gadget_driver *driver)
{
struct dev_data *dev = the_device;
...
/* preallocate control response and buffer */
dev->req = usb_ep_alloc_request (gadget->ep0, GFP_KERNEL);
if (!dev->req)
goto enomem;
...
if (activate_ep_files (dev) < 0)
goto enomem;
...
get_dev (dev);
return 0;
enomem:
gadgetfs_unbind (gadget);
return -ENOMEM;
}
```
If `gadgetfs_bind` fails, it *immediately* calls `gadgetfs_unbind`!
```c
static void
gadgetfs_unbind (struct usb_gadget *gadget)
{
|
| 83/2 |
2026/08/07 05:41 |
tool |
codesearch-definition-source |
0m
Args: map[ContextFile:drivers/usb/gadget/legacy/inode.c Name:gadgetfs_bind]
Results: map[SourceCode:1668: static int gadgetfs_bind(struct usb_gadget *gadget,
1669: struct usb_gadget_driver *driver)
1670: {
1671: struct dev_data *dev = the_device;
1672:
1673: if (!dev)
1674: return -ESRCH;
1675: if (0 != strcmp (CHIP, gadget->name)) {
1676: pr_err("%s expected %s controller not %s\n",
1677: shortname, CHIP, gadget->name);
1678: return -ENODEV;
1679: }
1680:
1681: set_gadget_data (gadget, dev);
1682: dev->gadget = gadget;
1683: gadget->ep0->driver_data = dev;
1684:
1685: /* preallocate control response and buffer */
1686: dev->req = usb_ep_alloc_request (gadget->ep0, GFP_KERNEL);
1687: if (!dev->req)
1688: goto enomem;
1689: dev->req->context = NULL;
1690: dev->req->complete = epio_complete;
1691:
1692: if (activate_ep_files (dev) < 0)
1693: goto enomem;
1694:
1695: INFO (dev, "bound to %s driver\n", gadget->name);
1696: spin_lock_irq(&dev->lock);
1697: dev->state = STATE_DEV_UNCONNECTED;
1698: spin_unlock_irq(&dev->lock);
1699: get_dev (dev);
1700: return 0;
1701:
1702: enomem:
1703: gadgetfs_unbind (gadget);
1704: return -ENOMEM;
1705: }
SourceFile:drivers/usb/gadget/legacy/inode.c]
|
| 84/2 |
2026/08/07 05:41 |
llm |
debugger |
0m
Model:
Tokens:
input: 61554
output: 41
thoughts: 129
Thoughts: **Reference Count Underflow Detected!**
Yes! I've found a critical issue. My analysis shows that if `usb_ep_alloc_request` fails, the code branches to `enomem`. That, in turn, calls `gadgetfs_unbind(gadget)`. Now, here's where it gets interesting – and concerning. `gadgetfs_unbind` then calls `put_dev(dev)`. The problem is that the corresponding `get_dev(dev)` call, which I'd expect to balance this, happens *later* at line 1699, and it happens AFTER the `activate_ep_files` call. This means `gadgetfs_unbind` is potentially dropping a reference that was never actually taken within the critical execution path! This is a textbook example of a reference count underflow, and it's something we *really* need to address before it causes major headaches. Let me just double-check that `gadgetfs_unbind` function...
|
| 85/2 |
2026/08/07 05:41 |
tool |
codesearch-definition-source |
0m
Args: map[ContextFile:drivers/usb/gadget/legacy/inode.c Name:gadgetfs_unbind]
Results: map[SourceCode:1639: static void
1640: gadgetfs_unbind (struct usb_gadget *gadget)
1641: {
1642: struct dev_data *dev = get_gadget_data (gadget);
1643:
1644: DBG (dev, "%s\n", __func__);
1645:
1646: spin_lock_irq (&dev->lock);
1647: dev->state = STATE_DEV_UNBOUND;
1648: while (dev->udc_usage > 0) {
1649: spin_unlock_irq(&dev->lock);
1650: usleep_range(1000, 2000);
1651: spin_lock_irq(&dev->lock);
1652: }
1653: spin_unlock_irq (&dev->lock);
1654:
1655: destroy_ep_files (dev);
1656: gadget->ep0->driver_data = NULL;
1657: set_gadget_data (gadget, NULL);
1658:
1659: /* we've already been disconnected ... no i/o is active */
1660: if (dev->req)
1661: usb_ep_free_request (gadget->ep0, dev->req);
1662: DBG (dev, "%s done\n", __func__);
1663: put_dev (dev);
1664: }
SourceFile:drivers/usb/gadget/legacy/inode.c]
|
| 86/2 |
2026/08/07 05:41 |
llm |
debugger |
0m
Model:
Tokens:
input: 62378
output: 41
thoughts: 76
Thoughts: **Analyzing the GadgetFS Unbind Process**
Okay, so I'm tracing this `gadgetfs_unbind` function. It looks like the critical thing to note here is that it definitely calls `put_dev(dev)`. That's important for resource management, obviously. And then, there's this connection to `gadgetfs_bind`. If `activate_ep_files` fails during the bind process, the code cleverly calls `gadgetfs_unbind` to clean up. Smart.
Now, the real potential gotcha here is `activate_ep_files`. I remember that function allocates memory, and that's a classic area for potential fault injection, especially in a system like this. It’s absolutely critical that we handle those allocation failures gracefully. This feels like a good place to double-check error handling. Time to dive into the `activate_ep_files` code itself and see how robust it really is.
|
| 87/2 |
2026/08/07 05:41 |
tool |
codesearch-definition-source |
0m
Args: map[ContextFile:drivers/usb/gadget/legacy/inode.c Name:activate_ep_files]
Results: map[SourceCode:1593: static int activate_ep_files (struct dev_data *dev)
1594: {
1595: struct usb_ep *ep;
1596: struct ep_data *data;
1597: int err;
1598:
1599: gadget_for_each_ep (ep, dev->gadget) {
1600:
1601: data = kzalloc_obj(*data);
1602: if (!data)
1603: goto enomem0;
1604: data->state = STATE_EP_DISABLED;
1605: mutex_init(&data->lock);
1606: init_waitqueue_head (&data->wait);
1607:
1608: strscpy(data->name, ep->name);
1609: refcount_set (&data->count, 1);
1610: data->dev = dev;
1611: get_dev (dev);
1612:
1613: data->ep = ep;
1614: ep->driver_data = data;
1615:
1616: data->req = usb_ep_alloc_request (ep, GFP_KERNEL);
1617: if (!data->req)
1618: goto enomem1;
1619:
1620: err = gadgetfs_create_file (dev->sb, data->name,
1621: data, &ep_io_operations);
1622: if (err)
1623: goto enomem2;
1624: list_add_tail (&data->epfiles, &dev->epfiles);
1625: }
1626: return 0;
1627:
1628: enomem2:
1629: usb_ep_free_request (ep, data->req);
1630: enomem1:
1631: put_dev (dev);
1632: kfree (data);
1633: enomem0:
1634: DBG (dev, "%s enomem\n", __func__);
1635: destroy_ep_files (dev);
1636: return -ENOMEM;
1637: }
SourceFile:drivers/usb/gadget/legacy/inode.c]
|
| 88/2 |
2026/08/07 05:41 |
llm |
debugger |
0m
Model:
Tokens:
input: 63352
output: 35
thoughts: 5376
Thoughts: **Bug Hunting: The Dreaded UAF in gadgetfs!**
Okay, so I've been staring at this code for a while now, and I think I've got a handle on this UAF issue, which is really exciting. The basic idea is this: we've got a reference counting problem. `dev_new` starts the refcount at one. `gadget_dev_open` bumps it to two. From then on things get interesting, and this is where it all goes sideways.
The core of the problem lies in the interaction between `gadgetfs_bind` and `gadgetfs_unbind`, and how they are used, or *misused*, in the error paths. I see that `gadgetfs_bind` is the function that causes the refcount to increase when `get_dev()` is called, and `gadgetfs_unbind` to cause the refcount to decrease, via `put_dev()`.
Here's my thought process on tracking down this nasty bug:
* **Scenario 1: `gadgetfs_bind` fails.** If `activate_ep_files` or `usb_ep_alloc_request` fails, `gadgetfs_bind` calls `gadgetfs_unbind` immediately. If it fails *before* calling `get_dev()` which increases the refcount, `gadgetfs_unbind` will call `put_dev()`, incorrectly decreasing the refcount. I initially thought this was the culprit, but then I realized the fault injection traces don't show the failure happening *there*, but instead in another, separate place in the code. I didn't see where the fault injection happened, and I was wrong in thinking this was the bug,
* **Scenario 2: `driver_sysfs_add` failing.** This seems to be where the fault injection is occurring. But if `driver_sysfs_add` fails, then `gadgetfs_bind` is NEVER called, thus `put_dev()` is never called in error. I spent way too much time here.
* **Scenario 3: `device_add_groups` fails.** If `device_add_groups` fails, `gadgetfs_unbind` calls `put_dev(dev)`. Refcount becomes 2. This does NOT cause a bug.
* **What if `gadgetfs_bind` FAILS?** Again, if `gadgetfs_bind` fails, then its `put_dev()` call decrements the refcount that it had never incremented.
* **The Big Picture:** Here's how it shakes out now. If the `driver_sysfs_add` fails (or even if it succeeds), the refcount is one at `dev_release`. `dev_release` calls `usb_gadget_unregister_driver`, and at the end of that call, `dev_release` calls `put_dev()`. The refcount is *still* one. Then, later, `gadgetfs_kill_sb` calls `put_dev(the_device)`, which is a UAF!
So, the root cause is this: `gadgetfs_bind` can fail and incorrectly call `put_dev` when it shouldn't.
|
| 89/2 |
2026/08/07 05:41 |
tool |
grepper |
0m
Args: map[Expression:put_dev PathPrefix:drivers/usb/gadget/legacy/inode.c]
Results: map[Output:drivers/usb/gadget/legacy/inode.c=158=static inline void get_dev (struct dev_data *data)
--
drivers/usb/gadget/legacy/inode.c-162-
drivers/usb/gadget/legacy/inode.c:163:static void put_dev (struct dev_data *data)
drivers/usb/gadget/legacy/inode.c-164-{
--
drivers/usb/gadget/legacy/inode.c=217=static void put_ep (struct ep_data *data)
--
drivers/usb/gadget/legacy/inode.c-220- return;
drivers/usb/gadget/legacy/inode.c:221: put_dev (data->dev);
drivers/usb/gadget/legacy/inode.c-222- /* needs no more cleanup */
--
drivers/usb/gadget/legacy/inode.c=1191=dev_release (struct inode *inode, struct file *fd)
--
drivers/usb/gadget/legacy/inode.c-1214-
drivers/usb/gadget/legacy/inode.c:1215: put_dev (dev);
drivers/usb/gadget/legacy/inode.c-1216- return 0;
--
drivers/usb/gadget/legacy/inode.c=1593=static int activate_ep_files (struct dev_data *dev)
--
drivers/usb/gadget/legacy/inode.c-1630-enomem1:
drivers/usb/gadget/legacy/inode.c:1631: put_dev (dev);
drivers/usb/gadget/legacy/inode.c-1632- kfree (data);
--
drivers/usb/gadget/legacy/inode.c=1640=gadgetfs_unbind (struct usb_gadget *gadget)
--
drivers/usb/gadget/legacy/inode.c-1662- DBG (dev, "%s done\n", __func__);
drivers/usb/gadget/legacy/inode.c:1663: put_dev (dev);
drivers/usb/gadget/legacy/inode.c-1664-}
--
drivers/usb/gadget/legacy/inode.c=2015=gadgetfs_fill_super (struct super_block *sb, struct fs_context *fc)
--
drivers/usb/gadget/legacy/inode.c-2060- if (rc) {
drivers/usb/gadget/legacy/inode.c:2061: put_dev(dev);
drivers/usb/gadget/legacy/inode.c-2062- goto Enomem;
--
drivers/usb/gadget/legacy/inode.c=2099=gadgetfs_kill_sb (struct super_block *sb)
--
drivers/usb/gadget/legacy/inode.c-2103- if (the_device) {
drivers/usb/gadget/legacy/inode.c:2104: put_dev (the_device);
drivers/usb/gadget/legacy/inode.c-2105- the_device = NULL;
]
|
| 90/2 |
2026/08/07 05:41 |
llm |
debugger |
0m
Model:
Tokens:
input: 70270
output: 52
thoughts: 28
Thoughts: Okay, here's my interpretation of those thoughts, tailored for an expert audience, presented in the first person:
**Analyzing the `put_ep` Function Call**
Right, so it's finally clicked. The `put_ep` function is calling `put_dev(data->dev)`. That's a critical detail I was missing. It directly links the endpoint release mechanism with the underlying device. Now the next thing I need to investigate is when is `put_ep` actually called? I need to understand the control flow to determine when the endpoint resources are released, and thus when the device resources are cleaned up. What triggers the `put_ep` to be called is the key question now.
|
| 91/2 |
2026/08/07 05:42 |
tool |
codesearch-find-references |
0m
Args: map[ContextFile:drivers/usb/gadget/legacy/inode.c IncludeSnippetLines:0 Name:put_ep SourceTreePrefix:]
Results: map[References:[map[ReferenceKind:calls ReferencingEntityKind:function ReferencingEntityName:destroy_ep_files SourceFile:drivers/usb/gadget/legacy/inode.c SourceLine:1582] map[ReferenceKind:calls ReferencingEntityKind:function ReferencingEntityName:ep_aio SourceFile:drivers/usb/gadget/legacy/inode.c SourceLine:575] map[ReferenceKind:calls ReferencingEntityKind:function ReferencingEntityName:ep_aio_complete SourceFile:drivers/usb/gadget/legacy/inode.c SourceLine:524] map[ReferenceKind:calls ReferencingEntityKind:function ReferencingEntityName:ep_release SourceFile:drivers/usb/gadget/legacy/inode.c SourceLine:398]] TruncatedOutput:false]
|
| 92/2 |
2026/08/07 05:42 |
llm |
debugger |
0m
Model:
Tokens:
input: 70732
output: 41
thoughts: 25
Thoughts: Here's a summary of my thought process, as if I were the one thinking it:
**Digging into the `destroy_ep_files` Function**
Okay, so `destroy_ep_files` seems to be calling `put_ep`. That's interesting, but I need to understand the relationship better. Let's start by diving directly into `destroy_ep_files` itself. I need to figure out what that specific function is doing, in order to properly understand its interaction with `put_ep` and, ultimately, what the implications are for the system. Time to get into the code!
|
| 93/2 |
2026/08/07 05:42 |
tool |
codesearch-definition-source |
0m
Args: map[ContextFile:drivers/usb/gadget/legacy/inode.c Name:destroy_ep_files]
Results: map[SourceCode:1554: static void destroy_ep_files (struct dev_data *dev)
1555: {
1556: DBG (dev, "%s %d\n", __func__, dev->state);
1557:
1558: /* dev->state must prevent interference */
1559: spin_lock_irq (&dev->lock);
1560: while (!list_empty(&dev->epfiles)) {
1561: struct ep_data *ep;
1562:
1563: /* break link to FS */
1564: ep = list_first_entry (&dev->epfiles, struct ep_data, epfiles);
1565: list_del_init (&ep->epfiles);
1566: spin_unlock_irq (&dev->lock);
1567:
1568: /* break link to controller */
1569: mutex_lock(&ep->lock);
1570: if (ep->state == STATE_EP_ENABLED)
1571: (void) usb_ep_disable (ep->ep);
1572: ep->state = STATE_EP_UNBOUND;
1573: usb_ep_free_request (ep->ep, ep->req);
1574: ep->ep = NULL;
1575: mutex_unlock(&ep->lock);
1576:
1577: wake_up (&ep->wait);
1578:
1579: /* break link to dcache */
1580: simple_remove_by_name(dev->sb->s_root, ep->name, NULL);
1581:
1582: put_ep (ep);
1583:
1584: spin_lock_irq (&dev->lock);
1585: }
1586: spin_unlock_irq (&dev->lock);
1587: }
SourceFile:drivers/usb/gadget/legacy/inode.c]
|
| 94/2 |
2026/08/07 05:42 |
llm |
debugger |
0m
Model:
Tokens:
input: 71470
output: 37
thoughts: 3317
Thoughts: **The UAF in GadgetFS: A Journey Through Driver Registration and Unbinding**
Okay, let's break this down. My initial thought process focused on how endpoint files are handled in the `gadgetfs` driver. It all seemed straightforward, with reference counting ensuring proper resource management during endpoint creation, destruction, and unbinding. I was mainly looking for the reference count imbalance, but this was a red herring. Then, I was looking into other failure cases. What happens if an allocation fails during endpoint setup? What if `gadgetfs_unbind` is called before `get_dev`?
Then I went through `gadgetfs_unbind` and started suspecting that it drops a reference that was never taken. But I dismissed it by tracing the calls. Then I went through failure cases, and determined the correct sequence.
I then re-examined the fault injection trace. The traces indicated that the fault injection occurs during driver registration, specifically in `bus_add_driver`. If `bus_add_driver` fails, the `dev_release` gets the ref count to 0, which is then re-used in `gadgetfs_kill_sb`!
So, to summarize this bug, the sequence is:
1. A failure during driver registration *before* the device is fully bound (e.g., `bus_add_driver` fails). This is simulated by the fault injection.
2. `gadgetfs_bind` does NOT run, and `get_dev()` is not called.
3. The device is "registered" because the driver register succeeded.
4. Then the unbind procedure is triggered.
5. `gadgetfs_unbind` calls `put_dev(dev)` and reduces the ref count to 0.
6. `gadgetfs_kill_sb` later calls `put_dev(the_device)`, and since the dev is now zero, this causes a use-after-free vulnerability!
Essentially, the bug stems from a race condition where the driver registration can *appear* to succeed partially, but a subsequent failure during the early setup phase skips the `get_dev` call. This leads to an imbalance in the device reference count, and a classic UAF!
|
| 95/2 |
2026/08/07 05:42 |
tool |
codesearch-definition-source |
0m
Args: map[ContextFile:drivers/base/dd.c Name:driver_sysfs_add]
Results: map[SourceCode: 483: static int driver_sysfs_add(struct device *dev)
484: {
485: int ret;
486:
487: bus_notify(dev, BUS_NOTIFY_BIND_DRIVER);
488:
489: ret = sysfs_create_link(&dev->driver->p->kobj, &dev->kobj,
490: kobject_name(&dev->kobj));
491: if (ret)
492: goto fail;
493:
494: ret = sysfs_create_link(&dev->kobj, &dev->driver->p->kobj,
495: "driver");
496: if (ret)
497: goto rm_dev;
498:
499: if (!IS_ENABLED(CONFIG_DEV_COREDUMP) || !dev->driver->coredump)
500: return 0;
501:
502: ret = device_create_file(dev, &dev_attr_coredump);
503: if (!ret)
504: return 0;
505:
506: sysfs_remove_link(&dev->kobj, "driver");
507:
508: rm_dev:
509: sysfs_remove_link(&dev->driver->p->kobj,
510: kobject_name(&dev->kobj));
511:
512: fail:
513: return ret;
514: }
SourceFile:drivers/base/dd.c]
|
| 96/2 |
2026/08/07 05:42 |
llm |
debugger |
0m
Model:
Tokens:
input: 75772
output: 1148
thoughts: 6281
Thoughts: **Analysis of the gadgetfs UAF vulnerability**
Okay, so I've dug into this reproducer for a while, and the root cause of this use-after-free (UAF) in the `gadgetfs` driver is a classic refcount imbalance. It boils down to `gadgetfs_bind` calling `gadgetfs_unbind` incorrectly in its error path. Let me lay out the thought process and the crucial steps.
1. **Device Initialization:** The process begins with the `dev` structure being allocated with an initial refcount of 1.
```c
// From dev_new()
dev->count = 1;
```
2. **User Opens Ep0:** When the user opens `/tmp/gadgetfs/ep0`, `gadget_dev_open` is called. This function does `get_dev(dev)`, incrementing the refcount to 2.
```c
spin_lock_irq(&dev->lock);
if (dev->state == STATE_DEV_DISABLED) {
dev->ev_next = 0;
dev->state = STATE_DEV_OPENED;
fd->private_data = dev;
get_dev (dev);
value = 0;
}
spin_unlock_irq(&dev->lock);
```
3. **Driver Registration and Bind:** Then, the user writes to the endpoint, which triggers `dev_config`. Inside `dev_config`, `usb_gadget_register_driver(&gadgetfs_driver)` is called. This registers the driver, and as part of the binding process, calls `gadgetfs_bind`.
4. **`gadgetfs_bind` (and Fault Injection):** Now, the critical part. `gadgetfs_bind` attempts to allocate resources, specifically using `usb_ep_alloc_request` or calls `activate_ep_files` (which allocates resources too), and this is where the fault injection comes in. If a memory allocation fails in one of these calls (simulating a "fault"), the code jumps to an error label called `enomem`.
5. **Refcount Leak in Error Path:** Here's where the problem arises: the `enomem` path incorrectly calls `gadgetfs_unbind(gadget)`.
```c
enomem:
gadgetfs_unbind (gadget);
return -ENOMEM;
```
`gadgetfs_unbind` *does* cleanup and frees memory. Crucially, it also calls `put_dev(dev)`.
```c
static void
gadgetfs_unbind (struct usb_gadget *gadget)
{
struct dev_data *dev = get_gadget_data (gadget);
DBG (dev, "%s\n", __func__);
spin_lock_irq (&dev->lock);
dev->state = STATE_DEV_UNBOUND;
while (dev->udc_usage > 0) {
spin_unlock_irq(&dev->lock);
usleep_range(1000, 2000);
spin_lock_irq(&dev->lock);
}
spin_unlock_irq (&dev->lock);
destroy_ep_files (dev);
gadget->ep0->driver_data = NULL;
set_gadget_data (gadget, NULL);
/* we've already been disconnected ... no i/o is active */
if (dev->req)
usb_ep_free_request (gadget->ep0, dev->req);
DBG (dev, "%s done\n", __func__);
put_dev (dev);
}
```
*Here's the leak:* `get_dev(dev)` is called at the very *end* of `gadgetfs_bind`, only after the allocation succeeds. But on *failure*, `put_dev(dev)` is called via `gadgetfs_unbind`, *before* `get_dev` has a chance to be called. Therefore, `gadgetfs_unbind` decrements a refcount it didn't increment. This creates a one-off error.
6. **Ignoring the Error:** The probe then fails and returns -ENOMEM, however, the driver core ( `__driver_attach` ) *ignores* this error. This means
|
| 97/1 |
2026/08/07 05:43 |
agent |
history-explorer |
3m
Model:
Instruction:
You are an experienced Linux kernel developer researching prior art for fixing a kernel bug.
You are given a bug explanation. This explanation details the root cause of the bug resulting
from debugging, but does not provide the final fix strategy. Your goal is to explore how
similar bugs were fixed in the past in the same subsystem or files.
CRITICAL: Do NOT attempt to debug the issue further or write a patch for it yourself.
Your ONLY objective is to research and provide the necessary historical context.
Use the git-log tool with the Since parameter set to "3 years" to focus on recent history.
Search for commits that address issues with similar root causes (e.g. similar missing locks,
incorrect refcounting, or similar error path bugs) in the affected files.
Your final reply must summarize your findings: what idioms, locking rules, or common patterns
should be followed when writing a fix for this bug based on how previous similar bugs were addressed.
If you find no relevant past fixes, clearly state that.
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.
Prompt:
The crash is:
udc dummy_udc.0: failed to start USB Gadget filesystem: -12
gadgetfs gadget.0: probe with driver gadgetfs failed with error -12
gadgetfs: bound to dummy_udc driver
==================================================================
BUG: KASAN: slab-use-after-free in instrument_atomic_read_write include/linux/instrumented.h:112 [inline]
BUG: KASAN: slab-use-after-free in atomic_fetch_sub_release include/linux/atomic/atomic-instrumented.h:400 [inline]
BUG: KASAN: slab-use-after-free in __refcount_sub_and_test include/linux/refcount.h:389 [inline]
BUG: KASAN: slab-use-after-free in __refcount_dec_and_test include/linux/refcount.h:432 [inline]
BUG: KASAN: slab-use-after-free in refcount_dec_and_test include/linux/refcount.h:450 [inline]
BUG: KASAN: slab-use-after-free in put_dev drivers/usb/gadget/legacy/inode.c:165 [inline]
BUG: KASAN: slab-use-after-free in gadgetfs_kill_sb+0x46/0x110 drivers/usb/gadget/legacy/inode.c:2104
Write of size 4 at addr ffff88811638c080 by task syz-executor169/5848
CPU: 0 UID: 0 PID: 5848 Comm: syz-executor169 Not tainted syzkaller #1 PREEMPT_{RT,(full)}
Hardware name: QEMU Standard PC (Q35 + ICH9, 2009), BIOS 1.16.3-debian-1.16.3-2 04/01/2014
Call Trace:
<TASK>
dump_stack_lvl+0xe8/0x150 lib/dump_stack.c:120
print_address_description+0x55/0x1e0 mm/kasan/report.c:378
print_report+0x58/0x70 mm/kasan/report.c:482
kasan_report+0x117/0x150 mm/kasan/report.c:595
check_region_inline mm/kasan/generic.c:-1 [inline]
kasan_check_range+0x264/0x2c0 mm/kasan/generic.c:200
instrument_atomic_read_write include/linux/instrumented.h:112 [inline]
atomic_fetch_sub_release include/linux/atomic/atomic-instrumented.h:400 [inline]
__refcount_sub_and_test include/linux/refcount.h:389 [inline]
__refcount_dec_and_test include/linux/refcount.h:432 [inline]
refcount_dec_and_test include/linux/refcount.h:450 [inline]
put_dev drivers/usb/gadget/legacy/inode.c:165 [inline]
gadgetfs_kill_sb+0x46/0x110 drivers/usb/gadget/legacy/inode.c:2104
deactivate_locked_super+0xbc/0x130 fs/super.c:477
cleanup_mnt+0x3d3/0x460 fs/namespace.c:1317
task_work_run+0x1d9/0x270 kernel/task_work.c:233
resume_user_mode_work include/linux/resume_user_mode.h:50 [inline]
__exit_to_user_mode_loop kernel/entry/common.c:70 [inline]
exit_to_user_mode_loop+0x1fa/0x730 kernel/entry/common.c:101
__exit_to_user_mode_prepare include/linux/irq-entry-common.h:207 [inline]
syscall_exit_to_user_mode_prepare include/linux/irq-entry-common.h:230 [inline]
syscall_exit_to_user_mode include/linux/entry-common.h:318 [inline]
do_syscall_64+0x353/0x580 arch/x86/entry/syscall_64.c:100
entry_SYSCALL_64_after_hwframe+0x77/0x7f
RIP: 0033:0x7f5f0e505027
Code: a2 c7 05 54 b0 08 00 00 00 00 00 eb 96 e8 a1 08 00 00 90 31 f6 e9 09 00 00 00 66 0f 1f 84 00 00 00 00 00 b8 a6 00 00 00 0f 05 <48> 3d 00 f0 ff ff 77 01 c3 48 c7 c2 d8 ff ff ff f7 d8 64 89 02 b8
RSP: 002b:00007ffcdf2b0288 EFLAGS: 00000246 ORIG_RAX: 00000000000000a6
RAX: 0000000000000000 RBX: 0000000000000003 RCX: 00007f5f0e505027
RDX: 0000000000000000 RSI: 0000000000000000 RDI: 00007f5f0e55f05e
RBP: 00007f5f0e55f05e R08: 0000000000000000 R09: 0000000000000000
R10: 0000000000000000 R11: 0000000000000246 R12: 00007f5f0e55f623
R13: 00007ffcdf2b03e0 R14: 00007f5f0e561510 R15: 00007f5f0e5614e8
</TASK>
Allocated by task 5848:
kasan_save_stack mm/kasan/common.c:57 [inline]
kasan_save_track+0x3e/0x80 mm/kasan/common.c:78
poison_kmalloc_redzone mm/kasan/common.c:398 [inline]
__kasan_kmalloc+0x93/0xb0 mm/kasan/common.c:415
kasan_kmalloc include/linux/kasan.h:263 [inline]
__kmalloc_cache_noprof+0x3d2/0x6b0 mm/slub.c:5489
_kmalloc_noprof include/linux/slab.h:988 [inline]
_kzalloc_noprof include/linux/slab.h:1309 [inline]
dev_new drivers/usb/gadget/legacy/inode.c:176 [inline]
gadgetfs_fill_super+0x27b/0x7a0 drivers/usb/gadget/legacy/inode.c:2054
vfs_get_super fs/super.c:1273 [inline]
get_tree_single+0xc0/0x150 fs/super.c:1300
vfs_get_tree+0x92/0x2a0 fs/super.c:1700
fc_mount fs/namespace.c:1198 [inline]
do_new_mount_fc fs/namespace.c:3765 [inline]
do_new_mount+0x319/0xdc0 fs/namespace.c:3841
do_mount fs/namespace.c:4174 [inline]
__do_sys_mount fs/namespace.c:4390 [inline]
__se_sys_mount+0x31d/0x420 fs/namespace.c:4367
do_syscall_x64 arch/x86/entry/syscall_64.c:63 [inline]
do_syscall_64+0x174/0x580 arch/x86/entry/syscall_64.c:94
entry_SYSCALL_64_after_hwframe+0x77/0x7f
Freed by task 5848:
kasan_save_stack mm/kasan/common.c:57 [inline]
kasan_save_track+0x3e/0x80 mm/kasan/common.c:78
kasan_save_free_info+0x40/0x50 mm/kasan/generic.c:584
poison_slab_object mm/kasan/common.c:253 [inline]
__kasan_slab_free+0x5c/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 mm/slub.c:6377 [inline]
kfree+0x1c5/0x6c0 mm/slub.c:6692
put_dev drivers/usb/gadget/legacy/inode.c:169 [inline]
dev_release+0x173/0x210 drivers/usb/gadget/legacy/inode.c:1215
__fput+0x42a/0xa80 fs/file_table.c:512
fput_close_sync+0x11f/0x240 fs/file_table.c:617
__do_sys_close fs/open.c:1511 [inline]
__se_sys_close fs/open.c:1496 [inline]
__x64_sys_close+0x7e/0x110 fs/open.c:1496
do_syscall_x64 arch/x86/entry/syscall_64.c:63 [inline]
do_syscall_64+0x174/0x580 arch/x86/entry/syscall_64.c:94
entry_SYSCALL_64_after_hwframe+0x77/0x7f
The buggy address belongs to the object at ffff88811638c000
which belongs to the cache kmalloc-1k of size 1024
The buggy address is located 128 bytes inside of
freed 1024-byte region [ffff88811638c000, ffff88811638c400)
The buggy address belongs to the physical page:
page: refcount:0 mapcount:0 mapping:0000000000000000 index:0xffff88811638d000 pfn:0x116388
head: order:3 mapcount:0 entire_mapcount:0 nr_pages_mapped:0 pincount:0
flags: 0x100000000000240(workingset|head|node=0|zone=2)
page_type: f5(slab)
raw: 0100000000000240 ffff888100041dc0 ffffea00044a8a10 ffffea00043da810
raw: ffff88811638d000 000000080010000f 00000000f5000000 0000000000000000
head: 0100000000000240 ffff888100041dc0 ffffea00044a8a10 ffffea00043da810
head: ffff88811638d000 000000080010000f 00000000f5000000 0000000000000000
head: 0100000000000003 fffffffffffffe01 00000000ffffffff 00000000ffffffff
head: 0000000000000000 0000000000000000 00000000ffffffff 0000000000000008
page dumped because: kasan: bad access detected
page_owner tracks the page as allocated
page last allocated via order 3, migratetype Unmovable, gfp_mask 0xd20c0(__GFP_IO|__GFP_FS|__GFP_NOWARN|__GFP_NORETRY|__GFP_COMP|__GFP_NOMEMALLOC), pid 5581, tgid 5581 (syz-executor), ts 49160475111, free_ts 0
set_page_owner include/linux/page_owner.h:32 [inline]
post_alloc_hook+0x1f9/0x250 mm/page_alloc.c:1859
prep_new_page mm/page_alloc.c:1867 [inline]
get_page_from_freelist+0x262a/0x26a0 mm/page_alloc.c:3946
__alloc_frozen_pages_noprof+0x18d/0x380 mm/page_alloc.c:5304
alloc_slab_page mm/slub.c:3266 [inline]
allocate_slab+0x79/0x5e0 mm/slub.c:3380
new_slab mm/slub.c:3426 [inline]
refill_objects+0x2d8/0x350 mm/slub.c:7310
refill_sheaf mm/slub.c:2804 [inline]
__pcs_replace_empty_main+0x330/0x690 mm/slub.c:4675
alloc_from_pcs mm/slub.c:4773 [inline]
slab_alloc_node mm/slub.c:4905 [inline]
__kmalloc_cache_noprof+0x468/0x6b0 mm/slub.c:5485
_kmalloc_noprof include/linux/slab.h:988 [inline]
_kzalloc_noprof include/linux/slab.h:1309 [inline]
rxrpc_service_prealloc+0x9f/0x130 net/rxrpc/call_accept.c:167
rxrpc_listen+0x1b7/0x360 net/rxrpc/af_rxrpc.c:237
afs_open_socket+0x2fa/0x3f0 fs/afs/rxrpc.c:107
afs_net_init+0x6ce/0x8f0 fs/afs/main.c:116
ops_init+0x35d/0x5d0 net/core/net_namespace.c:137
setup_net+0x118/0x350 net/core/net_namespace.c:446
copy_net_ns+0x4f9/0x720 net/core/net_namespace.c:579
create_new_namespaces+0x3f0/0x6b0 kernel/nsproxy.c:132
unshare_nsproxy_namespaces+0x149/0x190 kernel/nsproxy.c:234
page_owner free stack trace missing
Memory state around the buggy address:
ffff88811638bf80: fc fc fc fc fc fc fc fc fc fc fc fc fc fc fc fc
ffff88811638c000: fa fb fb fb fb fb fb fb fb fb fb fb fb fb fb fb
>ffff88811638c080: fb fb fb fb fb fb fb fb fb fb fb fb fb fb fb fb
^
ffff88811638c100: fb fb fb fb fb fb fb fb fb fb fb fb fb fb fb fb
ffff88811638c180: fb fb fb fb fb fb fb fb fb fb fb fb fb fb fb fb
==================================================================
The explanation of the root cause of the bug is:
The root cause of this KASAN slab-use-after-free is a reference count underflow in the error handling path of `gadgetfs_bind()`.
When fault injection forces a memory allocation failure during the binding process, `gadgetfs_bind()` incorrectly calls `gadgetfs_unbind()`, which drops a reference to the device (`dev`) that was never actually taken.
### Detailed Sequence of Events
1. **Initialization and Open:**
When the gadgetfs superblock is created, `dev_new()` allocates the `dev_data` structure (`dev`) and initializes its refcount to 1. When the user opens the `/tmp/gadgetfs/ep0` file, `gadget_dev_open()` is called, which increments the refcount to 2 via `get_dev(dev)`.
2. **Driver Registration and Binding:**
The user writes configuration data to `ep0`, invoking `dev_config()`. This function calls `usb_gadget_register_driver(&gadgetfs_driver)`, which registers the driver and attempts to bind it to the UDC, eventually calling `gadgetfs_bind()`.
3. **Fault Injection and Refcount Underflow:**
In `gadgetfs_bind()`, memory allocations are performed (e.g., `usb_ep_alloc_request()` or inside `activate_ep_files()`). If fault injection causes one of these to fail, the code jumps to the `enomem` label:
```c
// drivers/usb/gadget/legacy/inode.c : gadgetfs_bind()
dev->req = usb_ep_alloc_request (gadget->ep0, GFP_KERNEL);
if (!dev->req)
goto enomem;
...
if (activate_ep_files (dev) < 0)
goto enomem;
...
get_dev (dev); // <--- Reference is only taken on SUCCESS
return 0;
enomem:
gadgetfs_unbind (gadget);
return -ENOMEM;
```
The `enomem` path calls `gadgetfs_unbind()`, which performs cleanup and unconditionally calls `put_dev(dev)`. Because `get_dev(dev)` is only called at the very end of `gadgetfs_bind()` on success, `gadgetfs_unbind()` drops a reference that was never acquired. The refcount incorrectly drops from 2 to 1.
4. **Probe Failure Ignored:**
The `-ENOMEM` error propagates up to the driver core. However, `__driver_attach()` intentionally ignores probe failures and returns `0`. Because `gadgetfs_driver` does not set `match_existing_only = true`, `usb_gadget_register_driver_owner()` also returns `0` (success).
5. **Closing ep0 and Freeing:**
Seeing a success return value, `dev_config()` sets `dev->gadget_registered = true`.
Later, the reproducer closes the `ep0` file descriptor, triggering `dev_release()`. `dev_release()` cleans up and calls `put_dev(dev)`. The refcount drops from 1 to 0, and `dev` is prematurely freed.
6. **The Use-After-Free:**
Finally, the reproducer unmounts the filesystem, which invokes `gadgetfs_kill_sb()`.
```c
// drivers/usb/gadget/legacy/inode.c : gadgetfs_kill_sb()
if (the_device) {
put_dev (the_device); // <--- UAF here
the_device = NULL;
}
```
`gadgetfs_kill_sb()` attempts to drop the initial reference by calling `put_dev(the_device)`. Since `the_device` points to the `dev` structure that was already freed in step 5, this triggers the KASAN slab-use-after-free.
### Suggested Fix
`gadgetfs_bind()` should not call `gadgetfs_unbind()` in its error path, as `gadgetfs_unbind()` is designed to tear down a fully bound gadget (and thus expects the extra reference to exist). Instead, `gadgetfs_bind()` should manually clean up the specific state it altered before the failure occurred.
```c
enomem:
- gadgetfs_unbind (gadget);
+ if (dev->req) {
+ usb_ep_free_request (gadget->ep0, dev->req);
+ dev->req = NULL;
+ }
+ gadget->ep0->driver_data = NULL;
+ set_gadget_data (gadget, NULL);
+ dev->gadget = NULL;
return -ENOMEM;
```
*(Note: `activate_ep_files()` already perfectly cleans up after itself on failure, so no additional teardown is needed for it in `gadgetfs_bind()`'s error path).*
Reply: Based on a review of recent commits in the USB gadget subsystem, this type of bug—where an error path incorrectly calls a full teardown function (like `unbind`) on a partially initialized object—is a known and recurring pattern.
When a full teardown function is called during a failed initialization, it often assumes that all resources, locks, and reference counts were successfully acquired. If they were not, it leads to issues like the refcount underflow and subsequent use-after-free seen in this bug, or NULL pointer dereferences and double-frees.
### Prior Art and Similar Fixes
Recent history in the `drivers/usb/gadget/` tree shows several commits addressing the exact same class of error-path bugs:
1. **Refactoring to avoid unified teardown on error:**
In late 2025, a series of commits fixed similar issues across multiple gadget functions where a failed `bind` would jump to a unified error label that freed stale or uninitialized pointers (e.g., `notify_req`), causing NULL pointer dereferences.
- `47b2116e54b4 usb: gadget: f_acm: Refactor bind path to use __free()`
- `42988380ac67 usb: gadget: f_ecm: Refactor bind path to use __free()`
- `75a5b8d4ddd4 usb: gadget: f_ncm: Refactor bind path to use __free()`
- `082289414360 usb: gadget: f_rndis: Refactor bind path to use __free()`
**The fix strategy:** Instead of relying on a catch-all error label that cleans up struct members, the developers refactored the code to use the `__free()` automatic cleanup mechanism for local variables, only assigning them to the main structure upon complete success.
2. **Manual reverse-order cleanup:**
Another recent example is `62783c30d78a USB: gadget: f_hid: Fix memory leak in hidg_bind error path`. In this commit, the error handling was restructured to use proper `goto` cleanup labels (`fail_free_all`, `fail_free_descs`, `fail`).
**The fix strategy:** The commit ensured that allocated resources were properly freed manually in the exact reverse order of their allocation, rather than relying on a single cleanup path that might miss things or free things incorrectly.
3. **Stale state in `gadgetfs` itself:**
Historically, `drivers/usb/gadget/legacy/inode.c` has suffered from similar stale state issues on error paths. For example, in `add333a81a16 USB: gadgetfs: fix use-after-free bug`, an error path in `dev_config()` failed to clear a pointer (`dev->hs_config`), which was later dereferenced on a subsequent call, causing a use-after-free.
### Idioms and Patterns to Follow for the Fix
Based on how these similar bugs were addressed, the following idioms and rules should be followed when writing the fix for `gadgetfs_bind()`:
1. **Do not call `unbind()` from `bind()`'s error path:**
Functions like `gadgetfs_unbind()` are designed to tear down a *fully bound and initialized* gadget. They expect invariants (like the extra `get_dev()` reference) to be true. Calling them on a partially initialized object violates these invariants.
2. **Manual, localized cleanup:**
The error path in `gadgetfs_bind()` should manually undo *only* the specific state it successfully altered before the failure occurred. As suggested in your bug explanation, freeing the `dev->req` (if allocated) and clearing the pointers (`gadget->ep0->driver_data`, `dev->gadget`, etc.) is the correct historical pattern for this subsystem.
3. **Reverse order of initialization:**
If multiple allocations or state changes occur, the error path should clean them up in the exact reverse order of how they were set up, typically using cascading `goto` labels (though in `gadgetfs_bind()`, a single `enomem` label with localized `if` checks is sufficient since the setup is relatively short).
|
| 98/2 |
2026/08/07 05:43 |
llm |
history-explorer |
0m
Model:
Tokens:
input: 9303
output: 35
thoughts: 245
Thoughts: **Investigating a Refcount Underflow Bug in gadgetfs_bind()**
Okay, so I've got a tricky one here. It seems there's a potential refcount underflow in `drivers/usb/gadget/legacy/inode.c`, specifically around how `gadgetfs_bind()` and `gadgetfs_unbind()` interact on the error path. The core problem is that `gadgetfs_unbind()` expects `get_dev(dev)` to have been called, and the refcount increment, but it seems that's only happening on the successful path in `gadgetfs_bind()`. If an error occurs *before* that increment, `gadgetfs_unbind()` is going to decrement a refcount that was never increased, which is bad news bears.
The user, knowing my area of expertise, has asked me to dig into the prior art. They want me to find out if this kind of bug, with these kinds of underlying causes (missing locks, refcount issues, error path problems) has shown up before in this subsystem. I need to be efficient about it, so I'll limit the search to the past three years using `git-log`.
My focus will be on the files involved – specifically `drivers/usb/gadget/legacy/inode.c`. However, I need to be a bit broader in my search criteria. This kind of problem could also be hidden in `drivers/usb/gadget/` or even just in the generic `drivers/usb/` directory.
So, first I'll use `git-log` on `drivers/usb/gadget/legacy/inode.c` to look at recent commits there. That'll be a good starting point. I can then broaden the search if needed. This initial investigation should give me some insights into the code's history and if similar issues have been addressed before. Time to get to work.
|
| 99/2 |
2026/08/07 05:43 |
tool |
git-log |
0m
Args: map[PathPrefix:drivers/usb/gadget/legacy/inode.c Since:3 years]
Results: map[Output:bf4afc53b77a Convert 'alloc_obj' family to use the new default GFP_KERNEL argument
69050f8d6d07 treewide: Replace kmalloc with kmalloc_obj for non-scalar types
57db9d428ba5 convert gadgetfs
b65e3b11e923 gadgetfs: switch to simple_remove_by_name()
f99b3917789d fs: rename generic_delete_inode() and generic_drop_inode()
bad356bb50e6 functionfs, gadgetfs: use simple_recursive_removal()
bd3cf1a9396e USB: gadget: Replace deprecated strncpy() with strscpy()
5b6dc50e9ed8 USB: gadget: Use str_enable_disable-like helpers
cb787f4ac0c2 [tree-wide] finally take no_llseek out
69f73ca33d6b legacy: convert to new timestamp accessors
]
|
| 100/2 |
2026/08/07 05:43 |
llm |
history-explorer |
0m
Model:
Tokens:
input: 10242
output: 52
thoughts: 64
Thoughts: Okay, let's see what's going on.
**USB Gadget Driver Analysis: Identifying Potential Issues**
Hmm, `drivers/usb/gadget/legacy/inode.c` hasn't seen much activity in the way of bug fixes over the last three years. That makes me wonder if there are underlying issues we're not seeing surface actively. To get a broader perspective, I should expand my search scope. Let's cast a wider net and look at the entire `drivers/usb/gadget/` directory.
I'm thinking, specifically, about areas prone to subtle, hard-to-debug problems. Refcounting is always a good place to start – `refcount` itself. Then, I'll need to look at common memory safety issues, namely `use-after-free`, and of course the always tricky `error path` handling. Finally, I'll also want to examine anything related to the device `unbind` process, where resources might be mishandled when detaching and shutting down, which can often be a source of memory corruption. By focusing on these keywords and concepts, hopefully, I can identify areas that warrant a closer look.
|
| 101/2 |
2026/08/07 05:43 |
tool |
git-log |
0m
Args: map[MessageRegexps:[(refcount|use-after-free|error path|unbind)] PathPrefix:drivers/usb/gadget/ Since:3 years]
Results:
|
| 102/2 |
2026/08/07 05:43 |
llm |
history-explorer |
0m
Model:
Tokens:
input: 10617
output: 42
thoughts: 54
Thoughts: Okay, let's see...
**Debugging Thought Process: Narrowing Down the Search**
Hmm, I'm getting a hunch that the regex I was about to use might not be the most straightforward approach, or perhaps I'm overcomplicating things. Maybe the specific syntax isn't supported as I expect, or perhaps I should just try simpler, more direct terms. My mind immediately jumps to the classic suspects in memory corruption, especially when dealing with low-level kernel code – "refcount" issues, or the dreaded "use-after-free". That's often where the vulnerabilities hide in these sorts of subsystems.
Right, so instead of playing around with regex, let's keep it simple. My immediate focus will be on searching for "use-after-free" within the `drivers/usb/gadget/` directory. That seems like a pretty targeted and reasonable starting point, given the context we're dealing with. Let's see what that search turns up.
|
| 103/2 |
2026/08/07 05:43 |
tool |
git-log |
0m
Args: map[MessageRegexps:[use-after-free] PathPrefix:drivers/usb/gadget/ Since:3 years]
Results: map[Output:5650c18d93a1 usb: gadget: f_midi: cancel pending IN work before freeing the midi object
0583f2fbf8f8 usb: gadget: udc: bdc: free IRQ and drain func_wake_notify before teardown
79e2d75725c8 usb: gadget: f_tcm: synchronize delayed set_alt with teardown
67e511d2989e usb: gadget: udc: Fix use-after-free in gadget_match_driver
30adce93d5c4 usb: gadget: f_printer: take kref only for successful open
2796646f6d89 usb: gadget: f_fs: serialize DMABUF cancel against request completion
eba2936bbe6b usb: gadget: uvc: fix NULL pointer dereference during unbind race
0d6c8144ca4d usb: gadget: f_ncm: Fix atomic context locking issue
56a512a9b410 usb: gadget: f_ncm: align net_device lifecycle with bind/unbind
baeb66fbd420 usb: gadget: udc: fix use-after-free in usb_gadget_state_work
]
|
| 104/2 |
2026/08/07 05:43 |
llm |
history-explorer |
0m
Model:
Tokens:
input: 11230
output: 25
thoughts: 66
Thoughts: Okay, let's dive into these commits.
**Analyzing USB Gadget Driver Fixes**
Alright, I'm taking a look at `30adce93d5c4 usb: gadget: f_printer: take kref only for successful open` and `67e511d2989e usb: gadget: udc: Fix use-after-free in gadget_match_driver`. Seems like some core USB gadget driver issues are being addressed. The first commit, `30adce93d5c4`, is about the printer function driver. The description suggests there was likely a potential reference counting problem. Specifically, it appears the `kref` was being taken without ensuring a successful open, which is a classic resource management bug. This means the driver might have been prematurely incrementing the reference count, potentially leading to leaks or double frees if the open failed for any reason. I need to see how the code was structured to fully assess if the fix is sound and robust against different error conditions. The change probably ensures the `kref` is only incremented when the resource allocation is indeed successful. I need to determine how the open function reports the success/failure to properly evaluate the change.
Now, the second commit, `67e511d2989e`, that's a "use-after-free" bug in the `gadget_match_driver` function within the USB Device Controller (UDC) layer. Use-after-free bugs are nasty; they lead to unpredictable behavior and crashes. The title alone is enough to spike my interest. It implies that a pointer, likely to a data structure related to a gadget or driver, was being accessed *after* it had been freed, probably by the UDC. This is a very common vulnerability in C, and I need to see exactly where the reference count was lost and how they are addressing the memory management issues. The details of the code need close scrutiny to understand the context and how the fix truly resolves the race condition or incorrect memory handling that led to the vulnerability. I'm keen to examine the surrounding code to determine how this match function is used. The fix probably involved ensuring that any data referenced by the match function is properly managed via reference counting or careful checks on the lifetime of the objects involved. This will require understanding the lifetime of the devices, drivers, and gadgets involved.
|
| 105/2 |
2026/08/07 05:43 |
tool |
git-show |
0m
Args:
Results: map[Output:commit 30adce93d5c4a5a1ec29d9249e3fdfcc391d406b
Author: Xu Rao <raoxu@uniontech.com>
Date: Fri Jun 26 14:46:17 2026 +0800
usb: gadget: f_printer: take kref only for successful open
printer_open() returns -EBUSY when the character device is already
open, but it increments dev->kref regardless of the return value. VFS
does not call ->release() for a failed open, so every rejected second
open permanently leaks one reference.
Move kref_get() into the successful-open branch.
Fixes: e8d5f92b8d30 ("usb: gadget: function: printer: fix use-after-free in __lock_acquire")
Cc: stable <stable@kernel.org>
Signed-off-by: Xu Rao <raoxu@uniontech.com>
Link: https://patch.msgid.link/80295742B820DA9B+20260626064617.4090626-1-raoxu@uniontech.com
Signed-off-by: Greg Kroah-Hartman <gregkh@linuxfoundation.org>
diff --git a/drivers/usb/gadget/function/f_printer.c b/drivers/usb/gadget/function/f_printer.c
index e4f7828ae75d..837f753d0cae 100644
--- a/drivers/usb/gadget/function/f_printer.c
+++ b/drivers/usb/gadget/function/f_printer.c
@@ -363,12 +363,11 @@ printer_open(struct inode *inode, struct file *fd)
ret = 0;
/* Change the printer status to show that it's on-line. */
dev->printer_status |= PRINTER_SELECTED;
+ kref_get(&dev->kref);
}
spin_unlock_irqrestore(&dev->lock, flags);
- kref_get(&dev->kref);
-
return ret;
}
]
|
| 106/2 |
2026/08/07 05:43 |
llm |
history-explorer |
0m
Model:
Tokens:
input: 12410
output: 27
thoughts: 87
Thoughts: Okay, let's break this down.
**Fixing a Reference Mishap**
Right, so we're dealing with a reference counting issue, specifically the scenario where we're supposed to *only* increment the reference count on a successful operation. The problem boils down to a potential use-after-free situation because we're calling a cleanup function, in this case `gadgetfs_unbind`, which *expects* a reference to have been taken at some point. However, the logic allows for the cleanup to be invoked even if the initial reference wasn't successfully acquired. This is where things go sideways.
The code's going to trip over this if we don't handle the failure appropriately. That cleanup function shouldn't be called without the corresponding reference.
Let's dive into the details of that commit – `67e511d2989e usb: gadget: udc: Fix use-after-free in gadget_match_driver` – that should shed some light on the specific context and how it was resolved. That looks like the key to understanding the specific issue we are facing.
|
| 107/2 |
2026/08/07 05:43 |
tool |
git-show |
0m
Args:
Results: map[Output:commit 67e511d2989eb1c8c588b599ce2fcc6bb8e6f7ea
Author: Jimmy Hu <hhhuuu@google.com>
Date: Thu Jun 25 15:37:04 2026 +0800
usb: gadget: udc: Fix use-after-free in gadget_match_driver
The udc structure acts as the management structure for the gadget,
but their lifecycles are decoupled. A race condition exists where
usb_del_gadget() frees the udc memory (e.g., via mode-switch work)
while gadget_match_driver() concurrently accesses the freed udc memory
(e.g., via configfs), causing a Use-After-Free (UAF) that triggers a
NULL pointer dereference when the freed memory is zeroed:
[39430.908615][ T1171] Unable to handle kernel NULL pointer dereference at virtual address 0000000000000000
[39430.911397][ T1171] pc : __pi_strcmp+0x20/0x140
[39430.911441][ T1171] lr : gadget_match_driver+0x34/0x60
...
[39430.911890][ T1171] usb_gadget_register_driver_owner+0x50/0xf8
[39430.911910][ T1171] gadget_dev_desc_UDC_store+0xf4/0x140
[39430.931308][ T1171] configfs_write_iter+0xec/0x134
[39430.957058][ T1171] Workqueue: events_freezable __dwc3_set_mode
[39430.957287][ T1171] dwc3_gadget_exit+0x34/0x8c
[39430.957304][ T1171] __dwc3_set_mode+0xc0/0x664
Fix this by ensuring the udc structure remains allocated until the
gadget is released. To achieve this, introduce a new
usb_gadget_release() routine to the core. When the gadget is added,
usb_add_gadget() stores the gadget's release routine in the udc
structure and takes a reference to the udc. When the gadget is
released, usb_gadget_release() drops the reference to the udc and
then calls the gadget's release routine.
Suggested-by: Alan Stern <stern@rowland.harvard.edu>
Cc: stable <stable@kernel.org>
Signed-off-by: Jimmy Hu <hhhuuu@google.com>
Reviewed-by: Alan Stern <stern@rowland.harvard.edu>
Link: https://patch.msgid.link/20260625073705.803880-1-hhhuuu@google.com
Signed-off-by: Greg Kroah-Hartman <gregkh@linuxfoundation.org>
diff --git a/drivers/usb/gadget/udc/core.c b/drivers/usb/gadget/udc/core.c
index 60340ff9edbf..f6da12b553a0 100644
--- a/drivers/usb/gadget/udc/core.c
+++ b/drivers/usb/gadget/udc/core.c
@@ -31,8 +31,9 @@ static const struct bus_type gadget_bus_type;
/**
* struct usb_udc - describes one usb device controller
* @driver: the gadget driver pointer. For use by the class code
- * @dev: the child device to the actual controller
* @gadget: the gadget. For use by the class code
+ * @gadget_release: the gadget's release routine
+ * @dev: the child device to the actual controller
* @list: for use by the udc class driver
* @vbus: for udcs who care about vbus status, this value is real vbus status;
* for udcs who do not care about vbus status, this value is always true
@@ -53,6 +54,7 @@ static const struct bus_type gadget_bus_type;
struct usb_udc {
struct usb_gadget_driver *driver;
struct usb_gadget *gadget;
+ void (*gadget_release)(struct device *dev);
struct device dev;
struct list_head list;
bool vbus;
@@ -1362,6 +1364,17 @@ static void usb_udc_nop_release(struct device *dev)
dev_vdbg(dev, "%s\n", __func__);
}
+static void usb_gadget_release(struct device *dev)
+{
+ struct usb_gadget *gadget = dev_to_usb_gadget(dev);
+ struct usb_udc *udc = gadget->udc;
+ /* Cache the gadget's release routine to prevent UAF */
+ void (*release)(struct device *dev) = udc->gadget_release;
+
+ put_device(&udc->dev);
+ release(dev);
+}
+
/**
* usb_initialize_gadget - initialize a gadget and its embedded struct device
* @parent: the parent device to this udc. Usually the controller driver's
@@ -1418,6 +1431,14 @@ int usb_add_gadget(struct usb_gadget *gadget)
mutex_init(&udc->connect_lock);
udc->started = false;
+ /*
+ * Align decoupled lifecycles: take a UDC reference to ensure it
+ * remains allocated until the gadget is released, requiring an
+ * override of the gadget's release routine to drop it.
+ */
+ udc->gadget_release = gadget->dev.release;
+ gadget->dev.release = usb_gadget_release;
+ get_device(&udc->dev);
mutex_lock(&udc_lock);
list_add_tail(&udc->list, &udc_list);
@@ -1462,6 +1483,12 @@ int usb_add_gadget(struct usb_gadget *gadget)
mutex_lock(&udc_lock);
list_del(&udc->list);
mutex_unlock(&udc_lock);
+ /*
+ * Revert the override and drop the UDC reference to prevent
+ * leaking the UDC if the gadget was statically allocated.
+ */
+ gadget->dev.release = udc->gadget_release;
+ put_device(&udc->dev);
err_put_udc:
put_device(&udc->dev);
]
|
| 108/2 |
2026/08/07 05:43 |
llm |
history-explorer |
0m
Model:
Tokens:
input: 14570
output: 39
thoughts: 43
Thoughts: Okay, here's my take on those thoughts, framed as an expert's internal monologue:
**Navigating a Tricky Lifecycle Alignment Issue**
Alright, this isn't the usual kind of problem; this feels like a lifecycle alignment issue. I need to figure out how these components are interacting, especially during error handling or resource cleanup within the USB gadget driver framework. It's often a tangled web of dependencies and refcounting, making debugging a nightmare.
My initial approach will be targeted. Let's dig into the `drivers/usb/gadget/` directory. I'll do a quick `git grep` looking for commits that specifically mention "error path," because that's always a good starting point for understanding how potential failures are handled. And while I'm at it, I'll incorporate a search for keywords like "refcount" or "unbind," because those are key indicators of resource management and potential leak areas in a USB gadget driver, which can lead to instability if handled incorrectly. This targeted approach is much more efficient than a broad sweep. I need to understand the historical context of these keywords and how the developers previously approached these types of challenges in the code base. Let's see what the commit history reveals...
|
| 109/2 |
2026/08/07 05:43 |
tool |
git-log |
0m
Args: map[MessageRegexps:[error path] PathPrefix:drivers/usb/gadget/ Since:3 years]
Results: map[Output:0583f2fbf8f8 usb: gadget: udc: bdc: free IRQ and drain func_wake_notify before teardown
29a142d3e8b3 USB: gadget: snps-udc: fix device name leak on probe failure
6b874d00c466 USB: gadget: fsl-udc: fix device name leak on probe failure
2796646f6d89 usb: gadget: f_fs: serialize DMABUF cancel against request completion
68aa70648b62 usb: gadget: uvc: hold opts->lock across XU walks in uvc_function_bind
c8547c74988e usb: gadget: net2280: Fix double free in probe error path
2c863dbbeac7 usb: gadget: f_hid: Add missing error code
782be79e4551 usb: gadget: lpc32xx_udc: fix clock imbalance in error path
62783c30d78a USB: gadget: f_hid: Fix memory leak in hidg_bind error path
725d1f1e338b usb: gadget: pxa25x_udc: Convert to platform remove callback returning void
]
|
| 110/2 |
2026/08/07 05:43 |
llm |
history-explorer |
0m
Model:
Tokens:
input: 15227
output: 27
thoughts: 37
Thoughts: Okay, let's dive into this `62783c30d78a` commit, "USB: gadget: f_hid: Fix memory leak in hidg_bind error path." Immediately, I'm thinking, "f_hid," that's the USB HID gadget driver. A memory leak in the `hidg_bind` error path? That's definitely worth investigating. I know the `hidg_bind` function is crucial for setting up the interface. A leak there could be nasty, potentially leading to resource exhaustion, instability, or even system crashes during device setup. I wonder what the specific error condition is that triggers the leak. Is it related to failure during descriptor setup, allocation issues, or something else entirely? The description strongly suggests that an allocation that happened during the initialization is not being freed if an error occurs. I will try to think of which allocations that are involved here, and where they are placed in the code. I need to understand the control flow during binding to pinpoint the exact location and root cause. The "error path" wording implies a potential `goto` statement or similar mechanism to handle error scenarios. It's usually the error handling that is the issue here. I need to track down the affected memory, the context where it should have been freed, and the precise circumstances under which it's being missed.
|
| 111/2 |
2026/08/07 05:43 |
tool |
git-show |
0m
Args:
Results: map[Output:commit 62783c30d78aecf9810dae46fd4d11420ad38b74
Author: Yuhao Jiang <danisjiang@gmail.com>
Date: Mon Jun 23 17:48:44 2025 +0800
USB: gadget: f_hid: Fix memory leak in hidg_bind error path
In hidg_bind(), if alloc_workqueue() fails after usb_assign_descriptors()
has successfully allocated the USB descriptors, the current error handling
does not call usb_free_all_descriptors() to free the allocated descriptors,
resulting in a memory leak.
Restructure the error handling by adding proper cleanup labels:
- fail_free_all: cleans up workqueue and descriptors
- fail_free_descs: cleans up descriptors only
- fail: original cleanup for earlier failures
This ensures that allocated resources are properly freed in reverse order
of their allocation, preventing the memory leak when alloc_workqueue() fails.
Fixes: a139c98f760ef ("USB: gadget: f_hid: Add GET_REPORT via userspace IOCTL")
Cc: stable@vger.kernel.org
Signed-off-by: Yuhao Jiang <danisjiang@gmail.com>
Link: https://lore.kernel.org/r/20250623094844.244977-1-danisjiang@gmail.com
Signed-off-by: Greg Kroah-Hartman <gregkh@linuxfoundation.org>
diff --git a/drivers/usb/gadget/function/f_hid.c b/drivers/usb/gadget/function/f_hid.c
index 97a62b926415..8e1d1e884050 100644
--- a/drivers/usb/gadget/function/f_hid.c
+++ b/drivers/usb/gadget/function/f_hid.c
@@ -1278,18 +1278,19 @@ static int hidg_bind(struct usb_configuration *c, struct usb_function *f)
if (!hidg->workqueue) {
status = -ENOMEM;
- goto fail;
+ goto fail_free_descs;
}
/* create char device */
cdev_init(&hidg->cdev, &f_hidg_fops);
status = cdev_device_add(&hidg->cdev, &hidg->dev);
if (status)
- goto fail_free_descs;
+ goto fail_free_all;
return 0;
-fail_free_descs:
+fail_free_all:
destroy_workqueue(hidg->workqueue);
+fail_free_descs:
usb_free_all_descriptors(f);
fail:
ERROR(f->config->cdev, "hidg_bind FAILED\n");
]
|
| 112/2 |
2026/08/07 05:43 |
llm |
history-explorer |
0m
Model:
Tokens:
input: 16342
output: 41
thoughts: 62
Thoughts: Okay, here's my interpretation of that thought process, tailored for an expert audience:
**Analyzing State Cleanup in `bind` Function**
Hmm, this is interesting. It looks like we're seeing a trend here, a pattern of directly manipulating specific state variables *before* a failure within the `bind` function occurs. Instead of relying on a dedicated cleanup mechanism like `unbind`, the code is meticulously undoing the changes made prior to the potential error. I need to understand this better. Let's dig into the `gadgetfs_bind` function. I think it's in `drivers/usb/gadget/legacy/inode.c`, right? Let's check there and see what's going on with that function. I need to understand why they're doing manual state restoration instead of `unbind` calls.
|
| 113/2 |
2026/08/07 05:43 |
tool |
codesearch-definition-source |
0m
Args: map[ContextFile:drivers/usb/gadget/legacy/inode.c Name:gadgetfs_bind]
Results: map[SourceCode:1668: static int gadgetfs_bind(struct usb_gadget *gadget,
1669: struct usb_gadget_driver *driver)
1670: {
1671: struct dev_data *dev = the_device;
1672:
1673: if (!dev)
1674: return -ESRCH;
1675: if (0 != strcmp (CHIP, gadget->name)) {
1676: pr_err("%s expected %s controller not %s\n",
1677: shortname, CHIP, gadget->name);
1678: return -ENODEV;
1679: }
1680:
1681: set_gadget_data (gadget, dev);
1682: dev->gadget = gadget;
1683: gadget->ep0->driver_data = dev;
1684:
1685: /* preallocate control response and buffer */
1686: dev->req = usb_ep_alloc_request (gadget->ep0, GFP_KERNEL);
1687: if (!dev->req)
1688: goto enomem;
1689: dev->req->context = NULL;
1690: dev->req->complete = epio_complete;
1691:
1692: if (activate_ep_files (dev) < 0)
1693: goto enomem;
1694:
1695: INFO (dev, "bound to %s driver\n", gadget->name);
1696: spin_lock_irq(&dev->lock);
1697: dev->state = STATE_DEV_UNCONNECTED;
1698: spin_unlock_irq(&dev->lock);
1699: get_dev (dev);
1700: return 0;
1701:
1702: enomem:
1703: gadgetfs_unbind (gadget);
1704: return -ENOMEM;
1705: }
SourceFile:drivers/usb/gadget/legacy/inode.c]
|
| 114/2 |
2026/08/07 05:43 |
llm |
history-explorer |
0m
Model:
Tokens:
input: 17206
output: 41
thoughts: 84
Thoughts: **Analyzing a Bug Report: A Refinement in Gadgetfs Binding**
Okay, so this bug report is pointing out a potential flaw in the error handling within `gadgetfs_bind()`. The core issue is that `gadgetfs_unbind()` seems to be getting called during a failure scenario within `gadgetfs_bind()`. That immediately raises a flag for me. The report specifically calls out that `gadgetfs_unbind()` is intended to manage a *fully bound* gadget, which implies a certain state has been reached. Calling it prematurely, especially during an error, feels wrong. The report anticipates an expectation of a certain reference existing which will not exist if `gadgetfs_unbind` is called when it is not expected. This suggests a potential double-free or reference count corruption issue – something I need to be very wary of. Instead of relying on `gadgetfs_unbind()` to handle the cleanup during error paths in `gadgetfs_bind()`, the suggestion is to manually handle the state that was altered before the error. This is a solid approach; it makes error recovery much more precise and predictable and avoids this potential edge case, but requires careful consideration as to the exact state which needs to be cleaned up. I need to take a closer look at the implementation of `gadgetfs_unbind`.
|
| 115/2 |
2026/08/07 05:43 |
tool |
codesearch-definition-source |
0m
Args: map[ContextFile:drivers/usb/gadget/legacy/inode.c Name:gadgetfs_unbind]
Results: map[SourceCode:1639: static void
1640: gadgetfs_unbind (struct usb_gadget *gadget)
1641: {
1642: struct dev_data *dev = get_gadget_data (gadget);
1643:
1644: DBG (dev, "%s\n", __func__);
1645:
1646: spin_lock_irq (&dev->lock);
1647: dev->state = STATE_DEV_UNBOUND;
1648: while (dev->udc_usage > 0) {
1649: spin_unlock_irq(&dev->lock);
1650: usleep_range(1000, 2000);
1651: spin_lock_irq(&dev->lock);
1652: }
1653: spin_unlock_irq (&dev->lock);
1654:
1655: destroy_ep_files (dev);
1656: gadget->ep0->driver_data = NULL;
1657: set_gadget_data (gadget, NULL);
1658:
1659: /* we've already been disconnected ... no i/o is active */
1660: if (dev->req)
1661: usb_ep_free_request (gadget->ep0, dev->req);
1662: DBG (dev, "%s done\n", __func__);
1663: put_dev (dev);
1664: }
SourceFile:drivers/usb/gadget/legacy/inode.c]
|
| 116/2 |
2026/08/07 05:43 |
llm |
history-explorer |
0m
Model:
Tokens:
input: 18047
output: 43
thoughts: 74
Thoughts: Okay, here's my understanding, thinking through this USB gadget driver issue:
**Analyzing `gadgetfs_unbind` and Potential Error Handling**
Alright, so `gadgetfs_unbind` definitely calls `put_dev(dev)`. That's the baseline. Now, I need to investigate if this function, or others, potentially in the legacy or function directories, may have encountered similar problems where the original code would call `unbind` on the error path. Perhaps the fix was to avoid calling `unbind` and perform manual cleanup instead. That's a good place to start my hunt for code patterns.
|
| 117/2 |
2026/08/07 05:44 |
tool |
git-log |
0m
Args: map[MessageRegexps:[unbind error path] PathPrefix:drivers/usb/gadget/ Since:3 years]
Results:
|
| 118/2 |
2026/08/07 05:44 |
llm |
history-explorer |
0m
Model:
Tokens:
input: 18306
output: 42
thoughts: 28
Thoughts: Okay, here's my interpretation of that thought process, as if it were my own:
**Investigating USB Gadget Driver Errors**
Alright, time to dive into this USB gadget issue. My first instinct, given the likely symptom and the nature of the problem, is to target the core of the USB gadget driver code. I'm going to start by searching within the `drivers/usb/gadget/` directory.
I'll be specifically looking for instances of `bind`, `unbind`, and "error path." The binding and unbinding functions are critical entry points, and errors related to these would be a primary suspect, likely indicating problems during device initialization, detachment, or configuration. "Error path" will be the key search term, as this will help me quickly identify the driver's error handling routines and associated logging/debugging code. I want to understand what the driver does when something goes wrong, and where those error conditions are handled. This should give me a good starting point for debugging. Let's see what we find!
|
| 119/2 |
2026/08/07 05:44 |
tool |
git-log |
0m
Args: map[MessageRegexps:[bind error] PathPrefix:drivers/usb/gadget/ Since:3 years]
Results: map[Output:f05847a216a7 USB: gadget: fsl-udc: drop misleading unbind sanity check
50246d57233a usb: udc: pxa: fix error handling
68aa70648b62 usb: gadget: uvc: hold opts->lock across XU walks in uvc_function_bind
2c863dbbeac7 usb: gadget: f_hid: Add missing error code
2ca9e46f8f1f USB: dummy-hcd: Fix interrupt synchronization error
616a63ff495d USB: dummy-hcd: Fix locking/synchronization error
0d6c8144ca4d usb: gadget: f_ncm: Fix atomic context locking issue
56a512a9b410 usb: gadget: f_ncm: align net_device lifecycle with bind/unbind
082289414360 usb: gadget: f_rndis: Refactor bind path to use __free()
42988380ac67 usb: gadget: f_ecm: Refactor bind path to use __free()
]
|
| 120/2 |
2026/08/07 05:44 |
llm |
history-explorer |
0m
Model:
Tokens:
input: 18881
output: 27
thoughts: 29
Thoughts: Okay, here's my interpretation of that commit message, expressed as my internal thought process:
**Diving into that USB Fix**
Alright, let's take a look at this commit: `50246d57233a usb: udc: pxa: fix error handling`. The first thing that jumps out is the subject line: "fix error handling." Right away, I'm thinking, "Good, we need robust error handling in all our drivers." USB device controllers, especially the PXA ones, can be notoriously sensitive. It's crucial we're not silently swallowing errors or, even worse, misinterpreting them and causing data corruption or even system instability.
I'm immediately assuming this patch is addressing a specific scenario. Maybe a race condition where an error is missed, or perhaps an incorrect return code being checked. I'll need to dig into the commit itself to figure out the exact nature of the bug and the proposed solution. I bet it involves properly checking return values from specific USB functions, possibly involving the driver's interaction with the hardware or the USB subsystem. I’m hoping it’s not too complicated, but I'm prepared for a deep dive if it's necessary. Let's see what they've done!
|
| 121/2 |
2026/08/07 05:44 |
tool |
git-show |
0m
Args:
Results: map[Output:commit 50246d57233ad3b3c5dab99001a84d93e3e3d8f0
Author: Arnd Bergmann <arnd@arndb.de>
Date: Tue May 26 12:47:52 2026 +0200
usb: udc: pxa: fix error handling
As Dan Carpenter points out, my recent change makes subtle
changes to the error handling that were not intended.
Move the warning print up so it does not get skipped in
case of an error, but handle -EPROBE_DEFER properly now.
Change the devm_gpiod_get() to the _optional variant, which
is in line with the intended behavior and the DT binding,
though this did not work previously.
Reported-by: Dan Carpenter <error27@gmail.com>
Link: https://lore.kernel.org/linux-usb/ag6-xhfFjb5NpXQz@stanley.mountain/
Fixes: 25bd55f46032 ("usb: udc: pxa: remove unused platform_data")
Signed-off-by: Arnd Bergmann <arnd@arndb.de>
Link: https://patch.msgid.link/20260526104810.3906090-1-arnd@kernel.org
Signed-off-by: Greg Kroah-Hartman <gregkh@linuxfoundation.org>
diff --git a/drivers/usb/gadget/udc/pxa27x_udc.c b/drivers/usb/gadget/udc/pxa27x_udc.c
index 640f81988c04..df5cca43afbe 100644
--- a/drivers/usb/gadget/udc/pxa27x_udc.c
+++ b/drivers/usb/gadget/udc/pxa27x_udc.c
@@ -2374,9 +2374,10 @@ static int pxa_udc_probe(struct platform_device *pdev)
struct pxa_udc *udc = &memory;
int retval = 0;
- udc->gpiod = devm_gpiod_get(&pdev->dev, NULL, GPIOD_ASIS);
+ udc->gpiod = devm_gpiod_get_optional(&pdev->dev, NULL, GPIOD_ASIS);
if (IS_ERR(udc->gpiod))
- return PTR_ERR(udc->gpiod);
+ return dev_err_probe(&pdev->dev, PTR_ERR(udc->gpiod),
+ "Couldn't find or request D+ gpio\n");
udc->regs = devm_platform_ioremap_resource(pdev, 0);
if (IS_ERR(udc->regs))
@@ -2395,11 +2396,6 @@ static int pxa_udc_probe(struct platform_device *pdev)
udc->transceiver = usb_get_phy(USB_PHY_TYPE_USB2);
}
- if (IS_ERR(udc->gpiod)) {
- dev_err(&pdev->dev, "Couldn't find or request D+ gpio : %ld\n",
- PTR_ERR(udc->gpiod));
- return PTR_ERR(udc->gpiod);
- }
if (udc->gpiod)
gpiod_direction_output(udc->gpiod, 0);
]
|
| 122/2 |
2026/08/07 05:44 |
llm |
history-explorer |
0m
Model:
Tokens:
input: 20148
output: 43
thoughts: 27
Thoughts: Okay, here's my summary, presented as a stream of thought:
**USB Gadget Driver Analysis - Keywords & Code Search**
Alright, nothing there in the initial prompt is useful. Time to get down to brass tacks. I need to understand how the USB gadget driver is functioning, so I need to find specific areas to search within the code. Let's start with a focused search. Given the typical flow of USB gadget drivers, I'm thinking about how resources are managed and how the driver interfaces with the underlying USB hardware. "refcount" is a good starting point – I need to see how the driver handles reference counting to ensure objects aren't freed prematurely, particularly around device binding and unbinding. Then, the word "bind" is crucial. This will likely reveal how the device is attached to the USB bus. Searching within `drivers/usb/gadget/` is the correct place to begin the investigation. From there, I can begin to unpack the core mechanics.
|
| 123/2 |
2026/08/07 05:44 |
tool |
git-log |
0m
Args: map[MessageRegexps:[refcount bind] PathPrefix:drivers/usb/gadget/ Since:3 years]
Results: map[Output:5650c18d93a1 usb: gadget: f_midi: cancel pending IN work before freeing the midi object
]
|
| 124/2 |
2026/08/07 05:44 |
llm |
history-explorer |
0m
Model:
Tokens:
input: 20467
output: 27
thoughts: 35
Thoughts: Okay, here's my interpretation of that commit message, thinking through it as an expert in embedded systems and USB gadget drivers:
**Analyzing a USB MIDI Driver Fix**
Alright, so the commit message says "Let's see `5650c18d93a1 usb: gadget: f_midi: cancel pending IN work before freeing the midi object`." That immediately flags a specific area for me: the `f_midi` gadget driver. This is a common situation, it is important to think what problem the person who wrote the comment was trying to fix. I know that driver, and it handles MIDI (Musical Instrument Digital Interface) data over USB. The title suggests a critical problem related to object lifecycle and asynchronous operations.
The phrase "cancel pending IN work" is the key. In USB, especially in gadget mode where the device is acting as a peripheral, data transfers aren't always immediate. Data comes and goes, and each side can signal the other by creating "work" to read or send it. In this case, "IN work" refers to an *incoming* transfer – the device (the MIDI gadget) is *receiving* data from the host (e.g., a computer) via the USB connection.
"Freeing the midi object" means the driver is about to release the memory it's using to represent the MIDI device, likely during device disconnect or when a particular MIDI endpoint is closed. The potential bug is obvious: if there are still *outstanding* IN transfers – work that has been initiated but not completed – and the driver frees the object managing those transfers, it's a recipe for disaster. This leads to a crash, data corruption, or undefined behavior. The driver will try to access freed memory, causing a mess.
The fix, therefore, is to *cancel* those pending IN transfers *before* freeing the MIDI object. This ensures that the system handles those incoming data packets gracefully, either completing the transfers properly or, if necessary, cleanly aborting them. The developer realized that they needed to write new "work" to cancel pending "work". This is a common and important pattern in asynchronous systems: always clean up your mess before exiting or freeing resources. It's a fundamental principle of avoiding race conditions and memory leaks in embedded systems.
|
| 125/2 |
2026/08/07 05:44 |
tool |
git-show |
0m
Args:
Results: map[Output:commit 5650c18d93a1db7e27cb5a40b394747eb4686d5b
Author: Fan Wu <fanwu01@zju.edu.cn>
Date: Thu Jul 9 15:07:17 2026 +0000
usb: gadget: f_midi: cancel pending IN work before freeing the midi object
The f_midi driver embeds a work item (midi->work) whose handler,
f_midi_in_work(), dereferences the enclosing struct f_midi through
container_of(). This work is armed from two sites: f_midi_complete(),
on a normal IN-endpoint completion, and f_midi_in_trigger(), on an ALSA
rawmidi output-stream start.
Neither f_midi_disable() nor f_midi_unbind() cancels midi->work.
f_midi_disable() only disables the endpoints and drains the in_req_fifo;
it does not synchronize the work item, and the sound card is released
asynchronously to the final free of the midi object.
The midi object is reference-counted (midi->free_ref) and is freed in
f_midi_free() only once both the usb_function reference and the rawmidi
private_data reference have been dropped. In f_midi_unbind(),
f_midi_disable() runs before the sound card is released, so while the
USB endpoints are already disabled the rawmidi device is still usable by
an open substream. A concurrent userspace write on such a substream can
reach f_midi_in_trigger() and queue midi->work again after
f_midi_disable() has returned. A work item armed this way may still be
pending when the last reference drops and f_midi_free() proceeds to
kfree(midi), letting f_midi_in_work() dereference the struct after it
has been freed, a use-after-free.
For this reason cancelling midi->work in f_midi_disable() would not be
sufficient: the ALSA trigger path can rearm the work after disable()
returns. Cancelling at the refcount-zero free site is the boundary
after which neither arming source can survive, because by then both
references that keep the midi object alive have been dropped: the USB
endpoints are already disabled and the rawmidi device has been released.
Fix this by calling cancel_work_sync(&midi->work) in the refcount-zero
block of f_midi_free(), before the embedded work_struct is freed along
with the rest of the structure. opts->lock is a sleeping mutex, so
calling cancel_work_sync() under it is permitted, and the handler takes
midi->transmit_lock rather than opts->lock, so no self-deadlock can
occur while it waits for a running instance of the work to finish.
This issue was found by an in-house static analysis tool.
Fixes: 8653d71ce3763 ("usb/gadget: f_midi: Replace tasklet with work")
Cc: stable <stable@kernel.org>
Assisted-by: Codex:gpt-5.5
Signed-off-by: Fan Wu <fanwu01@zju.edu.cn>
Link: https://patch.msgid.link/20260709150717.399083-1-fanwu01@zju.edu.cn
Signed-off-by: Greg Kroah-Hartman <gregkh@linuxfoundation.org>
diff --git a/drivers/usb/gadget/function/f_midi.c b/drivers/usb/gadget/function/f_midi.c
index 4d9e4bd700d8..fba8cf787d6c 100644
--- a/drivers/usb/gadget/function/f_midi.c
+++ b/drivers/usb/gadget/function/f_midi.c
@@ -1309,6 +1309,7 @@ static void f_midi_free(struct usb_function *f)
opts = container_of(f->fi, struct f_midi_opts, func_inst);
mutex_lock(&opts->lock);
if (!--midi->free_ref) {
+ cancel_work_sync(&midi->work);
kfree(midi->id);
kfifo_free(&midi->in_req_fifo);
kfree(midi);
]
|
| 126/2 |
2026/08/07 05:44 |
llm |
history-explorer |
0m
Model:
Tokens:
input: 22114
output: 27
thoughts: 46
Thoughts: Okay, here's my interpretation of those thoughts, presented as my own mental process, given my expertise:
**Diving into the Workqueue Cancellation Problem**
Alright, this looks like a workqueue cancellation issue – that's my initial assessment. I need to figure out what's causing this problem. I've seen these before.
Let's start by digging into this commit: `082289414360 usb: gadget: f_rndis: Refactor bind path to use __free()`. This could be the starting point. I want to see if this change introduced, or exposed, a problem related to how the gadget's RNDIS function is bound and unbound, specifically within the context of workqueue management. The "bind path to use __free()" phrase is particularly interesting. That could definitely be a spot to look at for how resources are managed, particularly as things are cleaned up during the cancellation process. I'm already anticipating some of the common workqueue pitfalls related to race conditions or improper synchronization that I need to keep in mind. Let's see if this refactoring sheds any light.
|
| 127/2 |
2026/08/07 05:44 |
tool |
git-show |
0m
Args:
Results: map[Output:commit 08228941436047bdcd35a612c1aec0912a29d8cd
Author: Kuen-Han Tsai <khtsai@google.com>
Date: Tue Sep 16 16:21:37 2025 +0800
usb: gadget: f_rndis: Refactor bind path to use __free()
After an bind/unbind cycle, the rndis->notify_req is left stale. If a
subsequent bind fails, the unified error label attempts to free this
stale request, leading to a NULL pointer dereference when accessing
ep->ops->free_request.
Refactor the error handling in the bind path to use the __free()
automatic cleanup mechanism.
Fixes: 45fe3b8e5342 ("usb ethernet gadget: split RNDIS function")
Cc: stable@kernel.org
Signed-off-by: Kuen-Han Tsai <khtsai@google.com>
Link: https://lore.kernel.org/r/20250916-ready-v1-6-4997bf277548@google.com
Signed-off-by: Greg Kroah-Hartman <gregkh@linuxfoundation.org>
Link: https://lore.kernel.org/r/20250916-ready-v1-6-4997bf277548@google.com
diff --git a/drivers/usb/gadget/function/f_rndis.c b/drivers/usb/gadget/function/f_rndis.c
index 7cec19d65fb5..7451e7cb7a85 100644
--- a/drivers/usb/gadget/function/f_rndis.c
+++ b/drivers/usb/gadget/function/f_rndis.c
@@ -19,6 +19,8 @@
#include <linux/atomic.h>
+#include <linux/usb/gadget.h>
+
#include "u_ether.h"
#include "u_ether_configfs.h"
#include "u_rndis.h"
@@ -662,6 +664,8 @@ rndis_bind(struct usb_configuration *c, struct usb_function *f)
struct usb_ep *ep;
struct f_rndis_opts *rndis_opts;
+ struct usb_os_desc_table *os_desc_table __free(kfree) = NULL;
+ struct usb_request *request __free(free_usb_request) = NULL;
if (!can_support_rndis(c))
return -EINVAL;
@@ -669,12 +673,9 @@ rndis_bind(struct usb_configuration *c, struct usb_function *f)
rndis_opts = container_of(f->fi, struct f_rndis_opts, func_inst);
if (cdev->use_os_string) {
- f->os_desc_table = kzalloc(sizeof(*f->os_desc_table),
- GFP_KERNEL);
- if (!f->os_desc_table)
+ os_desc_table = kzalloc(sizeof(*os_desc_table), GFP_KERNEL);
+ if (!os_desc_table)
return -ENOMEM;
- f->os_desc_n = 1;
- f->os_desc_table[0].os_desc = &rndis_opts->rndis_os_desc;
}
rndis_iad_descriptor.bFunctionClass = rndis_opts->class;
@@ -692,16 +693,14 @@ rndis_bind(struct usb_configuration *c, struct usb_function *f)
gether_set_gadget(rndis_opts->net, cdev->gadget);
status = gether_register_netdev(rndis_opts->net);
if (status)
- goto fail;
+ return status;
rndis_opts->bound = true;
}
us = usb_gstrings_attach(cdev, rndis_strings,
ARRAY_SIZE(rndis_string_defs));
- if (IS_ERR(us)) {
- status = PTR_ERR(us);
- goto fail;
- }
+ if (IS_ERR(us))
+ return PTR_ERR(us);
rndis_control_intf.iInterface = us[0].id;
rndis_data_intf.iInterface = us[1].id;
rndis_iad_descriptor.iFunction = us[2].id;
@@ -709,36 +708,30 @@ rndis_bind(struct usb_configuration *c, struct usb_function *f)
/* allocate instance-specific interface IDs */
status = usb_interface_id(c, f);
if (status < 0)
- goto fail;
+ return status;
rndis->ctrl_id = status;
rndis_iad_descriptor.bFirstInterface = status;
rndis_control_intf.bInterfaceNumber = status;
rndis_union_desc.bMasterInterface0 = status;
- if (cdev->use_os_string)
- f->os_desc_table[0].if_id =
- rndis_iad_descriptor.bFirstInterface;
-
status = usb_interface_id(c, f);
if (status < 0)
- goto fail;
+ return status;
rndis->data_id = status;
rndis_data_intf.bInterfaceNumber = status;
rndis_union_desc.bSlaveInterface0 = status;
- status = -ENODEV;
-
/* allocate instance-specific endpoints */
ep = usb_ep_autoconfig(cdev->gadget, &fs_in_desc);
if (!ep)
- goto fail;
+ return -ENODEV;
rndis->port.in_ep = ep;
ep = usb_ep_autoconfig(cdev->gadget, &fs_out_desc);
if (!ep)
- goto fail;
+ return -ENODEV;
rndis->port.out_ep = ep;
/* NOTE: a status/notification endpoint is, strictly speaking,
@@ -747,21 +740,19 @@ rndis_bind(struct usb_configuration *c, struct usb_function *f)
*/
ep = usb_ep_autoconfig(cdev->gadget, &fs_notify_desc);
if (!ep)
- goto fail;
+ return -ENODEV;
rndis->notify = ep;
- status = -ENOMEM;
-
/* allocate notification request and buffer */
- rndis->notify_req = usb_ep_alloc_request(ep, GFP_KERNEL);
- if (!rndis->notify_req)
- goto fail;
- rndis->notify_req->buf = kmalloc(STATUS_BYTECOUNT, GFP_KERNEL);
- if (!rndis->notify_req->buf)
- goto fail;
- rndis->notify_req->length = STATUS_BYTECOUNT;
- rndis->notify_req->context = rndis;
- rndis->notify_req->complete = rndis_response_complete;
+ request = usb_ep_alloc_request(ep, GFP_KERNEL);
+ if (!request)
+ return -ENOMEM;
+ request->buf = kmalloc(STATUS_BYTECOUNT, GFP_KERNEL);
+ if (!request->buf)
+ return -ENOMEM;
+ request->length = STATUS_BYTECOUNT;
+ request->context = rndis;
+ request->complete = rndis_response_complete;
/* support all relevant hardware speeds... we expect that when
* hardware is dual speed, all bulk-capable endpoints work at
@@ -778,7 +769,7 @@ rndis_bind(struct usb_configuration *c, struct usb_function *f)
status = usb_assign_descriptors(f, eth_fs_function, eth_hs_function,
eth_ss_function, eth_ss_function);
if (status)
- goto fail;
+ return status;
rndis->port.open = rndis_open;
rndis->port.close = rndis_close;
@@ -789,9 +780,18 @@ rndis_bind(struct usb_configuration *c, struct usb_function *f)
if (rndis->manufacturer && rndis->vendorID &&
rndis_set_param_vendor(rndis->params, rndis->vendorID,
rndis->manufacturer)) {
- status = -EINVAL;
- goto fail_free_descs;
+ usb_free_all_descriptors(f);
+ return -EINVAL;
+ }
+
+ if (cdev->use_os_string) {
+ os_desc_table[0].os_desc = &rndis_opts->rndis_os_desc;
+ os_desc_table[0].if_id = rndis_iad_descriptor.bFirstInterface;
+ f->os_desc_table = no_free_ptr(os_desc_table);
+ f->os_desc_n = 1;
+
}
+ rndis->notify_req = no_free_ptr(request);
/* NOTE: all that is done without knowing or caring about
* the network link ... which is unavailable to this code
@@ -802,21 +802,6 @@ rndis_bind(struct usb_configuration *c, struct usb_function *f)
rndis->port.in_ep->name, rndis->port.out_ep->name,
rndis->notify->name);
return 0;
-
-fail_free_descs:
- usb_free_all_descriptors(f);
-fail:
- kfree(f->os_desc_table);
- f->os_desc_n = 0;
-
- if (rndis->notify_req) {
- kfree(rndis->notify_req->buf);
- usb_ep_free_request(rndis->notify, rndis->notify_req);
- }
-
- ERROR(cdev, "%s: can't bind, err %d\n", f->name, status);
-
- return status;
}
void rndis_borrow_net(struct usb_function_instance *f, struct net_device *net)
]
|
| 128/2 |
2026/08/07 05:44 |
llm |
history-explorer |
0m
Model:
Tokens:
input: 25152
output: 26
thoughts: 91
Thoughts: Okay, here's my take on those thoughts, framed from my expert perspective:
**Addressing the Dangling Pointer Bug in RNDIS**
Alright, so this commit's zeroing in on a critical vulnerability – a classic dangling pointer scenario in the RNDIS module. What's happening is a failed bind operation leaves some stale pointers hanging around, specifically `rndis->notify_req`, which can then become a source of NULL pointer dereferences later on. We all know how insidious those can be.
The proposed solution, as I understand it, is pretty straightforward and elegant: leverage `__free()` directly, and only assign the pointer on the condition of a successful operation. This prevents the possibility of a premature pointer allocation that leaves the driver in a bad state if the bind goes south.
I should probably take a closer look at the refactoring in `42988380ac67 usb: gadget: f_ecm: Refactor bind path to use __free()`. I suspect it's likely a well-implemented approach. From past experiences, I know it is always best to perform deallocation operations as early as possible so that a consistent state can be ensured.
|
| 129/2 |
2026/08/07 05:44 |
tool |
git-show |
0m
Args:
Results: map[Output:commit 42988380ac67c76bb9dff8f77d7ef3eefd50b7b5
Author: Kuen-Han Tsai <khtsai@google.com>
Date: Tue Sep 16 16:21:36 2025 +0800
usb: gadget: f_ecm: Refactor bind path to use __free()
After an bind/unbind cycle, the ecm->notify_req is left stale. If a
subsequent bind fails, the unified error label attempts to free this
stale request, leading to a NULL pointer dereference when accessing
ep->ops->free_request.
Refactor the error handling in the bind path to use the __free()
automatic cleanup mechanism.
Fixes: da741b8c56d6 ("usb ethernet gadget: split CDC Ethernet function")
Cc: stable@kernel.org
Signed-off-by: Kuen-Han Tsai <khtsai@google.com>
Link: https://lore.kernel.org/r/20250916-ready-v1-5-4997bf277548@google.com
Signed-off-by: Greg Kroah-Hartman <gregkh@linuxfoundation.org>
Link: https://lore.kernel.org/r/20250916-ready-v1-5-4997bf277548@google.com
diff --git a/drivers/usb/gadget/function/f_ecm.c b/drivers/usb/gadget/function/f_ecm.c
index 027226325039..675d2bc538a4 100644
--- a/drivers/usb/gadget/function/f_ecm.c
+++ b/drivers/usb/gadget/function/f_ecm.c
@@ -8,6 +8,7 @@
/* #define VERBOSE_DEBUG */
+#include <linux/cleanup.h>
#include <linux/slab.h>
#include <linux/kernel.h>
#include <linux/module.h>
@@ -15,6 +16,8 @@
#include <linux/etherdevice.h>
#include <linux/string_choices.h>
+#include <linux/usb/gadget.h>
+
#include "u_ether.h"
#include "u_ether_configfs.h"
#include "u_ecm.h"
@@ -678,6 +681,7 @@ ecm_bind(struct usb_configuration *c, struct usb_function *f)
struct usb_ep *ep;
struct f_ecm_opts *ecm_opts;
+ struct usb_request *request __free(free_usb_request) = NULL;
if (!can_support_ecm(cdev->gadget))
return -EINVAL;
@@ -711,7 +715,7 @@ ecm_bind(struct usb_configuration *c, struct usb_function *f)
/* allocate instance-specific interface IDs */
status = usb_interface_id(c, f);
if (status < 0)
- goto fail;
+ return status;
ecm->ctrl_id = status;
ecm_iad_descriptor.bFirstInterface = status;
@@ -720,24 +724,22 @@ ecm_bind(struct usb_configuration *c, struct usb_function *f)
status = usb_interface_id(c, f);
if (status < 0)
- goto fail;
+ return status;
ecm->data_id = status;
ecm_data_nop_intf.bInterfaceNumber = status;
ecm_data_intf.bInterfaceNumber = status;
ecm_union_desc.bSlaveInterface0 = status;
- status = -ENODEV;
-
/* allocate instance-specific endpoints */
ep = usb_ep_autoconfig(cdev->gadget, &fs_ecm_in_desc);
if (!ep)
- goto fail;
+ return -ENODEV;
ecm->port.in_ep = ep;
ep = usb_ep_autoconfig(cdev->gadget, &fs_ecm_out_desc);
if (!ep)
- goto fail;
+ return -ENODEV;
ecm->port.out_ep = ep;
/* NOTE: a status/notification endpoint is *OPTIONAL* but we
@@ -746,20 +748,18 @@ ecm_bind(struct usb_configuration *c, struct usb_function *f)
*/
ep = usb_ep_autoconfig(cdev->gadget, &fs_ecm_notify_desc);
if (!ep)
- goto fail;
+ return -ENODEV;
ecm->notify = ep;
- status = -ENOMEM;
-
/* allocate notification request and buffer */
- ecm->notify_req = usb_ep_alloc_request(ep, GFP_KERNEL);
- if (!ecm->notify_req)
- goto fail;
- ecm->notify_req->buf = kmalloc(ECM_STATUS_BYTECOUNT, GFP_KERNEL);
- if (!ecm->notify_req->buf)
- goto fail;
- ecm->notify_req->context = ecm;
- ecm->notify_req->complete = ecm_notify_complete;
+ request = usb_ep_alloc_request(ep, GFP_KERNEL);
+ if (!request)
+ return -ENOMEM;
+ request->buf = kmalloc(ECM_STATUS_BYTECOUNT, GFP_KERNEL);
+ if (!request->buf)
+ return -ENOMEM;
+ request->context = ecm;
+ request->complete = ecm_notify_complete;
/* support all relevant hardware speeds... we expect that when
* hardware is dual speed, all bulk-capable endpoints work at
@@ -778,7 +778,7 @@ ecm_bind(struct usb_configuration *c, struct usb_function *f)
status = usb_assign_descriptors(f, ecm_fs_function, ecm_hs_function,
ecm_ss_function, ecm_ss_function);
if (status)
- goto fail;
+ return status;
/* NOTE: all that is done without knowing or caring about
* the network link ... which is unavailable to this code
@@ -788,20 +788,12 @@ ecm_bind(struct usb_configuration *c, struct usb_function *f)
ecm->port.open = ecm_open;
ecm->port.close = ecm_close;
+ ecm->notify_req = no_free_ptr(request);
+
DBG(cdev, "CDC Ethernet: IN/%s OUT/%s NOTIFY/%s\n",
ecm->port.in_ep->name, ecm->port.out_ep->name,
ecm->notify->name);
return 0;
-
-fail:
- if (ecm->notify_req) {
- kfree(ecm->notify_req->buf);
- usb_ep_free_request(ecm->notify, ecm->notify_req);
- }
-
- ERROR(cdev, "%s: can't bind, err %d\n", f->name, status);
-
- return status;
}
static inline struct f_ecm_opts *to_f_ecm_opts(struct config_item *item)
]
|
| 130/2 |
2026/08/07 05:44 |
llm |
history-explorer |
0m
Model:
Tokens:
input: 27491
output: 46
thoughts: 177
Thoughts: **Analyzing a Nasty Pointer Dereference**
Okay, here we go again. This is the exact pattern I've seen before! The commit message nails it: after a bind/unbind cycle, the `ecm->notify_req` pointer is left dangling. A failed subsequent bind then tries to free this stale request through a unified error label, but the `ep->ops->free_request` call on that dangling pointer causes a NULL pointer dereference. Classic.
The fix is smart. Either rely on the `__free()` automatic cleanup, which is usually preferable, or meticulously clean up the specific states that were changed *before* the bind failure occurred. Avoid that catch-all error label that could inadvertently trash important memory. It's a textbook approach to robust error handling.
Hold on, I see something else. The problem is actually within `gadgetfs_bind`, which incorrectly calls `gadgetfs_unbind` during its error path. It looks like `gadgetfs_unbind` is prematurely dropping a reference that was never actually acquired. I wonder, is this a symptom of other similar errors where `unbind` is incorrectly called in the bind's error handling paths? I should probably dig through the commit history for any similar fixes to see if this is a systemic issue.
|
| 131/2 |
2026/08/07 05:44 |
tool |
git-log |
0m
Args: map[MessageRegexps:[unbind bind error] PathPrefix:drivers/usb/gadget/ Since:3 years]
Results: map[Output:f05847a216a7 USB: gadget: fsl-udc: drop misleading unbind sanity check
2ca9e46f8f1f USB: dummy-hcd: Fix interrupt synchronization error
616a63ff495d USB: dummy-hcd: Fix locking/synchronization error
0d6c8144ca4d usb: gadget: f_ncm: Fix atomic context locking issue
56a512a9b410 usb: gadget: f_ncm: align net_device lifecycle with bind/unbind
082289414360 usb: gadget: f_rndis: Refactor bind path to use __free()
42988380ac67 usb: gadget: f_ecm: Refactor bind path to use __free()
47b2116e54b4 usb: gadget: f_acm: Refactor bind path to use __free()
75a5b8d4ddd4 usb: gadget: f_ncm: Refactor bind path to use __free()
6334b8e4553c usb: gadget: f_ncm: Fix UAF ncm object at re-bind after usb ep transport error
]
|
| 132/2 |
2026/08/07 05:44 |
llm |
history-explorer |
0m
Model:
Tokens:
input: 28287
output: 27
thoughts: 36
Thoughts: Okay, let's dive into this commit, `47b2116e54b4 usb: gadget: f_acm: Refactor bind path to use __free()`. I need to understand what's been changed, what the motivation was, and why this refactoring is considered beneficial. The title itself suggests a move towards better memory management, which is always a good sign in kernel development. Using `__free()` instead of whatever was there previously implies a more direct and potentially safer approach to deallocating resources, specifically within the `f_acm` gadget driver. My gut feeling is that we're likely addressing some subtle memory leak or, perhaps, ensuring more predictable deallocation behavior during error handling or device detachment. I'll need to look at the diff to confirm those assumptions, of course. Hopefully, this commit simplifies the code and improves robustness. I'll be looking for improved clarity and potentially reduced chances of dangling pointers. A well-written commit message will be key to understanding the full context. Let's dig in.
|
| 133/2 |
2026/08/07 05:44 |
tool |
git-show |
0m
Args:
Results: map[Output:commit 47b2116e54b4a854600341487e8b55249e926324
Author: Kuen-Han Tsai <khtsai@google.com>
Date: Tue Sep 16 16:21:35 2025 +0800
usb: gadget: f_acm: Refactor bind path to use __free()
After an bind/unbind cycle, the acm->notify_req is left stale. If a
subsequent bind fails, the unified error label attempts to free this
stale request, leading to a NULL pointer dereference when accessing
ep->ops->free_request.
Refactor the error handling in the bind path to use the __free()
automatic cleanup mechanism.
Unable to handle kernel NULL pointer dereference at virtual address 0000000000000020
Call trace:
usb_ep_free_request+0x2c/0xec
gs_free_req+0x30/0x44
acm_bind+0x1b8/0x1f4
usb_add_function+0xcc/0x1f0
configfs_composite_bind+0x468/0x588
gadget_bind_driver+0x104/0x270
really_probe+0x190/0x374
__driver_probe_device+0xa0/0x12c
driver_probe_device+0x3c/0x218
__device_attach_driver+0x14c/0x188
bus_for_each_drv+0x10c/0x168
__device_attach+0xfc/0x198
device_initial_probe+0x14/0x24
bus_probe_device+0x94/0x11c
device_add+0x268/0x48c
usb_add_gadget+0x198/0x28c
dwc3_gadget_init+0x700/0x858
__dwc3_set_mode+0x3cc/0x664
process_scheduled_works+0x1d8/0x488
worker_thread+0x244/0x334
kthread+0x114/0x1bc
ret_from_fork+0x10/0x20
Fixes: 1f1ba11b6494 ("usb gadget: issue notifications from ACM function")
Cc: stable@kernel.org
Signed-off-by: Kuen-Han Tsai <khtsai@google.com>
Link: https://lore.kernel.org/r/20250916-ready-v1-4-4997bf277548@google.com
Signed-off-by: Greg Kroah-Hartman <gregkh@linuxfoundation.org>
Link: https://lore.kernel.org/r/20250916-ready-v1-4-4997bf277548@google.com
diff --git a/drivers/usb/gadget/function/f_acm.c b/drivers/usb/gadget/function/f_acm.c
index 7061720b9732..106046e17c4e 100644
--- a/drivers/usb/gadget/function/f_acm.c
+++ b/drivers/usb/gadget/function/f_acm.c
@@ -11,12 +11,15 @@
/* #define VERBOSE_DEBUG */
+#include <linux/cleanup.h>
#include <linux/slab.h>
#include <linux/kernel.h>
#include <linux/module.h>
#include <linux/device.h>
#include <linux/err.h>
+#include <linux/usb/gadget.h>
+
#include "u_serial.h"
@@ -613,6 +616,7 @@ acm_bind(struct usb_configuration *c, struct usb_function *f)
struct usb_string *us;
int status;
struct usb_ep *ep;
+ struct usb_request *request __free(free_usb_request) = NULL;
/* REVISIT might want instance-specific strings to help
* distinguish instances ...
@@ -630,7 +634,7 @@ acm_bind(struct usb_configuration *c, struct usb_function *f)
/* allocate instance-specific interface IDs, and patch descriptors */
status = usb_interface_id(c, f);
if (status < 0)
- goto fail;
+ return status;
acm->ctrl_id = status;
acm_iad_descriptor.bFirstInterface = status;
@@ -639,43 +643,41 @@ acm_bind(struct usb_configuration *c, struct usb_function *f)
status = usb_interface_id(c, f);
if (status < 0)
- goto fail;
+ return status;
acm->data_id = status;
acm_data_interface_desc.bInterfaceNumber = status;
acm_union_desc.bSlaveInterface0 = status;
acm_call_mgmt_descriptor.bDataInterface = status;
- status = -ENODEV;
-
/* allocate instance-specific endpoints */
ep = usb_ep_autoconfig(cdev->gadget, &acm_fs_in_desc);
if (!ep)
- goto fail;
+ return -ENODEV;
acm->port.in = ep;
ep = usb_ep_autoconfig(cdev->gadget, &acm_fs_out_desc);
if (!ep)
- goto fail;
+ return -ENODEV;
acm->port.out = ep;
ep = usb_ep_autoconfig(cdev->gadget, &acm_fs_notify_desc);
if (!ep)
- goto fail;
+ return -ENODEV;
acm->notify = ep;
acm_iad_descriptor.bFunctionProtocol = acm->bInterfaceProtocol;
acm_control_interface_desc.bInterfaceProtocol = acm->bInterfaceProtocol;
/* allocate notification */
- acm->notify_req = gs_alloc_req(ep,
- sizeof(struct usb_cdc_notification) + 2,
- GFP_KERNEL);
- if (!acm->notify_req)
- goto fail;
+ request = gs_alloc_req(ep,
+ sizeof(struct usb_cdc_notification) + 2,
+ GFP_KERNEL);
+ if (!request)
+ return -ENODEV;
- acm->notify_req->complete = acm_cdc_notify_complete;
- acm->notify_req->context = acm;
+ request->complete = acm_cdc_notify_complete;
+ request->context = acm;
/* support all relevant hardware speeds... we expect that when
* hardware is dual speed, all bulk-capable endpoints work at
@@ -692,7 +694,9 @@ acm_bind(struct usb_configuration *c, struct usb_function *f)
status = usb_assign_descriptors(f, acm_fs_function, acm_hs_function,
acm_ss_function, acm_ss_function);
if (status)
- goto fail;
+ return status;
+
+ acm->notify_req = no_free_ptr(request);
dev_dbg(&cdev->gadget->dev,
"acm ttyGS%d: IN/%s OUT/%s NOTIFY/%s\n",
@@ -700,14 +704,6 @@ acm_bind(struct usb_configuration *c, struct usb_function *f)
acm->port.in->name, acm->port.out->name,
acm->notify->name);
return 0;
-
-fail:
- if (acm->notify_req)
- gs_free_req(acm->notify, acm->notify_req);
-
- ERROR(cdev, "%s/%p: can't bind, err %d\n", f->name, f, status);
-
- return status;
}
static void acm_unbind(struct usb_configuration *c, struct usb_function *f)
]
|
| 134/2 |
2026/08/07 05:44 |
llm |
history-explorer |
0m
Model:
Tokens:
input: 30807
output: 25
thoughts: 213
Thoughts: **Analyzing a Tricky USB Gadget Driver Bug**
Okay, so I'm looking at this issue, and it's strikingly familiar. The root of the problem seems to be the error handling in the `bind` function. Specifically, it's that damn shared error path. In this case, `bind` frees `acm->notify_req`, but if that pointer is stale from a previous `unbind`, we're dealing with a NULL pointer dereference, or worse, a double free. Classic. The immediate solution that springs to mind is to abandon this unified error label and opt for either `__free()` directly, or the old-fashioned manual cleanup of local variables before they're assigned to the struct. Much less prone to these kinds of race conditions.
Hmm, this reminds me of something else. Wait, in `gadgetfs_bind` they're calling `gadgetfs_unbind(gadget)` in the error path. And I believe that `gadgetfs_unbind` then calls `put_dev(dev)` on a reference that was never actually taken. Yes, similar to how `f_acm`, `f_ecm`, and `f_rndis` handle errors. The error paths in `bind` are trying to clean up things that weren’t fully initialized, or were left in a stale state from a previous operation. We really need to get this error handling under control.
I need to confirm if my suspicions are correct. I should check the commit `75a5b8d4ddd4 usb: gadget: f_ncm: Refactor bind path to use __free()` to see if I'm on the right track.
|
| 135/2 |
2026/08/07 05:44 |
tool |
git-show |
0m
Args:
Results: map[Output:commit 75a5b8d4ddd4eb6b16cb0b475d14ff4ae64295ef
Author: Kuen-Han Tsai <khtsai@google.com>
Date: Tue Sep 16 16:21:34 2025 +0800
usb: gadget: f_ncm: Refactor bind path to use __free()
After an bind/unbind cycle, the ncm->notify_req is left stale. If a
subsequent bind fails, the unified error label attempts to free this
stale request, leading to a NULL pointer dereference when accessing
ep->ops->free_request.
Refactor the error handling in the bind path to use the __free()
automatic cleanup mechanism.
Unable to handle kernel NULL pointer dereference at virtual address 0000000000000020
Call trace:
usb_ep_free_request+0x2c/0xec
ncm_bind+0x39c/0x3dc
usb_add_function+0xcc/0x1f0
configfs_composite_bind+0x468/0x588
gadget_bind_driver+0x104/0x270
really_probe+0x190/0x374
__driver_probe_device+0xa0/0x12c
driver_probe_device+0x3c/0x218
__device_attach_driver+0x14c/0x188
bus_for_each_drv+0x10c/0x168
__device_attach+0xfc/0x198
device_initial_probe+0x14/0x24
bus_probe_device+0x94/0x11c
device_add+0x268/0x48c
usb_add_gadget+0x198/0x28c
dwc3_gadget_init+0x700/0x858
__dwc3_set_mode+0x3cc/0x664
process_scheduled_works+0x1d8/0x488
worker_thread+0x244/0x334
kthread+0x114/0x1bc
ret_from_fork+0x10/0x20
Fixes: 9f6ce4240a2b ("usb: gadget: f_ncm.c added")
Cc: stable@kernel.org
Signed-off-by: Kuen-Han Tsai <khtsai@google.com>
Link: https://lore.kernel.org/r/20250916-ready-v1-3-4997bf277548@google.com
Signed-off-by: Greg Kroah-Hartman <gregkh@linuxfoundation.org>
Link: https://lore.kernel.org/r/20250916-ready-v1-3-4997bf277548@google.com
diff --git a/drivers/usb/gadget/function/f_ncm.c b/drivers/usb/gadget/function/f_ncm.c
index 58b0dd575af3..0148d60926dc 100644
--- a/drivers/usb/gadget/function/f_ncm.c
+++ b/drivers/usb/gadget/function/f_ncm.c
@@ -11,6 +11,7 @@
* Copyright (C) 2008 Nokia Corporation
*/
+#include <linux/cleanup.h>
#include <linux/kernel.h>
#include <linux/interrupt.h>
#include <linux/module.h>
@@ -20,6 +21,7 @@
#include <linux/string_choices.h>
#include <linux/usb/cdc.h>
+#include <linux/usb/gadget.h>
#include "u_ether.h"
#include "u_ether_configfs.h"
@@ -1436,18 +1438,18 @@ static int ncm_bind(struct usb_configuration *c, struct usb_function *f)
struct usb_ep *ep;
struct f_ncm_opts *ncm_opts;
+ struct usb_os_desc_table *os_desc_table __free(kfree) = NULL;
+ struct usb_request *request __free(free_usb_request) = NULL;
+
if (!can_support_ecm(cdev->gadget))
return -EINVAL;
ncm_opts = container_of(f->fi, struct f_ncm_opts, func_inst);
if (cdev->use_os_string) {
- f->os_desc_table = kzalloc(sizeof(*f->os_desc_table),
- GFP_KERNEL);
- if (!f->os_desc_table)
+ os_desc_table = kzalloc(sizeof(*os_desc_table), GFP_KERNEL);
+ if (!os_desc_table)
return -ENOMEM;
- f->os_desc_n = 1;
- f->os_desc_table[0].os_desc = &ncm_opts->ncm_os_desc;
}
mutex_lock(&ncm_opts->lock);
@@ -1459,16 +1461,15 @@ static int ncm_bind(struct usb_configuration *c, struct usb_function *f)
mutex_unlock(&ncm_opts->lock);
if (status)
- goto fail;
+ return status;
ncm_opts->bound = true;
us = usb_gstrings_attach(cdev, ncm_strings,
ARRAY_SIZE(ncm_string_defs));
- if (IS_ERR(us)) {
- status = PTR_ERR(us);
- goto fail;
- }
+ if (IS_ERR(us))
+ return PTR_ERR(us);
+
ncm_control_intf.iInterface = us[STRING_CTRL_IDX].id;
ncm_data_nop_intf.iInterface = us[STRING_DATA_IDX].id;
ncm_data_intf.iInterface = us[STRING_DATA_IDX].id;
@@ -1478,20 +1479,16 @@ static int ncm_bind(struct usb_configuration *c, struct usb_function *f)
/* allocate instance-specific interface IDs */
status = usb_interface_id(c, f);
if (status < 0)
- goto fail;
+ return status;
ncm->ctrl_id = status;
ncm_iad_desc.bFirstInterface = status;
ncm_control_intf.bInterfaceNumber = status;
ncm_union_desc.bMasterInterface0 = status;
- if (cdev->use_os_string)
- f->os_desc_table[0].if_id =
- ncm_iad_desc.bFirstInterface;
-
status = usb_interface_id(c, f);
if (status < 0)
- goto fail;
+ return status;
ncm->data_id = status;
ncm_data_nop_intf.bInterfaceNumber = status;
@@ -1500,35 +1497,31 @@ static int ncm_bind(struct usb_configuration *c, struct usb_function *f)
ecm_desc.wMaxSegmentSize = cpu_to_le16(ncm_opts->max_segment_size);
- status = -ENODEV;
-
/* allocate instance-specific endpoints */
ep = usb_ep_autoconfig(cdev->gadget, &fs_ncm_in_desc);
if (!ep)
- goto fail;
+ return -ENODEV;
ncm->port.in_ep = ep;
ep = usb_ep_autoconfig(cdev->gadget, &fs_ncm_out_desc);
if (!ep)
- goto fail;
+ return -ENODEV;
ncm->port.out_ep = ep;
ep = usb_ep_autoconfig(cdev->gadget, &fs_ncm_notify_desc);
if (!ep)
- goto fail;
+ return -ENODEV;
ncm->notify = ep;
- status = -ENOMEM;
-
/* allocate notification request and buffer */
- ncm->notify_req = usb_ep_alloc_request(ep, GFP_KERNEL);
- if (!ncm->notify_req)
- goto fail;
- ncm->notify_req->buf = kmalloc(NCM_STATUS_BYTECOUNT, GFP_KERNEL);
- if (!ncm->notify_req->buf)
- goto fail;
- ncm->notify_req->context = ncm;
- ncm->notify_req->complete = ncm_notify_complete;
+ request = usb_ep_alloc_request(ep, GFP_KERNEL);
+ if (!request)
+ return -ENOMEM;
+ request->buf = kmalloc(NCM_STATUS_BYTECOUNT, GFP_KERNEL);
+ if (!request->buf)
+ return -ENOMEM;
+ request->context = ncm;
+ request->complete = ncm_notify_complete;
/*
* support all relevant hardware speeds... we expect that when
@@ -1548,7 +1541,7 @@ static int ncm_bind(struct usb_configuration *c, struct usb_function *f)
status = usb_assign_descriptors(f, ncm_fs_function, ncm_hs_function,
ncm_ss_function, ncm_ss_function);
if (status)
- goto fail;
+ return status;
/*
* NOTE: all that is done without knowing or caring about
@@ -1561,23 +1554,18 @@ static int ncm_bind(struct usb_configuration *c, struct usb_function *f)
hrtimer_setup(&ncm->task_timer, ncm_tx_timeout, CLOCK_MONOTONIC, HRTIMER_MODE_REL_SOFT);
+ if (cdev->use_os_string) {
+ os_desc_table[0].os_desc = &ncm_opts->ncm_os_desc;
+ os_desc_table[0].if_id = ncm_iad_desc.bFirstInterface;
+ f->os_desc_table = no_free_ptr(os_desc_table);
+ f->os_desc_n = 1;
+ }
+ ncm->notify_req = no_free_ptr(request);
+
DBG(cdev, "CDC Network: IN/%s OUT/%s NOTIFY/%s\n",
ncm->port.in_ep->name, ncm->port.out_ep->name,
ncm->notify->name);
return 0;
-
-fail:
- kfree(f->os_desc_table);
- f->os_desc_n = 0;
-
- if (ncm->notify_req) {
- kfree(ncm->notify_req->buf);
- usb_ep_free_request(ncm->notify, ncm->notify_req);
- }
-
- ERROR(cdev, "%s: can't bind, err %d\n", f->name, status);
-
- return status;
}
static inline struct f_ncm_opts *to_f_ncm_opts(struct config_item *item)
]
|
| 136/2 |
2026/08/07 05:44 |
llm |
history-explorer |
0m
Model:
Tokens:
input: 34451
output: 27
thoughts: 493
Thoughts: **Analyzing USB Gadget Driver Error Handling**
Okay, so I'm looking at this bug report, and it immediately screams familiarity. It's the same fundamental issue plaguing `f_acm`, `f_ecm`, and `f_rndis`. The core problem seems to be stale data left behind after a bind/unbind cycle. Specifically, a structure's member is left pointing to a dangling pointer. If a subsequent bind fails, the unified error handling attempts to clean up this stale request, leading to a nasty NULL pointer dereference when it calls `ep->ops->free_request`. That's bad.
Now, let's zoom in on `gadgetfs_bind`. The bug lies in how it handles errors. If `gadgetfs_bind` fails, it calls `gadgetfs_unbind(gadget)`. Wait, isn't `gadgetfs_unbind` designed to tear down a fully bound gadget? It expects an extra reference that might not exist at this point. Essentially, it's like calling `unbind` when there's nothing to unbind or, even worse, using a catch-all cleanup label that assumes things have been fully initialized when they haven't. The bug report is spot-on: `gadgetfs_bind` needs to do its own cleanup, manually undoing the steps it took *before* the failure, rather than assuming a full teardown.
I'm thinking this echoes similar problems I've seen before. Let me pull up some relevant commits. Here's `62783c30d78a USB: gadget: f_hid: Fix memory leak in hidg_bind error path`. This commit specifically addresses the issue by restructuring the error handling and introducing proper cleanup labels. That means it ensures that allocated resources are freed in the reverse order of their allocation. Good practice, that.
And look at `082289414360 usb: gadget: f_rndis: Refactor bind path to use __free()`. This one explicitly mentions the same stale request issue I described earlier and, again, points to using `__free()` for a more robust and cleaner approach to error handling.
Finally, `56a512a9b410 usb: gadget: f_ncm: align net_device lifecycle with bind/unbind`. Let's see if this applies.
So, the takeaway is clear: We need to refactor the error handling in these bind paths. Using `__free()` or adding specific cleanup labels is the key. The current unified error path approach is brittle and prone to these memory corruption issues. Let's get to work on that.
|
| 137/2 |
2026/08/07 05:45 |
tool |
git-show |
0m
Args:
Results: map[Output:commit 56a512a9b4107079f68701e7d55da8507eb963d9
Author: Kuen-Han Tsai <khtsai@google.com>
Date: Tue Dec 30 18:13:16 2025 +0800
usb: gadget: f_ncm: align net_device lifecycle with bind/unbind
Currently, the net_device is allocated in ncm_alloc_inst() and freed in
ncm_free_inst(). This ties the network interface's lifetime to the
configuration instance rather than the USB connection (bind/unbind).
This decoupling causes issues when the USB gadget is disconnected where
the underlying gadget device is removed. The net_device can outlive its
parent, leading to dangling sysfs links and NULL pointer dereferences
when accessing the freed gadget device.
Problem 1: NULL pointer dereference on disconnect
Unable to handle kernel NULL pointer dereference at virtual address
0000000000000000
Call trace:
__pi_strlen+0x14/0x150
rtnl_fill_ifinfo+0x6b4/0x708
rtmsg_ifinfo_build_skb+0xd8/0x13c
rtmsg_ifinfo+0x50/0xa0
__dev_notify_flags+0x4c/0x1f0
dev_change_flags+0x54/0x70
do_setlink+0x390/0xebc
rtnl_newlink+0x7d0/0xac8
rtnetlink_rcv_msg+0x27c/0x410
netlink_rcv_skb+0x134/0x150
rtnetlink_rcv+0x18/0x28
netlink_unicast+0x254/0x3f0
netlink_sendmsg+0x2e0/0x3d4
Problem 2: Dangling sysfs symlinks
console:/ # ls -l /sys/class/net/ncm0
lrwxrwxrwx ... /sys/class/net/ncm0 ->
/sys/devices/platform/.../gadget.0/net/ncm0
console:/ # ls -l /sys/devices/platform/.../gadget.0/net/ncm0
ls: .../gadget.0/net/ncm0: No such file or directory
Move the net_device allocation to ncm_bind() and deallocation to
ncm_unbind(). This ensures the network interface exists only when the
gadget function is actually bound to a configuration.
To support pre-bind configuration (e.g., setting interface name or MAC
address via configfs), cache user-provided options in f_ncm_opts
using the gether_opts structure. Apply these cached settings to the
net_device upon creation in ncm_bind().
Preserve the use-after-free fix from commit 6334b8e4553c ("usb: gadget:
f_ncm: Fix UAF ncm object at re-bind after usb ep transport error").
Check opts->net in ncm_set_alt() and ncm_disable() to ensure
gether_disconnect() runs only if a connection was established.
Fixes: 40d133d7f542 ("usb: gadget: f_ncm: convert to new function interface with backward compatibility")
Cc: stable@kernel.org
Signed-off-by: Kuen-Han Tsai <khtsai@google.com>
Link: https://patch.msgid.link/20251230-ncm-refactor-v1-3-793e347bc7a7@google.com
Signed-off-by: Greg Kroah-Hartman <gregkh@linuxfoundation.org>
diff --git a/drivers/usb/gadget/function/f_ncm.c b/drivers/usb/gadget/function/f_ncm.c
index 0e38330271d5..e23adc132f88 100644
--- a/drivers/usb/gadget/function/f_ncm.c
+++ b/drivers/usb/gadget/function/f_ncm.c
@@ -83,6 +83,11 @@ static inline struct f_ncm *func_to_ncm(struct usb_function *f)
return container_of(f, struct f_ncm, port.func);
}
+static inline struct f_ncm_opts *func_to_ncm_opts(struct usb_function *f)
+{
+ return container_of(f->fi, struct f_ncm_opts, func_inst);
+}
+
/*-------------------------------------------------------------------------*/
/*
@@ -859,6 +864,7 @@ static int ncm_setup(struct usb_function *f, const struct usb_ctrlrequest *ctrl)
static int ncm_set_alt(struct usb_function *f, unsigned intf, unsigned alt)
{
struct f_ncm *ncm = func_to_ncm(f);
+ struct f_ncm_opts *opts = func_to_ncm_opts(f);
struct usb_composite_dev *cdev = f->config->cdev;
/* Control interface has only altsetting 0 */
@@ -881,12 +887,13 @@ static int ncm_set_alt(struct usb_function *f, unsigned intf, unsigned alt)
if (alt > 1)
goto fail;
- if (ncm->netdev) {
- DBG(cdev, "reset ncm\n");
- ncm->netdev = NULL;
- gether_disconnect(&ncm->port);
- ncm_reset_values(ncm);
- }
+ scoped_guard(mutex, &opts->lock)
+ if (opts->net) {
+ DBG(cdev, "reset ncm\n");
+ opts->net = NULL;
+ gether_disconnect(&ncm->port);
+ ncm_reset_values(ncm);
+ }
/*
* CDC Network only sends data in non-default altsettings.
@@ -919,7 +926,8 @@ static int ncm_set_alt(struct usb_function *f, unsigned intf, unsigned alt)
net = gether_connect(&ncm->port);
if (IS_ERR(net))
return PTR_ERR(net);
- ncm->netdev = net;
+ scoped_guard(mutex, &opts->lock)
+ opts->net = net;
}
spin_lock(&ncm->lock);
@@ -1366,14 +1374,16 @@ static int ncm_unwrap_ntb(struct gether *port,
static void ncm_disable(struct usb_function *f)
{
struct f_ncm *ncm = func_to_ncm(f);
+ struct f_ncm_opts *opts = func_to_ncm_opts(f);
struct usb_composite_dev *cdev = f->config->cdev;
DBG(cdev, "ncm deactivated\n");
- if (ncm->netdev) {
- ncm->netdev = NULL;
- gether_disconnect(&ncm->port);
- }
+ scoped_guard(mutex, &opts->lock)
+ if (opts->net) {
+ opts->net = NULL;
+ gether_disconnect(&ncm->port);
+ }
if (ncm->notify->enabled) {
usb_ep_disable(ncm->notify);
@@ -1433,39 +1443,44 @@ static int ncm_bind(struct usb_configuration *c, struct usb_function *f)
{
struct usb_composite_dev *cdev = c->cdev;
struct f_ncm *ncm = func_to_ncm(f);
+ struct f_ncm_opts *ncm_opts = func_to_ncm_opts(f);
struct usb_string *us;
int status = 0;
struct usb_ep *ep;
- struct f_ncm_opts *ncm_opts;
struct usb_os_desc_table *os_desc_table __free(kfree) = NULL;
+ struct net_device *netdev __free(free_gether_netdev) = NULL;
struct usb_request *request __free(free_usb_request) = NULL;
if (!can_support_ecm(cdev->gadget))
return -EINVAL;
- ncm_opts = container_of(f->fi, struct f_ncm_opts, func_inst);
-
if (cdev->use_os_string) {
os_desc_table = kzalloc(sizeof(*os_desc_table), GFP_KERNEL);
if (!os_desc_table)
return -ENOMEM;
}
- mutex_lock(&ncm_opts->lock);
- gether_set_gadget(ncm_opts->net, cdev->gadget);
- if (!ncm_opts->bound) {
- ncm_opts->net->mtu = (ncm_opts->max_segment_size - ETH_HLEN);
- status = gether_register_netdev(ncm_opts->net);
+ netdev = gether_setup_default();
+ if (IS_ERR(netdev))
+ return -ENOMEM;
+
+ scoped_guard(mutex, &ncm_opts->lock) {
+ gether_apply_opts(netdev, &ncm_opts->net_opts);
+ netdev->mtu = ncm_opts->max_segment_size - ETH_HLEN;
}
- mutex_unlock(&ncm_opts->lock);
+ gether_set_gadget(netdev, cdev->gadget);
+ status = gether_register_netdev(netdev);
if (status)
return status;
- ncm_opts->bound = true;
-
- ncm_string_defs[1].s = ncm->ethaddr;
+ /* export host's Ethernet address in CDC format */
+ status = gether_get_host_addr_cdc(netdev, ncm->ethaddr,
+ sizeof(ncm->ethaddr));
+ if (status < 12)
+ return -EINVAL;
+ ncm_string_defs[STRING_MAC_IDX].s = ncm->ethaddr;
us = usb_gstrings_attach(cdev, ncm_strings,
ARRAY_SIZE(ncm_string_defs));
@@ -1563,6 +1578,8 @@ static int ncm_bind(struct usb_configuration *c, struct usb_function *f)
f->os_desc_n = 1;
}
ncm->notify_req = no_free_ptr(request);
+ ncm->netdev = no_free_ptr(netdev);
+ ncm->port.ioport = netdev_priv(ncm->netdev);
DBG(cdev, "CDC Network: IN/%s OUT/%s NOTIFY/%s\n",
ncm->port.in_ep->name, ncm->port.out_ep->name,
@@ -1577,19 +1594,19 @@ static inline struct f_ncm_opts *to_f_ncm_opts(struct config_item *item)
}
/* f_ncm_item_ops */
-USB_ETHERNET_CONFIGFS_ITEM(ncm);
+USB_ETHER_OPTS_ITEM(ncm);
/* f_ncm_opts_dev_addr */
-USB_ETHERNET_CONFIGFS_ITEM_ATTR_DEV_ADDR(ncm);
+USB_ETHER_OPTS_ATTR_DEV_ADDR(ncm);
/* f_ncm_opts_host_addr */
-USB_ETHERNET_CONFIGFS_ITEM_ATTR_HOST_ADDR(ncm);
+USB_ETHER_OPTS_ATTR_HOST_ADDR(ncm);
/* f_ncm_opts_qmult */
-USB_ETHERNET_CONFIGFS_ITEM_ATTR_QMULT(ncm);
+USB_ETHER_OPTS_ATTR_QMULT(ncm);
/* f_ncm_opts_ifname */
-USB_ETHERNET_CONFIGFS_ITEM_ATTR_IFNAME(ncm);
+USB_ETHER_OPTS_ATTR_IFNAME(ncm);
static ssize_t ncm_opts_max_segment_size_show(struct config_item *item,
char *page)
@@ -1655,34 +1672,27 @@ static void ncm_free_inst(struct usb_function_instance *f)
struct f_ncm_opts *opts;
opts = container_of(f, struct f_ncm_opts, func_inst);
- if (opts->bound)
- gether_cleanup(netdev_priv(opts->net));
- else
- free_netdev(opts->net);
kfree(opts->ncm_interf_group);
kfree(opts);
}
static struct usb_function_instance *ncm_alloc_inst(void)
{
- struct f_ncm_opts *opts;
+ struct usb_function_instance *ret;
struct usb_os_desc *descs[1];
char *names[1];
struct config_group *ncm_interf_group;
- opts = kzalloc(sizeof(*opts), GFP_KERNEL);
+ struct f_ncm_opts *opts __free(kfree) = kzalloc(sizeof(*opts), GFP_KERNEL);
if (!opts)
return ERR_PTR(-ENOMEM);
+
+ opts->net = NULL;
opts->ncm_os_desc.ext_compat_id = opts->ncm_ext_compat_id;
+ gether_setup_opts_default(&opts->net_opts, "usb");
mutex_init(&opts->lock);
opts->func_inst.free_func_inst = ncm_free_inst;
- opts->net = gether_setup_default();
- if (IS_ERR(opts->net)) {
- struct net_device *net = opts->net;
- kfree(opts);
- return ERR_CAST(net);
- }
opts->max_segment_size = ETH_FRAME_LEN;
INIT_LIST_HEAD(&opts->ncm_os_desc.ext_prop);
@@ -1693,26 +1703,22 @@ static struct usb_function_instance *ncm_alloc_inst(void)
ncm_interf_group =
usb_os_desc_prepare_interf_dir(&opts->func_inst.group, 1, descs,
names, THIS_MODULE);
- if (IS_ERR(ncm_interf_group)) {
- ncm_free_inst(&opts->func_inst);
+ if (IS_ERR(ncm_interf_group))
return ERR_CAST(ncm_interf_group);
- }
opts->ncm_interf_group = ncm_interf_group;
- return &opts->func_inst;
+ ret = &opts->func_inst;
+ retain_and_null_ptr(opts);
+ return ret;
}
static void ncm_free(struct usb_function *f)
{
- struct f_ncm *ncm;
- struct f_ncm_opts *opts;
+ struct f_ncm_opts *opts = func_to_ncm_opts(f);
- ncm = func_to_ncm(f);
- opts = container_of(f->fi, struct f_ncm_opts, func_inst);
- kfree(ncm);
- mutex_lock(&opts->lock);
- opts->refcnt--;
- mutex_unlock(&opts->lock);
+ scoped_guard(mutex, &opts->lock)
+ opts->refcnt--;
+ kfree(func_to_ncm(f));
}
static void ncm_unbind(struct usb_configuration *c, struct usb_function *f)
@@ -1736,13 +1742,15 @@ static void ncm_unbind(struct usb_configuration *c, struct usb_function *f)
kfree(ncm->notify_req->buf);
usb_ep_free_request(ncm->notify, ncm->notify_req);
+
+ ncm->port.ioport = NULL;
+ gether_cleanup(netdev_priv(ncm->netdev));
}
static struct usb_function *ncm_alloc(struct usb_function_instance *fi)
{
struct f_ncm *ncm;
struct f_ncm_opts *opts;
- int status;
/* allocate and initialize one new instance */
ncm = kzalloc(sizeof(*ncm), GFP_KERNEL);
@@ -1750,22 +1758,12 @@ static struct usb_function *ncm_alloc(struct usb_function_instance *fi)
return ERR_PTR(-ENOMEM);
opts = container_of(fi, struct f_ncm_opts, func_inst);
- mutex_lock(&opts->lock);
- opts->refcnt++;
- /* export host's Ethernet address in CDC format */
- status = gether_get_host_addr_cdc(opts->net, ncm->ethaddr,
- sizeof(ncm->ethaddr));
- if (status < 12) { /* strlen("01234567890a") */
- kfree(ncm);
- mutex_unlock(&opts->lock);
- return ERR_PTR(-EINVAL);
- }
+ scoped_guard(mutex, &opts->lock)
+ opts->refcnt++;
spin_lock_init(&ncm->lock);
ncm_reset_values(ncm);
- ncm->port.ioport = netdev_priv(opts->net);
- mutex_unlock(&opts->lock);
ncm->port.is_fixed = true;
ncm->port.supports_multi_frame = true;
diff --git a/drivers/usb/gadget/function/u_ncm.h b/drivers/usb/gadget/function/u_ncm.h
index 49ec095cdb4b..d99330fe31e8 100644
--- a/drivers/usb/gadget/function/u_ncm.h
+++ b/drivers/usb/gadget/function/u_ncm.h
@@ -15,11 +15,13 @@
#include <linux/usb/composite.h>
+#include "u_ether.h"
+
struct f_ncm_opts {
struct usb_function_instance func_inst;
struct net_device *net;
- bool bound;
+ struct gether_opts net_opts;
struct config_group *ncm_interf_group;
struct usb_os_desc ncm_os_desc;
char ncm_ext_compat_id[16];
]
|
| 138/2 |
2026/08/07 05:45 |
llm |
history-explorer |
0m
Model:
Tokens:
input: 40555
output: 27
thoughts: 50
Thoughts: Okay, here's my interpretation of that commit message, expressed as my internal thought process:
**Reflecting on Net Device Lifecycle Alignment**
Alright, so this commit's about bringing the handling of the network device lifecycle into closer alignment with the bind and unbind operations. Good, that makes sense. It's usually a good practice to keep those operations tightly coupled. Prevents a lot of subtle, hard-to-debug issues. Let's delve in and see what's changed.
Specifically, I'm thinking about the implications, it seems we have to look at `29a142d3e8b3 USB: gadget: snps-udc: fix device name leak on probe failure`. It must have been an example which brought this to their attention. The phrase "device name leak on probe failure" immediately grabs my attention. That's precisely the kind of situation where lifecycle management needs to be spot-on. I'll need to see how the binding and unbinding procedures were related there. Was the name allocated too early? Released too late? Or maybe missed entirely? This will probably be a good case to study.
|
| 139/2 |
2026/08/07 05:45 |
tool |
git-show |
0m
Args:
Results: map[Output:commit 29a142d3e8b35ebc9e0bcc78f4bc26c9b6a9ac0b
Author: Johan Hovold <johan@kernel.org>
Date: Thu Jul 2 16:15:34 2026 +0200
USB: gadget: snps-udc: fix device name leak on probe failure
The gadget device name is set by UDC core when registering the gadget
and must not be set before to avoid leaking the name in intermediate
error paths (e.g. when detecting an older chip revision).
Fixes: 12ad0fcaf2fb ("usb: gadget: amd5536udc: let udc-core manage gadget->dev")
Cc: stable <stable@kernel.org>
Signed-off-by: Johan Hovold <johan@kernel.org>
Link: https://patch.msgid.link/20260702141536.90887-3-johan@kernel.org
Signed-off-by: Greg Kroah-Hartman <gregkh@linuxfoundation.org>
diff --git a/drivers/usb/gadget/udc/snps_udc_core.c b/drivers/usb/gadget/udc/snps_udc_core.c
index 0e0db68e0b27..d506f9d92bca 100644
--- a/drivers/usb/gadget/udc/snps_udc_core.c
+++ b/drivers/usb/gadget/udc/snps_udc_core.c
@@ -3133,7 +3133,6 @@ int udc_probe(struct udc *dev)
/* device struct setup */
dev->gadget.ops = &udc_ops;
- dev_set_name(&dev->gadget.dev, "gadget");
dev->gadget.name = name;
dev->gadget.max_speed = USB_SPEED_HIGH;
]
|
| 140/2 |
2026/08/07 05:45 |
llm |
history-explorer |
0m
Model:
Tokens:
input: 41402
output: 43
thoughts: 141
Thoughts: **USB Gadget Driver Error Path Investigation**
Okay, this isn't relevant, let's skip that. Time to go back to the source. I need to re-examine the `drivers/usb/gadget/` directory, specifically looking for the terms "error path" and "unbind."
Ah, right, the bug report...it's reminding me that `gadgetfs_bind()` shouldn't call `gadgetfs_unbind()` within its error handling. That's a classic mistake, given `gadgetfs_unbind()` assumes a fully bound gadget with the extra reference. Instead, the focus should be on *manually* cleaning up only the state modified *before* the failure, a critical distinction for robustness.
So, let's expand the search. I should look through `drivers/usb/gadget/` for any other commits where the phrases "manually clean up" or "error path" appear in conjunction with "unbind". That broader scope might reveal related patterns or best practices I can leverage. I know there have been similar issues in the past, a systematic review should provide the answers.
|
| 141/2 |
2026/08/07 05:45 |
tool |
git-log |
0m
Args: map[MessageRegexps:[unbind error path] PathPrefix:drivers/usb/gadget/ Since:3 years]
Results:
|
| 142/2 |
2026/08/07 05:45 |
llm |
history-explorer |
0m
Model:
Tokens:
input: 41836
output: 43
thoughts: 24
Thoughts: Here's my thought process, as if I were considering this search:
**Investigating a Potential USB Gadget Driver Issue**
Okay, I need to get to the bottom of this USB gadget issue. My initial hunch is that there might be a problem related to error handling or binding, given the symptoms. Let's see... if I were going to start poking around in the driver code, my first approach would be a targeted search within the USB gadget directory. The key terms I'd focus on initially are "error path" – because any robust driver needs graceful failure handling – and "bind," since that's where the device should be initially associated and started with its driver. I'll search for those strings within `drivers/usb/gadget/` and then start digging from there.
|
| 143/2 |
2026/08/07 05:45 |
tool |
git-log |
0m
Args: map[MessageRegexps:[error path bind] PathPrefix:drivers/usb/gadget/ Since:3 years]
Results: map[Output:68aa70648b62 usb: gadget: uvc: hold opts->lock across XU walks in uvc_function_bind
2c863dbbeac7 usb: gadget: f_hid: Add missing error code
62783c30d78a USB: gadget: f_hid: Fix memory leak in hidg_bind error path
]
|
| 144/2 |
2026/08/07 05:45 |
llm |
history-explorer |
0m
Model:
Tokens:
input: 42169
output: 27
thoughts: 219
Thoughts: **Addressing a Memory Leak in USB Descriptor Allocation**
Okay, let's revisit commit `62783c30d78a`. The core issue, as I understand it, is a memory leak in the `hidg_bind()` function. It seems that when `alloc_workqueue()` fails *after* `usb_assign_descriptors()` has already allocated the USB descriptors, the error handling doesn't properly free those descriptors. This leaves them stranded, leading to a memory leak, which is precisely what the bug report highlighted, stating that `gadgetfs_bind()` should manually clean up the state before failure.
The solution being proposed is a more structured error handling mechanism. We're introducing cleanup labels—`fail_free_all`, `fail_free_descs`, and `fail`—to ensure that resources are freed in the reverse order of their allocation. So, `fail_free_all` handles the workqueue and descriptors, `fail_free_descs` frees just the descriptors, and `fail` takes care of the earlier failures. This meticulous approach guarantees complete cleanup and, crucially, prevents the memory leak when `alloc_workqueue()` fails.
I also want to quickly check related commit `f05847a216a7 USB: gadget: fsl-udc: drop misleading unbind sanity check` and confirm there are no regressions.
|
| 145/2 |
2026/08/07 05:45 |
tool |
git-show |
0m
Args:
Results: map[Output:commit f05847a216a74e1f2387169a336edbc198960016
Author: Johan Hovold <johan@kernel.org>
Date: Thu Jul 2 16:15:36 2026 +0200
USB: gadget: fsl-udc: drop misleading unbind sanity check
The UDC pointer is set on successful probe and will never be NULL when
the driver is later unbound so drop the misleading sanity check (and
confused error message).
Signed-off-by: Johan Hovold <johan@kernel.org>
Link: https://patch.msgid.link/20260702141536.90887-5-johan@kernel.org
Signed-off-by: Greg Kroah-Hartman <gregkh@linuxfoundation.org>
diff --git a/drivers/usb/gadget/udc/fsl_udc_core.c b/drivers/usb/gadget/udc/fsl_udc_core.c
index c4761933ec86..20392409afb1 100644
--- a/drivers/usb/gadget/udc/fsl_udc_core.c
+++ b/drivers/usb/gadget/udc/fsl_udc_core.c
@@ -2539,12 +2539,6 @@ static void fsl_udc_remove(struct platform_device *pdev)
DECLARE_COMPLETION_ONSTACK(done);
- if (!udc_controller) {
- dev_err(&pdev->dev,
- "Driver still in use but removing anyhow\n");
- return;
- }
-
udc_controller->done = &done;
usb_del_gadget_udc(&udc_controller->gadget);
]
|
| 146/2 |
2026/08/07 05:45 |
llm |
history-explorer |
0m
Model:
Tokens:
input: 43213
output: 44
thoughts: 18
Thoughts: Okay, here's my summary, presented as a stream of thought:
**Focusing In: Refcount and Error Handling**
Alright, that initial information was a dead end – nothing to see there. Time to narrow the focus. My gut says the interesting bits are likely related to how the code handles resource management and potential failures. Let's dig deeper. I'm going to start by searching specifically for instances of "refcount" and "error path." Those two terms, together, usually point me directly to the heart of concurrency and resilience challenges. This feels like the right angle to approach this situation, so I'll begin there.
|
| 147/2 |
2026/08/07 05:45 |
tool |
git-log |
0m
Args: map[MessageRegexps:[refcount error path] PathPrefix:drivers/usb/gadget/ Since:3 years]
Results:
|
| 148/2 |
2026/08/07 05:45 |
llm |
history-explorer |
0m
Model:
Tokens:
input: 43422
output: 39
thoughts: 19
Thoughts: Here's my thought process, summarized as you requested:
**Investigating Potential Underflow Issues in USB Gadget Drivers**
Okay, so I need to dig into the USB gadget driver code to look for potential underflow issues. My initial thought is to target the `drivers/usb/gadget/` directory. That's the logical place to start, given the context. I'll use "underflow" as my primary search term to see what comes up. This should help me identify areas where data might be prematurely exhausted or not handled correctly, which is, essentially, the definition of an underflow in this context. I'm expecting to find instances related to buffer management, data transfer rates, and potentially error handling surrounding those aspects. Time to see what the search reveals!
|
| 149/2 |
2026/08/07 05:45 |
tool |
git-log |
0m
Args: map[MessageRegexps:[underflow] PathPrefix:drivers/usb/gadget/ Since:3 years]
Results: map[Output:6c5dbc104dad usb: gadget: composite: fix integer underflow in WebUSB GET_URL handling
8f993d30b95d usb: gadget: f_ncm: validate minimum block_len in ncm_unwrap_ntb()
202ad1aaca77 usb: gadget: tegra-xudc: fix PM use count underflow
]
|
| 150/2 |
2026/08/07 05:45 |
llm |
history-explorer |
0m
Model:
Tokens:
input: 43745
output: 24
thoughts: 32
Thoughts: Okay, let's dive into this commit, `202ad1aaca77 usb: gadget: tegra-xudc: fix PM use count underflow`. My immediate thought is, "Ah, another USB driver tweak." I know this area intimately – power management is always a potential minefield, especially in embedded systems like Tegra. An underflow in the PM use count? That’s concerning. It suggests the driver could be releasing a power reference it never acquired, potentially leading to incorrect power state transitions for the gadget controller (Tegra-XUDC). This could manifest as all sorts of USB connectivity issues: intermittent disconnects, device not being recognized, poor performance, or even complete system hangs. The impact of such a bug could be widespread, affecting a range of USB peripherals and use cases. I'll need to carefully examine the code in this commit to understand the specific scenario where the underflow was occurring. I'm keen to see the exact logic that manages the power references and how they were incorrectly handled. This usually happens in error handling paths or race conditions; it’s going to be interesting.
|
| 151/2 |
2026/08/07 05:45 |
tool |
git-show |
0m
Args:
Results: map[Output:commit 202ad1aaca777dc7fd24f459f5f808f5abd2bfda
Author: Russell King (Oracle) <rmk+kernel@armlinux.org.uk>
Date: Fri Aug 1 18:40:41 2025 +0100
usb: gadget: tegra-xudc: fix PM use count underflow
Upon resume from system suspend, the PM runtime core issues the
following warning:
tegra-xudc 3550000.usb: Runtime PM usage count underflow!
This is because tegra_xudc_resume() unconditionally calls
schedule_work(&xudc->usb_role_sw_work) whether or not anything has
changed, which causes tegra_xudc_device_mode_off() to be called
even when we're already in that mode.
Keep track of the current state of "device_mode", and only schedule
this work if it has changed from the hardware state on resume.
Signed-off-by: "Russell King (Oracle)" <rmk+kernel@armlinux.org.uk>
Link: https://lore.kernel.org/r/E1uhtkH-007KDZ-JT@rmk-PC.armlinux.org.uk
Signed-off-by: Greg Kroah-Hartman <gregkh@linuxfoundation.org>
diff --git a/drivers/usb/gadget/udc/tegra-xudc.c b/drivers/usb/gadget/udc/tegra-xudc.c
index 2957316fd3d0..1d3085cc9d22 100644
--- a/drivers/usb/gadget/udc/tegra-xudc.c
+++ b/drivers/usb/gadget/udc/tegra-xudc.c
@@ -502,6 +502,7 @@ struct tegra_xudc {
struct clk_bulk_data *clks;
bool device_mode;
+ bool current_device_mode;
struct work_struct usb_role_sw_work;
struct phy **usb3_phy;
@@ -715,6 +716,8 @@ static void tegra_xudc_device_mode_on(struct tegra_xudc *xudc)
phy_set_mode_ext(xudc->curr_utmi_phy, PHY_MODE_USB_OTG,
USB_ROLE_DEVICE);
+
+ xudc->current_device_mode = true;
}
static void tegra_xudc_device_mode_off(struct tegra_xudc *xudc)
@@ -725,6 +728,8 @@ static void tegra_xudc_device_mode_off(struct tegra_xudc *xudc)
dev_dbg(xudc->dev, "device mode off\n");
+ xudc->current_device_mode = false;
+
connected = !!(xudc_readl(xudc, PORTSC) & PORTSC_CCS);
reinit_completion(&xudc->disconnect_complete);
@@ -4044,10 +4049,10 @@ static int __maybe_unused tegra_xudc_resume(struct device *dev)
spin_lock_irqsave(&xudc->lock, flags);
xudc->suspended = false;
+ if (xudc->device_mode != xudc->current_device_mode)
+ schedule_work(&xudc->usb_role_sw_work);
spin_unlock_irqrestore(&xudc->lock, flags);
- schedule_work(&xudc->usb_role_sw_work);
-
pm_runtime_enable(dev);
return 0;
]
|
| 152/2 |
2026/08/07 05:45 |
llm |
history-explorer |
0m
Model:
Tokens:
input: 45046
output: 19
thoughts: 30
Thoughts: Okay, here's my take on those thoughts, framed as an expert's internal monologue:
**Investigating a Potential PM Runtime Usage Count Underflow**
Alright, this looks like a PM runtime usage count underflow. That's never good. We've got a potential issue with how the power management runtime is tracking device usage, and if it's underflowing, that means the counts are getting messed up, which could lead to devices being powered down prematurely or other unpredictable behavior.
First things first, I need to understand where this might be happening. "Refcount" and "error path" are going to be key terms here. I need to get a handle on all the places within the kernel where these are mentioned in the power management area. I'm thinking a broad search across the entire kernel source tree is the right approach. That should catch any obscure or potentially related areas. I'm going to start there and then refine my search as I get results. The goal is to identify all places where we're decrementing or manipulating reference counts related to power management and pinpoint potential error handling pathways. Hopefully, this reveals the root cause quickly.
|
| 153/2 |
2026/08/07 05:45 |
tool |
grepper |
0m
Args: map[Expression:refcount.*error path]
Results: map[Output:kernel/events/core.c=5808=static void _free_event(struct perf_event *event)
--
kernel/events/core.c-5834-/*
kernel/events/core.c:5835: * Used to free events which have a known refcount of 1, such as in error paths
kernel/events/core.c-5836- * of inherited events.
]
|
| 154/2 |
2026/08/07 05:45 |
llm |
history-explorer |
0m
Model:
Tokens:
input: 45445
output: 44
thoughts: 1015
Thoughts: **Analysis of the gadgetfs_bind() Error Path**
Okay, so here's the deal: I've got a handle on this `gadgetfs_bind()` issue. The core problem is that `gadgetfs_bind()` calls `gadgetfs_unbind()` in its error path. That's a textbook anti-pattern. `gadgetfs_unbind()` is designed to tear down a *fully bound* gadget, which means it expects the `dev` reference to be held. By calling it when `gadgetfs_bind()` has only partially initialized the device, we're dropping a reference that was never taken, and that's just begging for trouble – refcount underflow, double frees, or worse, NULL pointer dereferences.
This is a familiar scenario, and I've seen it pop up in the kernel's USB gadget subsystem before. I was thinking of examples, and I was reminded of a few recent fixes. The commits highlighted are instructive here:
* **62783c30d78a:** "USB: gadget: f\_hid: Fix memory leak in hidg\_bind error path." This one refactored the error handling by introducing cleanup labels, carefully cleaning up allocated resources in reverse allocation order. This is a solid approach.
* **082289414360, 42988380ac67, 47b2116e54b4, 75a5b8d4ddd4:** These commits ("usb: gadget: f\_rndis: Refactor bind path to use \_\_free()", "usb: gadget: f\_ecm: Refactor bind path to use \_\_free()", "usb: gadget: f\_acm: Refactor bind path to use \_\_free()", and "usb: gadget: f\_ncm: Refactor bind path to use \_\_free()", respectively) addressed a similar issue in the `bind` path of several USB gadget functions. They had a unified error label that tried to clean up stale requests, resulting in NULL pointer dereferences. The fix in these cases was to switch to the `__free()` automatic cleanup mechanism or perform manual cleanup of only what had been initialized.
The bug report hits the nail on the head: We shouldn't be calling the complete `unbind` or `cleanup` routine in the `bind` error path unless the object is fully bound. Instead, `gadgetfs_bind()` needs to surgically clean up only the resources it actually allocated or modified before the error occurred. That's the key. This is a common pattern in kernel development. You either use `goto` labels to explicitly manage cleanup in reverse allocation order or adopt the newer `__free()` mechanism to handle automatic cleanup.
So, the summary is this: The problem is the incorrect use of a full teardown function in an error path, leading to reference count issues. The fix is to refactor error handling to manually clean up only the initialized state or use the `__free()` mechanism. I'm going to double-check `drivers/usb/gadget/legacy/inode.c` for any related commits I might have missed, but I'm pretty confident in this analysis.
|
| 155/2 |
2026/08/07 05:45 |
tool |
git-log |
0m
Args: map[MessageRegexps:[error] PathPrefix:drivers/usb/gadget/legacy/inode.c Since:10 years]
Results: map[Output:4c3d8accdce2 usb: remove res2 argument from gadget code completions
3343f376d4ba usb: gadget: prevent a ternary sign expansion bug
87bed3d7d26c usb: gadget: Fix memleak in gadgetfs_fill_super
f16443a034c7 USB: gadgetfs, dummy-hcd, net2280: fix locking for callbacks
add333a81a16 USB: gadgetfs: fix use-after-free bug
faab50984fe6 USB: gadgetfs: fix unbounded memory allocation bug
]
|
| 156/2 |
2026/08/07 05:45 |
llm |
history-explorer |
0m
Model:
Tokens:
input: 47358
output: 25
thoughts: 28
Thoughts: Here's my analysis of the provided text, rendered as a first-person expert summary:
**Analyzing the USB Gadgetfs Fix**
Alright, let's dive into this commit, `add333a81a16 USB: gadgetfs: fix use-after-free bug`. The title alone suggests a critical vulnerability. Use-after-free bugs in kernel code are nasty – they can lead to data corruption, privilege escalation, or even complete system crashes. Gadgetfs, being a userspace-driven USB device emulation framework, is a prime target for these kinds of problems, especially considering its interaction with user-provided data. I need to understand the specifics of the bug to assess its potential impact and the effectiveness of the fix. I'll need to dig into the diffs, focusing on where data pointers are being managed, freed, and accessed. What object is it related to? What sequence of actions leads to the free? Is it due to races or logic errors? Is the fix complete, or does it leave any edge cases unaddressed? This commit title has my attention, definitely worthy of an in-depth review.
|
| 157/2 |
2026/08/07 05:46 |
tool |
git-show |
0m
Args:
Results: map[Output:commit add333a81a16abbd4f106266a2553677a165725f
Author: Alan Stern <stern@rowland.harvard.edu>
Date: Fri Dec 9 15:18:43 2016 -0500
USB: gadgetfs: fix use-after-free bug
Andrey Konovalov reports that fuzz testing with syzkaller causes a
KASAN use-after-free bug report in gadgetfs:
BUG: KASAN: use-after-free in gadgetfs_setup+0x208a/0x20e0 at addr ffff88003dfe5bf2
Read of size 2 by task syz-executor0/22994
CPU: 3 PID: 22994 Comm: syz-executor0 Not tainted 4.9.0-rc7+ #16
Hardware name: QEMU Standard PC (i440FX + PIIX, 1996), BIOS Bochs 01/01/2011
ffff88006df06a18 ffffffff81f96aba ffffffffe0528500 1ffff1000dbe0cd6
ffffed000dbe0cce ffff88006df068f0 0000000041b58ab3 ffffffff8598b4c8
ffffffff81f96828 1ffff1000dbe0ccd ffff88006df06708 ffff88006df06748
Call Trace:
<IRQ> [ 201.343209] [< inline >] __dump_stack lib/dump_stack.c:15
<IRQ> [ 201.343209] [<ffffffff81f96aba>] dump_stack+0x292/0x398 lib/dump_stack.c:51
[<ffffffff817e4dec>] kasan_object_err+0x1c/0x70 mm/kasan/report.c:159
[< inline >] print_address_description mm/kasan/report.c:197
[<ffffffff817e5080>] kasan_report_error+0x1f0/0x4e0 mm/kasan/report.c:286
[< inline >] kasan_report mm/kasan/report.c:306
[<ffffffff817e562a>] __asan_report_load_n_noabort+0x3a/0x40 mm/kasan/report.c:337
[< inline >] config_buf drivers/usb/gadget/legacy/inode.c:1298
[<ffffffff8322c8fa>] gadgetfs_setup+0x208a/0x20e0 drivers/usb/gadget/legacy/inode.c:1368
[<ffffffff830fdcd0>] dummy_timer+0x11f0/0x36d0 drivers/usb/gadget/udc/dummy_hcd.c:1858
[<ffffffff814807c1>] call_timer_fn+0x241/0x800 kernel/time/timer.c:1308
[< inline >] expire_timers kernel/time/timer.c:1348
[<ffffffff81482de6>] __run_timers+0xa06/0xec0 kernel/time/timer.c:1641
[<ffffffff814832c1>] run_timer_softirq+0x21/0x80 kernel/time/timer.c:1654
[<ffffffff84f4af8b>] __do_softirq+0x2fb/0xb63 kernel/softirq.c:284
The cause of the bug is subtle. The dev_config() routine gets called
twice by the fuzzer. The first time, the user data contains both a
full-speed configuration descriptor and a high-speed config
descriptor, causing dev->hs_config to be set. But it also contains an
invalid device descriptor, so the buffer containing the descriptors is
deallocated and dev_config() returns an error.
The second time dev_config() is called, the user data contains only a
full-speed config descriptor. But dev->hs_config still has the stale
pointer remaining from the first call, causing the routine to think
that there is a valid high-speed config. Later on, when the driver
dereferences the stale pointer to copy that descriptor, we get a
use-after-free access.
The fix is simple: Clear dev->hs_config if the passed-in data does not
contain a high-speed config descriptor.
Signed-off-by: Alan Stern <stern@rowland.harvard.edu>
Reported-by: Andrey Konovalov <andreyknvl@google.com>
Tested-by: Andrey Konovalov <andreyknvl@google.com>
CC: <stable@vger.kernel.org>
Signed-off-by: Felipe Balbi <felipe.balbi@linux.intel.com>
diff --git a/drivers/usb/gadget/legacy/inode.c b/drivers/usb/gadget/legacy/inode.c
index 01ed3bc0c3c8..f1ca33942607 100644
--- a/drivers/usb/gadget/legacy/inode.c
+++ b/drivers/usb/gadget/legacy/inode.c
@@ -1800,6 +1800,8 @@ dev_config (struct file *fd, const char __user *buf, size_t len, loff_t *ptr)
goto fail;
kbuf += total;
length -= total;
+ } else {
+ dev->hs_config = NULL;
}
/* could support multiple configs, using another encoding! */
]
|
| 158/2 |
2026/08/07 05:46 |
llm |
history-explorer |
0m
Model:
Tokens:
input: 49212
output: 25
thoughts: 49
Thoughts: Okay, here's my take on those thoughts, framed as an expert's internal monologue:
**Investigating a Subtle State Bug**
Alright, this looks like *another* one of those gnarly bugs that pops up around stale state when things go sideways. We’re talking about error paths, which always seem to be fertile ground for this kind of thing. Let's dig in and see what's happening.
My gut tells me this is related to how we handle state transitions when an error occurs, probably in a corner case. I'm going to have to check this out - specifically the commit `87bed3d7d26c usb: gadget: Fix memleak in gadgetfs_fill_super`. It seems like the best place to start. Let's see if this has a familiar ring to it... I'm hoping I can trace this back to the root cause of my specific issue, this could be a good find.
|
| 159/2 |
2026/08/07 05:46 |
tool |
git-show |
0m
Args:
Results: map[Output:commit 87bed3d7d26c974948a3d6e7176f304b2d41272b
Author: Zhang Qilong <zhangqilong3@huawei.com>
Date: Tue Nov 17 10:16:29 2020 +0800
usb: gadget: Fix memleak in gadgetfs_fill_super
usb_get_gadget_udc_name will alloc memory for CHIP
in "Enomem" branch. we should free it before error
returns to prevent memleak.
Fixes: 175f712119c57 ("usb: gadget: provide interface for legacy gadgets to get UDC name")
Reported-by: Hulk Robot <hulkci@huawei.com>
Acked-by: Alan Stern <stern@rowland.harvard.edu>
Signed-off-by: Zhang Qilong <zhangqilong3@huawei.com>
Link: https://lore.kernel.org/r/20201117021629.1470544-3-zhangqilong3@huawei.com
Cc: stable <stable@vger.kernel.org>
Signed-off-by: Greg Kroah-Hartman <gregkh@linuxfoundation.org>
diff --git a/drivers/usb/gadget/legacy/inode.c b/drivers/usb/gadget/legacy/inode.c
index 1b430b36d0a6..71e7d10dd76b 100644
--- a/drivers/usb/gadget/legacy/inode.c
+++ b/drivers/usb/gadget/legacy/inode.c
@@ -2039,6 +2039,9 @@ gadgetfs_fill_super (struct super_block *sb, struct fs_context *fc)
return 0;
Enomem:
+ kfree(CHIP);
+ CHIP = NULL;
+
return -ENOMEM;
}
]
|
| 160/2 |
2026/08/07 05:46 |
llm |
history-explorer |
0m
Model:
Tokens:
input: 50018
output: 940
thoughts: 443
Thoughts: **Analyzing the Bug and Formulating a Solution**
Okay, here we go again. This looks like yet another error path bug, and it's in the same file as the last one I looked at. Fortunately, I have enough information to get to the bottom of this and provide a solid answer.
Here's what I'm seeing: the root of the problem is a classic error path issue. A full teardown function, `gadgetfs_unbind`, is being called even when the object hasn't been completely initialized. This leads to a refcount underflow because the teardown function assumes the object is in a fully initialized state and that a reference was properly taken.
I've seen similar bugs fixed in the USB gadget subsystem recently. For example, in `f_acm`, `f_ecm`, `f_ncm`, and `f_rndis` (those commits are good references!), the `bind` functions were using a single error label to clean up everything, including resources like `notify_req`, which might not have been initialized at the point of the error, causing null pointer dereferences. The fix was to refactor the error handling. They either used the `__free()` automatic cleanup mechanism, or they manually cleaned up only the specific parts that were actually altered.
Another example that comes to mind is the `62783c30d78a` commit, which fixed a memory leak in `f_hid`. The key there was restructuring the error handling to use cleanup labels that freed resources in the reverse order of allocation, instead of relying on a single catch-all.
So, the general pattern I see here is to avoid using the full unbind/teardown function in the bind/init error path unless the object is completely initialized. The error path needs to focus on manually cleaning up only the resources that were successfully allocated or initialized up to the point where the error happened. This is best achieved using `goto` labels for reverse-order cleanup, or adopting the new `__free()` mechanism for local variables. In that case, you only assign them to the main structure when everything is fully successful.
I'll craft a clear response based on these findings.
|
| 161/1 |
2026/08/07 05:46 |
action |
kernel-scratch-checkouter |
0m
Results: map[KernelScratchSrc:/app/workdir/cache/tmp/tmp4208508807]
|
| 162/1 |
2026/08/07 05:46 |
loop |
|
21m
|
| 163/2 |
2026/08/07 05:46 |
iteration |
0 |
21m
|
| 164/3 |
2026/08/07 05:46 |
agent |
patch-generator |
1m
Model:
Instruction:
You are an experienced Linux kernel developer tasked with creating a fix for a kernel bug.
You will be given a crash report, an initial explanation of the root cause done by another
kernel expert, and a summary of how similar bugs were fixed in the past.
Use the codeeditor tool to do code edits.
Note: you will not see your changes when looking at the code using codesearch tools.
Use the patch-diff tool to review the modifications you applied.
Your final reply should contain explanation of what you did in the patch and why
(details not present in the initial explanation of the bug).
Your fix must not just prevent the given crash, but also be the best fix for the underlying
root cause from the software engineering point of view. There can be several ways to fix the
same bug. Consider alternatives, and pick the best one. For example, additional checks may be
added at different locations/functions, it's usually better to place them earlier in the
execution to avoid multiple checks at various locations later.
Frequently the same coding mistake is done in several locations in the source code.
Check if your fix should be extended/applied to similar cases around to fix other similar bugs.
But don't go too wide, don't try to fix problems kernel-wide, fix similar issues
in the same file only.
If you are changing post-conditions of a function, consider all callers of the functions,
and if they need to be updated to handle new post-conditions. For example, if you make
a function that previously never returned a NULL, return NULL, consider if callers
need to be updated to handle NULL return value.
Prefer calling several tools at the same time to save round-trips.
Prompt:
The crash that corresponds to the bug is:
udc dummy_udc.0: failed to start USB Gadget filesystem: -12
gadgetfs gadget.0: probe with driver gadgetfs failed with error -12
gadgetfs: bound to dummy_udc driver
==================================================================
BUG: KASAN: slab-use-after-free in instrument_atomic_read_write include/linux/instrumented.h:112 [inline]
BUG: KASAN: slab-use-after-free in atomic_fetch_sub_release include/linux/atomic/atomic-instrumented.h:400 [inline]
BUG: KASAN: slab-use-after-free in __refcount_sub_and_test include/linux/refcount.h:389 [inline]
BUG: KASAN: slab-use-after-free in __refcount_dec_and_test include/linux/refcount.h:432 [inline]
BUG: KASAN: slab-use-after-free in refcount_dec_and_test include/linux/refcount.h:450 [inline]
BUG: KASAN: slab-use-after-free in put_dev drivers/usb/gadget/legacy/inode.c:165 [inline]
BUG: KASAN: slab-use-after-free in gadgetfs_kill_sb+0x46/0x110 drivers/usb/gadget/legacy/inode.c:2104
Write of size 4 at addr ffff88811638c080 by task syz-executor169/5848
CPU: 0 UID: 0 PID: 5848 Comm: syz-executor169 Not tainted syzkaller #1 PREEMPT_{RT,(full)}
Hardware name: QEMU Standard PC (Q35 + ICH9, 2009), BIOS 1.16.3-debian-1.16.3-2 04/01/2014
Call Trace:
<TASK>
dump_stack_lvl+0xe8/0x150 lib/dump_stack.c:120
print_address_description+0x55/0x1e0 mm/kasan/report.c:378
print_report+0x58/0x70 mm/kasan/report.c:482
kasan_report+0x117/0x150 mm/kasan/report.c:595
check_region_inline mm/kasan/generic.c:-1 [inline]
kasan_check_range+0x264/0x2c0 mm/kasan/generic.c:200
instrument_atomic_read_write include/linux/instrumented.h:112 [inline]
atomic_fetch_sub_release include/linux/atomic/atomic-instrumented.h:400 [inline]
__refcount_sub_and_test include/linux/refcount.h:389 [inline]
__refcount_dec_and_test include/linux/refcount.h:432 [inline]
refcount_dec_and_test include/linux/refcount.h:450 [inline]
put_dev drivers/usb/gadget/legacy/inode.c:165 [inline]
gadgetfs_kill_sb+0x46/0x110 drivers/usb/gadget/legacy/inode.c:2104
deactivate_locked_super+0xbc/0x130 fs/super.c:477
cleanup_mnt+0x3d3/0x460 fs/namespace.c:1317
task_work_run+0x1d9/0x270 kernel/task_work.c:233
resume_user_mode_work include/linux/resume_user_mode.h:50 [inline]
__exit_to_user_mode_loop kernel/entry/common.c:70 [inline]
exit_to_user_mode_loop+0x1fa/0x730 kernel/entry/common.c:101
__exit_to_user_mode_prepare include/linux/irq-entry-common.h:207 [inline]
syscall_exit_to_user_mode_prepare include/linux/irq-entry-common.h:230 [inline]
syscall_exit_to_user_mode include/linux/entry-common.h:318 [inline]
do_syscall_64+0x353/0x580 arch/x86/entry/syscall_64.c:100
entry_SYSCALL_64_after_hwframe+0x77/0x7f
RIP: 0033:0x7f5f0e505027
Code: a2 c7 05 54 b0 08 00 00 00 00 00 eb 96 e8 a1 08 00 00 90 31 f6 e9 09 00 00 00 66 0f 1f 84 00 00 00 00 00 b8 a6 00 00 00 0f 05 <48> 3d 00 f0 ff ff 77 01 c3 48 c7 c2 d8 ff ff ff f7 d8 64 89 02 b8
RSP: 002b:00007ffcdf2b0288 EFLAGS: 00000246 ORIG_RAX: 00000000000000a6
RAX: 0000000000000000 RBX: 0000000000000003 RCX: 00007f5f0e505027
RDX: 0000000000000000 RSI: 0000000000000000 RDI: 00007f5f0e55f05e
RBP: 00007f5f0e55f05e R08: 0000000000000000 R09: 0000000000000000
R10: 0000000000000000 R11: 0000000000000246 R12: 00007f5f0e55f623
R13: 00007ffcdf2b03e0 R14: 00007f5f0e561510 R15: 00007f5f0e5614e8
</TASK>
Allocated by task 5848:
kasan_save_stack mm/kasan/common.c:57 [inline]
kasan_save_track+0x3e/0x80 mm/kasan/common.c:78
poison_kmalloc_redzone mm/kasan/common.c:398 [inline]
__kasan_kmalloc+0x93/0xb0 mm/kasan/common.c:415
kasan_kmalloc include/linux/kasan.h:263 [inline]
__kmalloc_cache_noprof+0x3d2/0x6b0 mm/slub.c:5489
_kmalloc_noprof include/linux/slab.h:988 [inline]
_kzalloc_noprof include/linux/slab.h:1309 [inline]
dev_new drivers/usb/gadget/legacy/inode.c:176 [inline]
gadgetfs_fill_super+0x27b/0x7a0 drivers/usb/gadget/legacy/inode.c:2054
vfs_get_super fs/super.c:1273 [inline]
get_tree_single+0xc0/0x150 fs/super.c:1300
vfs_get_tree+0x92/0x2a0 fs/super.c:1700
fc_mount fs/namespace.c:1198 [inline]
do_new_mount_fc fs/namespace.c:3765 [inline]
do_new_mount+0x319/0xdc0 fs/namespace.c:3841
do_mount fs/namespace.c:4174 [inline]
__do_sys_mount fs/namespace.c:4390 [inline]
__se_sys_mount+0x31d/0x420 fs/namespace.c:4367
do_syscall_x64 arch/x86/entry/syscall_64.c:63 [inline]
do_syscall_64+0x174/0x580 arch/x86/entry/syscall_64.c:94
entry_SYSCALL_64_after_hwframe+0x77/0x7f
Freed by task 5848:
kasan_save_stack mm/kasan/common.c:57 [inline]
kasan_save_track+0x3e/0x80 mm/kasan/common.c:78
kasan_save_free_info+0x40/0x50 mm/kasan/generic.c:584
poison_slab_object mm/kasan/common.c:253 [inline]
__kasan_slab_free+0x5c/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 mm/slub.c:6377 [inline]
kfree+0x1c5/0x6c0 mm/slub.c:6692
put_dev drivers/usb/gadget/legacy/inode.c:169 [inline]
dev_release+0x173/0x210 drivers/usb/gadget/legacy/inode.c:1215
__fput+0x42a/0xa80 fs/file_table.c:512
fput_close_sync+0x11f/0x240 fs/file_table.c:617
__do_sys_close fs/open.c:1511 [inline]
__se_sys_close fs/open.c:1496 [inline]
__x64_sys_close+0x7e/0x110 fs/open.c:1496
do_syscall_x64 arch/x86/entry/syscall_64.c:63 [inline]
do_syscall_64+0x174/0x580 arch/x86/entry/syscall_64.c:94
entry_SYSCALL_64_after_hwframe+0x77/0x7f
The buggy address belongs to the object at ffff88811638c000
which belongs to the cache kmalloc-1k of size 1024
The buggy address is located 128 bytes inside of
freed 1024-byte region [ffff88811638c000, ffff88811638c400)
The buggy address belongs to the physical page:
page: refcount:0 mapcount:0 mapping:0000000000000000 index:0xffff88811638d000 pfn:0x116388
head: order:3 mapcount:0 entire_mapcount:0 nr_pages_mapped:0 pincount:0
flags: 0x100000000000240(workingset|head|node=0|zone=2)
page_type: f5(slab)
raw: 0100000000000240 ffff888100041dc0 ffffea00044a8a10 ffffea00043da810
raw: ffff88811638d000 000000080010000f 00000000f5000000 0000000000000000
head: 0100000000000240 ffff888100041dc0 ffffea00044a8a10 ffffea00043da810
head: ffff88811638d000 000000080010000f 00000000f5000000 0000000000000000
head: 0100000000000003 fffffffffffffe01 00000000ffffffff 00000000ffffffff
head: 0000000000000000 0000000000000000 00000000ffffffff 0000000000000008
page dumped because: kasan: bad access detected
page_owner tracks the page as allocated
page last allocated via order 3, migratetype Unmovable, gfp_mask 0xd20c0(__GFP_IO|__GFP_FS|__GFP_NOWARN|__GFP_NORETRY|__GFP_COMP|__GFP_NOMEMALLOC), pid 5581, tgid 5581 (syz-executor), ts 49160475111, free_ts 0
set_page_owner include/linux/page_owner.h:32 [inline]
post_alloc_hook+0x1f9/0x250 mm/page_alloc.c:1859
prep_new_page mm/page_alloc.c:1867 [inline]
get_page_from_freelist+0x262a/0x26a0 mm/page_alloc.c:3946
__alloc_frozen_pages_noprof+0x18d/0x380 mm/page_alloc.c:5304
alloc_slab_page mm/slub.c:3266 [inline]
allocate_slab+0x79/0x5e0 mm/slub.c:3380
new_slab mm/slub.c:3426 [inline]
refill_objects+0x2d8/0x350 mm/slub.c:7310
refill_sheaf mm/slub.c:2804 [inline]
__pcs_replace_empty_main+0x330/0x690 mm/slub.c:4675
alloc_from_pcs mm/slub.c:4773 [inline]
slab_alloc_node mm/slub.c:4905 [inline]
__kmalloc_cache_noprof+0x468/0x6b0 mm/slub.c:5485
_kmalloc_noprof include/linux/slab.h:988 [inline]
_kzalloc_noprof include/linux/slab.h:1309 [inline]
rxrpc_service_prealloc+0x9f/0x130 net/rxrpc/call_accept.c:167
rxrpc_listen+0x1b7/0x360 net/rxrpc/af_rxrpc.c:237
afs_open_socket+0x2fa/0x3f0 fs/afs/rxrpc.c:107
afs_net_init+0x6ce/0x8f0 fs/afs/main.c:116
ops_init+0x35d/0x5d0 net/core/net_namespace.c:137
setup_net+0x118/0x350 net/core/net_namespace.c:446
copy_net_ns+0x4f9/0x720 net/core/net_namespace.c:579
create_new_namespaces+0x3f0/0x6b0 kernel/nsproxy.c:132
unshare_nsproxy_namespaces+0x149/0x190 kernel/nsproxy.c:234
page_owner free stack trace missing
Memory state around the buggy address:
ffff88811638bf80: fc fc fc fc fc fc fc fc fc fc fc fc fc fc fc fc
ffff88811638c000: fa fb fb fb fb fb fb fb fb fb fb fb fb fb fb fb
>ffff88811638c080: fb fb fb fb fb fb fb fb fb fb fb fb fb fb fb fb
^
ffff88811638c100: fb fb fb fb fb fb fb fb fb fb fb fb fb fb fb fb
ffff88811638c180: fb fb fb fb fb fb fb fb fb fb fb fb fb fb fb fb
==================================================================
The explanation of the root cause of the bug is:
The root cause of this KASAN slab-use-after-free is a reference count underflow in the error handling path of `gadgetfs_bind()`.
When fault injection forces a memory allocation failure during the binding process, `gadgetfs_bind()` incorrectly calls `gadgetfs_unbind()`, which drops a reference to the device (`dev`) that was never actually taken.
### Detailed Sequence of Events
1. **Initialization and Open:**
When the gadgetfs superblock is created, `dev_new()` allocates the `dev_data` structure (`dev`) and initializes its refcount to 1. When the user opens the `/tmp/gadgetfs/ep0` file, `gadget_dev_open()` is called, which increments the refcount to 2 via `get_dev(dev)`.
2. **Driver Registration and Binding:**
The user writes configuration data to `ep0`, invoking `dev_config()`. This function calls `usb_gadget_register_driver(&gadgetfs_driver)`, which registers the driver and attempts to bind it to the UDC, eventually calling `gadgetfs_bind()`.
3. **Fault Injection and Refcount Underflow:**
In `gadgetfs_bind()`, memory allocations are performed (e.g., `usb_ep_alloc_request()` or inside `activate_ep_files()`). If fault injection causes one of these to fail, the code jumps to the `enomem` label:
```c
// drivers/usb/gadget/legacy/inode.c : gadgetfs_bind()
dev->req = usb_ep_alloc_request (gadget->ep0, GFP_KERNEL);
if (!dev->req)
goto enomem;
...
if (activate_ep_files (dev) < 0)
goto enomem;
...
get_dev (dev); // <--- Reference is only taken on SUCCESS
return 0;
enomem:
gadgetfs_unbind (gadget);
return -ENOMEM;
```
The `enomem` path calls `gadgetfs_unbind()`, which performs cleanup and unconditionally calls `put_dev(dev)`. Because `get_dev(dev)` is only called at the very end of `gadgetfs_bind()` on success, `gadgetfs_unbind()` drops a reference that was never acquired. The refcount incorrectly drops from 2 to 1.
4. **Probe Failure Ignored:**
The `-ENOMEM` error propagates up to the driver core. However, `__driver_attach()` intentionally ignores probe failures and returns `0`. Because `gadgetfs_driver` does not set `match_existing_only = true`, `usb_gadget_register_driver_owner()` also returns `0` (success).
5. **Closing ep0 and Freeing:**
Seeing a success return value, `dev_config()` sets `dev->gadget_registered = true`.
Later, the reproducer closes the `ep0` file descriptor, triggering `dev_release()`. `dev_release()` cleans up and calls `put_dev(dev)`. The refcount drops from 1 to 0, and `dev` is prematurely freed.
6. **The Use-After-Free:**
Finally, the reproducer unmounts the filesystem, which invokes `gadgetfs_kill_sb()`.
```c
// drivers/usb/gadget/legacy/inode.c : gadgetfs_kill_sb()
if (the_device) {
put_dev (the_device); // <--- UAF here
the_device = NULL;
}
```
`gadgetfs_kill_sb()` attempts to drop the initial reference by calling `put_dev(the_device)`. Since `the_device` points to the `dev` structure that was already freed in step 5, this triggers the KASAN slab-use-after-free.
### Suggested Fix
`gadgetfs_bind()` should not call `gadgetfs_unbind()` in its error path, as `gadgetfs_unbind()` is designed to tear down a fully bound gadget (and thus expects the extra reference to exist). Instead, `gadgetfs_bind()` should manually clean up the specific state it altered before the failure occurred.
```c
enomem:
- gadgetfs_unbind (gadget);
+ if (dev->req) {
+ usb_ep_free_request (gadget->ep0, dev->req);
+ dev->req = NULL;
+ }
+ gadget->ep0->driver_data = NULL;
+ set_gadget_data (gadget, NULL);
+ dev->gadget = NULL;
return -ENOMEM;
```
*(Note: `activate_ep_files()` already perfectly cleans up after itself on failure, so no additional teardown is needed for it in `gadgetfs_bind()`'s error path).*
Historical context on how similar bugs were fixed in the past:
Based on a review of recent commits in the USB gadget subsystem, this type of bug—where an error path incorrectly calls a full teardown function (like `unbind`) on a partially initialized object—is a known and recurring pattern.
When a full teardown function is called during a failed initialization, it often assumes that all resources, locks, and reference counts were successfully acquired. If they were not, it leads to issues like the refcount underflow and subsequent use-after-free seen in this bug, or NULL pointer dereferences and double-frees.
### Prior Art and Similar Fixes
Recent history in the `drivers/usb/gadget/` tree shows several commits addressing the exact same class of error-path bugs:
1. **Refactoring to avoid unified teardown on error:**
In late 2025, a series of commits fixed similar issues across multiple gadget functions where a failed `bind` would jump to a unified error label that freed stale or uninitialized pointers (e.g., `notify_req`), causing NULL pointer dereferences.
- `47b2116e54b4 usb: gadget: f_acm: Refactor bind path to use __free()`
- `42988380ac67 usb: gadget: f_ecm: Refactor bind path to use __free()`
- `75a5b8d4ddd4 usb: gadget: f_ncm: Refactor bind path to use __free()`
- `082289414360 usb: gadget: f_rndis: Refactor bind path to use __free()`
**The fix strategy:** Instead of relying on a catch-all error label that cleans up struct members, the developers refactored the code to use the `__free()` automatic cleanup mechanism for local variables, only assigning them to the main structure upon complete success.
2. **Manual reverse-order cleanup:**
Another recent example is `62783c30d78a USB: gadget: f_hid: Fix memory leak in hidg_bind error path`. In this commit, the error handling was restructured to use proper `goto` cleanup labels (`fail_free_all`, `fail_free_descs`, `fail`).
**The fix strategy:** The commit ensured that allocated resources were properly freed manually in the exact reverse order of their allocation, rather than relying on a single cleanup path that might miss things or free things incorrectly.
3. **Stale state in `gadgetfs` itself:**
Historically, `drivers/usb/gadget/legacy/inode.c` has suffered from similar stale state issues on error paths. For example, in `add333a81a16 USB: gadgetfs: fix use-after-free bug`, an error path in `dev_config()` failed to clear a pointer (`dev->hs_config`), which was later dereferenced on a subsequent call, causing a use-after-free.
### Idioms and Patterns to Follow for the Fix
Based on how these similar bugs were addressed, the following idioms and rules should be followed when writing the fix for `gadgetfs_bind()`:
1. **Do not call `unbind()` from `bind()`'s error path:**
Functions like `gadgetfs_unbind()` are designed to tear down a *fully bound and initialized* gadget. They expect invariants (like the extra `get_dev()` reference) to be true. Calling them on a partially initialized object violates these invariants.
2. **Manual, localized cleanup:**
The error path in `gadgetfs_bind()` should manually undo *only* the specific state it successfully altered before the failure occurred. As suggested in your bug explanation, freeing the `dev->req` (if allocated) and clearing the pointers (`gadget->ep0->driver_data`, `dev->gadget`, etc.) is the correct historical pattern for this subsystem.
3. **Reverse order of initialization:**
If multiple allocations or state changes occur, the error path should clean them up in the exact reverse order of how they were set up, typically using cascading `goto` labels (though in `gadgetfs_bind()`, a single `enomem` label with localized `if` checks is sufficient since the setup is relatively short).
Reply: I have applied a fix to resolve the KASAN slab-use-after-free bug in `gadgetfs_kill_sb()`.
### Cause of the Bug
The root cause is a reference count underflow in the error handling path of `gadgetfs_bind()`. When `gadgetfs_bind()` encounters an error (e.g., memory allocation failure during `usb_ep_alloc_request()` or `activate_ep_files()`), it jumps to the `enomem` label and calls `gadgetfs_unbind()`.
However, `gadgetfs_unbind()` is designed to tear down a fully initialized and bound gadget. It unconditionally drops a device reference by calling `put_dev(dev)`. Since `gadgetfs_bind()` only acquires this reference (`get_dev(dev)`) at the very end of the function upon complete success, calling `gadgetfs_unbind()` in the error path drops a reference that was never taken. This leads to a premature free of the `dev` structure when the `ep0` file is closed, and subsequently a use-after-free when the filesystem is unmounted and `gadgetfs_kill_sb()` tries to drop the initial reference.
### The Fix
To fix this, we must stop calling the full teardown function `gadgetfs_unbind()` from the `gadgetfs_bind()` error path. Instead, we should manually and locally clean up only the specific state that was successfully altered before the failure occurred.
I replaced the call to `gadgetfs_unbind(gadget)` in the `enomem` label with localized cleanup:
1. Freeing `dev->req` if it was allocated, and setting it to `NULL`.
2. Clearing the pointers that were set earlier in the function (`gadget->ep0->driver_data`, `dev->gadget`, and `set_gadget_data(gadget, NULL)`).
Note that if `activate_ep_files()` fails, it already perfectly cleans up after itself by calling `destroy_ep_files()`, so no additional teardown is needed for it in `gadgetfs_bind()`'s error path.
This aligns with the historical pattern in the USB gadget subsystem of avoiding unified teardown functions on error paths in favor of manual, reverse-order cleanup of partially initialized objects.
|
| 165/4 |
2026/08/07 05:46 |
llm |
patch-generator |
0m
Model:
Tokens:
input: 10703
output: 31
thoughts: 336
Thoughts: **Analyzing a KASAN Slab-Use-After-Free Vulnerability in gadgetfs**
Okay, so I've been digging into this KASAN slab-use-after-free issue, and it looks like the problem is stemming from `gadgetfs_kill_sb` ultimately calling `put_dev`. I can see that the root cause lies in an integer underflow in the reference counting within the `gadgetfs_bind` error path. The code jumps to the `enomem` label when `gadgetfs_bind` fails, say, due to a memory allocation error. From there, it calls `gadgetfs_unbind(gadget)`.
Here's the kicker: `gadgetfs_unbind` unconditionally calls `put_dev(dev)`. However, there's a crucial detail – `get_dev(dev)` is *only* called at the very end of `gadgetfs_bind`, when everything goes according to plan. That means `gadgetfs_unbind` is dropping a reference to the `dev` structure that was never actually acquired in the first place, leading to that underflow.
The proposed fix makes total sense. Instead of a blanket call to `gadgetfs_unbind(gadget)` in the `enomem` path, we should perform manual cleanup directly. By doing so, we prevent calling `put_dev` when it shouldn't be. Specifically, this manual cleanup focuses on freeing the USB endpoint request, nullifying driver data, and setting gadget/device pointers to null, ensuring there's no dangling reference. This seems like a much cleaner solution than the original `gadgetfs_unbind` call in the error path.
I'll now head to `drivers/usb/gadget/legacy/inode.c` to take a closer look at the actual code and make sure this fix is properly implemented.
|
| 166/4 |
2026/08/07 05:46 |
tool |
codesearch-file-index |
0m
Args: map[SourceFile:drivers/usb/gadget/legacy/inode.c]
Results: map[Entities:[map[Kind:function Name:__check_default_gid] map[Kind:function Name:__check_default_perm] map[Kind:function Name:__check_default_uid] map[Kind:function Name:activate_ep_files] map[Kind:function Name:clean_req] map[Kind:function Name:config_buf] map[Kind:function Name:destroy_ep_files] map[Kind:function Name:dev_config] map[Kind:function Name:dev_new] map[Kind:function Name:dev_release] map[Kind:function Name:ep0_complete] map[Kind:function Name:ep0_fasync] map[Kind:function Name:ep0_poll] map[Kind:function Name:ep0_read] map[Kind:function Name:ep0_readable] map[Kind:function Name:ep0_write] map[Kind:function Name:ep_aio] map[Kind:function Name:ep_aio_cancel] map[Kind:function Name:ep_aio_complete] map[Kind:function Name:ep_config] map[Kind:function Name:ep_io] map[Kind:function Name:ep_ioctl] map[Kind:function Name:ep_open] map[Kind:function Name:ep_read_iter] map[Kind:function Name:ep_release] map[Kind:function Name:ep_user_copy_worker] map[Kind:function Name:ep_write_iter] map[Kind:function Name:epio_complete] map[Kind:function Name:gadget_dev_ioctl] map[Kind:function Name:gadget_dev_open] map[Kind:function Name:gadgetfs_bind] map[Kind:function Name:gadgetfs_cleanup] map[Kind:function Name:gadgetfs_create_file] map[Kind:function Name:gadgetfs_disconnect] map[Kind:function Name:gadgetfs_fill_super] map[Kind:function Name:gadgetfs_get_tree] map[Kind:function Name:gadgetfs_init] map[Kind:function Name:gadgetfs_init_fs_context] map[Kind:function Name:gadgetfs_kill_sb] map[Kind:function Name:gadgetfs_make_inode] map[Kind:function Name:gadgetfs_setup] map[Kind:function Name:gadgetfs_suspend] map[Kind:function Name:gadgetfs_unbind] map[Kind:function Name:get_dev] map[Kind:function Name:get_ep] map[Kind:function Name:get_ready_ep] map[Kind:function Name:is_valid_config] map[Kind:function Name:make_qualifier] map[Kind:function Name:next_event] map[Kind:function Name:put_dev] map[Kind:function Name:put_ep] map[Kind:function Name:setup_req] map[Kind:struct Name:dev_data] map[Kind:struct Name:ep_data] map[Kind:struct Name:kiocb_priv] map[Kind:global_variable Name:__UNIQUE_ID_addressable_gadgetfs_init_1067] map[Kind:global_variable Name:__UNIQUE_ID_modinfo_1026] map[Kind:global_variable Name:__UNIQUE_ID_modinfo_1027] map[Kind:global_variable Name:__UNIQUE_ID_modinfo_1028] map[Kind:global_variable Name:__UNIQUE_ID_modinfo_1029] map[Kind:global_variable Name:__UNIQUE_ID_modinfo_1062] map[Kind:global_variable Name:__UNIQUE_ID_modinfo_1063] map[Kind:global_variable Name:__UNIQUE_ID_modinfo_1064] map[Kind:global_variable Name:__UNIQUE_ID_modinfo_1065] map[Kind:global_variable Name:__exitcall_gadgetfs_cleanup] map[Kind:global_variable Name:__param_default_gid] map[Kind:global_variable Name:__param_default_perm] map[Kind:global_variable Name:__param_default_uid] map[Kind:global_variable Name:__param_str_default_gid] map[Kind:global_variable Name:__param_str_default_perm] map[Kind:global_variable Name:__param_str_default_uid] map[Kind:global_variable Name:default_perm] map[Kind:global_variable Name:driver_desc] map[Kind:global_variable Name:ep0_operations] map[Kind:global_variable Name:ep_io_operations] map[Kind:global_variable Name:gadget_fs_operations] map[Kind:global_variable Name:gadgetfs_context_ops] map[Kind:global_variable Name:gadgetfs_driver] map[Kind:global_variable Name:gadgetfs_type] map[Kind:global_variable Name:sb_mutex] map[Kind:global_variable Name:shortname] map[Kind:enum Name:ep0_state] map[Kind:enum Name:ep_state]]]
|
| 167/4 |
2026/08/07 05:46 |
llm |
patch-generator |
0m
Model:
Tokens:
input: 12914
output: 41
thoughts: 19
Thoughts: Okay, let's dive into these functions. Here's what I'm thinking as I look at `gadgetfs_bind` and `gadgetfs_unbind`:
**Analyzing GadgetFS Bind and Unbind Operations**
Right, let's break down `gadgetfs_bind`. My immediate expectation is that this function is where the core connection between the USB gadget and the filesystem is established. I'd imagine it handles the initial setup, associating the filesystem's operations with the USB gadget's communication structures. It probably involves some interaction with the gadget driver, maybe setting up endpoints or registering some callbacks. I'm keen to see how the filesystem's inode operations are linked to the gadget's requests – that's where the magic has to happen. It'll also likely need to handle any setup specific to the filesystem itself, creating internal structures or allocating resources. I bet it has error handling in there somewhere to deal with things like resource exhaustion or if the gadget isn't properly initialized.
Now, shifting gears to `gadgetfs_unbind`. This will undoubtedly be the cleanup routine. My guess is it reverses everything `gadgetfs_bind` did. It's got to gracefully tear down the connection, free any allocated resources, unregister callbacks, and potentially inform the gadget driver that the filesystem is going away. I'm expecting a careful process to avoid dangling pointers or memory leaks. The crucial thing will be ensuring that all outstanding operations are properly handled – aborting any in-flight transactions or waiting for them to complete gracefully. It's likely this function will need to synchronize its operations to avoid race conditions during the shutdown process. I am keen to see how they handle any lingering data transfers or open files.
|
| 168/4 |
2026/08/07 05:46 |
tool |
codesearch-definition-source |
0m
Args: map[ContextFile:drivers/usb/gadget/legacy/inode.c Name:gadgetfs_bind]
Results: map[SourceCode:1668: static int gadgetfs_bind(struct usb_gadget *gadget,
1669: struct usb_gadget_driver *driver)
1670: {
1671: struct dev_data *dev = the_device;
1672:
1673: if (!dev)
1674: return -ESRCH;
1675: if (0 != strcmp (CHIP, gadget->name)) {
1676: pr_err("%s expected %s controller not %s\n",
1677: shortname, CHIP, gadget->name);
1678: return -ENODEV;
1679: }
1680:
1681: set_gadget_data (gadget, dev);
1682: dev->gadget = gadget;
1683: gadget->ep0->driver_data = dev;
1684:
1685: /* preallocate control response and buffer */
1686: dev->req = usb_ep_alloc_request (gadget->ep0, GFP_KERNEL);
1687: if (!dev->req)
1688: goto enomem;
1689: dev->req->context = NULL;
1690: dev->req->complete = epio_complete;
1691:
1692: if (activate_ep_files (dev) < 0)
1693: goto enomem;
1694:
1695: INFO (dev, "bound to %s driver\n", gadget->name);
1696: spin_lock_irq(&dev->lock);
1697: dev->state = STATE_DEV_UNCONNECTED;
1698: spin_unlock_irq(&dev->lock);
1699: get_dev (dev);
1700: return 0;
1701:
1702: enomem:
1703: gadgetfs_unbind (gadget);
1704: return -ENOMEM;
1705: }
SourceFile:drivers/usb/gadget/legacy/inode.c]
|
| 169/4 |
2026/08/07 05:46 |
llm |
patch-generator |
0m
Model:
Tokens:
input: 13922
output: 41
thoughts: 11
Thoughts: Okay, let's dive into this. Here's my take on `gadgetfs_unbind`, as if these are my immediate thoughts:
**Analyzing the `gadgetfs_unbind` Function**
Alright, `gadgetfs_unbind`. Time to unpack this one. My initial instinct is to go straight to the code and see how it handles the unbinding process. I'm already anticipating the typical challenges: resource cleanup, error handling, and making sure everything is neatly undone to prevent any lingering issues. I want to see how it interacts with the underlying USB gadget framework. Specifically, I'm looking for where it detaches the device from the filesystem, and how it handles potential race conditions with other operations. I'm wondering if it uses any locking mechanisms to ensure the integrity of the data structures. I'll need to pay close attention to the order of operations, especially if it involves releasing resources or signaling other threads. The devil is in the details with these unbind functions. I'm expecting this to be a crucial point in the device's lifecycle.
|
| 170/4 |
2026/08/07 05:47 |
tool |
codesearch-definition-source |
0m
Args: map[ContextFile:drivers/usb/gadget/legacy/inode.c Name:gadgetfs_unbind]
Results: map[SourceCode:1639: static void
1640: gadgetfs_unbind (struct usb_gadget *gadget)
1641: {
1642: struct dev_data *dev = get_gadget_data (gadget);
1643:
1644: DBG (dev, "%s\n", __func__);
1645:
1646: spin_lock_irq (&dev->lock);
1647: dev->state = STATE_DEV_UNBOUND;
1648: while (dev->udc_usage > 0) {
1649: spin_unlock_irq(&dev->lock);
1650: usleep_range(1000, 2000);
1651: spin_lock_irq(&dev->lock);
1652: }
1653: spin_unlock_irq (&dev->lock);
1654:
1655: destroy_ep_files (dev);
1656: gadget->ep0->driver_data = NULL;
1657: set_gadget_data (gadget, NULL);
1658:
1659: /* we've already been disconnected ... no i/o is active */
1660: if (dev->req)
1661: usb_ep_free_request (gadget->ep0, dev->req);
1662: DBG (dev, "%s done\n", __func__);
1663: put_dev (dev);
1664: }
SourceFile:drivers/usb/gadget/legacy/inode.c]
|
| 171/4 |
2026/08/07 05:47 |
llm |
patch-generator |
0m
Model:
Tokens:
input: 14638
output: 41
thoughts: 78
Thoughts: Here's my analysis of the situation:
**Investigating a Reference Counting Issue in GadgetFS**
Okay, so I'm looking at this `gadgetfs` code, and it seems there's a potential reference counting problem lurking in the `gadgetfs_unbind` function. My initial concern stems from the fact that it's calling `put_dev(dev)` directly. This is raising a red flag because it appears to be dropping a reference to the `dev` struct that wasn't properly acquired during the error handling path of `gadgetfs_bind`. That's a classic recipe for a double free or use-after-free scenario, and those are bugs I *really* want to avoid.
Furthermore, `gadgetfs_unbind` is also responsible for calling `destroy_ep_files(dev)`. That's got me thinking I need to carefully compare the behaviours of `activate_ep_files` and `destroy_ep_files` functions, to see if there's any inconsistencies in how they relate to the `dev` structure's lifecycle. I need to trace the references to that `dev` pointer throughout these functions to fully understand the issue. I'll need to examine how each function is related to the reference count of the device in the context of binding and unbinding. This is clearly a critical area that needs deeper scrutiny.
|
| 172/4 |
2026/08/07 05:47 |
tool |
codesearch-definition-source |
0m
Args: map[ContextFile:drivers/usb/gadget/legacy/inode.c Name:activate_ep_files]
Results: map[SourceCode:1593: static int activate_ep_files (struct dev_data *dev)
1594: {
1595: struct usb_ep *ep;
1596: struct ep_data *data;
1597: int err;
1598:
1599: gadget_for_each_ep (ep, dev->gadget) {
1600:
1601: data = kzalloc_obj(*data);
1602: if (!data)
1603: goto enomem0;
1604: data->state = STATE_EP_DISABLED;
1605: mutex_init(&data->lock);
1606: init_waitqueue_head (&data->wait);
1607:
1608: strscpy(data->name, ep->name);
1609: refcount_set (&data->count, 1);
1610: data->dev = dev;
1611: get_dev (dev);
1612:
1613: data->ep = ep;
1614: ep->driver_data = data;
1615:
1616: data->req = usb_ep_alloc_request (ep, GFP_KERNEL);
1617: if (!data->req)
1618: goto enomem1;
1619:
1620: err = gadgetfs_create_file (dev->sb, data->name,
1621: data, &ep_io_operations);
1622: if (err)
1623: goto enomem2;
1624: list_add_tail (&data->epfiles, &dev->epfiles);
1625: }
1626: return 0;
1627:
1628: enomem2:
1629: usb_ep_free_request (ep, data->req);
1630: enomem1:
1631: put_dev (dev);
1632: kfree (data);
1633: enomem0:
1634: DBG (dev, "%s enomem\n", __func__);
1635: destroy_ep_files (dev);
1636: return -ENOMEM;
1637: }
SourceFile:drivers/usb/gadget/legacy/inode.c]
|
| 173/4 |
2026/08/07 05:47 |
llm |
patch-generator |
0m
Model:
Tokens:
input: 15705
output: 41
thoughts: 93
Thoughts: Okay, let's break this down.
**Ensuring Cleanliness and Memory Safety**
Right, so `activate_ep_files` is designed pretty robustly. If it runs into trouble, like a memory allocation issue and returns `-ENOMEM`, it knows to call `destroy_ep_files(dev)` to clean up after itself. That means that if the operation fails, `dev->epfiles` will be empty. That's good - we don't want any lingering, partially allocated resources.
Now, if `gadgetfs_bind` is the culprit and fails, it should just free up the `dev->req` and clear those associated pointers. We've got to make sure all that is in place, we will take another look at the code responsible.
|
| 174/4 |
2026/08/07 05:47 |
tool |
codesearch-definition-source |
0m
Args: map[ContextFile:drivers/usb/gadget/legacy/inode.c Name:gadgetfs_bind]
Results: map[SourceCode:1668: static int gadgetfs_bind(struct usb_gadget *gadget,
1669: struct usb_gadget_driver *driver)
1670: {
1671: struct dev_data *dev = the_device;
1672:
1673: if (!dev)
1674: return -ESRCH;
1675: if (0 != strcmp (CHIP, gadget->name)) {
1676: pr_err("%s expected %s controller not %s\n",
1677: shortname, CHIP, gadget->name);
1678: return -ENODEV;
1679: }
1680:
1681: set_gadget_data (gadget, dev);
1682: dev->gadget = gadget;
1683: gadget->ep0->driver_data = dev;
1684:
1685: /* preallocate control response and buffer */
1686: dev->req = usb_ep_alloc_request (gadget->ep0, GFP_KERNEL);
1687: if (!dev->req)
1688: goto enomem;
1689: dev->req->context = NULL;
1690: dev->req->complete = epio_complete;
1691:
1692: if (activate_ep_files (dev) < 0)
1693: goto enomem;
1694:
1695: INFO (dev, "bound to %s driver\n", gadget->name);
1696: spin_lock_irq(&dev->lock);
1697: dev->state = STATE_DEV_UNCONNECTED;
1698: spin_unlock_irq(&dev->lock);
1699: get_dev (dev);
1700: return 0;
1701:
1702: enomem:
1703: gadgetfs_unbind (gadget);
1704: return -ENOMEM;
1705: }
SourceFile:drivers/usb/gadget/legacy/inode.c]
|
| 175/4 |
2026/08/07 05:47 |
llm |
patch-generator |
0m
Model:
Tokens:
input: 16591
output: 41
thoughts: 169
Thoughts: **Analyzing Potential Redundancy in USB Gadget Cleanup**
Okay, so I'm looking at some USB gadget code and I've spotted a potential inefficiency, or perhaps even a subtle bug. My thought process is centered around the interaction between `activate_ep_files`, `gadgetfs_unbind`, and `destroy_ep_files`. Here's what's going through my head:
If `activate_ep_files` fails, there's a cleanup routine that takes care of `dev->epfiles`. Fine, that seems standard. However, the `gadgetfs_unbind` function also calls `destroy_ep_files(dev)`. This triggers a red flag: Could we be doing redundant cleanup?
If we were to replace the call to `gadgetfs_unbind(gadget)` with a more manual cleanup approach (which might be necessary for some reason), we'd need to ensure we cover all the necessary steps, and avoid double-frees or other memory issues. I believe I've got this sketched out, and it would look like this:
```c
enomem:
if (dev->req) {
usb_ep_free_request (gadget->ep0, dev->req);
dev->req = NULL;
}
gadget->ep0->driver_data = NULL;
dev->gadget = NULL;
set_gadget_data (gadget, NULL);
return -ENOMEM;
```
Essentially, we need to handle the `req` (request) field, clear the driver data, null the gadget and dev, and return ENOMEM. Now, the next step is crucial – to verify that my assumptions are correct, I need to meticulously inspect the `destroy_ep_files` function. I'm going to dig into the source code to confirm the overlap and see if my manual approach is a viable alternative. This should help me identify any unexpected behaviors I should be aware of.
|
| 176/4 |
2026/08/07 05:47 |
tool |
codesearch-definition-source |
0m
Args: map[ContextFile:drivers/usb/gadget/legacy/inode.c Name:destroy_ep_files]
Results: map[SourceCode:1554: static void destroy_ep_files (struct dev_data *dev)
1555: {
1556: DBG (dev, "%s %d\n", __func__, dev->state);
1557:
1558: /* dev->state must prevent interference */
1559: spin_lock_irq (&dev->lock);
1560: while (!list_empty(&dev->epfiles)) {
1561: struct ep_data *ep;
1562:
1563: /* break link to FS */
1564: ep = list_first_entry (&dev->epfiles, struct ep_data, epfiles);
1565: list_del_init (&ep->epfiles);
1566: spin_unlock_irq (&dev->lock);
1567:
1568: /* break link to controller */
1569: mutex_lock(&ep->lock);
1570: if (ep->state == STATE_EP_ENABLED)
1571: (void) usb_ep_disable (ep->ep);
1572: ep->state = STATE_EP_UNBOUND;
1573: usb_ep_free_request (ep->ep, ep->req);
1574: ep->ep = NULL;
1575: mutex_unlock(&ep->lock);
1576:
1577: wake_up (&ep->wait);
1578:
1579: /* break link to dcache */
1580: simple_remove_by_name(dev->sb->s_root, ep->name, NULL);
1581:
1582: put_ep (ep);
1583:
1584: spin_lock_irq (&dev->lock);
1585: }
1586: spin_unlock_irq (&dev->lock);
1587: }
SourceFile:drivers/usb/gadget/legacy/inode.c]
|
| 177/4 |
2026/08/07 05:47 |
llm |
patch-generator |
0m
Model:
Tokens:
input: 17768
output: 155
thoughts: 62
Thoughts: **Understanding the Ephemeral File Cleanup**
Okay, so I'm looking at this cleanup process for the ephemeral files associated with a device (`dev->epfiles`), and I've got to make sure I understand the logic. It looks like `activate_ep_files` is responsible for setting up those files, but crucially, it's also responsible for cleaning them up if it encounters any kind of error during that setup.
This means that if `activate_ep_files` fails, or if it simply hasn't been called yet, `destroy_ep_files` will be handed an empty list to deal with. This makes sense; we don't want `destroy_ep_files` trying to free memory that doesn't exist or is corrupted. It handles the cleanup on its own, so we need to ensure this works.
Alright, with this understanding, I think I've got a solid grasp of how it should behave. Let's get this code edit done.
|
| 178/4 |
2026/08/07 05:47 |
tool |
codeeditor |
0m
Args: map[CurrentCode:enomem:
gadgetfs_unbind (gadget);
return -ENOMEM;
} NewCode:enomem:
if (dev->req) {
usb_ep_free_request (gadget->ep0, dev->req);
dev->req = NULL;
}
gadget->ep0->driver_data = NULL;
dev->gadget = NULL;
set_gadget_data (gadget, NULL);
return -ENOMEM;
} SourceFile:drivers/usb/gadget/legacy/inode.c]
Results:
|
| 179/4 |
2026/08/07 05:47 |
llm |
patch-generator |
0m
Model:
Tokens:
input: 18199
output: 27
thoughts: 8
Thoughts: Okay, let's dive into this. Here's my thought process as I approach the provided task, assuming I'm an expert in my field and presented as a fulsome summary:
**Initial Assessment: Navigating the Patch Diff**
Right, so we've got a patch diff to review. My immediate instinct is to get a handle on the scope of the changes. I'm already mentally forming questions: *What's the overall context? Is this a bug fix, a feature addition, a refactor, or something else entirely? What modules or areas of the codebase are impacted? What's the potential risk profile associated with these changes, considering our current architectural state and known vulnerabilities?*
My first step is to quickly scan the diff itself, probably starting with the file headers and then the most substantial hunks. I'm looking for a "lay of the land" view, noting any files that jump out at me as particularly critical or potentially problematic based on my prior knowledge of the system. I will look for patterns, as my mind is forming the big picture. Are there recurring changes? Are there changes touching core components? Are there areas I'm unfamiliar with?
Next, I need to evaluate the details. I will quickly compare with the current version and think about why each change was made and if the changes make sense. I'll need to assess the technical merit of each change. Does the logic hold? Are there obvious edge cases overlooked? Are there potential performance bottlenecks introduced? Does the code adhere to our established coding style and best practices? I need to analyze for any security vulnerabilities.
I'll be paying close attention to any changes in interfaces or data structures, as these have the potential to ripple throughout the codebase. The goal is to perform a sanity check to make sure the core design remains consistent with what's expected and intended.
Ultimately, I'm aiming for a comprehensive assessment, identifying both potential positives and negatives of the patch. I need to be ready to articulate my findings clearly, and support them with solid reasoning. This will allow for more focused discussions with the development team and other stakeholders.
|
| 180/4 |
2026/08/07 05:47 |
tool |
patch-diff |
0m
Args: map[File:drivers/usb/gadget/legacy/inode.c]
Results: map[Output:diff --git a/drivers/usb/gadget/legacy/inode.c b/drivers/usb/gadget/legacy/inode.c
index d87a8ab51..1a05819c0 100644
--- a/drivers/usb/gadget/legacy/inode.c
+++ b/drivers/usb/gadget/legacy/inode.c
@@ -1668,46 +1668,52 @@ static struct dev_data *the_device;
static int gadgetfs_bind(struct usb_gadget *gadget,
struct usb_gadget_driver *driver)
{
struct dev_data *dev = the_device;
if (!dev)
return -ESRCH;
if (0 != strcmp (CHIP, gadget->name)) {
pr_err("%s expected %s controller not %s\n",
shortname, CHIP, gadget->name);
return -ENODEV;
}
set_gadget_data (gadget, dev);
dev->gadget = gadget;
gadget->ep0->driver_data = dev;
/* preallocate control response and buffer */
dev->req = usb_ep_alloc_request (gadget->ep0, GFP_KERNEL);
if (!dev->req)
goto enomem;
dev->req->context = NULL;
dev->req->complete = epio_complete;
if (activate_ep_files (dev) < 0)
goto enomem;
INFO (dev, "bound to %s driver\n", gadget->name);
spin_lock_irq(&dev->lock);
dev->state = STATE_DEV_UNCONNECTED;
spin_unlock_irq(&dev->lock);
get_dev (dev);
return 0;
enomem:
- gadgetfs_unbind (gadget);
+ if (dev->req) {
+ usb_ep_free_request (gadget->ep0, dev->req);
+ dev->req = NULL;
+ }
+ gadget->ep0->driver_data = NULL;
+ dev->gadget = NULL;
+ set_gadget_data (gadget, NULL);
return -ENOMEM;
}
static void
gadgetfs_disconnect (struct usb_gadget *gadget)
{
struct dev_data *dev = get_gadget_data (gadget);
unsigned long flags;
spin_lock_irqsave (&dev->lock, flags);
]
|
| 181/4 |
2026/08/07 05:47 |
llm |
patch-generator |
0m
Model:
Tokens:
input: 19347
output: 54
thoughts: 44
Thoughts: Okay, here's my take on those thoughts, tailored for an expert audience:
**Analyzing the GadgetFS Cleanup Implementation**
Alright, that code review looks promising. This proposed fix, specifically replacing `gadgetfs_unbind` with the manual cleanup, seems spot-on in addressing that lingering issue. The logic appears to be sound, accurately addressing the problematic unbinding.
However, I'm not going to just sign off on this blindly. We need to be absolutely certain we haven't just patched a symptom and not the root cause, or, worse, missed other instances of that kind of error. Time to expand the search. I want to delve deeper now and scan the entire codebase – or at least, the relevant sections of it – for any *other* calls to `gadgetfs_unbind`. A comprehensive audit is critical here. It's imperative that we identify any other potential misuses of that function throughout the driver to ensure the system's overall stability and prevent future regressions. We should also investigate whether a higher-level refactoring may be appropriate if those extra calls exist.
|
| 182/4 |
2026/08/07 05:47 |
tool |
codesearch-find-references |
0m
Args: map[ContextFile:drivers/usb/gadget/legacy/inode.c IncludeSnippetLines:2 Name:gadgetfs_unbind SourceTreePrefix:]
Results: map[References:[map[ReferenceKind:calls ReferencingEntityKind:function ReferencingEntityName:gadgetfs_bind SourceFile:drivers/usb/gadget/legacy/inode.c SourceLine:1703 SourceSnippet:1701:
1702: enomem:
1703: gadgetfs_unbind (gadget);
1704: return -ENOMEM;
1705: }
] map[ReferenceKind:takes-address-of ReferencingEntityKind:global_variable ReferencingEntityName:gadgetfs_driver SourceFile:drivers/usb/gadget/legacy/inode.c SourceLine:1749 SourceSnippet:1747: .function = (char *) driver_desc,
1748: .bind = gadgetfs_bind,
1749: .unbind = gadgetfs_unbind,
1750: .setup = gadgetfs_setup,
1751: .reset = gadgetfs_disconnect,
]] TruncatedOutput:false]
|
| 183/4 |
2026/08/07 05:47 |
llm |
patch-generator |
0m
Model:
Tokens:
input: 19958
output: 52
thoughts: 49
Thoughts: Okay, here's my interpretation of those thoughts, framed as my own:
**Investigating `put_dev` Calls in `gadgetfs`**
Right, let me think this through. `gadgetfs_unbind` is *exclusively* called as the `.unbind` callback, or during an error path within `gadgetfs_bind`, which makes sense. I have to wonder though, is there *any* other place within the codebase where a `put_dev` call is made unconditionally, particularly within an error handling sequence? I need to be sure I'm not missing a potential resource leak or an unexpected release scenario. I have to think about other error paths in the module. I can't just assume these are the only ones, I need to do a broader search for any unexpected calls to `put_dev`.
|
| 184/4 |
2026/08/07 05:47 |
tool |
codesearch-find-references |
0m
Args: map[ContextFile:drivers/usb/gadget/legacy/inode.c IncludeSnippetLines:2 Name:put_dev SourceTreePrefix:]
Results: map[References:[map[ReferenceKind:calls ReferencingEntityKind:function ReferencingEntityName:activate_ep_files SourceFile:drivers/usb/gadget/legacy/inode.c SourceLine:1631 SourceSnippet:1629: usb_ep_free_request (ep, data->req);
1630: enomem1:
1631: put_dev (dev);
1632: kfree (data);
1633: enomem0:
] map[ReferenceKind:calls ReferencingEntityKind:function ReferencingEntityName:dev_release SourceFile:drivers/usb/gadget/legacy/inode.c SourceLine:1215 SourceSnippet:1213: spin_unlock_irq(&dev->lock);
1214:
1215: put_dev (dev);
1216: return 0;
1217: }
] map[ReferenceKind:calls ReferencingEntityKind:function ReferencingEntityName:gadgetfs_fill_super SourceFile:drivers/usb/gadget/legacy/inode.c SourceLine:2061 SourceSnippet:2059: rc = gadgetfs_create_file(sb, CHIP, dev, &ep0_operations);
2060: if (rc) {
2061: put_dev(dev);
2062: goto Enomem;
2063: }
] map[ReferenceKind:calls ReferencingEntityKind:function ReferencingEntityName:gadgetfs_kill_sb SourceFile:drivers/usb/gadget/legacy/inode.c SourceLine:2104 SourceSnippet:2102: kill_anon_super (sb);
2103: if (the_device) {
2104: put_dev (the_device);
2105: the_device = NULL;
2106: }
] map[ReferenceKind:calls ReferencingEntityKind:function ReferencingEntityName:gadgetfs_unbind SourceFile:drivers/usb/gadget/legacy/inode.c SourceLine:1663 SourceSnippet:1661: usb_ep_free_request (gadget->ep0, dev->req);
1662: DBG (dev, "%s done\n", __func__);
1663: put_dev (dev);
1664: }
] map[ReferenceKind:calls ReferencingEntityKind:function ReferencingEntityName:put_ep SourceFile:drivers/usb/gadget/legacy/inode.c SourceLine:221 SourceSnippet: 219: if (likely (!refcount_dec_and_test (&data->count)))
220: return;
221: put_dev (data->dev);
222: /* needs no more cleanup */
223: BUG_ON (!list_empty (&data->epfiles));
]] TruncatedOutput:false]
|
| 185/4 |
2026/08/07 05:47 |
llm |
patch-generator |
0m
Model:
Tokens:
input: 21033
output: 41
thoughts: 11
Thoughts: Okay, here's my attempt at summarizing that thought process, keeping in mind I'm speaking to a fellow expert:
**Checking `activate_ep_files`**
Alright, time to dig in. The immediate need here is to examine `activate_ep_files`. I know this function/script/module (depending on the context) is crucial. It’s likely the entry point, or at least a key component, in initializing the environment or data setup related to those "ep files". My experience tells me that without knowing what this does, I'm flying blind. I need to understand its functionality, dependencies, and any side effects it might have. My intuition is telling me that this is a critical step to ensure that I have all the proper files in place. I'm going to start by reading the documentation/comments, or if that's insufficient, dive into the code itself.
|
| 186/4 |
2026/08/07 05:47 |
tool |
codesearch-definition-source |
0m
Args: map[ContextFile:drivers/usb/gadget/legacy/inode.c Name:activate_ep_files]
Results: map[SourceCode:1593: static int activate_ep_files (struct dev_data *dev)
1594: {
1595: struct usb_ep *ep;
1596: struct ep_data *data;
1597: int err;
1598:
1599: gadget_for_each_ep (ep, dev->gadget) {
1600:
1601: data = kzalloc_obj(*data);
1602: if (!data)
1603: goto enomem0;
1604: data->state = STATE_EP_DISABLED;
1605: mutex_init(&data->lock);
1606: init_waitqueue_head (&data->wait);
1607:
1608: strscpy(data->name, ep->name);
1609: refcount_set (&data->count, 1);
1610: data->dev = dev;
1611: get_dev (dev);
1612:
1613: data->ep = ep;
1614: ep->driver_data = data;
1615:
1616: data->req = usb_ep_alloc_request (ep, GFP_KERNEL);
1617: if (!data->req)
1618: goto enomem1;
1619:
1620: err = gadgetfs_create_file (dev->sb, data->name,
1621: data, &ep_io_operations);
1622: if (err)
1623: goto enomem2;
1624: list_add_tail (&data->epfiles, &dev->epfiles);
1625: }
1626: return 0;
1627:
1628: enomem2:
1629: usb_ep_free_request (ep, data->req);
1630: enomem1:
1631: put_dev (dev);
1632: kfree (data);
1633: enomem0:
1634: DBG (dev, "%s enomem\n", __func__);
1635: destroy_ep_files (dev);
1636: return -ENOMEM;
1637: }
SourceFile:drivers/usb/gadget/legacy/inode.c]
|
| 187/4 |
2026/08/07 05:47 |
llm |
patch-generator |
0m
Model:
Tokens:
input: 21931
output: 43
thoughts: 274
Thoughts: **Analyzing the USB Endpoint File Activation Flow**
Okay, let's break down this `activate_ep_files` function. Line 1611, it's calling `get_dev(dev)`. That's the crucial first step to bump the device's reference count. Then, looking at the error handling, if `usb_ep_alloc_request` fails, we jump to `enomem1`, which immediately calls `put_dev(dev)`. That's right, because we *did* just call `get_dev(dev)` earlier for this endpoint. Perfect.
Now, if `gadgetfs_create_file` throws an error, we hit `enomem2`, free the request, and then we *fall through* to `enomem1`, which again calls `put_dev(dev)`. Makes sense. We have to release that initial `get_dev()` call. Still good. Then we fall to `enomem0`, which leads to `destroy_ep_files(dev)`.
Inside `destroy_ep_files(dev)`, the function iterates through `dev->epfiles` and for each endpoint `ep`, it calls `put_ep(ep)`. That's where it all comes together. `put_ep(ep)` will decrement `data->count`. And when that count drops to zero, *then* it calls `put_dev(data->dev)`. Bingo! This is correct because `get_dev(dev)` was called for each `ep` added to `dev->epfiles`. So, the entire cleanup process is linked to the initial reference increase, so nothing will be leaked.
So, yeah, after going over this, it looks like `activate_ep_files` is handling the reference counts correctly, it is well-designed. I'm satisfied. Now, let's switch gears and take a look at `gadgetfs_fill_super`.
|
| 188/4 |
2026/08/07 05:47 |
tool |
codesearch-definition-source |
0m
Args: map[ContextFile:drivers/usb/gadget/legacy/inode.c Name:gadgetfs_fill_super]
Results: map[SourceCode:2014: static int
2015: gadgetfs_fill_super (struct super_block *sb, struct fs_context *fc)
2016: {
2017: struct inode *inode;
2018: struct dev_data *dev;
2019: int rc;
2020:
2021: mutex_lock(&sb_mutex);
2022:
2023: if (the_device) {
2024: rc = -ESRCH;
2025: goto Done;
2026: }
2027:
2028: CHIP = usb_get_gadget_udc_name();
2029: if (!CHIP) {
2030: rc = -ENODEV;
2031: goto Done;
2032: }
2033:
2034: /* superblock */
2035: sb->s_blocksize = PAGE_SIZE;
2036: sb->s_blocksize_bits = PAGE_SHIFT;
2037: sb->s_magic = GADGETFS_MAGIC;
2038: sb->s_op = &gadget_fs_operations;
2039: sb->s_time_gran = 1;
2040:
2041: /* root inode */
2042: inode = gadgetfs_make_inode (sb,
2043: NULL, &simple_dir_operations,
2044: S_IFDIR | S_IRUGO | S_IXUGO);
2045: if (!inode)
2046: goto Enomem;
2047: inode->i_op = &simple_dir_inode_operations;
2048: if (!(sb->s_root = d_make_root (inode)))
2049: goto Enomem;
2050:
2051: /* the ep0 file is named after the controller we expect;
2052: * user mode code can use it for sanity checks, like we do.
2053: */
2054: dev = dev_new ();
2055: if (!dev)
2056: goto Enomem;
2057:
2058: dev->sb = sb;
2059: rc = gadgetfs_create_file(sb, CHIP, dev, &ep0_operations);
2060: if (rc) {
2061: put_dev(dev);
2062: goto Enomem;
2063: }
2064:
2065: /* other endpoint files are available after hardware setup,
2066: * from binding to a controller.
2067: */
2068: the_device = dev;
2069: rc = 0;
2070: goto Done;
2071:
2072: Enomem:
2073: kfree(CHIP);
2074: CHIP = NULL;
2075: rc = -ENOMEM;
2076:
2077: Done:
2078: mutex_unlock(&sb_mutex);
2079: return rc;
2080: }
SourceFile:drivers/usb/gadget/legacy/inode.c]
|
| 189/4 |
2026/08/07 05:47 |
llm |
patch-generator |
0m
Model:
Tokens:
input: 23577
output: 43
thoughts: 81
Thoughts: Here's my take on the thought process, as if it were my own:
**Verifying Resource Management in GadgetFS**
Okay, let's break this down. In `gadgetfs_fill_super`, `dev_new()` is used. Right, that's the function that allocates the `dev` structure – a critical device representation – and crucially, it initializes its reference count to 1. That's standard and expected.
Now, the important part: if `gadgetfs_create_file` hits an error... well, it's designed to *fail*. When it does, it calls `put_dev(dev)`. That's *precisely* what we want! `put_dev()` decrements the reference count. Since it was initialized to 1, a decrement to 0 on failure means the device is freed properly. Excellent. That's the correct cleanup path. No lingering resources, no memory leaks.
Now, let me shift gears and review `gadgetfs_kill_sb`. We need to verify that there are no leaks there.
|
| 190/4 |
2026/08/07 05:47 |
tool |
codesearch-definition-source |
0m
Args: map[ContextFile:drivers/usb/gadget/legacy/inode.c Name:gadgetfs_kill_sb]
Results: map[SourceCode:2098: static void
2099: gadgetfs_kill_sb (struct super_block *sb)
2100: {
2101: mutex_lock(&sb_mutex);
2102: kill_anon_super (sb);
2103: if (the_device) {
2104: put_dev (the_device);
2105: the_device = NULL;
2106: }
2107: kfree(CHIP);
2108: CHIP = NULL;
2109: mutex_unlock(&sb_mutex);
2110: }
SourceFile:drivers/usb/gadget/legacy/inode.c]
|
| 191/4 |
2026/08/07 05:47 |
llm |
patch-generator |
0m
Model:
Tokens:
input: 24135
output: 39
thoughts: 45
Thoughts: Okay, here's my take on those thoughts, framed as my own mental processing:
**GadgetFS Shutdown Sequence Analysis**
Alright, so `gadgetfs_kill_sb` is the right function for cleaning up the filesystem. It's calling `put_dev(the_device)` which should release the initial reference that `dev_new()` created. Yep, that's standard practice, makes sense. We're carefully releasing those resources, keeping the reference counting sane.
Now, I really need to confirm this is happening as expected. My next step should be to thoroughly examine `dev_release`. I'll want to see exactly what operations it's performing on that device structure. Is it properly freeing all associated resources, memory, and associated data. I'm especially interested in how it interacts with the lower level device drivers at this point. Gotta make sure we're not leaking anything or causing any unexpected side effects during this shutdown process. A deeper look at the release path is definitely warranted.
|
| 192/4 |
2026/08/07 05:47 |
tool |
codesearch-definition-source |
0m
Args: map[ContextFile:drivers/usb/gadget/legacy/inode.c Name:dev_release]
Results: map[SourceCode:1190: static int
1191: dev_release (struct inode *inode, struct file *fd)
1192: {
1193: struct dev_data *dev = fd->private_data;
1194:
1195: /* closing ep0 === shutdown all */
1196:
1197: if (dev->gadget_registered) {
1198: usb_gadget_unregister_driver (&gadgetfs_driver);
1199: dev->gadget_registered = false;
1200: }
1201:
1202: /* at this point "good" hardware has disconnected the
1203: * device from USB; the host won't see it any more.
1204: * alternatively, all host requests will time out.
1205: */
1206:
1207: kfree (dev->buf);
1208: dev->buf = NULL;
1209:
1210: /* other endpoints were all decoupled from this device */
1211: spin_lock_irq(&dev->lock);
1212: dev->state = STATE_DEV_DISABLED;
1213: spin_unlock_irq(&dev->lock);
1214:
1215: put_dev (dev);
1216: return 0;
1217: }
SourceFile:drivers/usb/gadget/legacy/inode.c]
|
| 193/4 |
2026/08/07 05:47 |
llm |
patch-generator |
0m
Model:
Tokens:
input: 24839
output: 42
thoughts: 33
Thoughts: Here's my understanding of the situation, tailored for my expert perspective:
**Interpreting the Device Release Sequence**
Okay, so I'm looking at this code and immediately keying in on a critical interaction: `dev_release` initiates a call to `put_dev(dev)`. That's the signal, the crucial point where the driver is relinquishing control, or at least its current hold on the device. Then, I remember how this fits into the broader picture of device lifecycle management, specifically within the `gadget_dev_open` context. I can see that the `put_dev(dev)` called here mirrors a previous `get_dev(dev)` call within `gadget_dev_open`. That connection is what tells me that this is intended as a balanced sequence.
|
| 194/4 |
2026/08/07 05:47 |
tool |
codesearch-definition-source |
0m
Args: map[ContextFile:drivers/usb/gadget/legacy/inode.c Name:gadget_dev_open]
Results: map[SourceCode:1913: static int
1914: gadget_dev_open (struct inode *inode, struct file *fd)
1915: {
1916: struct dev_data *dev = inode->i_private;
1917: int value = -EBUSY;
1918:
1919: spin_lock_irq(&dev->lock);
1920: if (dev->state == STATE_DEV_DISABLED) {
1921: dev->ev_next = 0;
1922: dev->state = STATE_DEV_OPENED;
1923: fd->private_data = dev;
1924: get_dev (dev);
1925: value = 0;
1926: }
1927: spin_unlock_irq(&dev->lock);
1928: return value;
1929: }
SourceFile:drivers/usb/gadget/legacy/inode.c]
|
| 195/4 |
2026/08/07 05:48 |
llm |
patch-generator |
0m
Model:
Tokens:
input: 25360
output: 27
thoughts: 45
Thoughts: Okay, here's my take on those thoughts, framed as my own mental processing:
**Analyzing the GadgetFS Bind Error**
Right, so `gadget_dev_open` directly calls `get_dev(dev)`. That's good to have confirmed; it's the expected behavior. This means the problem, the root cause of this particular issue we're seeing, *has* to be isolated within the error handling path of `gadgetfs_bind`. We're not seeing something unexpected in the device opening sequence itself.
Alright, let's step back and meticulously review that patch again, focusing specifically on how it handles errors in `gadgetfs_bind`. We need to be absolutely certain we haven't introduced any regressions or overlooked something in the cleanup or resource release sequences within that function. Time to dig in deep and be precise, as this is all that it could be.
|
| 196/4 |
2026/08/07 05:48 |
tool |
patch-diff |
0m
Args: map[File:drivers/usb/gadget/legacy/inode.c]
Results: map[Output:diff --git a/drivers/usb/gadget/legacy/inode.c b/drivers/usb/gadget/legacy/inode.c
index d87a8ab51..1a05819c0 100644
--- a/drivers/usb/gadget/legacy/inode.c
+++ b/drivers/usb/gadget/legacy/inode.c
@@ -1668,46 +1668,52 @@ static struct dev_data *the_device;
static int gadgetfs_bind(struct usb_gadget *gadget,
struct usb_gadget_driver *driver)
{
struct dev_data *dev = the_device;
if (!dev)
return -ESRCH;
if (0 != strcmp (CHIP, gadget->name)) {
pr_err("%s expected %s controller not %s\n",
shortname, CHIP, gadget->name);
return -ENODEV;
}
set_gadget_data (gadget, dev);
dev->gadget = gadget;
gadget->ep0->driver_data = dev;
/* preallocate control response and buffer */
dev->req = usb_ep_alloc_request (gadget->ep0, GFP_KERNEL);
if (!dev->req)
goto enomem;
dev->req->context = NULL;
dev->req->complete = epio_complete;
if (activate_ep_files (dev) < 0)
goto enomem;
INFO (dev, "bound to %s driver\n", gadget->name);
spin_lock_irq(&dev->lock);
dev->state = STATE_DEV_UNCONNECTED;
spin_unlock_irq(&dev->lock);
get_dev (dev);
return 0;
enomem:
- gadgetfs_unbind (gadget);
+ if (dev->req) {
+ usb_ep_free_request (gadget->ep0, dev->req);
+ dev->req = NULL;
+ }
+ gadget->ep0->driver_data = NULL;
+ dev->gadget = NULL;
+ set_gadget_data (gadget, NULL);
return -ENOMEM;
}
static void
gadgetfs_disconnect (struct usb_gadget *gadget)
{
struct dev_data *dev = get_gadget_data (gadget);
unsigned long flags;
spin_lock_irqsave (&dev->lock, flags);
]
|
| 197/4 |
2026/08/07 05:48 |
llm |
patch-generator |
0m
Model:
Tokens:
input: 26296
output: 41
thoughts: 30
Thoughts: Here's my take on the thought process, as requested:
**Assessing the Patch and Potential Blind Spots**
Okay, the patch itself appears sound. Everything seems to be present and accounted for, based on my initial review. But I've learned enough in this game to know that surface-level correctness isn't always enough. My immediate instinct is to dig deeper. I need to make *absolutely* certain that we haven't missed any subtle implications or side effects. The key question now is: does `gadgetfs_unbind` do anything else, anything *under the hood*, that could be relevant to this change? I need to meticulously verify that we haven't inadvertently broken something or created a new vulnerability by focusing solely on what's immediately obvious. I need to leave no stone unturned.
|
| 198/4 |
2026/08/07 05:48 |
tool |
codesearch-definition-source |
0m
Args: map[ContextFile:drivers/usb/gadget/legacy/inode.c Name:gadgetfs_unbind]
Results: map[SourceCode:1639: static void
1640: gadgetfs_unbind (struct usb_gadget *gadget)
1641: {
1642: struct dev_data *dev = get_gadget_data (gadget);
1643:
1644: DBG (dev, "%s\n", __func__);
1645:
1646: spin_lock_irq (&dev->lock);
1647: dev->state = STATE_DEV_UNBOUND;
1648: while (dev->udc_usage > 0) {
1649: spin_unlock_irq(&dev->lock);
1650: usleep_range(1000, 2000);
1651: spin_lock_irq(&dev->lock);
1652: }
1653: spin_unlock_irq (&dev->lock);
1654:
1655: destroy_ep_files (dev);
1656: gadget->ep0->driver_data = NULL;
1657: set_gadget_data (gadget, NULL);
1658:
1659: /* we've already been disconnected ... no i/o is active */
1660: if (dev->req)
1661: usb_ep_free_request (gadget->ep0, dev->req);
1662: DBG (dev, "%s done\n", __func__);
1663: put_dev (dev);
1664: }
SourceFile:drivers/usb/gadget/legacy/inode.c]
|
| 199/4 |
2026/08/07 05:48 |
llm |
patch-generator |
0m
Model:
Tokens:
input: 26977
output: 15
thoughts: 162
Thoughts: **Analyzing the gadgetfs Bind/Unbind State Transitions**
Okay, let's break down this `gadgetfs` bind/unbind logic. My initial concern was about how the device state is handled. `gadgetfs_unbind` clearly sets `dev->state` to `STATE_DEV_UNBOUND`. That's straightforward enough. The potential issue, though, is in the `gadgetfs_bind` function.
I see that `dev->state` isn't updated *before* `activate_ep_files` in `gadgetfs_bind`. It only transitions to `STATE_DEV_UNCONNECTED` *after* a successful operation. This means that, in between `gadget_dev_open` and the success of a bind, `dev->state` is remaining at whatever initial value was set in `gadget_dev_open`, likely `STATE_DEV_OPENED`. This isn't necessarily a *problem* given the rest of the flow, but it's a detail worth noting for state tracking.
What really matters is the cleanup, especially on error paths. The `dev->udc_usage` counter isn't incremented in `gadgetfs_bind`, which confirms my assumption that it is managed elsewhere, but does this mean there is some un-accounted usage on failure?
Fortunately, it looks like `destroy_ep_files` is already covered by `activate_ep_files` in the failure path. The error path within `gadgetfs_bind` *does* perform manual cleanup. This reinforces my view that this approach is sufficient, as `activate_ep_files` handles all endpoint files cleanup that is necessary on failure.
|
| 200/4 |
2026/08/07 05:48 |
tool |
patch-diff |
0m
Args:
Results: map[Output:diff --git a/drivers/usb/gadget/legacy/inode.c b/drivers/usb/gadget/legacy/inode.c
index d87a8ab51..1a05819c0 100644
--- a/drivers/usb/gadget/legacy/inode.c
+++ b/drivers/usb/gadget/legacy/inode.c
@@ -1668,46 +1668,52 @@ static struct dev_data *the_device;
static int gadgetfs_bind(struct usb_gadget *gadget,
struct usb_gadget_driver *driver)
{
struct dev_data *dev = the_device;
if (!dev)
return -ESRCH;
if (0 != strcmp (CHIP, gadget->name)) {
pr_err("%s expected %s controller not %s\n",
shortname, CHIP, gadget->name);
return -ENODEV;
}
set_gadget_data (gadget, dev);
dev->gadget = gadget;
gadget->ep0->driver_data = dev;
/* preallocate control response and buffer */
dev->req = usb_ep_alloc_request (gadget->ep0, GFP_KERNEL);
if (!dev->req)
goto enomem;
dev->req->context = NULL;
dev->req->complete = epio_complete;
if (activate_ep_files (dev) < 0)
goto enomem;
INFO (dev, "bound to %s driver\n", gadget->name);
spin_lock_irq(&dev->lock);
dev->state = STATE_DEV_UNCONNECTED;
spin_unlock_irq(&dev->lock);
get_dev (dev);
return 0;
enomem:
- gadgetfs_unbind (gadget);
+ if (dev->req) {
+ usb_ep_free_request (gadget->ep0, dev->req);
+ dev->req = NULL;
+ }
+ gadget->ep0->driver_data = NULL;
+ dev->gadget = NULL;
+ set_gadget_data (gadget, NULL);
return -ENOMEM;
}
static void
gadgetfs_disconnect (struct usb_gadget *gadget)
{
struct dev_data *dev = get_gadget_data (gadget);
unsigned long flags;
spin_lock_irqsave (&dev->lock, flags);
]
|
| 201/4 |
2026/08/07 05:48 |
llm |
patch-generator |
0m
Model:
Tokens:
input: 28185
output: 502
thoughts: 14
Thoughts: Here's a summary of my thinking process, rendered as requested:
**Patch Complete and Explanation Time**
Okay, the patch is finished, and the tests all pass. I've reviewed it thoroughly, and I'm confident in its correctness. Now, it's time to articulate exactly *why* this fix works and how it addresses the underlying problem. I need to be precise, clear, and ensure the explanation is easy to understand, even for someone who might not be as deeply familiar with this specific area of the codebase as I am. Time to structure my thoughts to make sure all important aspects are conveyed. I need to explain the problem concisely, describe my approach, detail the specific code changes, and then justify the choices made, including any trade-offs considered. The goal is to provide a complete and convincing narrative that leaves no room for confusion. Let's make this explanation rock-solid.
|
| 202/3 |
2026/08/07 05:48 |
action |
test-patch |
19m
Results: map[PatchDiff:diff --git a/drivers/usb/gadget/legacy/inode.c b/drivers/usb/gadget/legacy/inode.c
index d87a8ab51..1a05819c0 100644
--- a/drivers/usb/gadget/legacy/inode.c
+++ b/drivers/usb/gadget/legacy/inode.c
@@ -1700,7 +1700,13 @@ static int gadgetfs_bind(struct usb_gadget *gadget,
return 0;
enomem:
- gadgetfs_unbind (gadget);
+ if (dev->req) {
+ usb_ep_free_request (gadget->ep0, dev->req);
+ dev->req = NULL;
+ }
+ gadget->ep0->driver_data = NULL;
+ dev->gadget = NULL;
+ set_gadget_data (gadget, NULL);
return -ENOMEM;
}
TestError:]
|
| 203/1 |
2026/08/07 06:08 |
action |
apply-patch |
0m
Results:
|
| 204/1 |
2026/08/07 06:08 |
action |
init-style-items |
0m
Results:
|
| 205/1 |
2026/08/07 06:08 |
loop |
|
4m
|
| 206/2 |
2026/08/07 06:08 |
iteration |
0 |
4m
|
| 207/3 |
2026/08/07 06:08 |
agent |
patch-formatter |
0m
Model:
Instruction:
You are an expert Linux kernel developer tasked with formatting a kernel patch.
Your objective is purely formatting: you must ensure the patch complies with the kernel's coding style,
conforms to the surrounding code rules, and passes checkpatch.pl, while preserving the code logic exactly as it is.
You should stop once the requested formatting changes are done and checkpatch.pl is happy.
Do not question the requested changes unless they are obviously wrong.
If the code already conforms to the requested changes, surrounding code rules,
and checkpatch.pl is happy, you should just finish your task.
WARNING: The clang-format tool may break the formatting of the surrounding code (like manual alignment).
Use it with caution. We want to make the change fit into the existing formatting as much as possible.
Prefer calling several tools at the same time to save round-trips.
Prompt:
The current patch diff is:
diff --git a/drivers/usb/gadget/legacy/inode.c b/drivers/usb/gadget/legacy/inode.c
index d87a8ab51..1a05819c0 100644
--- a/drivers/usb/gadget/legacy/inode.c
+++ b/drivers/usb/gadget/legacy/inode.c
@@ -1700,7 +1700,13 @@ static int gadgetfs_bind(struct usb_gadget *gadget,
return 0;
enomem:
- gadgetfs_unbind (gadget);
+ if (dev->req) {
+ usb_ep_free_request (gadget->ep0, dev->req);
+ dev->req = NULL;
+ }
+ gadget->ep0->driver_data = NULL;
+ dev->gadget = NULL;
+ set_gadget_data (gadget, NULL);
return -ENOMEM;
}
Use the provided tools to format the patch.
Reply: I have formatted the patch to comply with the Linux kernel's coding style and pass `checkpatch.pl` with zero errors and zero warnings, while preserving the exact logic of the changes.
Here is the formatted patch:
```diff
diff --git a/drivers/usb/gadget/legacy/inode.c b/drivers/usb/gadget/legacy/inode.c
index d87a8ab51..365f9ab4d 100644
--- a/drivers/usb/gadget/legacy/inode.c
+++ b/drivers/usb/gadget/legacy/inode.c
@@ -1700,7 +1700,13 @@ static int gadgetfs_bind(struct usb_gadget *gadget,
return 0;
enomem:
- gadgetfs_unbind (gadget);
+ if (dev->req) {
+ usb_ep_free_request(gadget->ep0, dev->req);
+ dev->req = NULL;
+ }
+ gadget->ep0->driver_data = NULL;
+ dev->gadget = NULL;
+ set_gadget_data(gadget, NULL);
return -ENOMEM;
}
```
|
| 208/4 |
2026/08/07 06:08 |
llm |
patch-formatter |
0m
Model:
Tokens:
input: 4557
output: 41
thoughts: 95
Thoughts: **Kernel Patch Formatting - Initial Assessment**
Okay, so the user wants me to format a kernel patch. My first step is to get a solid grasp of what's happening. I need to dive into the patch diff itself and, importantly, the relevant file – `drivers/usb/gadget/legacy/inode.c`. That's where the heart of this seems to be. To truly understand the context of this patch, I'm going to read around line 1700 of `drivers/usb/gadget/legacy/inode.c`. Knowing the surrounding code is crucial for proper formatting and ensuring it aligns with existing style. I'll use `read-file` to get a good chunk of the code from there and get the context. Let's see what we are dealing with.
|
| 209/4 |
2026/08/07 06:08 |
tool |
read-file |
0m
Args: map[File:drivers/usb/gadget/legacy/inode.c FirstLine:1680 LineCount:40]
Results: map[Contents:1680:
1681: set_gadget_data (gadget, dev);
1682: dev->gadget = gadget;
1683: gadget->ep0->driver_data = dev;
1684:
1685: /* preallocate control response and buffer */
1686: dev->req = usb_ep_alloc_request (gadget->ep0, GFP_KERNEL);
1687: if (!dev->req)
1688: goto enomem;
1689: dev->req->context = NULL;
1690: dev->req->complete = epio_complete;
1691:
1692: if (activate_ep_files (dev) < 0)
1693: goto enomem;
1694:
1695: INFO (dev, "bound to %s driver\n", gadget->name);
1696: spin_lock_irq(&dev->lock);
1697: dev->state = STATE_DEV_UNCONNECTED;
1698: spin_unlock_irq(&dev->lock);
1699: get_dev (dev);
1700: return 0;
1701:
1702: enomem:
1703: gadgetfs_unbind (gadget);
1704: return -ENOMEM;
1705: }
1706:
1707: static void
1708: gadgetfs_disconnect (struct usb_gadget *gadget)
1709: {
1710: struct dev_data *dev = get_gadget_data (gadget);
1711: unsigned long flags;
1712:
1713: spin_lock_irqsave (&dev->lock, flags);
1714: if (dev->state == STATE_DEV_UNCONNECTED)
1715: goto exit;
1716: dev->state = STATE_DEV_UNCONNECTED;
1717:
1718: INFO (dev, "disconnected\n");
1719: next_event (dev, GADGETFS_DISCONNECT);
]
|
| 210/4 |
2026/08/07 06:08 |
llm |
patch-formatter |
0m
Model:
Tokens:
input: 5450
output: 27
thoughts: 58
Thoughts: Okay, here's my summary, considering my expertise:
**Checking Patch Application and Formatting**
Alright, before I dive into any deeper analysis, my initial step is always to ensure the patch is cleanly applied and adheres to the project's coding standards. My immediate instinct is to run `checkpatch`. That'll give me a rapid assessment of any style violations, potential issues, and basic coding errors in the diff. However, I'm just reminded, I have to assume the patch is in the repo, so a quick `patch-diff` check is probably best. This will tell me definitively whether the changes are already incorporated. It’s a crucial first step; nothing wastes time like reviewing already-merged code or debugging a patch that hasn't even been integrated yet.
|
| 211/4 |
2026/08/07 06:08 |
tool |
patch-diff |
0m
Args: map[File:drivers/usb/gadget/legacy/inode.c]
Results: map[Output:diff --git a/drivers/usb/gadget/legacy/inode.c b/drivers/usb/gadget/legacy/inode.c
index d87a8ab51..1a05819c0 100644
--- a/drivers/usb/gadget/legacy/inode.c
+++ b/drivers/usb/gadget/legacy/inode.c
@@ -1668,46 +1668,52 @@ static struct dev_data *the_device;
static int gadgetfs_bind(struct usb_gadget *gadget,
struct usb_gadget_driver *driver)
{
struct dev_data *dev = the_device;
if (!dev)
return -ESRCH;
if (0 != strcmp (CHIP, gadget->name)) {
pr_err("%s expected %s controller not %s\n",
shortname, CHIP, gadget->name);
return -ENODEV;
}
set_gadget_data (gadget, dev);
dev->gadget = gadget;
gadget->ep0->driver_data = dev;
/* preallocate control response and buffer */
dev->req = usb_ep_alloc_request (gadget->ep0, GFP_KERNEL);
if (!dev->req)
goto enomem;
dev->req->context = NULL;
dev->req->complete = epio_complete;
if (activate_ep_files (dev) < 0)
goto enomem;
INFO (dev, "bound to %s driver\n", gadget->name);
spin_lock_irq(&dev->lock);
dev->state = STATE_DEV_UNCONNECTED;
spin_unlock_irq(&dev->lock);
get_dev (dev);
return 0;
enomem:
- gadgetfs_unbind (gadget);
+ if (dev->req) {
+ usb_ep_free_request (gadget->ep0, dev->req);
+ dev->req = NULL;
+ }
+ gadget->ep0->driver_data = NULL;
+ dev->gadget = NULL;
+ set_gadget_data (gadget, NULL);
return -ENOMEM;
}
static void
gadgetfs_disconnect (struct usb_gadget *gadget)
{
struct dev_data *dev = get_gadget_data (gadget);
unsigned long flags;
spin_lock_irqsave (&dev->lock, flags);
]
|
| 212/4 |
2026/08/07 06:08 |
llm |
patch-formatter |
0m
Model:
Tokens:
input: 6370
output: 9
thoughts: 26
Thoughts: Here's my attempt at summarizing the thought process, tailored for an expert audience:
**Quick Check: Patch Applied, Time for Style**
Alright, looks like that patch I was waiting on is already integrated. Good, good. Now, before I even *think* about diving into the functional aspects, I want to ensure we're adhering to our established coding style. Muscle memory takes over – time to run `checkpatch`. I'm hoping it's clean, of course, but experience tells me there's almost *always* something to tweak. Might be a subtle whitespace issue, perhaps an overly long line, or maybe something more substantial regarding commenting conventions. Either way, `checkpatch` is the first line of defense; ensures consistency and readability, which are paramount, and a baseline before diving into the core logic.
|
| 213/4 |
2026/08/07 06:08 |
tool |
checkpatch |
0m
Results: map[Output:WARNING: space prohibited between function name and open parenthesis '('
#11: FILE: drivers/usb/gadget/legacy/inode.c:1704:
+ usb_ep_free_request (gadget->ep0, dev->req);
WARNING: space prohibited between function name and open parenthesis '('
#16: FILE: drivers/usb/gadget/legacy/inode.c:1709:
+ set_gadget_data (gadget, NULL);
total: 0 errors, 2 warnings, 14 lines checked
NOTE: For some of the reported defects, checkpatch may be able to
mechanically convert to the typical style using --fix or --fix-inplace.
Your patch has style problems, please review.
NOTE: If any of the errors are false positives, please report
them to the maintainer, see CHECKPATCH in MAINTAINERS.
]
|
| 214/4 |
2026/08/07 06:08 |
llm |
patch-formatter |
0m
Model:
Tokens:
input: 6789
output: 217
thoughts: 540
Thoughts: **Analyzing and Correcting C Code Formatting**
Okay, let's break this down. First, I'm seeing warnings from `checkpatch.pl` regarding spaces before parentheses in these two lines: `usb_ep_free_request (gadget->ep0, dev->req);` and `set_gadget_data (gadget, NULL);`. My instinct is always to check the surrounding code for consistency, and the snippet provided shows a mix. We have lines like `set_gadget_data (gadget, dev);` and `usb_ep_alloc_request (gadget->ep0, GFP_KERNEL);` with spaces, as well as `strcmp (CHIP, gadget->name)` and `spin_lock_irqsave (&dev->lock, flags);` using the proper formatting.
However, the directive is clear. My objective is formatting and `checkpatch.pl` compliance, preserving the code's functionality, so I need to listen to the tool.
The objective is to make `checkpatch.pl` happy with the formatting. Given the rules I am working within, it seems the formatting for the functions `usb_ep_free_request` and `set_gadget_data` needs to change.
So I need to check the relevant block of code.
```c
if (dev->req) {
usb_ep_free_request(gadget->ep0, dev->req);
dev->req = NULL;
}
gadget->ep0->driver_data = NULL;
dev->gadget = NULL;
set_gadget_data(gadget, NULL);
```
Checking this block, I see these two lines that need to be changed: `usb_ep_free_request(gadget->ep0, dev->req);` and `set_gadget_data(gadget, NULL);`. There are no other formatting issues. So, the fix is simple: remove the space before the open parenthesis for both function calls. That's it. Time to get this done.
|
| 215/4 |
2026/08/07 06:08 |
tool |
codeeditor |
0m
Args: map[CurrentCode:enomem:
if (dev->req) {
usb_ep_free_request (gadget->ep0, dev->req);
dev->req = NULL;
}
gadget->ep0->driver_data = NULL;
dev->gadget = NULL;
set_gadget_data (gadget, NULL);
return -ENOMEM; NewCode:enomem:
if (dev->req) {
usb_ep_free_request(gadget->ep0, dev->req);
dev->req = NULL;
}
gadget->ep0->driver_data = NULL;
dev->gadget = NULL;
set_gadget_data(gadget, NULL);
return -ENOMEM; SourceFile:drivers/usb/gadget/legacy/inode.c]
Results:
|
| 216/4 |
2026/08/07 06:08 |
llm |
patch-formatter |
0m
Model:
Tokens:
input: 8001
output: 9
thoughts: 17
Thoughts: Okay, here's my interpretation of those thoughts, crafted with an expert audience in mind:
**Rerunning the Patch Checker**
Alright, time to revisit this. I've been wrestling with this patch, making a few tweaks based on previous feedback. It's always a good practice to revisit the standard checking tool, so let's run `checkpatch` again. I want to make absolutely sure I've addressed all the style issues, coding conventions, and potential gotchas before I even consider submitting it for review. It's about being thorough and respecting the maintainers' time. Let's see if this iteration finally earns a clean bill of health. No point in wasting anyone's time with a submission that's going to be rejected for easily avoidable formatting issues. Hopefully this will be the last time I run it.
|
| 217/4 |
2026/08/07 06:08 |
tool |
checkpatch |
0m
Results: map[Output:total: 0 errors, 0 warnings, 14 lines checked
Your patch has no obvious style problems and is ready for submission.
]
|
| 218/4 |
2026/08/07 06:08 |
llm |
patch-formatter |
0m
Model:
Tokens:
input: 8244
output: 27
thoughts: 38
Thoughts: Okay, here's my take on those thoughts, summarizing them from a first-person perspective, assuming I'm an expert:
**Code Review - A Clean Bill of Health & Verification**
Alright, *finally*. `checkpatch` is giving me the green light – absolutely no errors or warnings. That's a good sign, a strong indication that the formatting and style are spot-on. However, I never fully trust an automated tool, not even this one which I have spent a lot of time reviewing. So, let's not get ahead of ourselves. I'm going to run a `patch-diff` on this now. I want to be 100% certain that the changes are clean, understandable at a glance, and reflect exactly what I intend. This extra step ensures that I haven't missed anything subtle, or introduced a hidden issue with my latest work, which sometimes even a seasoned expert such as myself has been known to do. Time to double-check those changes with another set of eyes – my own!
|
| 219/4 |
2026/08/07 06:08 |
tool |
patch-diff |
0m
Args: map[File:drivers/usb/gadget/legacy/inode.c]
Results: map[Output:diff --git a/drivers/usb/gadget/legacy/inode.c b/drivers/usb/gadget/legacy/inode.c
index d87a8ab51..365f9ab4d 100644
--- a/drivers/usb/gadget/legacy/inode.c
+++ b/drivers/usb/gadget/legacy/inode.c
@@ -1668,46 +1668,52 @@ static struct dev_data *the_device;
static int gadgetfs_bind(struct usb_gadget *gadget,
struct usb_gadget_driver *driver)
{
struct dev_data *dev = the_device;
if (!dev)
return -ESRCH;
if (0 != strcmp (CHIP, gadget->name)) {
pr_err("%s expected %s controller not %s\n",
shortname, CHIP, gadget->name);
return -ENODEV;
}
set_gadget_data (gadget, dev);
dev->gadget = gadget;
gadget->ep0->driver_data = dev;
/* preallocate control response and buffer */
dev->req = usb_ep_alloc_request (gadget->ep0, GFP_KERNEL);
if (!dev->req)
goto enomem;
dev->req->context = NULL;
dev->req->complete = epio_complete;
if (activate_ep_files (dev) < 0)
goto enomem;
INFO (dev, "bound to %s driver\n", gadget->name);
spin_lock_irq(&dev->lock);
dev->state = STATE_DEV_UNCONNECTED;
spin_unlock_irq(&dev->lock);
get_dev (dev);
return 0;
enomem:
- gadgetfs_unbind (gadget);
+ if (dev->req) {
+ usb_ep_free_request(gadget->ep0, dev->req);
+ dev->req = NULL;
+ }
+ gadget->ep0->driver_data = NULL;
+ dev->gadget = NULL;
+ set_gadget_data(gadget, NULL);
return -ENOMEM;
}
static void
gadgetfs_disconnect (struct usb_gadget *gadget)
{
struct dev_data *dev = get_gadget_data (gadget);
unsigned long flags;
spin_lock_irqsave (&dev->lock, flags);
]
|
| 220/4 |
2026/08/07 06:08 |
llm |
patch-formatter |
0m
Model:
Tokens:
input: 9198
output: 296
thoughts: 39
Thoughts: Okay, here's my summary, presented as my own internal thought process, assuming I'm an expert:
**Ready to Ship This Patch**
Alright, let's take a look. This diff is *pristine*. Whoever formatted this patch did an exceptional job. The formatting is spot on; it's clean, easy to read, and adheres perfectly to the style guidelines. The logic? It's all there, exactly as it should be. They haven't altered any functionality, just cleaned up the presentation, which is exactly what I needed. And let's see... *checkpatch.pl* is giving it a clean bill of health. No warnings, no errors, nothing. Perfect! I'm completely satisfied. This patch is ready for review. I'm ready to sign off on this and get it submitted. Time to wrap this up and present it.
|
| 221/3 |
2026/08/07 06:08 |
action |
test-patch-inplace |
4m
Results: map[PatchDiff:diff --git a/drivers/usb/gadget/legacy/inode.c b/drivers/usb/gadget/legacy/inode.c
index d87a8ab51..365f9ab4d 100644
--- a/drivers/usb/gadget/legacy/inode.c
+++ b/drivers/usb/gadget/legacy/inode.c
@@ -1700,7 +1700,13 @@ static int gadgetfs_bind(struct usb_gadget *gadget,
return 0;
enomem:
- gadgetfs_unbind (gadget);
+ if (dev->req) {
+ usb_ep_free_request(gadget->ep0, dev->req);
+ dev->req = NULL;
+ }
+ gadget->ep0->driver_data = NULL;
+ dev->gadget = NULL;
+ set_gadget_data(gadget, NULL);
return -ENOMEM;
}
TestError:]
|
| 222/3 |
2026/08/07 06:12 |
action |
run-checkpatch |
0m
Results: map[CheckpatchOutput:total: 0 errors, 0 warnings, 14 lines checked
Your patch has no obvious style problems and is ready for submission.
NeedRefinement:false]
|
| 223/1 |
2026/08/07 06:12 |
agent |
fixes-finder |
12m
Model:
Results: map[FixesHash:1da177e4c3f41524e886b7f1b8a0c1fc7321cac2]
Instruction:
You are an experienced Linux kernel developer tasked with identifying the commit
that introduced the bug being fixed. Identifying the correct buggy commit is crucial
for proper kernel maintenance (backporting to stable trees, etc.).
Your investigation strategy:
1. Examine the patch that fixes the bug. Use git tools (like git-log or git-blame)
to trace the history of the lines or functions modified by the patch.
2. Analyze the stack trace in the crash report. Identify the key files and functions
involved in the crash and investigate their history to see when the problematic
logic was introduced.
3. Compare the bug explanation with the commit history to find the point where
the described logic error first appeared.
A bug is typically introduced when a piece of code is first written, or when
a refactoring changed its logic in a way that introduced the bug.
Trace the history of relevant symbols or find when specific code patterns were introduced/removed.
You must provide exactly one bug-introducing commit hash.
If you are unable to confidently determine the bug-introducing commit after investigation,
return an empty string rather than guessing.
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 crash is:
udc dummy_udc.0: failed to start USB Gadget filesystem: -12
gadgetfs gadget.0: probe with driver gadgetfs failed with error -12
gadgetfs: bound to dummy_udc driver
==================================================================
BUG: KASAN: slab-use-after-free in instrument_atomic_read_write include/linux/instrumented.h:112 [inline]
BUG: KASAN: slab-use-after-free in atomic_fetch_sub_release include/linux/atomic/atomic-instrumented.h:400 [inline]
BUG: KASAN: slab-use-after-free in __refcount_sub_and_test include/linux/refcount.h:389 [inline]
BUG: KASAN: slab-use-after-free in __refcount_dec_and_test include/linux/refcount.h:432 [inline]
BUG: KASAN: slab-use-after-free in refcount_dec_and_test include/linux/refcount.h:450 [inline]
BUG: KASAN: slab-use-after-free in put_dev drivers/usb/gadget/legacy/inode.c:165 [inline]
BUG: KASAN: slab-use-after-free in gadgetfs_kill_sb+0x46/0x110 drivers/usb/gadget/legacy/inode.c:2104
Write of size 4 at addr ffff88811638c080 by task syz-executor169/5848
CPU: 0 UID: 0 PID: 5848 Comm: syz-executor169 Not tainted syzkaller #1 PREEMPT_{RT,(full)}
Hardware name: QEMU Standard PC (Q35 + ICH9, 2009), BIOS 1.16.3-debian-1.16.3-2 04/01/2014
Call Trace:
<TASK>
dump_stack_lvl+0xe8/0x150 lib/dump_stack.c:120
print_address_description+0x55/0x1e0 mm/kasan/report.c:378
print_report+0x58/0x70 mm/kasan/report.c:482
kasan_report+0x117/0x150 mm/kasan/report.c:595
check_region_inline mm/kasan/generic.c:-1 [inline]
kasan_check_range+0x264/0x2c0 mm/kasan/generic.c:200
instrument_atomic_read_write include/linux/instrumented.h:112 [inline]
atomic_fetch_sub_release include/linux/atomic/atomic-instrumented.h:400 [inline]
__refcount_sub_and_test include/linux/refcount.h:389 [inline]
__refcount_dec_and_test include/linux/refcount.h:432 [inline]
refcount_dec_and_test include/linux/refcount.h:450 [inline]
put_dev drivers/usb/gadget/legacy/inode.c:165 [inline]
gadgetfs_kill_sb+0x46/0x110 drivers/usb/gadget/legacy/inode.c:2104
deactivate_locked_super+0xbc/0x130 fs/super.c:477
cleanup_mnt+0x3d3/0x460 fs/namespace.c:1317
task_work_run+0x1d9/0x270 kernel/task_work.c:233
resume_user_mode_work include/linux/resume_user_mode.h:50 [inline]
__exit_to_user_mode_loop kernel/entry/common.c:70 [inline]
exit_to_user_mode_loop+0x1fa/0x730 kernel/entry/common.c:101
__exit_to_user_mode_prepare include/linux/irq-entry-common.h:207 [inline]
syscall_exit_to_user_mode_prepare include/linux/irq-entry-common.h:230 [inline]
syscall_exit_to_user_mode include/linux/entry-common.h:318 [inline]
do_syscall_64+0x353/0x580 arch/x86/entry/syscall_64.c:100
entry_SYSCALL_64_after_hwframe+0x77/0x7f
RIP: 0033:0x7f5f0e505027
Code: a2 c7 05 54 b0 08 00 00 00 00 00 eb 96 e8 a1 08 00 00 90 31 f6 e9 09 00 00 00 66 0f 1f 84 00 00 00 00 00 b8 a6 00 00 00 0f 05 <48> 3d 00 f0 ff ff 77 01 c3 48 c7 c2 d8 ff ff ff f7 d8 64 89 02 b8
RSP: 002b:00007ffcdf2b0288 EFLAGS: 00000246 ORIG_RAX: 00000000000000a6
RAX: 0000000000000000 RBX: 0000000000000003 RCX: 00007f5f0e505027
RDX: 0000000000000000 RSI: 0000000000000000 RDI: 00007f5f0e55f05e
RBP: 00007f5f0e55f05e R08: 0000000000000000 R09: 0000000000000000
R10: 0000000000000000 R11: 0000000000000246 R12: 00007f5f0e55f623
R13: 00007ffcdf2b03e0 R14: 00007f5f0e561510 R15: 00007f5f0e5614e8
</TASK>
Allocated by task 5848:
kasan_save_stack mm/kasan/common.c:57 [inline]
kasan_save_track+0x3e/0x80 mm/kasan/common.c:78
poison_kmalloc_redzone mm/kasan/common.c:398 [inline]
__kasan_kmalloc+0x93/0xb0 mm/kasan/common.c:415
kasan_kmalloc include/linux/kasan.h:263 [inline]
__kmalloc_cache_noprof+0x3d2/0x6b0 mm/slub.c:5489
_kmalloc_noprof include/linux/slab.h:988 [inline]
_kzalloc_noprof include/linux/slab.h:1309 [inline]
dev_new drivers/usb/gadget/legacy/inode.c:176 [inline]
gadgetfs_fill_super+0x27b/0x7a0 drivers/usb/gadget/legacy/inode.c:2054
vfs_get_super fs/super.c:1273 [inline]
get_tree_single+0xc0/0x150 fs/super.c:1300
vfs_get_tree+0x92/0x2a0 fs/super.c:1700
fc_mount fs/namespace.c:1198 [inline]
do_new_mount_fc fs/namespace.c:3765 [inline]
do_new_mount+0x319/0xdc0 fs/namespace.c:3841
do_mount fs/namespace.c:4174 [inline]
__do_sys_mount fs/namespace.c:4390 [inline]
__se_sys_mount+0x31d/0x420 fs/namespace.c:4367
do_syscall_x64 arch/x86/entry/syscall_64.c:63 [inline]
do_syscall_64+0x174/0x580 arch/x86/entry/syscall_64.c:94
entry_SYSCALL_64_after_hwframe+0x77/0x7f
Freed by task 5848:
kasan_save_stack mm/kasan/common.c:57 [inline]
kasan_save_track+0x3e/0x80 mm/kasan/common.c:78
kasan_save_free_info+0x40/0x50 mm/kasan/generic.c:584
poison_slab_object mm/kasan/common.c:253 [inline]
__kasan_slab_free+0x5c/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 mm/slub.c:6377 [inline]
kfree+0x1c5/0x6c0 mm/slub.c:6692
put_dev drivers/usb/gadget/legacy/inode.c:169 [inline]
dev_release+0x173/0x210 drivers/usb/gadget/legacy/inode.c:1215
__fput+0x42a/0xa80 fs/file_table.c:512
fput_close_sync+0x11f/0x240 fs/file_table.c:617
__do_sys_close fs/open.c:1511 [inline]
__se_sys_close fs/open.c:1496 [inline]
__x64_sys_close+0x7e/0x110 fs/open.c:1496
do_syscall_x64 arch/x86/entry/syscall_64.c:63 [inline]
do_syscall_64+0x174/0x580 arch/x86/entry/syscall_64.c:94
entry_SYSCALL_64_after_hwframe+0x77/0x7f
The buggy address belongs to the object at ffff88811638c000
which belongs to the cache kmalloc-1k of size 1024
The buggy address is located 128 bytes inside of
freed 1024-byte region [ffff88811638c000, ffff88811638c400)
The buggy address belongs to the physical page:
page: refcount:0 mapcount:0 mapping:0000000000000000 index:0xffff88811638d000 pfn:0x116388
head: order:3 mapcount:0 entire_mapcount:0 nr_pages_mapped:0 pincount:0
flags: 0x100000000000240(workingset|head|node=0|zone=2)
page_type: f5(slab)
raw: 0100000000000240 ffff888100041dc0 ffffea00044a8a10 ffffea00043da810
raw: ffff88811638d000 000000080010000f 00000000f5000000 0000000000000000
head: 0100000000000240 ffff888100041dc0 ffffea00044a8a10 ffffea00043da810
head: ffff88811638d000 000000080010000f 00000000f5000000 0000000000000000
head: 0100000000000003 fffffffffffffe01 00000000ffffffff 00000000ffffffff
head: 0000000000000000 0000000000000000 00000000ffffffff 0000000000000008
page dumped because: kasan: bad access detected
page_owner tracks the page as allocated
page last allocated via order 3, migratetype Unmovable, gfp_mask 0xd20c0(__GFP_IO|__GFP_FS|__GFP_NOWARN|__GFP_NORETRY|__GFP_COMP|__GFP_NOMEMALLOC), pid 5581, tgid 5581 (syz-executor), ts 49160475111, free_ts 0
set_page_owner include/linux/page_owner.h:32 [inline]
post_alloc_hook+0x1f9/0x250 mm/page_alloc.c:1859
prep_new_page mm/page_alloc.c:1867 [inline]
get_page_from_freelist+0x262a/0x26a0 mm/page_alloc.c:3946
__alloc_frozen_pages_noprof+0x18d/0x380 mm/page_alloc.c:5304
alloc_slab_page mm/slub.c:3266 [inline]
allocate_slab+0x79/0x5e0 mm/slub.c:3380
new_slab mm/slub.c:3426 [inline]
refill_objects+0x2d8/0x350 mm/slub.c:7310
refill_sheaf mm/slub.c:2804 [inline]
__pcs_replace_empty_main+0x330/0x690 mm/slub.c:4675
alloc_from_pcs mm/slub.c:4773 [inline]
slab_alloc_node mm/slub.c:4905 [inline]
__kmalloc_cache_noprof+0x468/0x6b0 mm/slub.c:5485
_kmalloc_noprof include/linux/slab.h:988 [inline]
_kzalloc_noprof include/linux/slab.h:1309 [inline]
rxrpc_service_prealloc+0x9f/0x130 net/rxrpc/call_accept.c:167
rxrpc_listen+0x1b7/0x360 net/rxrpc/af_rxrpc.c:237
afs_open_socket+0x2fa/0x3f0 fs/afs/rxrpc.c:107
afs_net_init+0x6ce/0x8f0 fs/afs/main.c:116
ops_init+0x35d/0x5d0 net/core/net_namespace.c:137
setup_net+0x118/0x350 net/core/net_namespace.c:446
copy_net_ns+0x4f9/0x720 net/core/net_namespace.c:579
create_new_namespaces+0x3f0/0x6b0 kernel/nsproxy.c:132
unshare_nsproxy_namespaces+0x149/0x190 kernel/nsproxy.c:234
page_owner free stack trace missing
Memory state around the buggy address:
ffff88811638bf80: fc fc fc fc fc fc fc fc fc fc fc fc fc fc fc fc
ffff88811638c000: fa fb fb fb fb fb fb fb fb fb fb fb fb fb fb fb
>ffff88811638c080: fb fb fb fb fb fb fb fb fb fb fb fb fb fb fb fb
^
ffff88811638c100: fb fb fb fb fb fb fb fb fb fb fb fb fb fb fb fb
ffff88811638c180: fb fb fb fb fb fb fb fb fb fb fb fb fb fb fb fb
==================================================================
The explanation of the root cause is:
The root cause of this KASAN slab-use-after-free is a reference count underflow in the error handling path of `gadgetfs_bind()`.
When fault injection forces a memory allocation failure during the binding process, `gadgetfs_bind()` incorrectly calls `gadgetfs_unbind()`, which drops a reference to the device (`dev`) that was never actually taken.
### Detailed Sequence of Events
1. **Initialization and Open:**
When the gadgetfs superblock is created, `dev_new()` allocates the `dev_data` structure (`dev`) and initializes its refcount to 1. When the user opens the `/tmp/gadgetfs/ep0` file, `gadget_dev_open()` is called, which increments the refcount to 2 via `get_dev(dev)`.
2. **Driver Registration and Binding:**
The user writes configuration data to `ep0`, invoking `dev_config()`. This function calls `usb_gadget_register_driver(&gadgetfs_driver)`, which registers the driver and attempts to bind it to the UDC, eventually calling `gadgetfs_bind()`.
3. **Fault Injection and Refcount Underflow:**
In `gadgetfs_bind()`, memory allocations are performed (e.g., `usb_ep_alloc_request()` or inside `activate_ep_files()`). If fault injection causes one of these to fail, the code jumps to the `enomem` label:
```c
// drivers/usb/gadget/legacy/inode.c : gadgetfs_bind()
dev->req = usb_ep_alloc_request (gadget->ep0, GFP_KERNEL);
if (!dev->req)
goto enomem;
...
if (activate_ep_files (dev) < 0)
goto enomem;
...
get_dev (dev); // <--- Reference is only taken on SUCCESS
return 0;
enomem:
gadgetfs_unbind (gadget);
return -ENOMEM;
```
The `enomem` path calls `gadgetfs_unbind()`, which performs cleanup and unconditionally calls `put_dev(dev)`. Because `get_dev(dev)` is only called at the very end of `gadgetfs_bind()` on success, `gadgetfs_unbind()` drops a reference that was never acquired. The refcount incorrectly drops from 2 to 1.
4. **Probe Failure Ignored:**
The `-ENOMEM` error propagates up to the driver core. However, `__driver_attach()` intentionally ignores probe failures and returns `0`. Because `gadgetfs_driver` does not set `match_existing_only = true`, `usb_gadget_register_driver_owner()` also returns `0` (success).
5. **Closing ep0 and Freeing:**
Seeing a success return value, `dev_config()` sets `dev->gadget_registered = true`.
Later, the reproducer closes the `ep0` file descriptor, triggering `dev_release()`. `dev_release()` cleans up and calls `put_dev(dev)`. The refcount drops from 1 to 0, and `dev` is prematurely freed.
6. **The Use-After-Free:**
Finally, the reproducer unmounts the filesystem, which invokes `gadgetfs_kill_sb()`.
```c
// drivers/usb/gadget/legacy/inode.c : gadgetfs_kill_sb()
if (the_device) {
put_dev (the_device); // <--- UAF here
the_device = NULL;
}
```
`gadgetfs_kill_sb()` attempts to drop the initial reference by calling `put_dev(the_device)`. Since `the_device` points to the `dev` structure that was already freed in step 5, this triggers the KASAN slab-use-after-free.
### Suggested Fix
`gadgetfs_bind()` should not call `gadgetfs_unbind()` in its error path, as `gadgetfs_unbind()` is designed to tear down a fully bound gadget (and thus expects the extra reference to exist). Instead, `gadgetfs_bind()` should manually clean up the specific state it altered before the failure occurred.
```c
enomem:
- gadgetfs_unbind (gadget);
+ if (dev->req) {
+ usb_ep_free_request (gadget->ep0, dev->req);
+ dev->req = NULL;
+ }
+ gadget->ep0->driver_data = NULL;
+ set_gadget_data (gadget, NULL);
+ dev->gadget = NULL;
return -ENOMEM;
```
*(Note: `activate_ep_files()` already perfectly cleans up after itself on failure, so no additional teardown is needed for it in `gadgetfs_bind()`'s error path).*
The patch that fixes the bug is:
diff --git a/drivers/usb/gadget/legacy/inode.c b/drivers/usb/gadget/legacy/inode.c
index d87a8ab51..365f9ab4d 100644
--- a/drivers/usb/gadget/legacy/inode.c
+++ b/drivers/usb/gadget/legacy/inode.c
@@ -1700,7 +1700,13 @@ static int gadgetfs_bind(struct usb_gadget *gadget,
return 0;
enomem:
- gadgetfs_unbind (gadget);
+ if (dev->req) {
+ usb_ep_free_request(gadget->ep0, dev->req);
+ dev->req = NULL;
+ }
+ gadget->ep0->driver_data = NULL;
+ dev->gadget = NULL;
+ set_gadget_data(gadget, NULL);
return -ENOMEM;
}
Search for the commit(s) that introduced this bug.
|
| 224/2 |
2026/08/07 06:12 |
llm |
fixes-finder |
0m
Model:
Tokens:
input: 9675
output: 39
thoughts: 211
Thoughts: **Pinpointing the Root Cause of the USB Gadgetfs Bug**
Okay, so we've got a patch that fixes a `gadgetfs` bug. My immediate thought is, let's nail down precisely *when* this bug was introduced. We're dealing with `drivers/usb/gadget/legacy/inode.c`, specifically the `gadgetfs_bind()` function. The core of the issue seems to be the error handling in the `enomem` path, where `gadgetfs_unbind(gadget)` is called, and the patch provides manual cleanup in its place.
My plan? Dive into the commit history. I need to trace the evolution of that `gadgetfs_bind()` function within `inode.c` (or perhaps the earlier incarnation, without the `legacy/` directory) and identify the specific commit that introduced that problematic `gadgetfs_unbind` call in the `enomem` error handling. I'm going to determine if it was a later addition or if the call was there from the start of `gadgetfs_bind`'s implementation.
Naturally, `git log` is the tool of choice here. I'll focus that command on `gadgetfs_bind` within the relevant file to get a clear and concise view of the changes over time. That's the best way to track down the root commit, and then we'll have a better understanding of how the bug arose.
|
| 225/2 |
2026/08/07 06:12 |
tool |
git-log |
0m
Args: map[SourcePath:drivers/usb/gadget/legacy/inode.c SymbolName:gadgetfs_bind]
Results: map[Output:ffe0b3350625 usb: gadget: remove global variable composite in composite.c
765f5b830e54 usb: gadget: defer setting maxpacket till ->setup()
00274921a052 USB: gadget code switches to pr_err() and friends
7489d1494318 USB: gadgetfs cleanups
1da177e4c3f4 Linux-2.6.12-rc2
]
|
| 226/2 |
2026/08/07 06:12 |
llm |
fixes-finder |
0m
Model:
Tokens:
input: 10361
output: 37
thoughts: 42
Thoughts: Okay, let's dive into this. Here's how I'd approach this task, as if these were my internal thoughts:
**Analyzing Initial Commit for `gadgetfs_unbind`**
Right, so we're trying to figure out if `gadgetfs_unbind` was already present or handled within the context of `gadgetfs_bind` in the very first commit, specifically `1da177e4c3f4`. That's a reasonable starting point. Checking the genesis, the very foundation, is the logical first step. I need to establish the baseline of how these functions interacted, or didn't, from the beginning. This might give us clues about the evolution of the gadgetfs design and whether unbinding was always a critical consideration from the outset. Understanding the initial architecture is fundamental before going further down the rabbit hole.
|
| 227/2 |
2026/08/07 06:12 |
tool |
git-show |
0m
Args: map[Commit:1da177e4c3f4:drivers/usb/gadget/inode.c]
Results: map[Output:
Full output is too long, showing 1000 out of 2110 lines.
[/*
* inode.c -- user mode filesystem api for usb gadget controllers
*
* Copyright (C) 2003-2004 David Brownell
* Copyright (C) 2003 Agilent Technologies
*
* This program is free software; you can redistribute it and/or modify
* it under the terms of the GNU General Public License as published by
* the Free Software Foundation; either version 2 of the License, or
* (at your option) any later version.
*
* This program is distributed in the hope that it will be useful,
* but WITHOUT ANY WARRANTY; without even the implied warranty of
* MERCHANTABILITY or FITNESS FOR A PARTICULAR PURPOSE. See the
* GNU General Public License for more details.
*
* You should have received a copy of the GNU General Public License
* along with this program; if not, write to the Free Software
* Foundation, Inc., 59 Temple Place, Suite 330, Boston, MA 02111-1307 USA
*/
// #define DEBUG /* data to help fault diagnosis */
// #define VERBOSE /* extra debug messages (success too) */
#include <linux/init.h>
#include <linux/module.h>
#include <linux/fs.h>
#include <linux/pagemap.h>
#include <linux/uts.h>
#include <linux/wait.h>
#include <linux/compiler.h>
#include <asm/uaccess.h>
#include <linux/slab.h>
#include <linux/device.h>
#include <linux/moduleparam.h>
#include <linux/usb_gadgetfs.h>
#include <linux/usb_gadget.h>
/*
* The gadgetfs API maps each endpoint to a file descriptor so that you
* can use standard synchronous read/write calls for I/O. There's some
* O_NONBLOCK and O_ASYNC/FASYNC style i/o support. Example usermode
* drivers show how this works in practice. You can also use AIO to
* eliminate I/O gaps between requests, to help when streaming data.
*
* Key parts that must be USB-specific are protocols defining how the
* read/write operations relate to the hardware state machines. There
* are two types of files. One type is for the device, implementing ep0.
* The other type is for each IN or OUT endpoint. In both cases, the
* user mode driver must configure the hardware before using it.
*
* - First, dev_config() is called when /dev/gadget/$CHIP is configured
* (by writing configuration and device descriptors). Afterwards it
* may serve as a source of device events, used to handle all control
* requests other than basic enumeration.
*
* - Then either immediately, or after a SET_CONFIGURATION control request,
* ep_config() is called when each /dev/gadget/ep* file is configured
* (by writing endpoint descriptors). Afterwards these files are used
* to write() IN data or to read() OUT data. To halt the endpoint, a
* "wrong direction" request is issued (like reading an IN endpoint).
*
* Unlike "usbfs" the only ioctl()s are for things that are rare, and maybe
* not possible on all hardware. For example, precise fault handling with
* respect to data left in endpoint fifos after aborted operations; or
* selective clearing of endpoint halts, to implement SET_INTERFACE.
*/
#define DRIVER_DESC "USB Gadget filesystem"
#define DRIVER_VERSION "24 Aug 2004"
static const char driver_desc [] = DRIVER_DESC;
static const char shortname [] = "gadgetfs";
MODULE_DESCRIPTION (DRIVER_DESC);
MODULE_AUTHOR ("David Brownell");
MODULE_LICENSE ("GPL");
/*----------------------------------------------------------------------*/
#define GADGETFS_MAGIC 0xaee71ee7
#define DMA_ADDR_INVALID (~(dma_addr_t)0)
/* /dev/gadget/$CHIP represents ep0 and the whole device */
enum ep0_state {
/* DISBLED is the initial state.
*/
STATE_DEV_DISABLED = 0,
/* Only one open() of /dev/gadget/$CHIP; only one file tracks
* ep0/device i/o modes and binding to the controller. Driver
* must always write descriptors to initialize the device, then
* the device becomes UNCONNECTED until enumeration.
*/
STATE_OPENED,
/* From then on, ep0 fd is in either of two basic modes:
* - (UN)CONNECTED: read usb_gadgetfs_event(s) from it
* - SETUP: read/write will transfer control data and succeed;
* or if "wrong direction", performs protocol stall
*/
STATE_UNCONNECTED,
STATE_CONNECTED,
STATE_SETUP,
/* UNBOUND means the driver closed ep0, so the device won't be
* accessible again (DEV_DISABLED) until all fds are closed.
*/
STATE_DEV_UNBOUND,
};
/* enough for the whole queue: most events invalidate others */
#define N_EVENT 5
struct dev_data {
spinlock_t lock;
atomic_t count;
enum ep0_state state;
struct usb_gadgetfs_event event [N_EVENT];
unsigned ev_next;
struct fasync_struct *fasync;
u8 current_config;
/* drivers reading ep0 MUST handle control requests (SETUP)
* reported that way; else the host will time out.
*/
unsigned usermode_setup : 1,
setup_in : 1,
setup_can_stall : 1,
setup_out_ready : 1,
setup_out_error : 1,
setup_abort : 1;
/* the rest is basically write-once */
struct usb_config_descriptor *config, *hs_config;
struct usb_device_descriptor *dev;
struct usb_request *req;
struct usb_gadget *gadget;
struct list_head epfiles;
void *buf;
wait_queue_head_t wait;
struct super_block *sb;
struct dentry *dentry;
/* except this scratch i/o buffer for ep0 */
u8 rbuf [256];
};
static inline void get_dev (struct dev_data *data)
{
atomic_inc (&data->count);
}
static void put_dev (struct dev_data *data)
{
if (likely (!atomic_dec_and_test (&data->count)))
return;
/* needs no more cleanup */
BUG_ON (waitqueue_active (&data->wait));
kfree (data);
}
static struct dev_data *dev_new (void)
{
struct dev_data *dev;
dev = kmalloc (sizeof *dev, GFP_KERNEL);
if (!dev)
return NULL;
memset (dev, 0, sizeof *dev);
dev->state = STATE_DEV_DISABLED;
atomic_set (&dev->count, 1);
spin_lock_init (&dev->lock);
INIT_LIST_HEAD (&dev->epfiles);
init_waitqueue_head (&dev->wait);
return dev;
}
/*----------------------------------------------------------------------*/
/* other /dev/gadget/$ENDPOINT files represent endpoints */
enum ep_state {
STATE_EP_DISABLED = 0,
STATE_EP_READY,
STATE_EP_DEFER_ENABLE,
STATE_EP_ENABLED,
STATE_EP_UNBOUND,
};
struct ep_data {
struct semaphore lock;
enum ep_state state;
atomic_t count;
struct dev_data *dev;
/* must hold dev->lock before accessing ep or req */
struct usb_ep *ep;
struct usb_request *req;
ssize_t status;
char name [16];
struct usb_endpoint_descriptor desc, hs_desc;
struct list_head epfiles;
wait_queue_head_t wait;
struct dentry *dentry;
struct inode *inode;
};
static inline void get_ep (struct ep_data *data)
{
atomic_inc (&data->count);
}
static void put_ep (struct ep_data *data)
{
if (likely (!atomic_dec_and_test (&data->count)))
return;
put_dev (data->dev);
/* needs no more cleanup */
BUG_ON (!list_empty (&data->epfiles));
BUG_ON (waitqueue_active (&data->wait));
BUG_ON (down_trylock (&data->lock) != 0);
kfree (data);
}
/*----------------------------------------------------------------------*/
/* most "how to use the hardware" policy choices are in userspace:
* mapping endpoint roles (which the driver needs) to the capabilities
* which the usb controller has. most of those capabilities are exposed
* implicitly, starting with the driver name and then endpoint names.
*/
static const char *CHIP;
/*----------------------------------------------------------------------*/
/* NOTE: don't use dev_printk calls before binding to the gadget
* at the end of ep0 configuration, or after unbind.
*/
/* too wordy: dev_printk(level , &(d)->gadget->dev , fmt , ## args) */
#define xprintk(d,level,fmt,args...) \
printk(level "%s: " fmt , shortname , ## args)
#ifdef DEBUG
#define DBG(dev,fmt,args...) \
xprintk(dev , KERN_DEBUG , fmt , ## args)
#else
#define DBG(dev,fmt,args...) \
do { } while (0)
#endif /* DEBUG */
#ifdef VERBOSE
#define VDEBUG DBG
#else
#define VDEBUG(dev,fmt,args...) \
do { } while (0)
#endif /* DEBUG */
#define ERROR(dev,fmt,args...) \
xprintk(dev , KERN_ERR , fmt , ## args)
#define WARN(dev,fmt,args...) \
xprintk(dev , KERN_WARNING , fmt , ## args)
#define INFO(dev,fmt,args...) \
xprintk(dev , KERN_INFO , fmt , ## args)
/*----------------------------------------------------------------------*/
/* SYNCHRONOUS ENDPOINT OPERATIONS (bulk/intr/iso)
*
* After opening, configure non-control endpoints. Then use normal
* stream read() and write() requests; and maybe ioctl() to get more
* precise FIFO status when recovering from cancelation.
*/
static void epio_complete (struct usb_ep *ep, struct usb_request *req)
{
struct ep_data *epdata = ep->driver_data;
if (!req->context)
return;
if (req->status)
epdata->status = req->status;
else
epdata->status = req->actual;
complete ((struct completion *)req->context);
}
/* tasklock endpoint, returning when it's connected.
* still need dev->lock to use epdata->ep.
*/
static int
get_ready_ep (unsigned f_flags, struct ep_data *epdata)
{
int val;
if (f_flags & O_NONBLOCK) {
if (down_trylock (&epdata->lock) != 0)
goto nonblock;
if (epdata->state != STATE_EP_ENABLED) {
up (&epdata->lock);
nonblock:
val = -EAGAIN;
} else
val = 0;
return val;
}
if ((val = down_interruptible (&epdata->lock)) < 0)
return val;
newstate:
switch (epdata->state) {
case STATE_EP_ENABLED:
break;
case STATE_EP_DEFER_ENABLE:
DBG (epdata->dev, "%s wait for host\n", epdata->name);
if ((val = wait_event_interruptible (epdata->wait,
epdata->state != STATE_EP_DEFER_ENABLE
|| epdata->dev->state == STATE_DEV_UNBOUND
)) < 0)
goto fail;
goto newstate;
// case STATE_EP_DISABLED: /* "can't happen" */
// case STATE_EP_READY: /* "can't happen" */
default: /* error! */
pr_debug ("%s: ep %p not available, state %d\n",
shortname, epdata, epdata->state);
// FALLTHROUGH
case STATE_EP_UNBOUND: /* clean disconnect */
val = -ENODEV;
fail:
up (&epdata->lock);
}
return val;
}
static ssize_t
ep_io (struct ep_data *epdata, void *buf, unsigned len)
{
DECLARE_COMPLETION (done);
int value;
spin_lock_irq (&epdata->dev->lock);
if (likely (epdata->ep != NULL)) {
struct usb_request *req = epdata->req;
req->context = &done;
req->complete = epio_complete;
req->buf = buf;
req->length = len;
value = usb_ep_queue (epdata->ep, req, GFP_ATOMIC);
} else
value = -ENODEV;
spin_unlock_irq (&epdata->dev->lock);
if (likely (value == 0)) {
value = wait_event_interruptible (done.wait, done.done);
if (value != 0) {
spin_lock_irq (&epdata->dev->lock);
if (likely (epdata->ep != NULL)) {
DBG (epdata->dev, "%s i/o interrupted\n",
epdata->name);
usb_ep_dequeue (epdata->ep, epdata->req);
spin_unlock_irq (&epdata->dev->lock);
wait_event (done.wait, done.done);
if (epdata->status == -ECONNRESET)
epdata->status = -EINTR;
} else {
spin_unlock_irq (&epdata->dev->lock);
DBG (epdata->dev, "endpoint gone\n");
epdata->status = -ENODEV;
}
}
return epdata->status;
}
return value;
}
/* handle a synchronous OUT bulk/intr/iso transfer */
static ssize_t
ep_read (struct file *fd, char __user *buf, size_t len, loff_t *ptr)
{
struct ep_data *data = fd->private_data;
void *kbuf;
ssize_t value;
if ((value = get_ready_ep (fd->f_flags, data)) < 0)
return value;
/* halt any endpoint by doing a "wrong direction" i/o call */
if (data->desc.bEndpointAddress & USB_DIR_IN) {
if ((data->desc.bmAttributes & USB_ENDPOINT_XFERTYPE_MASK)
== USB_ENDPOINT_XFER_ISOC)
return -EINVAL;
DBG (data->dev, "%s halt\n", data->name);
spin_lock_irq (&data->dev->lock);
if (likely (data->ep != NULL))
usb_ep_set_halt (data->ep);
spin_unlock_irq (&data->dev->lock);
up (&data->lock);
return -EBADMSG;
}
/* FIXME readahead for O_NONBLOCK and poll(); careful with ZLPs */
value = -ENOMEM;
kbuf = kmalloc (len, SLAB_KERNEL);
if (unlikely (!kbuf))
goto free1;
value = ep_io (data, kbuf, len);
VDEBUG (data->dev, "%s read %d OUT, status %d\n",
data->name, len, value);
if (value >= 0 && copy_to_user (buf, kbuf, value))
value = -EFAULT;
free1:
up (&data->lock);
kfree (kbuf);
return value;
}
/* handle a synchronous IN bulk/intr/iso transfer */
static ssize_t
ep_write (struct file *fd, const char __user *buf, size_t len, loff_t *ptr)
{
struct ep_data *data = fd->private_data;
void *kbuf;
ssize_t value;
if ((value = get_ready_ep (fd->f_flags, data)) < 0)
return value;
/* halt any endpoint by doing a "wrong direction" i/o call */
if (!(data->desc.bEndpointAddress & USB_DIR_IN)) {
if ((data->desc.bmAttributes & USB_ENDPOINT_XFERTYPE_MASK)
== USB_ENDPOINT_XFER_ISOC)
return -EINVAL;
DBG (data->dev, "%s halt\n", data->name);
spin_lock_irq (&data->dev->lock);
if (likely (data->ep != NULL))
usb_ep_set_halt (data->ep);
spin_unlock_irq (&data->dev->lock);
up (&data->lock);
return -EBADMSG;
}
/* FIXME writebehind for O_NONBLOCK and poll(), qlen = 1 */
value = -ENOMEM;
kbuf = kmalloc (len, SLAB_KERNEL);
if (!kbuf)
goto free1;
if (copy_from_user (kbuf, buf, len)) {
value = -EFAULT;
goto free1;
}
value = ep_io (data, kbuf, len);
VDEBUG (data->dev, "%s write %d IN, status %d\n",
data->name, len, value);
free1:
up (&data->lock);
kfree (kbuf);
return value;
}
static int
ep_release (struct inode *inode, struct file *fd)
{
struct ep_data *data = fd->private_data;
/* clean up if this can be reopened */
if (data->state != STATE_EP_UNBOUND) {
data->state = STATE_EP_DISABLED;
data->desc.bDescriptorType = 0;
data->hs_desc.bDescriptorType = 0;
}
put_ep (data);
return 0;
}
static int ep_ioctl (struct inode *inode, struct file *fd,
unsigned code, unsigned long value)
{
struct ep_data *data = fd->private_data;
int status;
if ((status = get_ready_ep (fd->f_flags, data)) < 0)
return status;
spin_lock_irq (&data->dev->lock);
if (likely (data->ep != NULL)) {
switch (code) {
case GADGETFS_FIFO_STATUS:
status = usb_ep_fifo_status (data->ep);
break;
case GADGETFS_FIFO_FLUSH:
usb_ep_fifo_flush (data->ep);
break;
case GADGETFS_CLEAR_HALT:
status = usb_ep_clear_halt (data->ep);
break;
default:
status = -ENOTTY;
}
} else
status = -ENODEV;
spin_unlock_irq (&data->dev->lock);
up (&data->lock);
return status;
}
/*----------------------------------------------------------------------*/
/* ASYNCHRONOUS ENDPOINT I/O OPERATIONS (bulk/intr/iso) */
struct kiocb_priv {
struct usb_request *req;
struct ep_data *epdata;
void *buf;
char __user *ubuf;
unsigned actual;
};
static int ep_aio_cancel(struct kiocb *iocb, struct io_event *e)
{
struct kiocb_priv *priv = iocb->private;
struct ep_data *epdata;
int value;
local_irq_disable();
epdata = priv->epdata;
// spin_lock(&epdata->dev->lock);
kiocbSetCancelled(iocb);
if (likely(epdata && epdata->ep && priv->req))
value = usb_ep_dequeue (epdata->ep, priv->req);
else
value = -EINVAL;
// spin_unlock(&epdata->dev->lock);
local_irq_enable();
aio_put_req(iocb);
return value;
}
static ssize_t ep_aio_read_retry(struct kiocb *iocb)
{
struct kiocb_priv *priv = iocb->private;
ssize_t status = priv->actual;
/* we "retry" to get the right mm context for this: */
status = copy_to_user(priv->ubuf, priv->buf, priv->actual);
if (unlikely(0 != status))
status = -EFAULT;
else
status = priv->actual;
kfree(priv->buf);
kfree(priv);
aio_put_req(iocb);
return status;
}
static void ep_aio_complete(struct usb_ep *ep, struct usb_request *req)
{
struct kiocb *iocb = req->context;
struct kiocb_priv *priv = iocb->private;
struct ep_data *epdata = priv->epdata;
/* lock against disconnect (and ideally, cancel) */
spin_lock(&epdata->dev->lock);
priv->req = NULL;
priv->epdata = NULL;
if (NULL == iocb->ki_retry
|| unlikely(0 == req->actual)
|| unlikely(kiocbIsCancelled(iocb))) {
kfree(req->buf);
kfree(priv);
iocb->private = NULL;
/* aio_complete() reports bytes-transferred _and_ faults */
if (unlikely(kiocbIsCancelled(iocb)))
aio_put_req(iocb);
else
aio_complete(iocb,
req->actual ? req->actual : req->status,
req->status);
} else {
/* retry() won't report both; so we hide some faults */
if (unlikely(0 != req->status))
DBG(epdata->dev, "%s fault %d len %d\n",
ep->name, req->status, req->actual);
priv->buf = req->buf;
priv->actual = req->actual;
kick_iocb(iocb);
}
spin_unlock(&epdata->dev->lock);
usb_ep_free_request(ep, req);
put_ep(epdata);
}
static ssize_t
ep_aio_rwtail(
struct kiocb *iocb,
char *buf,
size_t len,
struct ep_data *epdata,
char __user *ubuf
)
{
struct kiocb_priv *priv = (void *) &iocb->private;
struct usb_request *req;
ssize_t value;
priv = kmalloc(sizeof *priv, GFP_KERNEL);
if (!priv) {
value = -ENOMEM;
fail:
kfree(buf);
return value;
}
iocb->private = priv;
priv->ubuf = ubuf;
value = get_ready_ep(iocb->ki_filp->f_flags, epdata);
if (unlikely(value < 0)) {
kfree(priv);
goto fail;
}
iocb->ki_cancel = ep_aio_cancel;
get_ep(epdata);
priv->epdata = epdata;
priv->actual = 0;
/* each kiocb is coupled to one usb_request, but we can't
* allocate or submit those if the host disconnected.
*/
spin_lock_irq(&epdata->dev->lock);
if (likely(epdata->ep)) {
req = usb_ep_alloc_request(epdata->ep, GFP_ATOMIC);
if (likely(req)) {
priv->req = req;
req->buf = buf;
req->length = len;
req->complete = ep_aio_complete;
req->context = iocb;
value = usb_ep_queue(epdata->ep, req, GFP_ATOMIC);
if (unlikely(0 != value))
usb_ep_free_request(epdata->ep, req);
} else
value = -EAGAIN;
} else
value = -ENODEV;
spin_unlock_irq(&epdata->dev->lock);
up(&epdata->lock);
if (unlikely(value)) {
kfree(priv);
put_ep(epdata);
} else
value = -EIOCBQUEUED;
return value;
}
static ssize_t
ep_aio_read(struct kiocb *iocb, char __user *ubuf, size_t len, loff_t o)
{
struct ep_data *epdata = iocb->ki_filp->private_data;
char *buf;
if (unlikely(epdata->desc.bEndpointAddress & USB_DIR_IN))
return -EINVAL;
buf = kmalloc(len, GFP_KERNEL);
if (unlikely(!buf))
return -ENOMEM;
iocb->ki_retry = ep_aio_read_retry;
return ep_aio_rwtail(iocb, buf, len, epdata, ubuf);
}
static ssize_t
ep_aio_write(struct kiocb *iocb, const char __user *ubuf, size_t len, loff_t o)
{
struct ep_data *epdata = iocb->ki_filp->private_data;
char *buf;
if (unlikely(!(epdata->desc.bEndpointAddress & USB_DIR_IN)))
return -EINVAL;
buf = kmalloc(len, GFP_KERNEL);
if (unlikely(!buf))
return -ENOMEM;
if (unlikely(copy_from_user(buf, ubuf, len) != 0)) {
kfree(buf);
return -EFAULT;
}
return ep_aio_rwtail(iocb, buf, len, epdata, NULL);
}
/*----------------------------------------------------------------------*/
/* used after endpoint configuration */
static struct file_operations ep_io_operations = {
.owner = THIS_MODULE,
.llseek = no_llseek,
.read = ep_read,
.write = ep_write,
.ioctl = ep_ioctl,
.release = ep_release,
.aio_read = ep_aio_read,
.aio_write = ep_aio_write,
};
/* ENDPOINT INITIALIZATION
*
* fd = open ("/dev/gadget/$ENDPOINT", O_RDWR)
* status = write (fd, descriptors, sizeof descriptors)
*
* That write establishes the endpoint configuration, configuring
* the controller to process bulk, interrupt, or isochronous transfers
* at the right maxpacket size, and so on.
*
* The descriptors are message type 1, identified by a host order u32
* at the beginning of what's written. Descriptor order is: full/low
* speed descriptor, then optional high speed descriptor.
*/
static ssize_t
ep_config (struct file *fd, const char __user *buf, size_t len, loff_t *ptr)
{
struct ep_data *data = fd->private_data;
struct usb_ep *ep;
u32 tag;
int value;
if ((value = down_interruptible (&data->lock)) < 0)
return value;
if (data->state != STATE_EP_READY) {
value = -EL2HLT;
goto fail;
}
value = len;
if (len < USB_DT_ENDPOINT_SIZE + 4)
goto fail0;
/* we might need to change message format someday */
if (copy_from_user (&tag, buf, 4)) {
goto fail1;
}
if (tag != 1) {
DBG(data->dev, "config %s, bad tag %d\n", data->name, tag);
goto fail0;
}
buf += 4;
len -= 4;
/* NOTE: audio endpoint extensions not accepted here;
* just don't include the extra bytes.
*/
/* full/low speed descriptor, then high speed */
if (copy_from_user (&data->desc, buf, USB_DT_ENDPOINT_SIZE)) {
goto fail1;
}
if (data->desc.bLength != USB_DT_ENDPOINT_SIZE
|| data->desc.bDescriptorType != USB_DT_ENDPOINT)
goto fail0;
if (len != USB_DT_ENDPOINT_SIZE) {
if (len != 2 * USB_DT_ENDPOINT_SIZE)
goto fail0;
if (copy_from_user (&data->hs_desc, buf + USB_DT_ENDPOINT_SIZE,
USB_DT_ENDPOINT_SIZE)) {
goto fail1;
}
if (data->hs_desc.bLength != USB_DT_ENDPOINT_SIZE
|| data->hs_desc.bDescriptorType
!= USB_DT_ENDPOINT) {
DBG(data->dev, "config %s, bad hs length or type\n",
data->name);
goto fail0;
}
}
value = len;
spin_lock_irq (&data->dev->lock);
if (data->dev->state == STATE_DEV_UNBOUND) {
value = -ENOENT;
goto gone;
} else if ((ep = data->ep) == NULL) {
value = -ENODEV;
goto gone;
}
switch (data->dev->gadget->speed) {
case USB_SPEED_LOW:
case USB_SPEED_FULL:
value = usb_ep_enable (ep, &data->desc);
if (value == 0)
data->state = STATE_EP_ENABLED;
break;
#ifdef HIGHSPEED
case USB_SPEED_HIGH:
/* fails if caller didn't provide that descriptor... */
value = usb_ep_enable (ep, &data->hs_desc);
if (value == 0)
data->state = STATE_EP_ENABLED;
break;
#endif
default:
DBG (data->dev, "unconnected, %s init deferred\n",
data->name);
data->state = STATE_EP_DEFER_ENABLE;
}
if (value == 0)
fd->f_op = &ep_io_operations;
gone:
spin_unlock_irq (&data->dev->lock);
if (value < 0) {
fail:
data->desc.bDescriptorType = 0;
data->hs_desc.bDescriptorType = 0;
}
up (&data->lock);
return value;
fail0:
value = -EINVAL;
goto fail;
fail1:
value = -EFAULT;
goto fail;
}
static int
ep_open (struct inode *inode, struct file *fd)
{
struct ep_data *data = inode->u.generic_ip;
int value = -EBUSY;
if (down_interruptible (&data->lock) != 0)
return -EINTR;
spin_lock_irq (&data->dev->lock);
if (data->dev->state == STATE_DEV_UNBOUND)
value = -ENOENT;
else if (data->state == STATE_EP_DISABLED) {
value = 0;
data->state = STATE_EP_READY;
get_ep (data);
fd->private_data = data;
VDEBUG (data->dev, "%s ready\n", data->name);
} else
DBG (data->dev, "%s state %d\n",
data->name, data->state);
spin_unlock_irq (&data->dev->lock);
up (&data->lock);
return value;
}
/* used before endpoint configuration */
static struct file_operations ep_config_operations = {
.owner = THIS_MODULE,
.llseek = no_llseek,
.open = ep_open,
.write = ep_config,
.release = ep_release,
};
/*----------------------------------------------------------------------*/
/* EP0 IMPLEMENTATION can be partly in userspace.
*
* Drivers that use this facility receive various events, including
* control requests the kernel doesn't handle. Drivers that don't
* use this facility may be too simple-minded for real applications.
*/
static inline void ep0_readable (struct dev_data *dev)
{
wake_up (&dev->wait);
kill_fasync (&dev->fasync, SIGIO, POLL_IN);
}
static void clean_req (struct usb_ep *ep, struct usb_request *req)
{
struct dev_data *dev = ep->driver_data;
if (req->buf != dev->rbuf) {
usb_ep_free_buffer (ep, req->buf, req->dma, req->length);
req->buf = dev->rbuf;
req->dma = DMA_ADDR_INVALID;
}
req->complete = epio_complete;
dev->setup_out_ready = 0;
}
static void ep0_complete (struct usb_ep *ep, struct usb_request *req)
{
struct dev_data *dev = ep->driver_data;
int free = 1;
/* for control OUT, data must still get to userspace */
if (!dev->setup_in) {
dev->setup_out_error = (req->status != 0);
if (!dev->setup_out_error)
free = 0;
dev->setup_out_ready = 1;
ep0_readable (dev);
} else if (dev->state == STATE_SETUP)
dev->state = STATE_CONNECTED;
/* clean up as appropriate */
if (free && req->buf != &dev->rbuf)
clean_req (ep, req);
req->complete = epio_complete;
}
static int setup_req (struct usb_ep *ep, struct usb_request *req, u16 len)
{
struct dev_data *dev = ep->driver_data;
if (dev->setup_out_ready) {
DBG (dev, "ep0 request busy!\n");
return -EBUSY;
}
if (len > sizeof (dev->rbuf))
req->buf = usb_ep_alloc_buffer (ep, len, &req->dma, GFP_ATOMIC);
if (req->buf == 0) {
req->buf = dev->rbuf;
return -ENOMEM;
}
req->complete = ep0_complete;
req->length = len;
return 0;
}
static ssize_t
ep0_read (struct file *fd, char __user *buf, size_t len, loff_t *ptr)
{
struct dev_data *dev = fd->private_data;
ssize_t retval;
enum ep0_state state;
spin_lock_irq (&dev->lock);
/* report fd mode change before acting on it */
if (dev->setup_abort) {
dev->setup_abort = 0;
retval = -EIDRM;
goto done;
}
/* control DATA stage */
if ((state = dev->state) == STATE_SETUP) {
if (dev->setup_in) { /* stall IN */
VDEBUG(dev, "ep0in stall\n");
(void) usb_ep_set_halt (dev->gadget->ep0);
retval = -EL2HLT;
dev->state = STATE_CONNECTED;
} else if (len == 0) { /* ack SET_CONFIGURATION etc */
struct usb_ep *ep = dev->gadget->ep0;
struct usb_request *req = dev->req;
if ((retval = setup_req (ep, req, 0)) == 0)
retval = usb_ep_queue (ep, req, GFP_ATOMIC);
dev->state = STATE_CONNECTED;
/* assume that was SET_CONFIGURATION */
if (dev->current_config) {
unsigned power;
#ifdef HIGHSPEED
if (dev->gadget->speed == USB_SPEED_HIGH)
power = dev->hs_config->bMaxPower;
else
#endif
power = dev->config->bMaxPower;
usb_gadget_vbus_draw(dev->gadget, 2 * power);
}
} else { /* collect OUT data */
if ((fd->f_flags & O_NONBLOCK) != 0
&& !dev->setup_out_ready) {
retval = -EAGAIN;
goto done;
}
spin_unlock_irq (&dev->lock);
retval = wait_event_interruptible (dev->wait,
dev->setup_out_ready != 0);
]
]
|
| 228/2 |
2026/08/07 06:12 |
llm |
fixes-finder |
0m
Model:
Tokens:
input: 20912
output: 35
thoughts: 72
Thoughts: Here's my summary, as the thought process unfolds:
**Examining a Truncated Output**
Okay, the output's been cut off, which is frustrating. I need to find the context for that. Right, I remember seeing something about `gadgetfs_bind`. That's the key. Let's see...I need to trace that function. Given that I have a specific commit ID (`1da177e4c3f4`) and I know the relevant file is `drivers/usb/gadget/inode.c`, the quickest way is probably to just grab the source of that file from that commit and grep for the definition. I could use `codesearch-definition-source` if I was sure it was indexed, but `git-show` with a grep should be faster and more reliable right now. It'll give me the precise source I need. That's the direct approach – no need to overcomplicate things. I'll get the definition and understand how it’s being used.
|
| 229/2 |
2026/08/07 06:12 |
tool |
grepper |
0m
Error:
Args: map[Expression:gadgetfs_bind PathPrefix:drivers/usb/gadget/inode.c]
Results:
|
| 230/2 |
2026/08/07 06:12 |
llm |
fixes-finder |
0m
Model:
Tokens:
input: 21252
output: 39
thoughts: 65
Thoughts: **Investigating a Potential File Path Change**
Okay, hold on a second. I have a hunch, a nagging feeling that the `drivers/usb/gadget/inode.c` file *might* have been reorganized, perhaps moved to `drivers/usb/gadget/legacy/inode.c`. That kind of change happens in these projects. Let's not assume, though, and go directly to checking for it, so I should now investigate the commit history around the `gadgetfs_bind` function. I will check the legacy file path first, and then, if necessary, the original. It would be good to confirm that this is where the code now resides, and to understand *when* the change occurred. That will give me the context I need to proceed confidently.
|
| 231/2 |
2026/08/07 06:12 |
tool |
git-log |
0m
Args: map[SourcePath:drivers/usb/gadget/legacy/inode.c SymbolName:gadgetfs_bind]
Results: map[Output:ffe0b3350625 usb: gadget: remove global variable composite in composite.c
765f5b830e54 usb: gadget: defer setting maxpacket till ->setup()
00274921a052 USB: gadget code switches to pr_err() and friends
7489d1494318 USB: gadgetfs cleanups
1da177e4c3f4 Linux-2.6.12-rc2
]
|
| 232/2 |
2026/08/07 06:13 |
llm |
fixes-finder |
0m
Model:
Tokens:
input: 21648
output: 26
thoughts: 26
Thoughts: Okay, let's dive into this. Here's my take on those thoughts, as if they're my own:
**Analyzing the GadgetFS Bind Implementation**
Right, let's start by digging into `1da177e4c3f4` and see what's happening with `gadgetfs_bind`. My immediate thought is, what are the key aspects of this binding process in `gadgetfs`? I'm already mentally running through the various steps: is it handling device registration, resource allocation, permission checks, potential race conditions? I need to understand how the filesystem interfaces with the gadget driver and the underlying USB stack here. I'm assuming this function is critical for getting the virtual device instantiated and ready for use. So, I need to see if it's handling the initial setup, ensuring the right hooks are in place, and the data structures are properly initialized. I'll need to pay close attention to the error handling and see how it gracefully deals with potential failures during the binding phase. Ultimately, I'm expecting this to be the core of making the gadget appear as a usable USB device to the host.
|
| 233/2 |
2026/08/07 06:13 |
tool |
git-show |
0m
Args:
Results: map[Output:
Full output is too long, showing 1000 out of 6822520 lines.
[commit 1da177e4c3f41524e886b7f1b8a0c1fc7321cac2
Author: Linus Torvalds <torvalds@ppc970.osdl.org>
Date: Sat Apr 16 15:20:36 2005 -0700
Linux-2.6.12-rc2
Initial git repository build. I'm not bothering with the full history,
even though we have it. We can create a separate "historical" git
archive of that later if we want to, and in the meantime it's about
3.2GB when imported into git - space that would just make the early
git days unnecessarily complicated, when we don't have a lot of good
infrastructure for it.
Let it rip!
diff --git a/COPYING b/COPYING
new file mode 100644
index 000000000000..2a7e338ec2fc
--- /dev/null
+++ b/COPYING
@@ -0,0 +1,356 @@
+
+ NOTE! This copyright does *not* cover user programs that use kernel
+ services by normal system calls - this is merely considered normal use
+ of the kernel, and does *not* fall under the heading of "derived work".
+ Also note that the GPL below is copyrighted by the Free Software
+ Foundation, but the instance of code that it refers to (the Linux
+ kernel) is copyrighted by me and others who actually wrote it.
+
+ Also note that the only valid version of the GPL as far as the kernel
+ is concerned is _this_ particular version of the license (ie v2, not
+ v2.2 or v3.x or whatever), unless explicitly otherwise stated.
+
+ Linus Torvalds
+
+----------------------------------------
+
+ GNU GENERAL PUBLIC LICENSE
+ Version 2, June 1991
+
+ Copyright (C) 1989, 1991 Free Software Foundation, Inc.
+ 59 Temple Place, Suite 330, Boston, MA 02111-1307 USA
+ Everyone is permitted to copy and distribute verbatim copies
+ of this license document, but changing it is not allowed.
+
+ Preamble
+
+ The licenses for most software are designed to take away your
+freedom to share and change it. By contrast, the GNU General Public
+License is intended to guarantee your freedom to share and change free
+software--to make sure the software is free for all its users. This
+General Public License applies to most of the Free Software
+Foundation's software and to any other program whose authors commit to
+using it. (Some other Free Software Foundation software is covered by
+the GNU Library General Public License instead.) You can apply it to
+your programs, too.
+
+ When we speak of free software, we are referring to freedom, not
+price. Our General Public Licenses are designed to make sure that you
+have the freedom to distribute copies of free software (and charge for
+this service if you wish), that you receive source code or can get it
+if you want it, that you can change the software or use pieces of it
+in new free programs; and that you know you can do these things.
+
+ To protect your rights, we need to make restrictions that forbid
+anyone to deny you these rights or to ask you to surrender the rights.
+These restrictions translate to certain responsibilities for you if you
+distribute copies of the software, or if you modify it.
+
+ For example, if you distribute copies of such a program, whether
+gratis or for a fee, you must give the recipients all the rights that
+you have. You must make sure that they, too, receive or can get the
+source code. And you must show them these terms so they know their
+rights.
+
+ We protect your rights with two steps: (1) copyright the software, and
+(2) offer you this license which gives you legal permission to copy,
+distribute and/or modify the software.
+
+ Also, for each author's protection and ours, we want to make certain
+that everyone understands that there is no warranty for this free
+software. If the software is modified by someone else and passed on, we
+want its recipients to know that what they have is not the original, so
+that any problems introduced by others will not reflect on the original
+authors' reputations.
+
+ Finally, any free program is threatened constantly by software
+patents. We wish to avoid the danger that redistributors of a free
+program will individually obtain patent licenses, in effect making the
+program proprietary. To prevent this, we have made it clear that any
+patent must be licensed for everyone's free use or not licensed at all.
+
+ The precise terms and conditions for copying, distribution and
+modification follow.
+
+ GNU GENERAL PUBLIC LICENSE
+ TERMS AND CONDITIONS FOR COPYING, DISTRIBUTION AND MODIFICATION
+
+ 0. This License applies to any program or other work which contains
+a notice placed by the copyright holder saying it may be distributed
+under the terms of this General Public License. The "Program", below,
+refers to any such program or work, and a "work based on the Program"
+means either the Program or any derivative work under copyright law:
+that is to say, a work containing the Program or a portion of it,
+either verbatim or with modifications and/or translated into another
+language. (Hereinafter, translation is included without limitation in
+the term "modification".) Each licensee is addressed as "you".
+
+Activities other than copying, distribution and modification are not
+covered by this License; they are outside its scope. The act of
+running the Program is not restricted, and the output from the Program
+is covered only if its contents constitute a work based on the
+Program (independent of having been made by running the Program).
+Whether that is true depends on what the Program does.
+
+ 1. You may copy and distribute verbatim copies of the Program's
+source code as you receive it, in any medium, provided that you
+conspicuously and appropriately publish on each copy an appropriate
+copyright notice and disclaimer of warranty; keep intact all the
+notices that refer to this License and to the absence of any warranty;
+and give any other recipients of the Program a copy of this License
+along with the Program.
+
+You may charge a fee for the physical act of transferring a copy, and
+you may at your option offer warranty protection in exchange for a fee.
+
+ 2. You may modify your copy or copies of the Program or any portion
+of it, thus forming a work based on the Program, and copy and
+distribute such modifications or work under the terms of Section 1
+above, provided that you also meet all of these conditions:
+
+ a) You must cause the modified files to carry prominent notices
+ stating that you changed the files and the date of any change.
+
+ b) You must cause any work that you distribute or publish, that in
+ whole or in part contains or is derived from the Program or any
+ part thereof, to be licensed as a whole at no charge to all third
+ parties under the terms of this License.
+
+ c) If the modified program normally reads commands interactively
+ when run, you must cause it, when started running for such
+ interactive use in the most ordinary way, to print or display an
+ announcement including an appropriate copyright notice and a
+ notice that there is no warranty (or else, saying that you provide
+ a warranty) and that users may redistribute the program under
+ these conditions, and telling the user how to view a copy of this
+ License. (Exception: if the Program itself is interactive but
+ does not normally print such an announcement, your work based on
+ the Program is not required to print an announcement.)
+
+These requirements apply to the modified work as a whole. If
+identifiable sections of that work are not derived from the Program,
+and can be reasonably considered independent and separate works in
+themselves, then this License, and its terms, do not apply to those
+sections when you distribute them as separate works. But when you
+distribute the same sections as part of a whole which is a work based
+on the Program, the distribution of the whole must be on the terms of
+this License, whose permissions for other licensees extend to the
+entire whole, and thus to each and every part regardless of who wrote it.
+
+Thus, it is not the intent of this section to claim rights or contest
+your rights to work written entirely by you; rather, the intent is to
+exercise the right to control the distribution of derivative or
+collective works based on the Program.
+
+In addition, mere aggregation of another work not based on the Program
+with the Program (or with a work based on the Program) on a volume of
+a storage or distribution medium does not bring the other work under
+the scope of this License.
+
+ 3. You may copy and distribute the Program (or a work based on it,
+under Section 2) in object code or executable form under the terms of
+Sections 1 and 2 above provided that you also do one of the following:
+
+ a) Accompany it with the complete corresponding machine-readable
+ source code, which must be distributed under the terms of Sections
+ 1 and 2 above on a medium customarily used for software interchange; or,
+
+ b) Accompany it with a written offer, valid for at least three
+ years, to give any third party, for a charge no more than your
+ cost of physically performing source distribution, a complete
+ machine-readable copy of the corresponding source code, to be
+ distributed under the terms of Sections 1 and 2 above on a medium
+ customarily used for software interchange; or,
+
+ c) Accompany it with the information you received as to the offer
+ to distribute corresponding source code. (This alternative is
+ allowed only for noncommercial distribution and only if you
+ received the program in object code or executable form with such
+ an offer, in accord with Subsection b above.)
+
+The source code for a work means the preferred form of the work for
+making modifications to it. For an executable work, complete source
+code means all the source code for all modules it contains, plus any
+associated interface definition files, plus the scripts used to
+control compilation and installation of the executable. However, as a
+special exception, the source code distributed need not include
+anything that is normally distributed (in either source or binary
+form) with the major components (compiler, kernel, and so on) of the
+operating system on which the executable runs, unless that component
+itself accompanies the executable.
+
+If distribution of executable or object code is made by offering
+access to copy from a designated place, then offering equivalent
+access to copy the source code from the same place counts as
+distribution of the source code, even though third parties are not
+compelled to copy the source along with the object code.
+
+ 4. You may not copy, modify, sublicense, or distribute the Program
+except as expressly provided under this License. Any attempt
+otherwise to copy, modify, sublicense or distribute the Program is
+void, and will automatically terminate your rights under this License.
+However, parties who have received copies, or rights, from you under
+this License will not have their licenses terminated so long as such
+parties remain in full compliance.
+
+ 5. You are not required to accept this License, since you have not
+signed it. However, nothing else grants you permission to modify or
+distribute the Program or its derivative works. These actions are
+prohibited by law if you do not accept this License. Therefore, by
+modifying or distributing the Program (or any work based on the
+Program), you indicate your acceptance of this License to do so, and
+all its terms and conditions for copying, distributing or modifying
+the Program or works based on it.
+
+ 6. Each time you redistribute the Program (or any work based on the
+Program), the recipient automatically receives a license from the
+original licensor to copy, distribute or modify the Program subject to
+these terms and conditions. You may not impose any further
+restrictions on the recipients' exercise of the rights granted herein.
+You are not responsible for enforcing compliance by third parties to
+this License.
+
+ 7. If, as a consequence of a court judgment or allegation of patent
+infringement or for any other reason (not limited to patent issues),
+conditions are imposed on you (whether by court order, agreement or
+otherwise) that contradict the conditions of this License, they do not
+excuse you from the conditions of this License. If you cannot
+distribute so as to satisfy simultaneously your obligations under this
+License and any other pertinent obligations, then as a consequence you
+may not distribute the Program at all. For example, if a patent
+license would not permit royalty-free redistribution of the Program by
+all those who receive copies directly or indirectly through you, then
+the only way you could satisfy both it and this License would be to
+refrain entirely from distribution of the Program.
+
+If any portion of this section is held invalid or unenforceable under
+any particular circumstance, the balance of the section is intended to
+apply and the section as a whole is intended to apply in other
+circumstances.
+
+It is not the purpose of this section to induce you to infringe any
+patents or other property right claims or to contest validity of any
+such claims; this section has the sole purpose of protecting the
+integrity of the free software distribution system, which is
+implemented by public license practices. Many people have made
+generous contributions to the wide range of software distributed
+through that system in reliance on consistent application of that
+system; it is up to the author/donor to decide if he or she is willing
+to distribute software through any other system and a licensee cannot
+impose that choice.
+
+This section is intended to make thoroughly clear what is believed to
+be a consequence of the rest of this License.
+
+ 8. If the distribution and/or use of the Program is restricted in
+certain countries either by patents or by copyrighted interfaces, the
+original copyright holder who places the Program under this License
+may add an explicit geographical distribution limitation excluding
+those countries, so that distribution is permitted only in or among
+countries not thus excluded. In such case, this License incorporates
+the limitation as if written in the body of this License.
+
+ 9. The Free Software Foundation may publish revised and/or new versions
+of the General Public License from time to time. Such new versions will
+be similar in spirit to the present version, but may differ in detail to
+address new problems or concerns.
+
+Each version is given a distinguishing version number. If the Program
+specifies a version number of this License which applies to it and "any
+later version", you have the option of following the terms and conditions
+either of that version or of any later version published by the Free
+Software Foundation. If the Program does not specify a version number of
+this License, you may choose any version ever published by the Free Software
+Foundation.
+
+ 10. If you wish to incorporate parts of the Program into other free
+programs whose distribution conditions are different, write to the author
+to ask for permission. For software which is copyrighted by the Free
+Software Foundation, write to the Free Software Foundation; we sometimes
+make exceptions for this. Our decision will be guided by the two goals
+of preserving the free status of all derivatives of our free software and
+of promoting the sharing and reuse of software generally.
+
+ NO WARRANTY
+
+ 11. BECAUSE THE PROGRAM IS LICENSED FREE OF CHARGE, THERE IS NO WARRANTY
+FOR THE PROGRAM, TO THE EXTENT PERMITTED BY APPLICABLE LAW. EXCEPT WHEN
+OTHERWISE STATED IN WRITING THE COPYRIGHT HOLDERS AND/OR OTHER PARTIES
+PROVIDE THE PROGRAM "AS IS" WITHOUT WARRANTY OF ANY KIND, EITHER EXPRESSED
+OR IMPLIED, INCLUDING, BUT NOT LIMITED TO, THE IMPLIED WARRANTIES OF
+MERCHANTABILITY AND FITNESS FOR A PARTICULAR PURPOSE. THE ENTIRE RISK AS
+TO THE QUALITY AND PERFORMANCE OF THE PROGRAM IS WITH YOU. SHOULD THE
+PROGRAM PROVE DEFECTIVE, YOU ASSUME THE COST OF ALL NECESSARY SERVICING,
+REPAIR OR CORRECTION.
+
+ 12. IN NO EVENT UNLESS REQUIRED BY APPLICABLE LAW OR AGREED TO IN WRITING
+WILL ANY COPYRIGHT HOLDER, OR ANY OTHER PARTY WHO MAY MODIFY AND/OR
+REDISTRIBUTE THE PROGRAM AS PERMITTED ABOVE, BE LIABLE TO YOU FOR DAMAGES,
+INCLUDING ANY GENERAL, SPECIAL, INCIDENTAL OR CONSEQUENTIAL DAMAGES ARISING
+OUT OF THE USE OR INABILITY TO USE THE PROGRAM (INCLUDING BUT NOT LIMITED
+TO LOSS OF DATA OR DATA BEING RENDERED INACCURATE OR LOSSES SUSTAINED BY
+YOU OR THIRD PARTIES OR A FAILURE OF THE PROGRAM TO OPERATE WITH ANY OTHER
+PROGRAMS), EVEN IF SUCH HOLDER OR OTHER PARTY HAS BEEN ADVISED OF THE
+POSSIBILITY OF SUCH DAMAGES.
+
+ END OF TERMS AND CONDITIONS
+
+ How to Apply These Terms to Your New Programs
+
+ If you develop a new program, and you want it to be of the greatest
+possible use to the public, the best way to achieve this is to make it
+free software which everyone can redistribute and change under these terms.
+
+ To do so, attach the following notices to the program. It is safest
+to attach them to the start of each source file to most effectively
+convey the exclusion of warranty; and each file should have at least
+the "copyright" line and a pointer to where the full notice is found.
+
+ <one line to give the program's name and a brief idea of what it does.>
+ Copyright (C) <year> <name of author>
+
+ This program is free software; you can redistribute it and/or modify
+ it under the terms of the GNU General Public License as published by
+ the Free Software Foundation; either version 2 of the License, or
+ (at your option) any later version.
+
+ This program is distributed in the hope that it will be useful,
+ but WITHOUT ANY WARRANTY; without even the implied warranty of
+ MERCHANTABILITY or FITNESS FOR A PARTICULAR PURPOSE. See the
+ GNU General Public License for more details.
+
+ You should have received a copy of the GNU General Public License
+ along with this program; if not, write to the Free Software
+ Foundation, Inc., 59 Temple Place, Suite 330, Boston, MA 02111-1307 USA
+
+
+Also add information on how to contact you by electronic and paper mail.
+
+If the program is interactive, make it output a short notice like this
+when it starts in an interactive mode:
+
+ Gnomovision version 69, Copyright (C) year name of author
+ Gnomovision comes with ABSOLUTELY NO WARRANTY; for details type `show w'.
+ This is free software, and you are welcome to redistribute it
+ under certain conditions; type `show c' for details.
+
+The hypothetical commands `show w' and `show c' should show the appropriate
+parts of the General Public License. Of course, the commands you use may
+be called something other than `show w' and `show c'; they could even be
+mouse-clicks or menu items--whatever suits your program.
+
+You should also get your employer (if you work as a programmer) or your
+school, if any, to sign a "copyright disclaimer" for the program, if
+necessary. Here is a sample; alter the names:
+
+ Yoyodyne, Inc., hereby disclaims all copyright interest in the program
+ `Gnomovision' (which makes passes at compilers) written by James Hacker.
+
+ <signature of Ty Coon>, 1 April 1989
+ Ty Coon, President of Vice
+
+This General Public License does not permit incorporating your program into
+proprietary programs. If your program is a subroutine library, you may
+consider it more useful to permit linking proprietary applications with the
+library. If this is what you want to do, use the GNU Library General
+Public License instead of this License.
diff --git a/CREDITS b/CREDITS
new file mode 100644
index 000000000000..671e9c2d31fe
--- /dev/null
+++ b/CREDITS
@@ -0,0 +1,3743 @@
+ This is at least a partial credits-file of people that have
+ contributed to the Linux project. It is sorted by name and
+ formatted to allow easy grepping and beautification by
+ scripts. The fields are: name (N), email (E), web-address
+ (W), PGP key ID and fingerprint (P), description (D), and
+ snail-mail address (S).
+ Thanks,
+
+ Linus
+----------
+
+N: Matti Aarnio
+E: mea@nic.funet.fi
+D: Alpha systems hacking, IPv6 and other network related stuff
+D: One of assisting postmasters for vger.kernel.org's lists
+S: (ask for current address)
+S: Finland
+
+N: Dragos Acostachioaie
+E: dragos@iname.com
+W: http://www.arbornet.org/~dragos
+D: /proc/sysvipc
+S: C. Negri 6, bl. D3
+S: Iasi 6600
+S: Romania
+
+N: Monalisa Agrawal
+E: magrawal@nortelnetworks.com
+D: Basic Interphase 5575 driver with UBR and ABR support.
+S: 75 Donald St, Apt 42
+S: Weymouth, MA 02188
+
+N: Dave Airlie
+E: airlied@linux.ie
+W: http://www.csn.ul.ie/~airlied
+D: NFS over TCP patches
+D: in-kernel DRM Maintainer
+S: Longford, Ireland
+S: Sydney, Australia
+
+N: Tigran A. Aivazian
+E: tigran@veritas.com
+W: http://www.moses.uklinux.net/patches
+D: BFS filesystem
+D: Intel IA32 CPU microcode update support
+D: Various kernel patches
+S: United Kingdom
+
+N: Werner Almesberger
+E: werner@almesberger.net
+W: http://www.almesberger.net/
+D: dosfs, LILO, some fd features, ATM, various other hacks here and there
+S: Buenos Aires
+S: Argentina
+
+N: Tim Alpaerts
+E: tim_alpaerts@toyota-motor-europe.com
+D: 802.2 class II logical link control layer,
+D: the humble start of an opening towards the IBM SNA protocols
+S: Klaproosstraat 72 c 10
+S: B-2610 Wilrijk-Antwerpen
+S: Belgium
+
+N: Anton Altaparmakov
+E: aia21@cantab.net
+W: http://www-stu.christs.cam.ac.uk/~aia21/
+D: Author of new NTFS driver, various other kernel hacks.
+S: Christ's College
+S: Cambridge CB2 3BU
+S: United Kingdom
+
+N: C. Scott Ananian
+E: cananian@alumni.princeton.edu
+W: http://www.pdos.lcs.mit.edu/~cananian
+P: 1024/85AD9EED AD C0 49 08 91 67 DF D7 FA 04 1A EE 09 E8 44 B0
+D: Unix98 pty support.
+D: APM update to 1.2 spec.
+D: /devfs hacking.
+S: 7 Kiwi Loop
+S: Howell, NJ 07731
+S: USA
+
+N: Erik Andersen
+E: andersen@codepoet.org
+W: http://www.codepoet.org/
+P: 1024D/30D39057 1BC4 2742 E885 E4DE 9301 0C82 5F9B 643E 30D3 9057
+D: Maintainer of ide-cd and Uniform CD-ROM driver,
+D: ATAPI CD-Changer support, Major 2.1.x CD-ROM update.
+S: 352 North 525 East
+S: Springville, Utah 84663
+S: USA
+
+N: Michael Ang
+E: mang@subcarrier.org
+W: http://www.subcarrier.org/mang
+D: Linux/PA-RISC hacker
+S: 85 Frank St.
+S: Ottawa, Ontario
+S: Canada K2P 0X3
+
+N: H. Peter Anvin
+E: hpa@zytor.com
+W: http://www.zytor.com/~hpa/
+P: 2047/2A960705 BA 03 D3 2C 14 A8 A8 BD 1E DF FE 69 EE 35 BD 74
+D: Author of the SYSLINUX boot loader, maintainer of the linux.* news
+D: hierarchy and the Linux Device List; various kernel hacks
+S: 4390 Albany Drive #46
+S: San Jose, California 95129
+S: USA
+
+N: Andrea Arcangeli
+E: andrea@suse.de
+W: http://www.kernel.org/pub/linux/kernel/people/andrea/
+P: 1024D/68B9CB43 13D9 8355 295F 4823 7C49 C012 DFA1 686E 68B9 CB43
+P: 1024R/CB4660B9 CC A0 71 81 F4 A0 63 AC C0 4B 81 1D 8C 15 C8 E5
+D: Parport hacker
+D: Implemented a workaround for some interrupt buggy printers
+D: Author of pscan that helps to fix lp/parport bugs
+D: Author of lil (Linux Interrupt Latency benchmark)
+D: Fixed the shm swap deallocation at swapoff time (try_to_unuse message)
+D: VM hacker
+D: Various other kernel hacks
+S: Via Cicalini 26
+S: Imola 40026
+S: Italy
+
+N: Derek Atkins
+E: warlord@MIT.EDU
+D: Linux-AFS Port, random kernel hacker,
+D: VFS fixes (new notify_change in particular)
+D: Moving all VFS access checks into the file systems
+S: MIT Room E15-341
+S: 20 Ames Street
+S: Cambridge, Massachusetts 02139
+S: USA
+
+N: Michel Aubry
+E: giovanni <giovanni@sudfr.com>
+D: Aladdin 1533/1543(C) chipset IDE
+D: VIA MVP-3/TX Pro III chipset IDE
+
+N: Jens Axboe
+E: axboe@suse.de
+D: Linux CD-ROM maintainer, DVD support
+D: elevator + block layer rewrites
+D: highmem I/O support
+D: misc hacking on IDE, SCSI, block drivers, etc
+S: Peter Bangs Vej 258, 2TH
+S: 2500 Valby
+S: Denmark
+
+N: John Aycock
+E: aycock@cpsc.ucalgary.ca
+D: Adaptec 274x driver
+S: Department of Computer Science
+S: University of Calgary
+S: Calgary, Alberta
+S: Canada
+
+N: Miles Bader
+E: miles@gnu.org
+D: v850 port (uClinux)
+S: NEC Corporation
+S: 1753 Shimonumabe, Nakahara-ku
+S: Kawasaki 211-8666
+S: Japan
+
+N: Ralf Baechle
+E: ralf@gnu.org
+P: 1024/AF7B30C1 CF 97 C2 CC 6D AE A7 FE C8 BA 9C FC 88 DE 32 C3
+D: Linux/MIPS port
+D: Linux/68k hacker
+S: Hauptstrasse 19
+S: 79837 St. Blasien
+S: Germany
+
+N: Krishna Balasubramanian
+E: balasub@cis.ohio-state.edu
+D: Wrote SYS V IPC (part of standard kernel since 0.99.10)
+
+N: Dario Ballabio
+E: ballabio_dario@emc.com
+E: dario.ballabio@tiscalinet.it
+E: dario.ballabio@inwind.it
+D: Author and maintainer of the Ultrastor 14F/34F SCSI driver
+D: Author and maintainer of the EATA ISA/EISA/PCI SCSI driver
+S: EMC Corporation
+S: Milano
+S: Italy
+
+N: Paul Bame
+E: bame@debian.org
+E: bame@puffin.external.hp.com
+E: paul_bame@hp.com
+W: http://www.parisc-linux.org
+D: PA-RISC 32 and 64-bit early boot, firmware interface, interrupts, misc
+S: MS42
+S: Hewlett-Packard
+S: 3404 E Harmony Rd
+S: Fort Collins, CO 80525
+
+N: Arindam Banerji
+E: axb@cse.nd.edu
+D: Contributed ESDI driver routines needed to port LINUX to the PS/2 MCA.
+S: Department of Computer Science & Eng.
+S: University of Notre Dame
+S: Notre Dame, Indiana
+S: USA
+
+N: Greg Banks
+E: gnb@alphalink.com.au
+D: IDT77105 ATM network driver
+D: some SuperH port work
+D: some trivial futzing with kconfig
+
+N: James Banks
+E: james@sovereign.org
+D: TLAN network driver
+D: Logitech Busmouse driver
+
+N: Krzysztof G. Baranowski
+E: kgb@manjak.knm.org.pl
+P: 1024/FA6F16D1 96 D1 1A CF 5F CA 69 EC F9 4F 36 1F 6D 60 7B DA
+D: Maintainer of the System V file system.
+D: System V fs update for 2.1.x dcache.
+D: Forward ported a couple of SCSI drivers.
+D: Various bugfixes.
+S: ul. Koscielna 12a
+S: 62-300 Wrzesnia
+S: Poland
+
+N: Fred Barnes
+E: frmb2@ukc.ac.uk
+D: Various parport/ppdev hacks and fixes
+S: Computing Lab, The University
+S: Canterbury, KENT
+S: CT2 7NF
+S: England
+
+N: Paul Barton-Davis
+E: pbd@op.net
+D: Driver for WaveFront soundcards (Turtle Beach Maui, Tropez, Tropez+)
+D: Various bugfixes and changes to sound drivers
+S: USA
+
+N: Carlos Henrique Bauer
+E: chbauer@acm.org
+E: bauer@atlas.unisinos.br
+D: Some new sysctl entries for the parport driver.
+D: New sysctl function for handling unsigned longs
+S: Universidade do Vale do Rio dos Sinos - UNISINOS
+S: DSI/IDASI
+S: Av. Unisinos, 950
+S: 93022000 Sao Leopoldo RS
+S: Brazil
+
+N: Peter Bauer
+E: 100136.3530@compuserve.com
+D: Driver for depca-ethernet-board
+S: 69259 Wilhemsfeld
+S: Rainweg 15
+S: Germany
+
+N: Fred Baumgarten
+E: dc6iq@insl1.etec.uni-karlsruhe.de
+E: dc6iq@adacom.org
+E: dc6iq@db0ais.#hes.deu.eu (packet radio)
+D: NET-2 & netstat(8)
+S: Soevener Strasse 11
+S: 53773 Hennef
+S: Germany
+
+N: Donald Becker
+E: becker@cesdis.gsfc.nasa.gov
+D: General low-level networking hacker
+D: Most of the ethercard drivers
+D: Original author of the NFS server
+S: USRA Center of Excellence in Space Data and Information Sciences
+S: Code 930.5, Goddard Space Flight Center
+S: Greenbelt, Maryland 20771
+S: USA
+
+N: Adam Belay
+E: ambx1@neo.rr.com
+D: Linux Plug and Play Support
+S: USA
+
+N: Daniele Bellucci
+E: bellucda@tiscali.it
+D: Various Janitor work.
+W: http://web.tiscali.it/bellucda
+S: Via Delle Palme, 9
+S: Terni 05100
+S: Italy
+
+N: Krzysztof Benedyczak
+E: golbi@mat.uni.torun.pl
+W: http://www.mat.uni.torun.pl/~golbi
+D: POSIX message queues fs (with M. Wronski)
+S: ul. Podmiejska 52
+S: Radunica
+S: 83-000 Pruszcz Gdanski
+S: Poland
+
+N: Randolph Bentson
+E: bentson@grieg.seaslug.org
+W: http://www.aa.net/~bentson/
+P: 1024/39ED5729 5C A8 7A F4 B2 7A D1 3E B5 3B 81 CF 47 30 11 71
+D: Author of driver for Cyclom-Y and Cyclades-Z async mux
+S: 2322 37th Ave SW
+S: Seattle, Washington 98126-2010
+S: USA
+
+N: Stephen R. van den Berg (AKA BuGless)
+E: berg@pool.informatik.rwth-aachen.de
+D: General kernel, gcc, and libc hacker
+D: Specialisation: tweaking, ensuring portability, tweaking, cleaning,
+D: tweaking and occasionally debugging :-)
+S: Bouwensstraat 22
+S: 6369 BG Simpelveld
+S: The Netherlands
+
+N: Peter Berger
+E: pberger@brimson.com
+W: http://www.brimson.com
+D: Author/maintainer of Digi AccelePort USB driver
+S: 1549 Hiironen Rd.
+S: Brimson, MN 55602
+S: USA
+
+N: Hennus Bergman
+P: 1024/77D50909 76 99 FD 31 91 E1 96 1C 90 BB 22 80 62 F6 BD 63
+D: Author and maintainer of the QIC-02 tape driver
+S: The Netherlands
+
+N: Tomas Berndtsson
+E: tomas@nocrew.org
+W: http://tomas.nocrew.org/
+D: dsp56k device driver
+
+N: Ross Biro
+E: bir7@leland.Stanford.Edu
+D: Original author of the Linux networking code
+
+N: Anton Blanchard
+E: anton@samba.org
+W: http://samba.org/~anton/
+P: 1024/8462A731 4C 55 86 34 44 59 A7 99 2B 97 88 4A 88 9A 0D 97
+D: sun4 port, Sparc hacker
+
+N: Hugh Blemings
+E: hugh@misc.nu
+W: http://misc.nu/hugh/
+D: Author and maintainer of the Keyspan USB to Serial drivers
+S: Po Box 234
+S: Belconnen ACT 2616
+S: Australia
+
+N: Philip Blundell
+E: philb@gnu.org
+D: Linux/ARM hacker
+D: Device driver hacker (eexpress, 3c505, c-qcam, ...)
+D: m68k port to HP9000/300
+D: AUN network protocols
+D: Co-architect of the parallel port sharing system
+D: IPv6 netfilter
+S: FutureTV Labs Ltd
+S: Brunswick House, 61-69 Newmarket Rd, Cambridge CB5 8EG
+S: United Kingdom
+
+N: Thomas Bogend�rfer
+E: tsbogend@alpha.franken.de
+D: PCnet32 driver, SONIC driver, JAZZ_ESP driver
+D: newport abscon driver, g364 framebuffer driver
+D: strace for Linux/Alpha
+D: Linux/MIPS hacker
+S: Schafhofstr. 40
+S: 90556 Cadolzburg
+S: Germany
+
+N: Bill Bogstad
+E: bogstad@pobox.com
+D: wrote /proc/self hack, minor samba & dosemu patches
+
+N: Axel Boldt
+E: axel@uni-paderborn.de
+W: http://math-www.uni-paderborn.de/~axel/
+D: Configuration help text support
+D: Linux CD and Support Giveaway List
+
+N: Erik Inge Bols�
+E: knan@mo.himolde.no
+D: Misc kernel hacks
+
+N: Andreas E. Bombe
+E: andreas.bombe@munich.netsurf.de
+W: http://home.pages.de/~andreas.bombe/
+P: 1024/04880A44 72E5 7031 4414 2EB6 F6B4 4CBD 1181 7032 0488 0A44
+D: IEEE 1394 subsystem rewrite and maintainer
+D: Texas Instruments PCILynx IEEE 1394 driver
+
+N: Al Borchers
+E: alborchers@steinerpoint.com
+D: Author/maintainer of Digi AccelePort USB driver
+D: work on usbserial and keyspan_pda drivers
+S: 4912 Zenith Ave. S.
+S: Minneapolis, MN 55410
+S: USA
+
+N: Marc Boucher
+E: marc@mbsi.ca
+P: CA 67 A5 1A 38 CE B6 F2 D5 83 51 03 D2 9C 30 9E CE D2 DD 65
+D: Netfilter core
+D: IP policy routing by mark
+D: Various fixes (mostly networking)
+S: Montreal, Quebec
+S: Canada
+
+N: Zolt�n B�sz�rm�nyi
+E: zboszor@mail.externet.hu
+D: MTRR emulation with Cyrix style ARR registers, Athlon MTRR support
+
+N: John Boyd
+E: boyd@cis.ohio-state.edu
+D: Co-author of wd7000 SCSI driver
+S: 101 Curl Drive #591
+S: Columbus, Ohio 43210
+S: USA
+
+N: Peter Braam
+E: braam@clusterfs.com
+W: http://www.clusterfs.com/
+D: Coda & InterMezzo filesystems
+S: 181 McNeil
+S: Canmore, AB
+S: Canada, T1W 2R9
+
+N: Ryan Bradetich
+E: rbradetich@uswest.net
+D: Linux/PA-RISC hacker
+S: 1200 Goldenrod Dr.
+S: Nampa, Idaho 83686
+
+N: Derrick J. Brashear
+E: shadow@dementia.org
+W: http://www.dementia.org/~shadow
+P: 512/71EC9367 C5 29 0F BC 83 51 B9 F0 BC 05 89 A0 4F 1F 30 05
+D: Author of Sparc CS4231 audio driver, random Sparc work
+S: 403 Gilmore Avenue
+S: Trafford, Pennsylvania 15085
+S: USA
+
+N: Dag Brattli
+E: dagb@cs.uit.no
+W: http://www.cs.uit.no/~dagb
+D: IrDA Subsystem
+S: 19. Wellington Road
+S: Lancaster, LA1 4DN
+S: UK, England
+
+N: Lars Brinkhoff
+E: lars@nocrew.org
+W: http://lars.nocrew.org/
+D: dsp56k device driver
+D: ptrace proxy in user mode kernel port
+S: Kopmansg 2
+S: 411 13 Goteborg
+S: Sweden
+
+N: Dominik Brodowski
+E: linux@brodo.de
+W: http://www.brodo.de/
+P: 1024D/725B37C6 190F 3E77 9C89 3B6D BECD 46EE 67C3 0308 725B 37C6
+D: parts of CPUFreq code, ACPI bugfixes
+S: Tuebingen, Germany
+
+N: Andries Brouwer
+E: aeb@cwi.nl
+D: random Linux hacker
+S: Bessemerstraat 21
+S: Amsterdam
+S: The Netherlands
+
+N: Zach Brown
+E: zab@zabbo.net
+D: maestro pci sound
+
+N: Gary Brubaker
+E: xavyer@ix.netcom.com
+D: USB Serial Empeg Empeg-car Mark I/II Driver
+
+N: Matthias Bruestle
+E: m@mbsks.franken.de
+D: REINER SCT cyberJack pinpad/e-com USB chipcard reader driver
+S: Germany
+
+N: Adrian Bunk
+E: bunk@stusta.de
+P: 1024D/4F12B400 B29C E71E FE19 6755 5C8A 84D4 99FC EA98 4F12 B400
+D: misc kernel hacking and testing
+S: Grasmeierstrasse 11
+S: 80805 Muenchen
+S: Germany
+
+N: Ray Burr
+E: ryb@nightmare.com
+D: Original author of Amiga FFS filesystem
+S: Orlando, Florida
+S: USA
+
+N: Lennert Buytenhek
+E: buytenh@gnu.org
+D: Rewrite of the ethernet bridging code
+S: Ravenhorst 58B
+S: 2317 AK Leiden
+S: The Netherlands
+
+N: Michael Callahan
+E: callahan@maths.ox.ac.uk
+D: PPP for Linux
+S: The Mathematical Institute
+S: 25-29 St Giles
+S: Oxford
+S: United Kingdom
+
+N: Luiz Fernando N. Capitulino
+E: lcapitulino@terra.com.br
+E: lcapitulino@prefeitura.sp.gov.br
+W: http://www.telecentros.sp.gov.br
+D: Little fixes and a lot of janitorial work
+S: E-GOV Telecentros SP
+S: Brazil
+
+N: Remy Card
+E: Remy.Card@masi.ibp.fr
+E: Remy.Card@linux.org
+D: Extended file system [defunct] designer and developer
+D: Second extended file system designer and developer
+S: Institut Blaise Pascal
+S: 4 Place Jussieu
+S: 75252 Paris Cedex 05
+S: France
+
+N: Ulf Carlsson
+D: SGI Indy audio (HAL2) drivers
+E: ulfc@bun.falkenberg.se
+
+N: Ed Carp
+E: ecarp@netcom.com
+D: uucp, elm, pine, pico port
+D: cron, at(1) developer
+S: 48287 Sawleaf
+S: Fremont, California 94539
+S: USA
+
+N: Florent Chabaud
+E: florent.chabaud@polytechnique.org
+D: software suspend
+S: SGDN/DCSSI/SDS/LTI
+S: 58, Bd Latour-Maubourg
+S: 75700 Paris 07 SP
+S: France
+
+N: Gordon Chaffee
+E: chaffee@cs.berkeley.edu
+W: http://bmrc.berkeley.edu/people/chaffee/
+D: vfat, fat32, joliet, native language support
+S: 3700 Warwick Road
+S: Fremont, California 94555
+S: USA
+
+N: Chih-Jen Chang
+E: chihjenc@scf.usc.edu
+E: chihjen@iis.sinica.edu.tw
+D: IGMP(Internet Group Management Protocol) version 2
+S: 3F, 65 Tajen street
+S: Tamsui town, Taipei county,
+S: Taiwan 251
+S: Republic of China
+
+N: Michael Elizabeth Chastain
+E: mec@shout.net
+D: Configure, Menuconfig, xconfig
+
+N: Raymond Chen
+E: raymondc@microsoft.com
+D: Author of Configure script
+S: 14509 NE 39th Street #1096
+S: Bellevue, Washington 98007
+S: USA
+
+N: Christopher L. Cheney
+E: ccheney@debian.org
+E: ccheney@cheney.cx
+W: http://www.cheney.cx
+P: 1024D/8E384AF2 2D31 1927 87D7 1F24 9FF9 1BC5 D106 5AB3 8E38 4AF2
+D: Vista Imaging usb webcam driver
+S: 314 Prince of Wales
+S: Conroe, TX 77304
+S: USA
+
+N: Stuart Cheshire
+E: cheshire@cs.stanford.edu
+D: Author of Starmode Radio IP (STRIP) driver
+D: Originator of design for new combined interrupt handlers
+S: William Gates Department
+S: Stanford University
+S: Stanford, California 94305
+S: USA
+
+N: Randolph Chung
+E: tausq@debian.org
+D: Linux/PA-RISC hacker
+S: Los Altos, CA 94022
+S: USA
+
+N: Juan Jose Ciarlante
]
]
|
| 234/2 |
2026/08/07 06:13 |
llm |
fixes-finder |
0m
Model:
Tokens:
input: 33809
output: 37
thoughts: 218
Thoughts: **Initial Investigation of the Git Import for `gadgetfs_bind` and `gadgetfs_unbind`**
Alright, so the first commit, `1da177e4c3f4`, that’s the starting point. I need to understand if `gadgetfs_unbind` was present from the very beginning, and how it relates to `gadgetfs_bind`. My first thought was to just check the full content of `gadgetfs_bind` in that commit. I glanced at the output, but realized I only got a snippet, so I'll need to go deeper.
Actually, now I recall from the `git-show` output that `gadgetfs_unbind` was indeed included, and it was in the error handling for `ENOMEM`. It’s looking like:
```c
static int gadgetfs_bind(struct usb_gadget *gadget)
{
// ...
enomem:
gadgetfs_unbind (gadget);
return -ENOMEM;
}
```
However, I'm not seeing the complete picture of `gadgetfs_bind`. This means the snippet isn’t sufficient to fully understand the context. So, I need to be absolutely sure. Let's `grep` for `gadgetfs_bind` within the `1da177e4c3f4` commit, specifically within `drivers/usb/gadget/inode.c`. That should give me the complete definition and surrounding code. Let's see what we find.
|
| 235/2 |
2026/08/07 06:13 |
tool |
git-show |
0m
Args: map[Commit:1da177e4c3f4:drivers/usb/gadget/inode.c]
Results: map[Output:
Full output is too long, showing 1000 out of 2110 lines.
[/*
* inode.c -- user mode filesystem api for usb gadget controllers
*
* Copyright (C) 2003-2004 David Brownell
* Copyright (C) 2003 Agilent Technologies
*
* This program is free software; you can redistribute it and/or modify
* it under the terms of the GNU General Public License as published by
* the Free Software Foundation; either version 2 of the License, or
* (at your option) any later version.
*
* This program is distributed in the hope that it will be useful,
* but WITHOUT ANY WARRANTY; without even the implied warranty of
* MERCHANTABILITY or FITNESS FOR A PARTICULAR PURPOSE. See the
* GNU General Public License for more details.
*
* You should have received a copy of the GNU General Public License
* along with this program; if not, write to the Free Software
* Foundation, Inc., 59 Temple Place, Suite 330, Boston, MA 02111-1307 USA
*/
// #define DEBUG /* data to help fault diagnosis */
// #define VERBOSE /* extra debug messages (success too) */
#include <linux/init.h>
#include <linux/module.h>
#include <linux/fs.h>
#include <linux/pagemap.h>
#include <linux/uts.h>
#include <linux/wait.h>
#include <linux/compiler.h>
#include <asm/uaccess.h>
#include <linux/slab.h>
#include <linux/device.h>
#include <linux/moduleparam.h>
#include <linux/usb_gadgetfs.h>
#include <linux/usb_gadget.h>
/*
* The gadgetfs API maps each endpoint to a file descriptor so that you
* can use standard synchronous read/write calls for I/O. There's some
* O_NONBLOCK and O_ASYNC/FASYNC style i/o support. Example usermode
* drivers show how this works in practice. You can also use AIO to
* eliminate I/O gaps between requests, to help when streaming data.
*
* Key parts that must be USB-specific are protocols defining how the
* read/write operations relate to the hardware state machines. There
* are two types of files. One type is for the device, implementing ep0.
* The other type is for each IN or OUT endpoint. In both cases, the
* user mode driver must configure the hardware before using it.
*
* - First, dev_config() is called when /dev/gadget/$CHIP is configured
* (by writing configuration and device descriptors). Afterwards it
* may serve as a source of device events, used to handle all control
* requests other than basic enumeration.
*
* - Then either immediately, or after a SET_CONFIGURATION control request,
* ep_config() is called when each /dev/gadget/ep* file is configured
* (by writing endpoint descriptors). Afterwards these files are used
* to write() IN data or to read() OUT data. To halt the endpoint, a
* "wrong direction" request is issued (like reading an IN endpoint).
*
* Unlike "usbfs" the only ioctl()s are for things that are rare, and maybe
* not possible on all hardware. For example, precise fault handling with
* respect to data left in endpoint fifos after aborted operations; or
* selective clearing of endpoint halts, to implement SET_INTERFACE.
*/
#define DRIVER_DESC "USB Gadget filesystem"
#define DRIVER_VERSION "24 Aug 2004"
static const char driver_desc [] = DRIVER_DESC;
static const char shortname [] = "gadgetfs";
MODULE_DESCRIPTION (DRIVER_DESC);
MODULE_AUTHOR ("David Brownell");
MODULE_LICENSE ("GPL");
/*----------------------------------------------------------------------*/
#define GADGETFS_MAGIC 0xaee71ee7
#define DMA_ADDR_INVALID (~(dma_addr_t)0)
/* /dev/gadget/$CHIP represents ep0 and the whole device */
enum ep0_state {
/* DISBLED is the initial state.
*/
STATE_DEV_DISABLED = 0,
/* Only one open() of /dev/gadget/$CHIP; only one file tracks
* ep0/device i/o modes and binding to the controller. Driver
* must always write descriptors to initialize the device, then
* the device becomes UNCONNECTED until enumeration.
*/
STATE_OPENED,
/* From then on, ep0 fd is in either of two basic modes:
* - (UN)CONNECTED: read usb_gadgetfs_event(s) from it
* - SETUP: read/write will transfer control data and succeed;
* or if "wrong direction", performs protocol stall
*/
STATE_UNCONNECTED,
STATE_CONNECTED,
STATE_SETUP,
/* UNBOUND means the driver closed ep0, so the device won't be
* accessible again (DEV_DISABLED) until all fds are closed.
*/
STATE_DEV_UNBOUND,
};
/* enough for the whole queue: most events invalidate others */
#define N_EVENT 5
struct dev_data {
spinlock_t lock;
atomic_t count;
enum ep0_state state;
struct usb_gadgetfs_event event [N_EVENT];
unsigned ev_next;
struct fasync_struct *fasync;
u8 current_config;
/* drivers reading ep0 MUST handle control requests (SETUP)
* reported that way; else the host will time out.
*/
unsigned usermode_setup : 1,
setup_in : 1,
setup_can_stall : 1,
setup_out_ready : 1,
setup_out_error : 1,
setup_abort : 1;
/* the rest is basically write-once */
struct usb_config_descriptor *config, *hs_config;
struct usb_device_descriptor *dev;
struct usb_request *req;
struct usb_gadget *gadget;
struct list_head epfiles;
void *buf;
wait_queue_head_t wait;
struct super_block *sb;
struct dentry *dentry;
/* except this scratch i/o buffer for ep0 */
u8 rbuf [256];
};
static inline void get_dev (struct dev_data *data)
{
atomic_inc (&data->count);
}
static void put_dev (struct dev_data *data)
{
if (likely (!atomic_dec_and_test (&data->count)))
return;
/* needs no more cleanup */
BUG_ON (waitqueue_active (&data->wait));
kfree (data);
}
static struct dev_data *dev_new (void)
{
struct dev_data *dev;
dev = kmalloc (sizeof *dev, GFP_KERNEL);
if (!dev)
return NULL;
memset (dev, 0, sizeof *dev);
dev->state = STATE_DEV_DISABLED;
atomic_set (&dev->count, 1);
spin_lock_init (&dev->lock);
INIT_LIST_HEAD (&dev->epfiles);
init_waitqueue_head (&dev->wait);
return dev;
}
/*----------------------------------------------------------------------*/
/* other /dev/gadget/$ENDPOINT files represent endpoints */
enum ep_state {
STATE_EP_DISABLED = 0,
STATE_EP_READY,
STATE_EP_DEFER_ENABLE,
STATE_EP_ENABLED,
STATE_EP_UNBOUND,
};
struct ep_data {
struct semaphore lock;
enum ep_state state;
atomic_t count;
struct dev_data *dev;
/* must hold dev->lock before accessing ep or req */
struct usb_ep *ep;
struct usb_request *req;
ssize_t status;
char name [16];
struct usb_endpoint_descriptor desc, hs_desc;
struct list_head epfiles;
wait_queue_head_t wait;
struct dentry *dentry;
struct inode *inode;
};
static inline void get_ep (struct ep_data *data)
{
atomic_inc (&data->count);
}
static void put_ep (struct ep_data *data)
{
if (likely (!atomic_dec_and_test (&data->count)))
return;
put_dev (data->dev);
/* needs no more cleanup */
BUG_ON (!list_empty (&data->epfiles));
BUG_ON (waitqueue_active (&data->wait));
BUG_ON (down_trylock (&data->lock) != 0);
kfree (data);
}
/*----------------------------------------------------------------------*/
/* most "how to use the hardware" policy choices are in userspace:
* mapping endpoint roles (which the driver needs) to the capabilities
* which the usb controller has. most of those capabilities are exposed
* implicitly, starting with the driver name and then endpoint names.
*/
static const char *CHIP;
/*----------------------------------------------------------------------*/
/* NOTE: don't use dev_printk calls before binding to the gadget
* at the end of ep0 configuration, or after unbind.
*/
/* too wordy: dev_printk(level , &(d)->gadget->dev , fmt , ## args) */
#define xprintk(d,level,fmt,args...) \
printk(level "%s: " fmt , shortname , ## args)
#ifdef DEBUG
#define DBG(dev,fmt,args...) \
xprintk(dev , KERN_DEBUG , fmt , ## args)
#else
#define DBG(dev,fmt,args...) \
do { } while (0)
#endif /* DEBUG */
#ifdef VERBOSE
#define VDEBUG DBG
#else
#define VDEBUG(dev,fmt,args...) \
do { } while (0)
#endif /* DEBUG */
#define ERROR(dev,fmt,args...) \
xprintk(dev , KERN_ERR , fmt , ## args)
#define WARN(dev,fmt,args...) \
xprintk(dev , KERN_WARNING , fmt , ## args)
#define INFO(dev,fmt,args...) \
xprintk(dev , KERN_INFO , fmt , ## args)
/*----------------------------------------------------------------------*/
/* SYNCHRONOUS ENDPOINT OPERATIONS (bulk/intr/iso)
*
* After opening, configure non-control endpoints. Then use normal
* stream read() and write() requests; and maybe ioctl() to get more
* precise FIFO status when recovering from cancelation.
*/
static void epio_complete (struct usb_ep *ep, struct usb_request *req)
{
struct ep_data *epdata = ep->driver_data;
if (!req->context)
return;
if (req->status)
epdata->status = req->status;
else
epdata->status = req->actual;
complete ((struct completion *)req->context);
}
/* tasklock endpoint, returning when it's connected.
* still need dev->lock to use epdata->ep.
*/
static int
get_ready_ep (unsigned f_flags, struct ep_data *epdata)
{
int val;
if (f_flags & O_NONBLOCK) {
if (down_trylock (&epdata->lock) != 0)
goto nonblock;
if (epdata->state != STATE_EP_ENABLED) {
up (&epdata->lock);
nonblock:
val = -EAGAIN;
} else
val = 0;
return val;
}
if ((val = down_interruptible (&epdata->lock)) < 0)
return val;
newstate:
switch (epdata->state) {
case STATE_EP_ENABLED:
break;
case STATE_EP_DEFER_ENABLE:
DBG (epdata->dev, "%s wait for host\n", epdata->name);
if ((val = wait_event_interruptible (epdata->wait,
epdata->state != STATE_EP_DEFER_ENABLE
|| epdata->dev->state == STATE_DEV_UNBOUND
)) < 0)
goto fail;
goto newstate;
// case STATE_EP_DISABLED: /* "can't happen" */
// case STATE_EP_READY: /* "can't happen" */
default: /* error! */
pr_debug ("%s: ep %p not available, state %d\n",
shortname, epdata, epdata->state);
// FALLTHROUGH
case STATE_EP_UNBOUND: /* clean disconnect */
val = -ENODEV;
fail:
up (&epdata->lock);
}
return val;
}
static ssize_t
ep_io (struct ep_data *epdata, void *buf, unsigned len)
{
DECLARE_COMPLETION (done);
int value;
spin_lock_irq (&epdata->dev->lock);
if (likely (epdata->ep != NULL)) {
struct usb_request *req = epdata->req;
req->context = &done;
req->complete = epio_complete;
req->buf = buf;
req->length = len;
value = usb_ep_queue (epdata->ep, req, GFP_ATOMIC);
} else
value = -ENODEV;
spin_unlock_irq (&epdata->dev->lock);
if (likely (value == 0)) {
value = wait_event_interruptible (done.wait, done.done);
if (value != 0) {
spin_lock_irq (&epdata->dev->lock);
if (likely (epdata->ep != NULL)) {
DBG (epdata->dev, "%s i/o interrupted\n",
epdata->name);
usb_ep_dequeue (epdata->ep, epdata->req);
spin_unlock_irq (&epdata->dev->lock);
wait_event (done.wait, done.done);
if (epdata->status == -ECONNRESET)
epdata->status = -EINTR;
} else {
spin_unlock_irq (&epdata->dev->lock);
DBG (epdata->dev, "endpoint gone\n");
epdata->status = -ENODEV;
}
}
return epdata->status;
}
return value;
}
/* handle a synchronous OUT bulk/intr/iso transfer */
static ssize_t
ep_read (struct file *fd, char __user *buf, size_t len, loff_t *ptr)
{
struct ep_data *data = fd->private_data;
void *kbuf;
ssize_t value;
if ((value = get_ready_ep (fd->f_flags, data)) < 0)
return value;
/* halt any endpoint by doing a "wrong direction" i/o call */
if (data->desc.bEndpointAddress & USB_DIR_IN) {
if ((data->desc.bmAttributes & USB_ENDPOINT_XFERTYPE_MASK)
== USB_ENDPOINT_XFER_ISOC)
return -EINVAL;
DBG (data->dev, "%s halt\n", data->name);
spin_lock_irq (&data->dev->lock);
if (likely (data->ep != NULL))
usb_ep_set_halt (data->ep);
spin_unlock_irq (&data->dev->lock);
up (&data->lock);
return -EBADMSG;
}
/* FIXME readahead for O_NONBLOCK and poll(); careful with ZLPs */
value = -ENOMEM;
kbuf = kmalloc (len, SLAB_KERNEL);
if (unlikely (!kbuf))
goto free1;
value = ep_io (data, kbuf, len);
VDEBUG (data->dev, "%s read %d OUT, status %d\n",
data->name, len, value);
if (value >= 0 && copy_to_user (buf, kbuf, value))
value = -EFAULT;
free1:
up (&data->lock);
kfree (kbuf);
return value;
}
/* handle a synchronous IN bulk/intr/iso transfer */
static ssize_t
ep_write (struct file *fd, const char __user *buf, size_t len, loff_t *ptr)
{
struct ep_data *data = fd->private_data;
void *kbuf;
ssize_t value;
if ((value = get_ready_ep (fd->f_flags, data)) < 0)
return value;
/* halt any endpoint by doing a "wrong direction" i/o call */
if (!(data->desc.bEndpointAddress & USB_DIR_IN)) {
if ((data->desc.bmAttributes & USB_ENDPOINT_XFERTYPE_MASK)
== USB_ENDPOINT_XFER_ISOC)
return -EINVAL;
DBG (data->dev, "%s halt\n", data->name);
spin_lock_irq (&data->dev->lock);
if (likely (data->ep != NULL))
usb_ep_set_halt (data->ep);
spin_unlock_irq (&data->dev->lock);
up (&data->lock);
return -EBADMSG;
}
/* FIXME writebehind for O_NONBLOCK and poll(), qlen = 1 */
value = -ENOMEM;
kbuf = kmalloc (len, SLAB_KERNEL);
if (!kbuf)
goto free1;
if (copy_from_user (kbuf, buf, len)) {
value = -EFAULT;
goto free1;
}
value = ep_io (data, kbuf, len);
VDEBUG (data->dev, "%s write %d IN, status %d\n",
data->name, len, value);
free1:
up (&data->lock);
kfree (kbuf);
return value;
}
static int
ep_release (struct inode *inode, struct file *fd)
{
struct ep_data *data = fd->private_data;
/* clean up if this can be reopened */
if (data->state != STATE_EP_UNBOUND) {
data->state = STATE_EP_DISABLED;
data->desc.bDescriptorType = 0;
data->hs_desc.bDescriptorType = 0;
}
put_ep (data);
return 0;
}
static int ep_ioctl (struct inode *inode, struct file *fd,
unsigned code, unsigned long value)
{
struct ep_data *data = fd->private_data;
int status;
if ((status = get_ready_ep (fd->f_flags, data)) < 0)
return status;
spin_lock_irq (&data->dev->lock);
if (likely (data->ep != NULL)) {
switch (code) {
case GADGETFS_FIFO_STATUS:
status = usb_ep_fifo_status (data->ep);
break;
case GADGETFS_FIFO_FLUSH:
usb_ep_fifo_flush (data->ep);
break;
case GADGETFS_CLEAR_HALT:
status = usb_ep_clear_halt (data->ep);
break;
default:
status = -ENOTTY;
}
} else
status = -ENODEV;
spin_unlock_irq (&data->dev->lock);
up (&data->lock);
return status;
}
/*----------------------------------------------------------------------*/
/* ASYNCHRONOUS ENDPOINT I/O OPERATIONS (bulk/intr/iso) */
struct kiocb_priv {
struct usb_request *req;
struct ep_data *epdata;
void *buf;
char __user *ubuf;
unsigned actual;
};
static int ep_aio_cancel(struct kiocb *iocb, struct io_event *e)
{
struct kiocb_priv *priv = iocb->private;
struct ep_data *epdata;
int value;
local_irq_disable();
epdata = priv->epdata;
// spin_lock(&epdata->dev->lock);
kiocbSetCancelled(iocb);
if (likely(epdata && epdata->ep && priv->req))
value = usb_ep_dequeue (epdata->ep, priv->req);
else
value = -EINVAL;
// spin_unlock(&epdata->dev->lock);
local_irq_enable();
aio_put_req(iocb);
return value;
}
static ssize_t ep_aio_read_retry(struct kiocb *iocb)
{
struct kiocb_priv *priv = iocb->private;
ssize_t status = priv->actual;
/* we "retry" to get the right mm context for this: */
status = copy_to_user(priv->ubuf, priv->buf, priv->actual);
if (unlikely(0 != status))
status = -EFAULT;
else
status = priv->actual;
kfree(priv->buf);
kfree(priv);
aio_put_req(iocb);
return status;
}
static void ep_aio_complete(struct usb_ep *ep, struct usb_request *req)
{
struct kiocb *iocb = req->context;
struct kiocb_priv *priv = iocb->private;
struct ep_data *epdata = priv->epdata;
/* lock against disconnect (and ideally, cancel) */
spin_lock(&epdata->dev->lock);
priv->req = NULL;
priv->epdata = NULL;
if (NULL == iocb->ki_retry
|| unlikely(0 == req->actual)
|| unlikely(kiocbIsCancelled(iocb))) {
kfree(req->buf);
kfree(priv);
iocb->private = NULL;
/* aio_complete() reports bytes-transferred _and_ faults */
if (unlikely(kiocbIsCancelled(iocb)))
aio_put_req(iocb);
else
aio_complete(iocb,
req->actual ? req->actual : req->status,
req->status);
} else {
/* retry() won't report both; so we hide some faults */
if (unlikely(0 != req->status))
DBG(epdata->dev, "%s fault %d len %d\n",
ep->name, req->status, req->actual);
priv->buf = req->buf;
priv->actual = req->actual;
kick_iocb(iocb);
}
spin_unlock(&epdata->dev->lock);
usb_ep_free_request(ep, req);
put_ep(epdata);
}
static ssize_t
ep_aio_rwtail(
struct kiocb *iocb,
char *buf,
size_t len,
struct ep_data *epdata,
char __user *ubuf
)
{
struct kiocb_priv *priv = (void *) &iocb->private;
struct usb_request *req;
ssize_t value;
priv = kmalloc(sizeof *priv, GFP_KERNEL);
if (!priv) {
value = -ENOMEM;
fail:
kfree(buf);
return value;
}
iocb->private = priv;
priv->ubuf = ubuf;
value = get_ready_ep(iocb->ki_filp->f_flags, epdata);
if (unlikely(value < 0)) {
kfree(priv);
goto fail;
}
iocb->ki_cancel = ep_aio_cancel;
get_ep(epdata);
priv->epdata = epdata;
priv->actual = 0;
/* each kiocb is coupled to one usb_request, but we can't
* allocate or submit those if the host disconnected.
*/
spin_lock_irq(&epdata->dev->lock);
if (likely(epdata->ep)) {
req = usb_ep_alloc_request(epdata->ep, GFP_ATOMIC);
if (likely(req)) {
priv->req = req;
req->buf = buf;
req->length = len;
req->complete = ep_aio_complete;
req->context = iocb;
value = usb_ep_queue(epdata->ep, req, GFP_ATOMIC);
if (unlikely(0 != value))
usb_ep_free_request(epdata->ep, req);
} else
value = -EAGAIN;
} else
value = -ENODEV;
spin_unlock_irq(&epdata->dev->lock);
up(&epdata->lock);
if (unlikely(value)) {
kfree(priv);
put_ep(epdata);
} else
value = -EIOCBQUEUED;
return value;
}
static ssize_t
ep_aio_read(struct kiocb *iocb, char __user *ubuf, size_t len, loff_t o)
{
struct ep_data *epdata = iocb->ki_filp->private_data;
char *buf;
if (unlikely(epdata->desc.bEndpointAddress & USB_DIR_IN))
return -EINVAL;
buf = kmalloc(len, GFP_KERNEL);
if (unlikely(!buf))
return -ENOMEM;
iocb->ki_retry = ep_aio_read_retry;
return ep_aio_rwtail(iocb, buf, len, epdata, ubuf);
}
static ssize_t
ep_aio_write(struct kiocb *iocb, const char __user *ubuf, size_t len, loff_t o)
{
struct ep_data *epdata = iocb->ki_filp->private_data;
char *buf;
if (unlikely(!(epdata->desc.bEndpointAddress & USB_DIR_IN)))
return -EINVAL;
buf = kmalloc(len, GFP_KERNEL);
if (unlikely(!buf))
return -ENOMEM;
if (unlikely(copy_from_user(buf, ubuf, len) != 0)) {
kfree(buf);
return -EFAULT;
}
return ep_aio_rwtail(iocb, buf, len, epdata, NULL);
}
/*----------------------------------------------------------------------*/
/* used after endpoint configuration */
static struct file_operations ep_io_operations = {
.owner = THIS_MODULE,
.llseek = no_llseek,
.read = ep_read,
.write = ep_write,
.ioctl = ep_ioctl,
.release = ep_release,
.aio_read = ep_aio_read,
.aio_write = ep_aio_write,
};
/* ENDPOINT INITIALIZATION
*
* fd = open ("/dev/gadget/$ENDPOINT", O_RDWR)
* status = write (fd, descriptors, sizeof descriptors)
*
* That write establishes the endpoint configuration, configuring
* the controller to process bulk, interrupt, or isochronous transfers
* at the right maxpacket size, and so on.
*
* The descriptors are message type 1, identified by a host order u32
* at the beginning of what's written. Descriptor order is: full/low
* speed descriptor, then optional high speed descriptor.
*/
static ssize_t
ep_config (struct file *fd, const char __user *buf, size_t len, loff_t *ptr)
{
struct ep_data *data = fd->private_data;
struct usb_ep *ep;
u32 tag;
int value;
if ((value = down_interruptible (&data->lock)) < 0)
return value;
if (data->state != STATE_EP_READY) {
value = -EL2HLT;
goto fail;
}
value = len;
if (len < USB_DT_ENDPOINT_SIZE + 4)
goto fail0;
/* we might need to change message format someday */
if (copy_from_user (&tag, buf, 4)) {
goto fail1;
}
if (tag != 1) {
DBG(data->dev, "config %s, bad tag %d\n", data->name, tag);
goto fail0;
}
buf += 4;
len -= 4;
/* NOTE: audio endpoint extensions not accepted here;
* just don't include the extra bytes.
*/
/* full/low speed descriptor, then high speed */
if (copy_from_user (&data->desc, buf, USB_DT_ENDPOINT_SIZE)) {
goto fail1;
}
if (data->desc.bLength != USB_DT_ENDPOINT_SIZE
|| data->desc.bDescriptorType != USB_DT_ENDPOINT)
goto fail0;
if (len != USB_DT_ENDPOINT_SIZE) {
if (len != 2 * USB_DT_ENDPOINT_SIZE)
goto fail0;
if (copy_from_user (&data->hs_desc, buf + USB_DT_ENDPOINT_SIZE,
USB_DT_ENDPOINT_SIZE)) {
goto fail1;
}
if (data->hs_desc.bLength != USB_DT_ENDPOINT_SIZE
|| data->hs_desc.bDescriptorType
!= USB_DT_ENDPOINT) {
DBG(data->dev, "config %s, bad hs length or type\n",
data->name);
goto fail0;
}
}
value = len;
spin_lock_irq (&data->dev->lock);
if (data->dev->state == STATE_DEV_UNBOUND) {
value = -ENOENT;
goto gone;
} else if ((ep = data->ep) == NULL) {
value = -ENODEV;
goto gone;
}
switch (data->dev->gadget->speed) {
case USB_SPEED_LOW:
case USB_SPEED_FULL:
value = usb_ep_enable (ep, &data->desc);
if (value == 0)
data->state = STATE_EP_ENABLED;
break;
#ifdef HIGHSPEED
case USB_SPEED_HIGH:
/* fails if caller didn't provide that descriptor... */
value = usb_ep_enable (ep, &data->hs_desc);
if (value == 0)
data->state = STATE_EP_ENABLED;
break;
#endif
default:
DBG (data->dev, "unconnected, %s init deferred\n",
data->name);
data->state = STATE_EP_DEFER_ENABLE;
}
if (value == 0)
fd->f_op = &ep_io_operations;
gone:
spin_unlock_irq (&data->dev->lock);
if (value < 0) {
fail:
data->desc.bDescriptorType = 0;
data->hs_desc.bDescriptorType = 0;
}
up (&data->lock);
return value;
fail0:
value = -EINVAL;
goto fail;
fail1:
value = -EFAULT;
goto fail;
}
static int
ep_open (struct inode *inode, struct file *fd)
{
struct ep_data *data = inode->u.generic_ip;
int value = -EBUSY;
if (down_interruptible (&data->lock) != 0)
return -EINTR;
spin_lock_irq (&data->dev->lock);
if (data->dev->state == STATE_DEV_UNBOUND)
value = -ENOENT;
else if (data->state == STATE_EP_DISABLED) {
value = 0;
data->state = STATE_EP_READY;
get_ep (data);
fd->private_data = data;
VDEBUG (data->dev, "%s ready\n", data->name);
} else
DBG (data->dev, "%s state %d\n",
data->name, data->state);
spin_unlock_irq (&data->dev->lock);
up (&data->lock);
return value;
}
/* used before endpoint configuration */
static struct file_operations ep_config_operations = {
.owner = THIS_MODULE,
.llseek = no_llseek,
.open = ep_open,
.write = ep_config,
.release = ep_release,
};
/*----------------------------------------------------------------------*/
/* EP0 IMPLEMENTATION can be partly in userspace.
*
* Drivers that use this facility receive various events, including
* control requests the kernel doesn't handle. Drivers that don't
* use this facility may be too simple-minded for real applications.
*/
static inline void ep0_readable (struct dev_data *dev)
{
wake_up (&dev->wait);
kill_fasync (&dev->fasync, SIGIO, POLL_IN);
}
static void clean_req (struct usb_ep *ep, struct usb_request *req)
{
struct dev_data *dev = ep->driver_data;
if (req->buf != dev->rbuf) {
usb_ep_free_buffer (ep, req->buf, req->dma, req->length);
req->buf = dev->rbuf;
req->dma = DMA_ADDR_INVALID;
}
req->complete = epio_complete;
dev->setup_out_ready = 0;
}
static void ep0_complete (struct usb_ep *ep, struct usb_request *req)
{
struct dev_data *dev = ep->driver_data;
int free = 1;
/* for control OUT, data must still get to userspace */
if (!dev->setup_in) {
dev->setup_out_error = (req->status != 0);
if (!dev->setup_out_error)
free = 0;
dev->setup_out_ready = 1;
ep0_readable (dev);
} else if (dev->state == STATE_SETUP)
dev->state = STATE_CONNECTED;
/* clean up as appropriate */
if (free && req->buf != &dev->rbuf)
clean_req (ep, req);
req->complete = epio_complete;
}
static int setup_req (struct usb_ep *ep, struct usb_request *req, u16 len)
{
struct dev_data *dev = ep->driver_data;
if (dev->setup_out_ready) {
DBG (dev, "ep0 request busy!\n");
return -EBUSY;
}
if (len > sizeof (dev->rbuf))
req->buf = usb_ep_alloc_buffer (ep, len, &req->dma, GFP_ATOMIC);
if (req->buf == 0) {
req->buf = dev->rbuf;
return -ENOMEM;
}
req->complete = ep0_complete;
req->length = len;
return 0;
}
static ssize_t
ep0_read (struct file *fd, char __user *buf, size_t len, loff_t *ptr)
{
struct dev_data *dev = fd->private_data;
ssize_t retval;
enum ep0_state state;
spin_lock_irq (&dev->lock);
/* report fd mode change before acting on it */
if (dev->setup_abort) {
dev->setup_abort = 0;
retval = -EIDRM;
goto done;
}
/* control DATA stage */
if ((state = dev->state) == STATE_SETUP) {
if (dev->setup_in) { /* stall IN */
VDEBUG(dev, "ep0in stall\n");
(void) usb_ep_set_halt (dev->gadget->ep0);
retval = -EL2HLT;
dev->state = STATE_CONNECTED;
} else if (len == 0) { /* ack SET_CONFIGURATION etc */
struct usb_ep *ep = dev->gadget->ep0;
struct usb_request *req = dev->req;
if ((retval = setup_req (ep, req, 0)) == 0)
retval = usb_ep_queue (ep, req, GFP_ATOMIC);
dev->state = STATE_CONNECTED;
/* assume that was SET_CONFIGURATION */
if (dev->current_config) {
unsigned power;
#ifdef HIGHSPEED
if (dev->gadget->speed == USB_SPEED_HIGH)
power = dev->hs_config->bMaxPower;
else
#endif
power = dev->config->bMaxPower;
usb_gadget_vbus_draw(dev->gadget, 2 * power);
}
} else { /* collect OUT data */
if ((fd->f_flags & O_NONBLOCK) != 0
&& !dev->setup_out_ready) {
retval = -EAGAIN;
goto done;
}
spin_unlock_irq (&dev->lock);
retval = wait_event_interruptible (dev->wait,
dev->setup_out_ready != 0);
]
]
|
| 236/2 |
2026/08/07 06:13 |
llm |
fixes-finder |
0m
Model:
Tokens:
input: 44688
output: 40
thoughts: 185
Thoughts: **Investigating the GadgetFS Unbind Vulnerability**
Alright, let's get down to brass tacks. I'm dealing with a potentially nasty slab-use-after-free issue, and the context points to `drivers/usb/gadget/legacy/inode.c`. Since I don't have the whole file readily available, my initial instinct is to use `git-blame` on that specific file, pinpointing the commit *before* the fix. That should give me the clearest picture of the code as it stood. Alternatively, `git-log -G"gadgetfs_unbind"` is a quick and dirty way to trace the introduction of that function.
Hold on a second, the bug description is pretty explicit: it's a reference count underflow in the error handling of `gadgetfs_bind()`. If a fault injection causes a memory allocation failure during the binding process, `gadgetfs_bind()` incorrectly calls `gadgetfs_unbind()`, dropping a reference to the device before it's actually been properly acquired.
Given this, I want to quickly verify if the `gadgetfs_unbind` function was part of the `gadgetfs_bind` implementation from the beginning.
|
| 237/2 |
2026/08/07 06:13 |
tool |
git-log |
10m
Error:
Args: map[CodeRegexp:gadgetfs_unbind SourcePath:drivers/usb/gadget/legacy/inode.c]
Results:
|
| 238/2 |
2026/08/07 06:23 |
llm |
fixes-finder |
0m
Model:
Tokens:
input: 45207
output: 35
thoughts: 70
Thoughts: Here's my summary, as though I'm thinking through this process:
**Diving into the GadgetFS Bindings**
Okay, so I need to understand what's going on with `gadgetfs_bind` in this specific commit, `1da177e4c3f4`. The most efficient way to get a focused view of the relevant code is to directly extract the section that defines the function. I can use `git show` to grab the content of `drivers/usb/gadget/inode.c` as it existed in that commit. Then, a quick `grep` will get me the surrounding lines containing `gadgetfs_bind`. I'll grab a few lines of context around the `gadgetfs_bind` occurrences, both before and after, to understand its immediate surrounding logic. Specifically, I'll take a view of the code from the beginning of the function and 5 lines prior, to 20 lines after its first usage. This should give me a pretty clear picture of how `gadgetfs_bind` is used, its parameters, and what other functions it interacts with within this specific commit. Let's see what that looks like...
|
| 239/2 |
2026/08/07 06:23 |
tool |
grepper |
0m
Error:
Args: map[Expression:gadgetfs_bind PathPrefix:drivers/usb/gadget/inode.c]
Results:
|
| 240/2 |
2026/08/07 06:23 |
llm |
fixes-finder |
0m
Model:
Tokens:
input: 45579
output: 42
thoughts: 49
Thoughts: Okay, here's my interpretation of those thoughts, tailored for an expert audience, as if they were my own:
**Initial Strategy Assessment for Finding Code History**
Right, so I'm looking for the history, the changes, behind some specific code within that USB gadget driver, specifically `drivers/usb/gadget/legacy/inode.c`. My first instinct was to leverage `grepper`, but then it hits me: that's going to be local, isn't it? Just searching the *current* working tree. Not what I want. I need to delve into the full history of that file.
So, I'm thinking, `git log` is the way to go. Given the targeted file, I can either use `-L` with specific lines to get a more focused view of change history, which is going to be my preferred option given the need for specificity. Alternatively, I could just `git blame` the entire file. That *could* work, but might be too noisy depending on the size of the file and how much churn it's seen over time. `-L` seems a more targeted and therefore efficient route to understand the code's evolution within this driver.
|
| 241/2 |
2026/08/07 06:23 |
tool |
git-blame |
0m
Args: map[End:1710 File:drivers/usb/gadget/legacy/inode.c Start:1690]
Results: map[Output:^1da177e4c3f4 drivers/usb/gadget/inode.c 1690) dev->req->complete = epio_complete;
^1da177e4c3f4 drivers/usb/gadget/inode.c 1691)
^1da177e4c3f4 drivers/usb/gadget/inode.c 1692) if (activate_ep_files (dev) < 0)
^1da177e4c3f4 drivers/usb/gadget/inode.c 1693) goto enomem;
^1da177e4c3f4 drivers/usb/gadget/inode.c 1694)
^1da177e4c3f4 drivers/usb/gadget/inode.c 1695) INFO (dev, "bound to %s driver\n", gadget->name);
7489d14943181 drivers/usb/gadget/inode.c 1696) spin_lock_irq(&dev->lock);
7489d14943181 drivers/usb/gadget/inode.c 1697) dev->state = STATE_DEV_UNCONNECTED;
7489d14943181 drivers/usb/gadget/inode.c 1698) spin_unlock_irq(&dev->lock);
^1da177e4c3f4 drivers/usb/gadget/inode.c 1699) get_dev (dev);
^1da177e4c3f4 drivers/usb/gadget/inode.c 1700) return 0;
^1da177e4c3f4 drivers/usb/gadget/inode.c 1701)
^1da177e4c3f4 drivers/usb/gadget/inode.c 1702) enomem:
^1da177e4c3f4 drivers/usb/gadget/inode.c 1703) gadgetfs_unbind (gadget);
^1da177e4c3f4 drivers/usb/gadget/inode.c 1704) return -ENOMEM;
^1da177e4c3f4 drivers/usb/gadget/inode.c 1705) }
^1da177e4c3f4 drivers/usb/gadget/inode.c 1706)
^1da177e4c3f4 drivers/usb/gadget/inode.c 1707) static void
^1da177e4c3f4 drivers/usb/gadget/inode.c 1708) gadgetfs_disconnect (struct usb_gadget *gadget)
^1da177e4c3f4 drivers/usb/gadget/inode.c 1709) {
^1da177e4c3f4 drivers/usb/gadget/inode.c 1710) struct dev_data *dev = get_gadget_data (gadget);
]
|
| 242/2 |
2026/08/07 06:23 |
llm |
fixes-finder |
0m
Model:
Tokens:
input: 46720
output: 42
thoughts: 193
Thoughts: **Analyzing the GadgetFS Bug Root**
Okay, so I'm digging into this `gadgetfs` issue, and the first thing that jumps out is that `gadgetfs_unbind` was present *way* back in the initial commit, `1da177e4c3f4`. Hmm, that means if the bug relates to `gadgetfs_unbind`, then the bug itself probably originated with that initial code. No need to look for a later regression, which is handy.
Now, let's refresh our understanding of the problem. It seems the core of the issue is a KASAN slab-use-after-free, triggered by an underflow in the reference count within `gadgetfs_bind()`'s error handling. Apparently, a forced memory allocation failure during the binding process leads to an incorrect call to `gadgetfs_unbind()`, where the device reference is incorrectly dropped.
The question now becomes: *does* `gadgetfs_unbind` always drop a reference to the device? That's the crux. If we can confirm that, then we're well on our way to understanding why this is happening.
|
| 243/2 |
2026/08/07 06:23 |
tool |
git-blame |
0m
Args: map[End:1730 File:drivers/usb/gadget/legacy/inode.c Start:1711]
Results: map[Output:001428e4871d6 drivers/usb/gadget/inode.c 1711) unsigned long flags;
^1da177e4c3f4 drivers/usb/gadget/inode.c 1712)
001428e4871d6 drivers/usb/gadget/inode.c 1713) spin_lock_irqsave (&dev->lock, flags);
7489d14943181 drivers/usb/gadget/inode.c 1714) if (dev->state == STATE_DEV_UNCONNECTED)
07cb7f23d07aa drivers/usb/gadget/inode.c 1715) goto exit;
7489d14943181 drivers/usb/gadget/inode.c 1716) dev->state = STATE_DEV_UNCONNECTED;
^1da177e4c3f4 drivers/usb/gadget/inode.c 1717)
^1da177e4c3f4 drivers/usb/gadget/inode.c 1718) INFO (dev, "disconnected\n");
^1da177e4c3f4 drivers/usb/gadget/inode.c 1719) next_event (dev, GADGETFS_DISCONNECT);
^1da177e4c3f4 drivers/usb/gadget/inode.c 1720) ep0_readable (dev);
07cb7f23d07aa drivers/usb/gadget/inode.c 1721) exit:
001428e4871d6 drivers/usb/gadget/inode.c 1722) spin_unlock_irqrestore (&dev->lock, flags);
^1da177e4c3f4 drivers/usb/gadget/inode.c 1723) }
^1da177e4c3f4 drivers/usb/gadget/inode.c 1724)
^1da177e4c3f4 drivers/usb/gadget/inode.c 1725) static void
^1da177e4c3f4 drivers/usb/gadget/inode.c 1726) gadgetfs_suspend (struct usb_gadget *gadget)
^1da177e4c3f4 drivers/usb/gadget/inode.c 1727) {
^1da177e4c3f4 drivers/usb/gadget/inode.c 1728) struct dev_data *dev = get_gadget_data (gadget);
f16443a034c7a drivers/usb/gadget/legacy/inode.c 1729) unsigned long flags;
^1da177e4c3f4 drivers/usb/gadget/inode.c 1730)
]
|
| 244/2 |
2026/08/07 06:24 |
llm |
fixes-finder |
0m
Model:
Tokens:
input: 47961
output: 42
thoughts: 14
Thoughts: Here's my thought process, as if I were considering this function:
**Analyzing `gadgetfs_unbind`**
Okay, let's see, I need to focus on `gadgetfs_unbind`. This is where the cleanup likely happens when a file system is unmounted or a device is detached, right? I'll need to understand how resources are released, any potential locking issues, and any potential for leaks. I imagine this function is crucial for preventing dangling pointers and ensuring data integrity during unmount. Specifically, I'll be looking for any signs of memory not being freed, file descriptors not being closed properly, or threads not being joined. I should also check if it handles error conditions gracefully, especially if an unbind operation is interrupted. The devil's always in the details, so I need to pay close attention to the order of operations and how the system interacts with the underlying storage and device drivers. Let's dig in.
|
| 245/2 |
2026/08/07 06:24 |
tool |
git-blame |
0m
Args: map[End:1690 File:drivers/usb/gadget/legacy/inode.c Start:1600]
Results: map[Output:^1da177e4c3f4 drivers/usb/gadget/inode.c 1600)
bf4afc53b77ae drivers/usb/gadget/legacy/inode.c 1601) data = kzalloc_obj(*data);
^1da177e4c3f4 drivers/usb/gadget/inode.c 1602) if (!data)
0ae4ea8092e12 drivers/usb/gadget/inode.c 1603) goto enomem0;
^1da177e4c3f4 drivers/usb/gadget/inode.c 1604) data->state = STATE_EP_DISABLED;
a79df50bbad3b drivers/usb/gadget/inode.c 1605) mutex_init(&data->lock);
^1da177e4c3f4 drivers/usb/gadget/inode.c 1606) init_waitqueue_head (&data->wait);
^1da177e4c3f4 drivers/usb/gadget/inode.c 1607)
bd3cf1a9396e6 drivers/usb/gadget/legacy/inode.c 1608) strscpy(data->name, ep->name);
8d66db50b2ac9 drivers/usb/gadget/legacy/inode.c 1609) refcount_set (&data->count, 1);
^1da177e4c3f4 drivers/usb/gadget/inode.c 1610) data->dev = dev;
^1da177e4c3f4 drivers/usb/gadget/inode.c 1611) get_dev (dev);
^1da177e4c3f4 drivers/usb/gadget/inode.c 1612)
^1da177e4c3f4 drivers/usb/gadget/inode.c 1613) data->ep = ep;
^1da177e4c3f4 drivers/usb/gadget/inode.c 1614) ep->driver_data = data;
^1da177e4c3f4 drivers/usb/gadget/inode.c 1615)
^1da177e4c3f4 drivers/usb/gadget/inode.c 1616) data->req = usb_ep_alloc_request (ep, GFP_KERNEL);
^1da177e4c3f4 drivers/usb/gadget/inode.c 1617) if (!data->req)
0ae4ea8092e12 drivers/usb/gadget/inode.c 1618) goto enomem1;
^1da177e4c3f4 drivers/usb/gadget/inode.c 1619)
b65e3b11e9236 drivers/usb/gadget/legacy/inode.c 1620) err = gadgetfs_create_file (dev->sb, data->name,
d4461a602cf39 drivers/usb/gadget/legacy/inode.c 1621) data, &ep_io_operations);
b65e3b11e9236 drivers/usb/gadget/legacy/inode.c 1622) if (err)
0ae4ea8092e12 drivers/usb/gadget/inode.c 1623) goto enomem2;
^1da177e4c3f4 drivers/usb/gadget/inode.c 1624) list_add_tail (&data->epfiles, &dev->epfiles);
^1da177e4c3f4 drivers/usb/gadget/inode.c 1625) }
^1da177e4c3f4 drivers/usb/gadget/inode.c 1626) return 0;
^1da177e4c3f4 drivers/usb/gadget/inode.c 1627)
0ae4ea8092e12 drivers/usb/gadget/inode.c 1628) enomem2:
0ae4ea8092e12 drivers/usb/gadget/inode.c 1629) usb_ep_free_request (ep, data->req);
0ae4ea8092e12 drivers/usb/gadget/inode.c 1630) enomem1:
0ae4ea8092e12 drivers/usb/gadget/inode.c 1631) put_dev (dev);
0ae4ea8092e12 drivers/usb/gadget/inode.c 1632) kfree (data);
0ae4ea8092e12 drivers/usb/gadget/inode.c 1633) enomem0:
441b62c1edb98 drivers/usb/gadget/inode.c 1634) DBG (dev, "%s enomem\n", __func__);
^1da177e4c3f4 drivers/usb/gadget/inode.c 1635) destroy_ep_files (dev);
^1da177e4c3f4 drivers/usb/gadget/inode.c 1636) return -ENOMEM;
^1da177e4c3f4 drivers/usb/gadget/inode.c 1637) }
^1da177e4c3f4 drivers/usb/gadget/inode.c 1638)
^1da177e4c3f4 drivers/usb/gadget/inode.c 1639) static void
^1da177e4c3f4 drivers/usb/gadget/inode.c 1640) gadgetfs_unbind (struct usb_gadget *gadget)
^1da177e4c3f4 drivers/usb/gadget/inode.c 1641) {
^1da177e4c3f4 drivers/usb/gadget/inode.c 1642) struct dev_data *dev = get_gadget_data (gadget);
^1da177e4c3f4 drivers/usb/gadget/inode.c 1643)
441b62c1edb98 drivers/usb/gadget/inode.c 1644) DBG (dev, "%s\n", __func__);
^1da177e4c3f4 drivers/usb/gadget/inode.c 1645)
^1da177e4c3f4 drivers/usb/gadget/inode.c 1646) spin_lock_irq (&dev->lock);
^1da177e4c3f4 drivers/usb/gadget/inode.c 1647) dev->state = STATE_DEV_UNBOUND;
520b72fc64deb drivers/usb/gadget/legacy/inode.c 1648) while (dev->udc_usage > 0) {
520b72fc64deb drivers/usb/gadget/legacy/inode.c 1649) spin_unlock_irq(&dev->lock);
520b72fc64deb drivers/usb/gadget/legacy/inode.c 1650) usleep_range(1000, 2000);
520b72fc64deb drivers/usb/gadget/legacy/inode.c 1651) spin_lock_irq(&dev->lock);
520b72fc64deb drivers/usb/gadget/legacy/inode.c 1652) }
^1da177e4c3f4 drivers/usb/gadget/inode.c 1653) spin_unlock_irq (&dev->lock);
^1da177e4c3f4 drivers/usb/gadget/inode.c 1654)
^1da177e4c3f4 drivers/usb/gadget/inode.c 1655) destroy_ep_files (dev);
^1da177e4c3f4 drivers/usb/gadget/inode.c 1656) gadget->ep0->driver_data = NULL;
^1da177e4c3f4 drivers/usb/gadget/inode.c 1657) set_gadget_data (gadget, NULL);
^1da177e4c3f4 drivers/usb/gadget/inode.c 1658)
^1da177e4c3f4 drivers/usb/gadget/inode.c 1659) /* we've already been disconnected ... no i/o is active */
^1da177e4c3f4 drivers/usb/gadget/inode.c 1660) if (dev->req)
^1da177e4c3f4 drivers/usb/gadget/inode.c 1661) usb_ep_free_request (gadget->ep0, dev->req);
441b62c1edb98 drivers/usb/gadget/inode.c 1662) DBG (dev, "%s done\n", __func__);
^1da177e4c3f4 drivers/usb/gadget/inode.c 1663) put_dev (dev);
^1da177e4c3f4 drivers/usb/gadget/inode.c 1664) }
^1da177e4c3f4 drivers/usb/gadget/inode.c 1665)
^1da177e4c3f4 drivers/usb/gadget/inode.c 1666) static struct dev_data *the_device;
^1da177e4c3f4 drivers/usb/gadget/inode.c 1667)
ffe0b33506250 drivers/usb/gadget/inode.c 1668) static int gadgetfs_bind(struct usb_gadget *gadget,
ffe0b33506250 drivers/usb/gadget/inode.c 1669) struct usb_gadget_driver *driver)
^1da177e4c3f4 drivers/usb/gadget/inode.c 1670) {
^1da177e4c3f4 drivers/usb/gadget/inode.c 1671) struct dev_data *dev = the_device;
^1da177e4c3f4 drivers/usb/gadget/inode.c 1672)
^1da177e4c3f4 drivers/usb/gadget/inode.c 1673) if (!dev)
^1da177e4c3f4 drivers/usb/gadget/inode.c 1674) return -ESRCH;
^1da177e4c3f4 drivers/usb/gadget/inode.c 1675) if (0 != strcmp (CHIP, gadget->name)) {
00274921a052d drivers/usb/gadget/inode.c 1676) pr_err("%s expected %s controller not %s\n",
^1da177e4c3f4 drivers/usb/gadget/inode.c 1677) shortname, CHIP, gadget->name);
^1da177e4c3f4 drivers/usb/gadget/inode.c 1678) return -ENODEV;
^1da177e4c3f4 drivers/usb/gadget/inode.c 1679) }
^1da177e4c3f4 drivers/usb/gadget/inode.c 1680)
^1da177e4c3f4 drivers/usb/gadget/inode.c 1681) set_gadget_data (gadget, dev);
^1da177e4c3f4 drivers/usb/gadget/inode.c 1682) dev->gadget = gadget;
^1da177e4c3f4 drivers/usb/gadget/inode.c 1683) gadget->ep0->driver_data = dev;
^1da177e4c3f4 drivers/usb/gadget/inode.c 1684)
^1da177e4c3f4 drivers/usb/gadget/inode.c 1685) /* preallocate control response and buffer */
^1da177e4c3f4 drivers/usb/gadget/inode.c 1686) dev->req = usb_ep_alloc_request (gadget->ep0, GFP_KERNEL);
^1da177e4c3f4 drivers/usb/gadget/inode.c 1687) if (!dev->req)
^1da177e4c3f4 drivers/usb/gadget/inode.c 1688) goto enomem;
^1da177e4c3f4 drivers/usb/gadget/inode.c 1689) dev->req->context = NULL;
^1da177e4c3f4 drivers/usb/gadget/inode.c 1690) dev->req->complete = epio_complete;
]
|
| 246/2 |
2026/08/07 06:24 |
llm |
fixes-finder |
0m
Model:
Tokens:
input: 51686
output: 53
thoughts: 1595
Thoughts: **Refcount Underflow in gadgetfs_bind/unbind: A Root Cause Analysis**
Okay, here's what I've figured out: it looks like there's a serious refcount underflow issue in the gadgetfs driver, specifically in how `gadgetfs_bind` and `gadgetfs_unbind` interact. I can see that `gadgetfs_unbind` calls `put_dev(dev)` unconditionally at line 1663. Now, `gadgetfs_bind` calls `get_dev(dev)` at line 1699, but *only* on a successful bind.
Here's the problem: if `gadgetfs_bind` fails, say, during the `usb_ep_alloc_request` or `activate_ep_files` calls, it jumps to the `enomem` label and then calls `gadgetfs_unbind`. The crucial point is that `get_dev(dev)` *wasn't* called in the error path. So, `gadgetfs_unbind` ends up calling `put_dev(dev)` on a reference that was never taken in the first place, leading to a refcount underflow.
I need to confirm this is indeed the root cause of the issue, and that the code's behavior hasn't changed.
I checked the initial commit of the code, `1da177e4c3f41524e886b7f1b8a0c1fc7321cac2`, and verified the functions' code:
* In `1da177e4c3f4` at line 1699, `get_dev(dev)` occurs right before `return 0;`.
* And `enomem:` is at line 1702, which calls `gadgetfs_unbind(gadget);`.
* At line 1663 of `1da177e4c3f4` `gadgetfs_unbind` calls `put_dev(dev);`.
Therefore the issue was present since the beginning of git history.
I double-checked to ensure that `gadgetfs_bind` and `gadgetfs_unbind` have always been this way since the original commit, and yes, it's the exact same problem: `gadgetfs_unbind` unconditionally calls `put_dev(dev)`, while `gadgetfs_bind` only calls `get_dev(dev)` on success. If `gadgetfs_bind` fails (e.g., if `usb_ep_alloc_request` fails or `activate_ep_files` fails), it jumps to `enomem` and calls `gadgetfs_unbind`, which then calls `put_dev(dev)` and drops a reference that was never taken.
I also checked to see if any other commit could have introduced the problem or any changes that could alter the refcounting process. But no, the basic structure has been there from the start.
So, this refcount underflow has been baked in since the initial commit, `1da177e4c3f41524e886b7f1b8a0c1fc7321cac2`, which is what I'll report. It's a fundamental error in how the error handling interacts with the refcounting.
|
| 247/2 |
2026/08/07 06:24 |
tool |
set-results |
0m
Args: map[FixesHash:1da177e4c3f41524e886b7f1b8a0c1fc7321cac2]
Results: map[FixesHash:1da177e4c3f41524e886b7f1b8a0c1fc7321cac2]
|
| 248/1 |
2026/08/07 06:24 |
action |
format-fixes |
0m
Results: map[Fixes:map[AuthorEmail:torvalds@ppc970.osdl.org AuthorName:Linus Torvalds Hash:1da177e4c3f41524e886b7f1b8a0c1fc7321cac2 Title:Linux-2.6.12-rc2]]
|
| 249/1 |
2026/08/07 06:24 |
action |
get-maintainers |
0m
Results: map[Recipients:[map[Email:brauner@kernel.org Name:Christian Brauner To:false] map[Email:gregkh@linuxfoundation.org Name:Greg Kroah-Hartman To:true] map[Email:jack@suse.cz Name:Jan Kara To:false] map[Email:kees@kernel.org Name:Kees Cook To:false] map[Email:linux-kernel@vger.kernel.org Name: To:false] map[Email:linux-usb@vger.kernel.org Name: To:true] map[Email:mjguzik@gmail.com Name:Mateusz Guzik To:false] map[Email:viro@zeniv.linux.org.uk Name:Al Viro To:false] map[Email:torvalds@ppc970.osdl.org Name:Linus Torvalds To:true]]]
|
| 250/1 |
2026/08/07 06:24 |
action |
get-recent-commits |
0m
Results: map[RecentCommits:Convert 'alloc_obj' family to use the new default GFP_KERNEL argument
treewide: Replace kmalloc with kmalloc_obj for non-scalar types
convert gadgetfs
gadgetfs: switch to simple_remove_by_name()
fs: rename generic_delete_inode() and generic_drop_inode()
functionfs, gadgetfs: use simple_recursive_removal()
USB: gadget: Replace deprecated strncpy() with strscpy()
USB: gadget: Use str_enable_disable-like helpers
[tree-wide] finally take no_llseek out
legacy: convert to new timestamp accessors
usb: gadgetfs: return USB_GADGET_DELAYED_STATUS from setup()
usb: convert to ctime accessor functions
usb: gadgetfs: Fix ep_read_iter to handle ITER_UBUF
USB: gadgetfs: Fix race between mounting and unmounting
gadgetfs: ep_io - wait until IRQ finishes
USB: gadget: Rename usb_gadget_probe_driver()
usb: gadget: eliminate anonymous module_init & module_exit
headers/prep: usb: gadget: Fix namespace collision
usb: gadget: clear related members when goto fail
usb: gadget: don't release an existing dev->buf
]
|
| 251/1 |
2026/08/07 06:24 |
agent |
description-generator |
0m
Model:
Instruction:
You are an experienced Linux kernel developer tasked with writing a commit description for
a kernel bug fixing commit. The description should start with a one-line summary,
and then include description of the bug being fixed, and how it's fixed by the provided patch.
The one-line summary should describe the change being made, rather than mention the tool that
detected the bug.
The description must not contain lines starting with '#' because they will dropped by git as comments.
The description must not contain lines starting with '--' or '---' (including inline code diffs)
because they may confuse git/patch utilities.
Your final reply should contain only the text of the commit description.
The one-line summary must be not longer than 72 characters.
IMPORTANT: Do not wrap lines manually (e.g., at 80 characters); we will reformat the text
automatically, so keep paragraphs as single lines without newlines.
Generally try to phrase the description without mentioning syzkaller
(avoid phrases like "the bug was triggered by syzkaller" or "the bug was triggered by fuzzer", etc).
How the bug was triggered is generally an irrelevant detail.
Any bug triggered by a fuzzer can also be triggered by a malicious user, or a buggy program.
If the crash is reported by a sanitizer (e.g., KASAN, KMSAN, lockdep), include the relevant
parts of the sanitizer output to illustrate the problem. Exclude less relevant sections,
as the stack trace can be very long. Describe the execution path that leads to the manifestation
of the kernel bug.
Prefer calling several tools at the same time to save round-trips.
Prompt:
The crash that corresponds to the bug is:
udc dummy_udc.0: failed to start USB Gadget filesystem: -12
gadgetfs gadget.0: probe with driver gadgetfs failed with error -12
gadgetfs: bound to dummy_udc driver
==================================================================
BUG: KASAN: slab-use-after-free in instrument_atomic_read_write include/linux/instrumented.h:112 [inline]
BUG: KASAN: slab-use-after-free in atomic_fetch_sub_release include/linux/atomic/atomic-instrumented.h:400 [inline]
BUG: KASAN: slab-use-after-free in __refcount_sub_and_test include/linux/refcount.h:389 [inline]
BUG: KASAN: slab-use-after-free in __refcount_dec_and_test include/linux/refcount.h:432 [inline]
BUG: KASAN: slab-use-after-free in refcount_dec_and_test include/linux/refcount.h:450 [inline]
BUG: KASAN: slab-use-after-free in put_dev drivers/usb/gadget/legacy/inode.c:165 [inline]
BUG: KASAN: slab-use-after-free in gadgetfs_kill_sb+0x46/0x110 drivers/usb/gadget/legacy/inode.c:2104
Write of size 4 at addr ffff88811638c080 by task syz-executor169/5848
CPU: 0 UID: 0 PID: 5848 Comm: syz-executor169 Not tainted syzkaller #1 PREEMPT_{RT,(full)}
Hardware name: QEMU Standard PC (Q35 + ICH9, 2009), BIOS 1.16.3-debian-1.16.3-2 04/01/2014
Call Trace:
<TASK>
dump_stack_lvl+0xe8/0x150 lib/dump_stack.c:120
print_address_description+0x55/0x1e0 mm/kasan/report.c:378
print_report+0x58/0x70 mm/kasan/report.c:482
kasan_report+0x117/0x150 mm/kasan/report.c:595
check_region_inline mm/kasan/generic.c:-1 [inline]
kasan_check_range+0x264/0x2c0 mm/kasan/generic.c:200
instrument_atomic_read_write include/linux/instrumented.h:112 [inline]
atomic_fetch_sub_release include/linux/atomic/atomic-instrumented.h:400 [inline]
__refcount_sub_and_test include/linux/refcount.h:389 [inline]
__refcount_dec_and_test include/linux/refcount.h:432 [inline]
refcount_dec_and_test include/linux/refcount.h:450 [inline]
put_dev drivers/usb/gadget/legacy/inode.c:165 [inline]
gadgetfs_kill_sb+0x46/0x110 drivers/usb/gadget/legacy/inode.c:2104
deactivate_locked_super+0xbc/0x130 fs/super.c:477
cleanup_mnt+0x3d3/0x460 fs/namespace.c:1317
task_work_run+0x1d9/0x270 kernel/task_work.c:233
resume_user_mode_work include/linux/resume_user_mode.h:50 [inline]
__exit_to_user_mode_loop kernel/entry/common.c:70 [inline]
exit_to_user_mode_loop+0x1fa/0x730 kernel/entry/common.c:101
__exit_to_user_mode_prepare include/linux/irq-entry-common.h:207 [inline]
syscall_exit_to_user_mode_prepare include/linux/irq-entry-common.h:230 [inline]
syscall_exit_to_user_mode include/linux/entry-common.h:318 [inline]
do_syscall_64+0x353/0x580 arch/x86/entry/syscall_64.c:100
entry_SYSCALL_64_after_hwframe+0x77/0x7f
RIP: 0033:0x7f5f0e505027
Code: a2 c7 05 54 b0 08 00 00 00 00 00 eb 96 e8 a1 08 00 00 90 31 f6 e9 09 00 00 00 66 0f 1f 84 00 00 00 00 00 b8 a6 00 00 00 0f 05 <48> 3d 00 f0 ff ff 77 01 c3 48 c7 c2 d8 ff ff ff f7 d8 64 89 02 b8
RSP: 002b:00007ffcdf2b0288 EFLAGS: 00000246 ORIG_RAX: 00000000000000a6
RAX: 0000000000000000 RBX: 0000000000000003 RCX: 00007f5f0e505027
RDX: 0000000000000000 RSI: 0000000000000000 RDI: 00007f5f0e55f05e
RBP: 00007f5f0e55f05e R08: 0000000000000000 R09: 0000000000000000
R10: 0000000000000000 R11: 0000000000000246 R12: 00007f5f0e55f623
R13: 00007ffcdf2b03e0 R14: 00007f5f0e561510 R15: 00007f5f0e5614e8
</TASK>
Allocated by task 5848:
kasan_save_stack mm/kasan/common.c:57 [inline]
kasan_save_track+0x3e/0x80 mm/kasan/common.c:78
poison_kmalloc_redzone mm/kasan/common.c:398 [inline]
__kasan_kmalloc+0x93/0xb0 mm/kasan/common.c:415
kasan_kmalloc include/linux/kasan.h:263 [inline]
__kmalloc_cache_noprof+0x3d2/0x6b0 mm/slub.c:5489
_kmalloc_noprof include/linux/slab.h:988 [inline]
_kzalloc_noprof include/linux/slab.h:1309 [inline]
dev_new drivers/usb/gadget/legacy/inode.c:176 [inline]
gadgetfs_fill_super+0x27b/0x7a0 drivers/usb/gadget/legacy/inode.c:2054
vfs_get_super fs/super.c:1273 [inline]
get_tree_single+0xc0/0x150 fs/super.c:1300
vfs_get_tree+0x92/0x2a0 fs/super.c:1700
fc_mount fs/namespace.c:1198 [inline]
do_new_mount_fc fs/namespace.c:3765 [inline]
do_new_mount+0x319/0xdc0 fs/namespace.c:3841
do_mount fs/namespace.c:4174 [inline]
__do_sys_mount fs/namespace.c:4390 [inline]
__se_sys_mount+0x31d/0x420 fs/namespace.c:4367
do_syscall_x64 arch/x86/entry/syscall_64.c:63 [inline]
do_syscall_64+0x174/0x580 arch/x86/entry/syscall_64.c:94
entry_SYSCALL_64_after_hwframe+0x77/0x7f
Freed by task 5848:
kasan_save_stack mm/kasan/common.c:57 [inline]
kasan_save_track+0x3e/0x80 mm/kasan/common.c:78
kasan_save_free_info+0x40/0x50 mm/kasan/generic.c:584
poison_slab_object mm/kasan/common.c:253 [inline]
__kasan_slab_free+0x5c/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 mm/slub.c:6377 [inline]
kfree+0x1c5/0x6c0 mm/slub.c:6692
put_dev drivers/usb/gadget/legacy/inode.c:169 [inline]
dev_release+0x173/0x210 drivers/usb/gadget/legacy/inode.c:1215
__fput+0x42a/0xa80 fs/file_table.c:512
fput_close_sync+0x11f/0x240 fs/file_table.c:617
__do_sys_close fs/open.c:1511 [inline]
__se_sys_close fs/open.c:1496 [inline]
__x64_sys_close+0x7e/0x110 fs/open.c:1496
do_syscall_x64 arch/x86/entry/syscall_64.c:63 [inline]
do_syscall_64+0x174/0x580 arch/x86/entry/syscall_64.c:94
entry_SYSCALL_64_after_hwframe+0x77/0x7f
The buggy address belongs to the object at ffff88811638c000
which belongs to the cache kmalloc-1k of size 1024
The buggy address is located 128 bytes inside of
freed 1024-byte region [ffff88811638c000, ffff88811638c400)
The buggy address belongs to the physical page:
page: refcount:0 mapcount:0 mapping:0000000000000000 index:0xffff88811638d000 pfn:0x116388
head: order:3 mapcount:0 entire_mapcount:0 nr_pages_mapped:0 pincount:0
flags: 0x100000000000240(workingset|head|node=0|zone=2)
page_type: f5(slab)
raw: 0100000000000240 ffff888100041dc0 ffffea00044a8a10 ffffea00043da810
raw: ffff88811638d000 000000080010000f 00000000f5000000 0000000000000000
head: 0100000000000240 ffff888100041dc0 ffffea00044a8a10 ffffea00043da810
head: ffff88811638d000 000000080010000f 00000000f5000000 0000000000000000
head: 0100000000000003 fffffffffffffe01 00000000ffffffff 00000000ffffffff
head: 0000000000000000 0000000000000000 00000000ffffffff 0000000000000008
page dumped because: kasan: bad access detected
page_owner tracks the page as allocated
page last allocated via order 3, migratetype Unmovable, gfp_mask 0xd20c0(__GFP_IO|__GFP_FS|__GFP_NOWARN|__GFP_NORETRY|__GFP_COMP|__GFP_NOMEMALLOC), pid 5581, tgid 5581 (syz-executor), ts 49160475111, free_ts 0
set_page_owner include/linux/page_owner.h:32 [inline]
post_alloc_hook+0x1f9/0x250 mm/page_alloc.c:1859
prep_new_page mm/page_alloc.c:1867 [inline]
get_page_from_freelist+0x262a/0x26a0 mm/page_alloc.c:3946
__alloc_frozen_pages_noprof+0x18d/0x380 mm/page_alloc.c:5304
alloc_slab_page mm/slub.c:3266 [inline]
allocate_slab+0x79/0x5e0 mm/slub.c:3380
new_slab mm/slub.c:3426 [inline]
refill_objects+0x2d8/0x350 mm/slub.c:7310
refill_sheaf mm/slub.c:2804 [inline]
__pcs_replace_empty_main+0x330/0x690 mm/slub.c:4675
alloc_from_pcs mm/slub.c:4773 [inline]
slab_alloc_node mm/slub.c:4905 [inline]
__kmalloc_cache_noprof+0x468/0x6b0 mm/slub.c:5485
_kmalloc_noprof include/linux/slab.h:988 [inline]
_kzalloc_noprof include/linux/slab.h:1309 [inline]
rxrpc_service_prealloc+0x9f/0x130 net/rxrpc/call_accept.c:167
rxrpc_listen+0x1b7/0x360 net/rxrpc/af_rxrpc.c:237
afs_open_socket+0x2fa/0x3f0 fs/afs/rxrpc.c:107
afs_net_init+0x6ce/0x8f0 fs/afs/main.c:116
ops_init+0x35d/0x5d0 net/core/net_namespace.c:137
setup_net+0x118/0x350 net/core/net_namespace.c:446
copy_net_ns+0x4f9/0x720 net/core/net_namespace.c:579
create_new_namespaces+0x3f0/0x6b0 kernel/nsproxy.c:132
unshare_nsproxy_namespaces+0x149/0x190 kernel/nsproxy.c:234
page_owner free stack trace missing
Memory state around the buggy address:
ffff88811638bf80: fc fc fc fc fc fc fc fc fc fc fc fc fc fc fc fc
ffff88811638c000: fa fb fb fb fb fb fb fb fb fb fb fb fb fb fb fb
>ffff88811638c080: fb fb fb fb fb fb fb fb fb fb fb fb fb fb fb fb
^
ffff88811638c100: fb fb fb fb fb fb fb fb fb fb fb fb fb fb fb fb
ffff88811638c180: fb fb fb fb fb fb fb fb fb fb fb fb fb fb fb fb
==================================================================
The explanation of the root cause of the bug is:
The root cause of this KASAN slab-use-after-free is a reference count underflow in the error handling path of `gadgetfs_bind()`.
When fault injection forces a memory allocation failure during the binding process, `gadgetfs_bind()` incorrectly calls `gadgetfs_unbind()`, which drops a reference to the device (`dev`) that was never actually taken.
### Detailed Sequence of Events
1. **Initialization and Open:**
When the gadgetfs superblock is created, `dev_new()` allocates the `dev_data` structure (`dev`) and initializes its refcount to 1. When the user opens the `/tmp/gadgetfs/ep0` file, `gadget_dev_open()` is called, which increments the refcount to 2 via `get_dev(dev)`.
2. **Driver Registration and Binding:**
The user writes configuration data to `ep0`, invoking `dev_config()`. This function calls `usb_gadget_register_driver(&gadgetfs_driver)`, which registers the driver and attempts to bind it to the UDC, eventually calling `gadgetfs_bind()`.
3. **Fault Injection and Refcount Underflow:**
In `gadgetfs_bind()`, memory allocations are performed (e.g., `usb_ep_alloc_request()` or inside `activate_ep_files()`). If fault injection causes one of these to fail, the code jumps to the `enomem` label:
```c
// drivers/usb/gadget/legacy/inode.c : gadgetfs_bind()
dev->req = usb_ep_alloc_request (gadget->ep0, GFP_KERNEL);
if (!dev->req)
goto enomem;
...
if (activate_ep_files (dev) < 0)
goto enomem;
...
get_dev (dev); // <--- Reference is only taken on SUCCESS
return 0;
enomem:
gadgetfs_unbind (gadget);
return -ENOMEM;
```
The `enomem` path calls `gadgetfs_unbind()`, which performs cleanup and unconditionally calls `put_dev(dev)`. Because `get_dev(dev)` is only called at the very end of `gadgetfs_bind()` on success, `gadgetfs_unbind()` drops a reference that was never acquired. The refcount incorrectly drops from 2 to 1.
4. **Probe Failure Ignored:**
The `-ENOMEM` error propagates up to the driver core. However, `__driver_attach()` intentionally ignores probe failures and returns `0`. Because `gadgetfs_driver` does not set `match_existing_only = true`, `usb_gadget_register_driver_owner()` also returns `0` (success).
5. **Closing ep0 and Freeing:**
Seeing a success return value, `dev_config()` sets `dev->gadget_registered = true`.
Later, the reproducer closes the `ep0` file descriptor, triggering `dev_release()`. `dev_release()` cleans up and calls `put_dev(dev)`. The refcount drops from 1 to 0, and `dev` is prematurely freed.
6. **The Use-After-Free:**
Finally, the reproducer unmounts the filesystem, which invokes `gadgetfs_kill_sb()`.
```c
// drivers/usb/gadget/legacy/inode.c : gadgetfs_kill_sb()
if (the_device) {
put_dev (the_device); // <--- UAF here
the_device = NULL;
}
```
`gadgetfs_kill_sb()` attempts to drop the initial reference by calling `put_dev(the_device)`. Since `the_device` points to the `dev` structure that was already freed in step 5, this triggers the KASAN slab-use-after-free.
### Suggested Fix
`gadgetfs_bind()` should not call `gadgetfs_unbind()` in its error path, as `gadgetfs_unbind()` is designed to tear down a fully bound gadget (and thus expects the extra reference to exist). Instead, `gadgetfs_bind()` should manually clean up the specific state it altered before the failure occurred.
```c
enomem:
- gadgetfs_unbind (gadget);
+ if (dev->req) {
+ usb_ep_free_request (gadget->ep0, dev->req);
+ dev->req = NULL;
+ }
+ gadget->ep0->driver_data = NULL;
+ set_gadget_data (gadget, NULL);
+ dev->gadget = NULL;
return -ENOMEM;
```
*(Note: `activate_ep_files()` already perfectly cleans up after itself on failure, so no additional teardown is needed for it in `gadgetfs_bind()`'s error path).*
The diff of the bug fix is:
diff --git a/drivers/usb/gadget/legacy/inode.c b/drivers/usb/gadget/legacy/inode.c
index d87a8ab51..365f9ab4d 100644
--- a/drivers/usb/gadget/legacy/inode.c
+++ b/drivers/usb/gadget/legacy/inode.c
@@ -1700,7 +1700,13 @@ static int gadgetfs_bind(struct usb_gadget *gadget,
return 0;
enomem:
- gadgetfs_unbind (gadget);
+ if (dev->req) {
+ usb_ep_free_request(gadget->ep0, dev->req);
+ dev->req = NULL;
+ }
+ gadget->ep0->driver_data = NULL;
+ dev->gadget = NULL;
+ set_gadget_data(gadget, NULL);
return -ENOMEM;
}
Additional description of the patch:
I have applied a fix to resolve the KASAN slab-use-after-free bug in `gadgetfs_kill_sb()`.
### Cause of the Bug
The root cause is a reference count underflow in the error handling path of `gadgetfs_bind()`. When `gadgetfs_bind()` encounters an error (e.g., memory allocation failure during `usb_ep_alloc_request()` or `activate_ep_files()`), it jumps to the `enomem` label and calls `gadgetfs_unbind()`.
However, `gadgetfs_unbind()` is designed to tear down a fully initialized and bound gadget. It unconditionally drops a device reference by calling `put_dev(dev)`. Since `gadgetfs_bind()` only acquires this reference (`get_dev(dev)`) at the very end of the function upon complete success, calling `gadgetfs_unbind()` in the error path drops a reference that was never taken. This leads to a premature free of the `dev` structure when the `ep0` file is closed, and subsequently a use-after-free when the filesystem is unmounted and `gadgetfs_kill_sb()` tries to drop the initial reference.
### The Fix
To fix this, we must stop calling the full teardown function `gadgetfs_unbind()` from the `gadgetfs_bind()` error path. Instead, we should manually and locally clean up only the specific state that was successfully altered before the failure occurred.
I replaced the call to `gadgetfs_unbind(gadget)` in the `enomem` label with localized cleanup:
1. Freeing `dev->req` if it was allocated, and setting it to `NULL`.
2. Clearing the pointers that were set earlier in the function (`gadget->ep0->driver_data`, `dev->gadget`, and `set_gadget_data(gadget, NULL)`).
Note that if `activate_ep_files()` fails, it already perfectly cleans up after itself by calling `destroy_ep_files()`, so no additional teardown is needed for it in `gadgetfs_bind()`'s error path.
This aligns with the historical pattern in the USB gadget subsystem of avoiding unified teardown functions on error paths in favor of manual, reverse-order cleanup of partially initialized objects.
Here are summaries of recent commits that touched the same files.
Format the summary line consistently with these, look how prefixes
are specified, letter capitalization, style, etc.
Convert 'alloc_obj' family to use the new default GFP_KERNEL argument
treewide: Replace kmalloc with kmalloc_obj for non-scalar types
convert gadgetfs
gadgetfs: switch to simple_remove_by_name()
fs: rename generic_delete_inode() and generic_drop_inode()
functionfs, gadgetfs: use simple_recursive_removal()
USB: gadget: Replace deprecated strncpy() with strscpy()
USB: gadget: Use str_enable_disable-like helpers
[tree-wide] finally take no_llseek out
legacy: convert to new timestamp accessors
usb: gadgetfs: return USB_GADGET_DELAYED_STATUS from setup()
usb: convert to ctime accessor functions
usb: gadgetfs: Fix ep_read_iter to handle ITER_UBUF
USB: gadgetfs: Fix race between mounting and unmounting
gadgetfs: ep_io - wait until IRQ finishes
USB: gadget: Rename usb_gadget_probe_driver()
usb: gadget: eliminate anonymous module_init & module_exit
headers/prep: usb: gadget: Fix namespace collision
usb: gadget: clear related members when goto fail
usb: gadget: don't release an existing dev->buf
Reply: usb: gadgetfs: fix reference count underflow in gadgetfs_bind()
A KASAN slab-use-after-free bug was found in gadgetfs_kill_sb():
BUG: KASAN: slab-use-after-free in put_dev
drivers/usb/gadget/legacy/inode.c:165 [inline]
BUG: KASAN: slab-use-after-free in gadgetfs_kill_sb+0x46/0x110
drivers/usb/gadget/legacy/inode.c:2104
Write of size 4 at addr ffff88811638c080 by task syz-executor169/5848
CPU: 0 UID: 0 PID: 5848 Comm: syz-executor169 Not tainted
Hardware name: QEMU Standard PC (Q35 + ICH9, 2009), BIOS
1.16.3-debian-1.16.3-2 04/01/2014
Call Trace:
<TASK>
dump_stack_lvl+0xe8/0x150 lib/dump_stack.c:120
print_address_description+0x55/0x1e0 mm/kasan/report.c:378
print_report+0x58/0x70 mm/kasan/report.c:482
kasan_report+0x117/0x150 mm/kasan/report.c:595
check_region_inline mm/kasan/generic.c:-1 [inline]
kasan_check_range+0x264/0x2c0 mm/kasan/generic.c:200
instrument_atomic_read_write include/linux/instrumented.h:112 [inline]
atomic_fetch_sub_release include/linux/atomic/atomic-instrumented.h:400
[inline]
__refcount_sub_and_test include/linux/refcount.h:389 [inline]
__refcount_dec_and_test include/linux/refcount.h:432 [inline]
refcount_dec_and_test include/linux/refcount.h:450 [inline]
put_dev drivers/usb/gadget/legacy/inode.c:165 [inline]
gadgetfs_kill_sb+0x46/0x110 drivers/usb/gadget/legacy/inode.c:2104
deactivate_locked_super+0xbc/0x130 fs/super.c:477
cleanup_mnt+0x3d3/0x460 fs/namespace.c:1317
task_work_run+0x1d9/0x270 kernel/task_work.c:233
The root cause is a reference count underflow in the error handling path of
gadgetfs_bind(). When gadgetfs_bind() encounters an error (e.g., memory
allocation failure), it jumps to the enomem label and calls
gadgetfs_unbind(). However, gadgetfs_unbind() is designed to tear down a
fully initialized and bound gadget, and unconditionally drops a device
reference by calling put_dev(dev). Since gadgetfs_bind() only acquires this
reference (get_dev(dev)) at the very end of the function upon complete
success, calling gadgetfs_unbind() in the error path drops a reference that
was never taken.
This leads to a premature free of the dev structure when the ep0 file is
closed, and subsequently a use-after-free when the filesystem is unmounted
and gadgetfs_kill_sb() tries to drop the initial reference.
To fix this, stop calling the full teardown function gadgetfs_unbind() from
the gadgetfs_bind() error path. Instead, manually and locally clean up only
the specific state that was successfully altered before the failure
occurred.
|
| 252/2 |
2026/08/07 06:24 |
llm |
description-generator |
0m
Model:
Tokens:
input: 10458
output: 900
thoughts: 2538
Thoughts: **Commit Message: Fixing a Reference Count Underflow in gadgetfs_bind()**
Okay, so here's the deal. I've got a patch to fix a gnarly KASAN slab-use-after-free bug, triggered in `gadgetfs_kill_sb()`. The relevant KASAN report looks like this:
```
BUG: KASAN: slab-use-after-free in put_dev drivers/usb/gadget/legacy/inode.c:165 [inline]
BUG: KASAN: slab-use-after-free in gadgetfs_kill_sb+0x46/0x110 drivers/usb/gadget/legacy/inode.c:2104
Write of size 4 at addr ffff88811638c080 by task syz-executor169/5848
CPU: 0 UID: 0 PID: 5848 Comm: syz-executor169 Not tainted
Hardware name: QEMU Standard PC (Q35 + ICH9, 2009), BIOS 1.16.3-debian-1.16.3-2 04/01/2014
Call Trace:
<TASK>
dump_stack_lvl+0xe8/0x150 lib/dump_stack.c:120
print_address_description+0x55/0x1e0 mm/kasan/report.c:378
print_report+0x58/0x70 mm/kasan/report.c:482
kasan_report+0x117/0x150 mm/kasan/report.c:595
check_region_inline mm/kasan/generic.c:-1 [inline]
kasan_check_range+0x264/0x2c0 mm/kasan/generic.c:200
instrument_atomic_read_write include/linux/instrumented.h:112 [inline]
atomic_fetch_sub_release include/linux/atomic/atomic-instrumented.h:400 [inline]
__refcount_sub_and_test include/linux/refcount.h:389 [inline]
__refcount_dec_and_test include/linux/refcount.h:432 [inline]
refcount_dec_and_test include/linux/refcount.h:450 [inline]
put_dev drivers/usb/gadget/legacy/inode.c:165 [inline]
gadgetfs_kill_sb+0x46/0x110 drivers/usb/gadget/legacy/inode.c:2104
deactivate_locked_super+0xbc/0x130 fs/super.c:477
cleanup_mnt+0x3d3/0x460 fs/namespace.c:1317
task_work_run+0x1d9/0x270 kernel/task_work.c:233
```
The root of this problem lies in a reference count underflow within the error handling of `gadgetfs_bind()`. Specifically, if `gadgetfs_bind()` encounters an error, let's say a memory allocation failure, it calls `gadgetfs_unbind()`. However, `gadgetfs_unbind()`'s job is to fully tear down a properly bound gadget, and it unconditionally decrements the device's reference count via `put_dev(dev)`. The corresponding `get_dev(dev)` is only called at the very end of `gadgetfs_bind()` – upon success. Thus, the error path is dropping a reference that was never acquired.
This is what leads to a premature free of the `dev` structure when the `ep0` file is closed. Later, it causes a use-after-free when the filesystem is unmounted and `gadgetfs_kill_sb()` attempts to decrement the initial reference.
The fix is straightforward: Instead of using `gadgetfs_unbind()` in the error path, I've replaced it with a more localized cleanup. Now, when `gadgetfs_bind()` fails, it only cleans up the specific bits of state that were altered before the error arose, avoiding the premature `put_dev()`. I believe this approach is much cleaner and avoids the problem entirely.
|