| 0/0 |
2026/07/31 01:30 |
flow |
assessment-security |
2h02m
Results: map[DenialOfService:true Explanation:**Analysis of the Bug Report**
**1. Bug Mechanism:**
The bug is an **invalid-free (unaligned free)** in the
`f_uac1_opts_c_srate_store` function (and similar functions in the USB audio
gadget driver).
The vulnerability occurs due to the misuse of the `strsep()` function:
```c
split_page = kstrdup(page, GFP_KERNEL);
while ((token = strsep(&split_page, ",")) != NULL) {
ret = kstrtou32(token, 0, &num);
if (ret)
goto end;
// ...
};
end:
kfree(split_page);
```
`strsep()` modifies the `split_page` pointer to point to the next token in the
string. If an invalid rate string is provided (causing `kstrtou32()` to fail),
the code jumps to the `end` label and calls `kfree()` on the modified
`split_page` pointer. Because the pointer has been advanced, it no longer points
to the beginning of the allocated memory block, resulting in an unaligned free.
**2. Exploitability:**
**Exploitable: Yes**
In production kernels where debugging features like KASAN or `CONFIG_SLUB_DEBUG`
are disabled, the SLUB allocator does not verify if the freed pointer is aligned
to the start of an object.
When the unaligned pointer is passed to `kfree()`, SLUB links it directly into
the freelist. The next time an object is allocated from this cache, the
allocator will return this unaligned pointer. Because the new allocation is
shifted, any writes to it will overlap with the adjacent active object in the
slab. This creates a powerful out-of-bounds write primitive that can be used to
overwrite critical data (e.g., function pointers, object references) in
neighboring objects, making it highly exploitable for privilege escalation.
**3. Denial of Service:**
**Denial of Service: Yes**
Even if not successfully exploited for privilege escalation, corrupting the SLUB
freelist will inevitably lead to memory corruption, page faults, and kernel
panics, resulting in a denial of service.
**4. Access Requirements:**
**Accessible From Unprivileged Processes: No**
The vulnerable code is triggered by writing to a configfs attribute (typically
located under `/sys/kernel/config/usb-gadget/`). By default, configfs and its
contents are owned by `root` and are only writable by users with root
privileges. An unprivileged user cannot write to these files to trigger the bug.
**Accessible From User Namespaces: No**
The `configfs` filesystem does not have the `FS_USERNS_MOUNT` flag set in its
`file_system_type` definition (`fs/configfs/mount.c`). Consequently, the kernel
requires the `CAP_SYS_ADMIN` capability in the **initial** user namespace to
mount it. An unprivileged user cannot mount configfs inside a restricted user
namespace to gain access to the vulnerable attributes.
**5. Other Triggers:**
* **VM Guest Trigger: No.** The bug is specific to configuring USB gadgets via
configfs, not standard paravirtualized VM devices.
* **VM Host Trigger: No.**
* **Ethernet Network Trigger: No.**
* **Other Remote Trigger: No.**
* **Peripheral Trigger: No.** The bug is triggered by configuring the gadget via
the local filesystem interface (configfs), not by plugging in a malicious USB
device.
* **Malicious Filesystem Trigger: No.**
**Conclusion:**
While the bug provides a very strong memory corruption primitive (unaligned free
leading to overlapping objects), it is heavily mitigated by its attack surface.
It requires root privileges (or write access to restricted configfs endpoints)
to trigger, making it inaccessible to unprivileged local attackers or remote
actors. Exploitable:true FilesystemTrigger:false NetworkTrigger:false PeripheralTrigger:false RemoteTrigger:false Unprivileged:false UserNamespace:false VMGuestTrigger:false VMHostTrigger:false]
|
| 1/1 |
2026/07/31 01:30 |
action |
syz-repro-to-c-repro |
0m
Results:
|
| 2/1 |
2026/07/31 01:30 |
action |
kernel-checkouter |
1m
Results: map[KernelSrc:/app/workdir/cache/src/59323400ba46e708d687f6274371ea997808e9ce]
|
| 3/1 |
2026/07/31 01:31 |
action |
kernel-builder |
13m
Results: map[KernelObj:/app/workdir/cache/build/c2d143e3786c309801d7837ceb9848818dada918]
|
| 4/1 |
2026/07/31 01:45 |
action |
codesearch-prepare |
9m
Results: map[Index:codesearch-index]
|
| 5/1 |
2026/07/31 01:54 |
agent |
expert |
1h38m
Model:
Results: map[DenialOfService:true Exploitable:true FilesystemTrigger:false NetworkTrigger:false PeripheralTrigger:false RemoteTrigger:false Unprivileged:false UserNamespace:false VMGuestTrigger:false VMHostTrigger:false]
Instruction: You are an experienced Linux kernel security engineer. Your task is to analyze given kernel bug report
and determine its security impact based on the following dimensions.
Use the provided tools to examine the source code, check for capability checks (e.g., capable(), ns_capable()),
and understand the nature of the bug. Analyze the given kernel build and configuration.
You can check the kernel config by grepping ".config" file; you can check kernel cmdline by grepping
".config" file for "CONFIG_CMDLINE=". Assume sysctl parameters have default values.
But analyze for the corresponding production build w/o debugging tools enabled (like KASAN, KMSAN, UBSAN).
Try different strategies when analyzing the bug:
- think of ways in which the vulnerable code is unreachable
- or the other way around: try to come up with different ideas of how an unprivileged user can reach the bug
If still unsure err on the side of the bug being non-exploitable/not-accessible.
In the final reply, provide a reasoning for your assessment.
Analysis dimensions:
* Exploitable:
Determine if the bug can result in memory corruption, elevated privileges, or an information leak.
Memory safety issues are almost always exploitable (KASAN or UBSAN reports for use-after-free, out-of-bounds;
refcounting issues, corrupted lists, etc). When kernel is crashing on a completely wild pointer access
(e.g. user-space address, or non-canonical address, but not on NULL or address corresponding to KASAN shadow
for NULL address), including both data accesses and control transfers, that also usually implies possibility
of exploitation. Such reports usually say "unable to handle kernel paging request".
Uses of uninitialized values detected by KMSAN may be exploitable b/c attacker frequently can affect uninit
values with spraying techniques. However, for these exploitability depends on how exactly the uninit value
is used in the code, and what it affects.
Information leaks are exploitable on their own and should be classified as such. A bug that copies kernel
memory contents to userspace (e.g. an out-of-bounds read whose result is returned to the caller, or
uninitialized stack/heap bytes written to a user buffer) is exploitable: it can reveal kernel pointer
values and defeat KASLR, expose sensitive data such as cryptographic keys or other processes' memory, and
serves as a necessary building block in most modern kernel privilege-escalation exploit chains. Do not classify
an information leak as non-exploitable solely because it does not directly cause a memory write or control-flow
hijack; the leak itself is the exploit primitive.
Think of what happens after the bug is triggered. Some bugs cause kernel panic and halt execution,
they are harder to exploit. For example, BUG reports halts the kernel. However, WARNING reports don't halt
execution in production builds. Debug bug detection tools (like KASAN, KMSAN, KCSAN, UBSAN) are also not enabled
in production builds, so attacker can freely exploit these bugs w/o being detected by these tools.
If you see an integer overflow, think how the overflowed value used later (if it's used as allocation size,
or an array index). If you see an out-of-bounds read, think if it's followed by an out-of-bounds write as well.
Some KCSAN data-races may be exploitable by skilled attackers as well. Think what data structures got corrupted
as the result of data races and how. However, note that kernel has lots of "benign" data races that don't lead
to any runtime misbehavior at all.
* Denial Of Service:
Determine if the bug can result in denial-of-service. Most bugs can, since they cause system crash,
hangs, deadlocks, or resource leaks. This is mostly applicable to WARNING bugs that won't cause system crash
in production. For these think what will be consequences of the violation of the kernel assumptions flagged
by the WARNING. In some cases the unexpected condition is also properly handled by the normal control flow
(e.g. with "if (WARN_ON(...))"), these won't cause denial-of-service. If the condition is not handled,
then it may or may not cause denial-of-service.
* Accessible From Unprivileged Processes:
Determine if the bug can be reached from a typical (non-root) user process that does NOT have any special capabilities
(like CAP_SYS_ADMIN, CAP_NET_ADMIN, CAP_NET_RAW, CAP_PERFMON) or access to device nodes restricted to root.
Assume that unprivileged_bpf_disabled=1, that is eBPF loading is not accessible. However, cBPF (classical BPF)
is still accessible to non-root processes.
Assume that user namespaces are not accessible, that is, the process cannot get the mentioned capabilities even
within a new user namespace (checked by ns_capable() function in the kernel sources).
* Accessible From User Namespaces:
Determine if the bug can be reached within a user-namespace where the process has all capabilities
(including CAP_SYS_ADMIN, CAP_NET_ADMIN, CAP_NET_RAW, CAP_PERFMON). Such capabilities are checked with ns_capable()
function in the kernel sources.
* VM Guest Trigger:
Determine if the bug can be triggered from the context of a typical KVM guest (e.g., set up by a QEMU VMM).
Consider accesses to standard Linux host paravirtualized features (virtio-blk, virtio-net, etc.),
and handling of VM exits in the KVM code.
* VM Host Trigger in The Confidential Computing Context:
Determine if the bug can be triggered in a confidential computing guest kernel from the context of a KVM host.
Consider access to standard Linux guest paravirtualized features (virtio-blk, virtio-net, etc.).
* Ethernet Network Trigger:
Determine if the bug can be triggered by processing ingress network Ethernet traffic, either directly (network stack)
or via drivers exposed to network data.
* Other Remote Trigger:
Determine if the bug can be triggered by processing remote traffic other than Ethernet (Wifi, Bluetooth, NFC, etc).
* Peripheral Trigger:
Determine if the bug can be triggered via an untrusted peripheral device that can be physically plugged
into a system, such as a USB device or a niche hardware driver handling external hardware inputs.
This is particularly important for mobile and desktop environments where users can plug in unknown devices.
* Malicious Filesystem Trigger:
Determine if the bug can be triggered by the kernel mounting and parsing a malicious filesystem image.
This is highly critical for Desktop and Mobile environments where external media or downloaded images
might be auto-mounted.
Don't make assumptions about the kernel source code (it may be different from what you assume it is).
Extensively use the provided code access tools (codesearch-*, git-*, grepper, etc)
to examine the actual source code, and confirm any assumptions.
Prefer calling several tools at the same time to save round-trips.
Use set-results tool to provide results of the analysis.
It must be called exactly once before the final reply.
Ignore results of this tool.
Prompt:
The kernel bug report is:
loop0: detected capacity change from 0 to 1024
EXT4-fs (loop0): mounted filesystem 00000000-0000-0000-0000-000000000000 r/w without journal. Quota mode: none.
==================================================================
BUG: KASAN: invalid-free in f_uac1_opts_c_srate_store+0x225/0x2b0 drivers/usb/gadget/function/f_uac1.c:1666
Free of addr ffff888012a90e87 by task syz.0.0/5330
CPU: 0 UID: 0 PID: 5330 Comm: syz.0.0 Not tainted syzkaller #0 PREEMPT(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_invalid_free+0xea/0x110 mm/kasan/report.c:557
check_slab_allocation mm/kasan/common.c:-1 [inline]
__kasan_slab_pre_free+0x104/0x120 mm/kasan/common.c:261
kasan_slab_pre_free include/linux/kasan.h:199 [inline]
slab_free_hook mm/slub.c:2622 [inline]
slab_free mm/slub.c:6377 [inline]
kfree+0x173/0x640 mm/slub.c:6692
f_uac1_opts_c_srate_store+0x225/0x2b0 drivers/usb/gadget/function/f_uac1.c:1666
flush_write_buffer fs/configfs/file.c:207 [inline]
configfs_write_iter+0x337/0x430 fs/configfs/file.c:229
new_sync_write fs/read_write.c:595 [inline]
vfs_write+0x612/0xba0 fs/read_write.c:687
ksys_write+0x150/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
RIP: 0033:0x7fd42f39de99
Code: ff c3 66 2e 0f 1f 84 00 00 00 00 00 0f 1f 44 00 00 48 89 f8 48 89 f7 48 89 d6 48 89 ca 4d 89 c2 4d 89 c8 4c 8b 4c 24 08 0f 05 <48> 3d 01 f0 ff ff 73 01 c3 48 c7 c1 e8 ff ff ff f7 d8 64 89 01 48
RSP: 002b:00007fd4301aafe8 EFLAGS: 00000246 ORIG_RAX: 0000000000000001
RAX: ffffffffffffffda RBX: 00007fd42f625fa0 RCX: 00007fd42f39de99
RDX: 0000000000000012 RSI: 0000200000000a40 RDI: 0000000000000007
RBP: 00007fd42f433eaf R08: 0000000000000000 R09: 0000000000000000
R10: 0000000000000000 R11: 0000000000000246 R12: 0000000000000000
R13: 00007fd42f626038 R14: 00007fd42f625fa0 R15: 00007fff56e02128
</TASK>
Allocated by task 5330:
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]
__do_kmalloc_node mm/slub.c:5334 [inline]
__kmalloc_node_track_caller_noprof+0x4c3/0x730 mm/slub.c:5471
__kmemdup_nul mm/util.c:64 [inline]
kstrdup+0x42/0x100 mm/util.c:84
f_uac1_opts_c_srate_store+0x11f/0x2b0 drivers/usb/gadget/function/f_uac1.c:1666
flush_write_buffer fs/configfs/file.c:207 [inline]
configfs_write_iter+0x337/0x430 fs/configfs/file.c:229
new_sync_write fs/read_write.c:595 [inline]
vfs_write+0x612/0xba0 fs/read_write.c:687
ksys_write+0x150/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
The buggy address belongs to the object at ffff888012a90e80
which belongs to the cache kmalloc-32 of size 32
The buggy address is located 7 bytes inside of
19-byte region [ffff888012a90e80, ffff888012a90e93)
The buggy address belongs to the physical page:
page: refcount:0 mapcount:0 mapping:0000000000000000 index:0xffff888012a90180 pfn:0x12a90
flags: 0xfff00000000200(workingset|node=0|zone=1|lastcpupid=0x7ff)
page_type: f5(slab)
raw: 00fff00000000200 ffff88801b041780 ffff88801b0404c8 ffffea00006ba590
raw: ffff888012a90180 0000000800400029 00000000f5000000 0000000000000000
page dumped because: kasan: bad access detected
page_owner tracks the page as allocated
page last allocated via order 0, migratetype Unmovable, gfp_mask 0xd2800(GFP_NOWAIT|__GFP_NORETRY|__GFP_COMP|__GFP_NOMEMALLOC), pid 5117, tgid 5117 (rm), ts 62075882467, free_ts 62074814632
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+0x21fa/0x2270 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+0x2d5/0x350 mm/slub.c:7310
refill_sheaf mm/slub.c:2804 [inline]
__pcs_replace_empty_main+0x2bf/0x6b0 mm/slub.c:4675
alloc_from_pcs mm/slub.c:4773 [inline]
slab_alloc_node mm/slub.c:4905 [inline]
__kmalloc_cache_noprof+0x3a7/0x660 mm/slub.c:5485
_kmalloc_noprof include/linux/slab.h:988 [inline]
slab_free_hook mm/slub.c:2629 [inline]
slab_free mm/slub.c:6377 [inline]
kmem_cache_free+0x156/0x650 mm/slub.c:6504
tear_down_vmas+0x312/0x520 mm/mmap.c:1262
exit_mmap+0x4ca/0x9f0 mm/mmap.c:1320
__mmput+0x118/0x420 kernel/fork.c:1187
exit_mm+0x221/0x2d0 kernel/exit.c:615
do_exit+0x6cd/0x2360 kernel/exit.c:997
do_group_exit+0x22d/0x2f0 kernel/exit.c:1152
__do_sys_exit_group kernel/exit.c:1163 [inline]
__se_sys_exit_group kernel/exit.c:1161 [inline]
__x64_sys_exit_group+0x3f/0x40 kernel/exit.c:1161
x64_sys_call+0x221a/0x2240 arch/x86/include/generated/asm/syscalls_64.h:232
page last free pid 5117 tgid 5117 stack trace:
reset_page_owner include/linux/page_owner.h:25 [inline]
__free_pages_prepare mm/page_alloc.c:1406 [inline]
__free_frozen_pages+0xc1e/0xd10 mm/page_alloc.c:2950
tlb_batch_list_free mm/mmu_gather.c:161 [inline]
tlb_finish_mmu+0x13e/0x220 mm/mmu_gather.c:552
exit_mmap+0x4b2/0x9f0 mm/mmap.c:1313
__mmput+0x118/0x420 kernel/fork.c:1187
exit_mm+0x221/0x2d0 kernel/exit.c:615
do_exit+0x6cd/0x2360 kernel/exit.c:997
do_group_exit+0x22d/0x2f0 kernel/exit.c:1152
__do_sys_exit_group kernel/exit.c:1163 [inline]
__se_sys_exit_group kernel/exit.c:1161 [inline]
__x64_sys_exit_group+0x3f/0x40 kernel/exit.c:1161
x64_sys_call+0x221a/0x2240 arch/x86/include/generated/asm/syscalls_64.h:232
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
Memory state around the buggy address:
ffff888012a90d80: fa fb fb fb fc fc fc fc fa fb fb fb fc fc fc fc
ffff888012a90e00: fa fb fb fb fc fc fc fc fa fb fb fb fc fc fc fc
>ffff888012a90e80: 00 00 03 fc fc fc fc fc 00 00 03 fc fc fc fc fc
^
ffff888012a90f00: fa fb fb fb fc fc fc fc 00 00 00 00 fc fc fc fc
ffff888012a90f80: fa fb fb fb fc fc fc fc fa fb fb fb fc fc fc fc
==================================================================
Reply: **Analysis of the Bug Report**
**1. Bug Mechanism:**
The bug is an **invalid-free (unaligned free)** in the `f_uac1_opts_c_srate_store` function (and similar functions in the USB audio gadget driver).
The vulnerability occurs due to the misuse of the `strsep()` function:
```c
split_page = kstrdup(page, GFP_KERNEL);
while ((token = strsep(&split_page, ",")) != NULL) {
ret = kstrtou32(token, 0, &num);
if (ret)
goto end;
// ...
};
end:
kfree(split_page);
```
`strsep()` modifies the `split_page` pointer to point to the next token in the string. If an invalid rate string is provided (causing `kstrtou32()` to fail), the code jumps to the `end` label and calls `kfree()` on the modified `split_page` pointer. Because the pointer has been advanced, it no longer points to the beginning of the allocated memory block, resulting in an unaligned free.
**2. Exploitability:**
**Exploitable: Yes**
In production kernels where debugging features like KASAN or `CONFIG_SLUB_DEBUG` are disabled, the SLUB allocator does not verify if the freed pointer is aligned to the start of an object.
When the unaligned pointer is passed to `kfree()`, SLUB links it directly into the freelist. The next time an object is allocated from this cache, the allocator will return this unaligned pointer. Because the new allocation is shifted, any writes to it will overlap with the adjacent active object in the slab. This creates a powerful out-of-bounds write primitive that can be used to overwrite critical data (e.g., function pointers, object references) in neighboring objects, making it highly exploitable for privilege escalation.
**3. Denial of Service:**
**Denial of Service: Yes**
Even if not successfully exploited for privilege escalation, corrupting the SLUB freelist will inevitably lead to memory corruption, page faults, and kernel panics, resulting in a denial of service.
**4. Access Requirements:**
**Accessible From Unprivileged Processes: No**
The vulnerable code is triggered by writing to a configfs attribute (typically located under `/sys/kernel/config/usb-gadget/`). By default, configfs and its contents are owned by `root` and are only writable by users with root privileges. An unprivileged user cannot write to these files to trigger the bug.
**Accessible From User Namespaces: No**
The `configfs` filesystem does not have the `FS_USERNS_MOUNT` flag set in its `file_system_type` definition (`fs/configfs/mount.c`). Consequently, the kernel requires the `CAP_SYS_ADMIN` capability in the **initial** user namespace to mount it. An unprivileged user cannot mount configfs inside a restricted user namespace to gain access to the vulnerable attributes.
**5. Other Triggers:**
* **VM Guest Trigger: No.** The bug is specific to configuring USB gadgets via configfs, not standard paravirtualized VM devices.
* **VM Host Trigger: No.**
* **Ethernet Network Trigger: No.**
* **Other Remote Trigger: No.**
* **Peripheral Trigger: No.** The bug is triggered by configuring the gadget via the local filesystem interface (configfs), not by plugging in a malicious USB device.
* **Malicious Filesystem Trigger: No.**
**Conclusion:**
While the bug provides a very strong memory corruption primitive (unaligned free leading to overlapping objects), it is heavily mitigated by its attack surface. It requires root privileges (or write access to restricted configfs endpoints) to trigger, making it inaccessible to unprivileged local attackers or remote actors.
|
| 6/2 |
2026/07/31 01:54 |
llm |
expert |
0m
Model:
Tokens:
input: 9532
output: 32
thoughts: 118
Thoughts: Okay, here's my breakdown of this bug report, from my perspective as an expert in this area:
**Analyzing a USB Gadget UAC1 Invalid-Free**
Alright, I see a bug report highlighting an invalid-free situation within `f_uac1_opts_c_srate_store`, specifically at line 1666 of `drivers/usb/gadget/function/f_uac1.c`. The report mentions the task ID `syz.0.0` is the culprit, and that it's related to writing to a configfs file. My immediate reaction is to dive right into that function.
My next step is clear: I'm going to leverage my `codesearch-definition-source` tool. This is the fastest way to get a solid grasp of what `f_uac1_opts_c_srate_store` actually *does*. I need to see the function definition, how it's called, and most importantly, how it handles memory allocation and deallocation related to that configfs interaction. This will be the starting point to understand the conditions that trigger this invalid-free.
|
| 7/2 |
2026/07/31 01:54 |
tool |
codesearch-definition-source |
0m
Args: map[Name:f_uac1_opts_c_srate_store]
Results: map[SourceCode:1666: UAC1_RATE_ATTRIBUTE(c_srate);
SourceFile:drivers/usb/gadget/function/f_uac1.c]
|
| 8/2 |
2026/07/31 01:54 |
llm |
expert |
0m
Model:
Tokens:
input: 9867
output: 25
thoughts: 30
Thoughts: Okay, here's the summary, rendered as your internal thought process:
**Diving into the Code**
Alright, looking at this code snippet... aha! I see that familiar macro, `UAC1_RATE_ATTRIBUTE`. Right away, my internal radar pings. It's a key identifier. My next step is obvious: I need to get a better handle on what `UAC1_RATE_ATTRIBUTE` represents. Let's fire up the search and trace this macro to its definition. I'm going to start by searching for the exact string, `UAC1_RATE_ATTRIBUTE`, in the project. This will give me the contextual definition and related usages. I expect it's something fundamental, related to audio data, probably. Let's see what the code reveals.
|
| 9/2 |
2026/07/31 01:54 |
tool |
codesearch-definition-source |
0m
Error:
Args: map[Name:UAC1_RATE_ATTRIBUTE]
Results: map[SourceCode: SourceFile:]
|
| 10/2 |
2026/07/31 01:54 |
llm |
expert |
0m
Model:
Tokens:
input: 10081
output: 43
thoughts: 72
Thoughts: **Analyzing the USB Audio Class 1 Driver**
Okay, so I'm looking at this function, `f_uac1_opts_c_srate_store`, and it looks like its declaration is hidden behind a macro, `UAC1_RATE_ATTRIBUTE(c_srate)`. That's a common pattern, but it means I need to dig a little deeper to fully understand what's going on. The macro likely expands to the actual function definition, but I need to see *how* it does that, what the arguments are, and what the overall structure looks like.
Therefore, the next logical step is to search for the `UAC1_RATE_ATTRIBUTE` macro within the `f_uac1.c` file, located in the `drivers/usb/gadget/function` directory. That's where I expect to find the macro's definition and, more importantly, how it's being used within this USB Audio Class 1 driver implementation. Understanding the macro will give me the context I need to properly interpret the behavior of this particular store function.
|
| 11/2 |
2026/07/31 01:54 |
tool |
grepper |
0m
Args: map[Expression:UAC1_RATE_ATTRIBUTE PathPrefix:drivers/usb/gadget/function/f_uac1.c]
Results: map[Output:drivers/usb/gadget/function/f_uac1.c=1568=CONFIGFS_ATTR(f_uac1_opts_, name)
drivers/usb/gadget/function/f_uac1.c-1569-
drivers/usb/gadget/function/f_uac1.c:1570:#define UAC1_RATE_ATTRIBUTE(name) \
drivers/usb/gadget/function/f_uac1.c-1571-static ssize_t f_uac1_opts_##name##_show(struct config_item *item, \
--
drivers/usb/gadget/function/f_uac1.c=1665=UAC1_ATTRIBUTE(u32, c_chmask);
drivers/usb/gadget/function/f_uac1.c:1666:UAC1_RATE_ATTRIBUTE(c_srate);
drivers/usb/gadget/function/f_uac1.c-1667-UAC1_ATTRIBUTE(u32, c_ssize);
drivers/usb/gadget/function/f_uac1.c=1668=UAC1_ATTRIBUTE(u32, p_chmask);
drivers/usb/gadget/function/f_uac1.c:1669:UAC1_RATE_ATTRIBUTE(p_srate);
drivers/usb/gadget/function/f_uac1.c-1670-UAC1_ATTRIBUTE(u32, p_ssize);
]
|
| 12/2 |
2026/07/31 01:54 |
llm |
expert |
0m
Model:
Tokens:
input: 10741
output: 46
thoughts: 37
Thoughts: Here's my summary, as the thought process unfolds:
**Bug Hunt: UAC1 Sampling Rate Store**
Okay, so the trace indicates a problem. My initial assessment points directly to the `f_uac1_opts_c_srate_store` function. That's the likely culprit. Now, let's refresh my memory on the underlying structure... right, let's take a look at the definition of the `UAC1_RATE_ATTRIBUTE`. I need to ensure I have a solid grasp of how that specific attribute is handled and stored, so I'm thinking about the data structures involved, the bit fields, everything. Need to understand how this `UAC1_RATE_ATTRIBUTE` functions, so I can see what might be breaking. This should give me a solid foundation for debugging the root cause, assuming my initial analysis is correct.
|
| 13/2 |
2026/07/31 01:55 |
tool |
read-file |
0m
Args: map[File:drivers/usb/gadget/function/f_uac1.c FirstLine:1570 LineCount:100]
Results: map[Contents:1570: #define UAC1_RATE_ATTRIBUTE(name) \
1571: static ssize_t f_uac1_opts_##name##_show(struct config_item *item, \
1572: char *page) \
1573: { \
1574: struct f_uac1_opts *opts = to_f_uac1_opts(item); \
1575: int result = 0; \
1576: int i; \
1577: \
1578: mutex_lock(&opts->lock); \
1579: page[0] = '\0'; \
1580: for (i = 0; i < UAC_MAX_RATES; i++) { \
1581: if (opts->name##s[i] == 0) \
1582: break; \
1583: result += sprintf(page + strlen(page), "%u,", \
1584: opts->name##s[i]); \
1585: } \
1586: if (strlen(page) > 0) \
1587: page[strlen(page) - 1] = '\n'; \
1588: mutex_unlock(&opts->lock); \
1589: \
1590: return result; \
1591: } \
1592: \
1593: static ssize_t f_uac1_opts_##name##_store(struct config_item *item, \
1594: const char *page, size_t len) \
1595: { \
1596: struct f_uac1_opts *opts = to_f_uac1_opts(item); \
1597: char *split_page = NULL; \
1598: int ret = -EINVAL; \
1599: char *token; \
1600: u32 num; \
1601: int i; \
1602: \
1603: mutex_lock(&opts->lock); \
1604: if (opts->refcnt) { \
1605: ret = -EBUSY; \
1606: goto end; \
1607: } \
1608: \
1609: i = 0; \
1610: memset(opts->name##s, 0x00, sizeof(opts->name##s)); \
1611: split_page = kstrdup(page, GFP_KERNEL); \
1612: while ((token = strsep(&split_page, ",")) != NULL) { \
1613: ret = kstrtou32(token, 0, &num); \
1614: if (ret) \
1615: goto end; \
1616: \
1617: opts->name##s[i++] = num; \
1618: ret = len; \
1619: }; \
1620: \
1621: end: \
1622: kfree(split_page); \
1623: mutex_unlock(&opts->lock); \
1624: return ret; \
1625: } \
1626: \
1627: CONFIGFS_ATTR(f_uac1_opts_, name)
1628:
1629: #define UAC1_ATTRIBUTE_STRING(name) \
1630: static ssize_t f_uac1_opts_##name##_show(struct config_item *item, \
1631: char *page) \
1632: { \
1633: struct f_uac1_opts *opts = to_f_uac1_opts(item); \
1634: int result; \
1635: \
1636: mutex_lock(&opts->lock); \
1637: result = sysfs_emit(page, "%s", opts->name); \
1638: mutex_unlock(&opts->lock); \
1639: \
1640: return result; \
1641: } \
1642: \
1643: static ssize_t f_uac1_opts_##name##_store(struct config_item *item, \
1644: const char *page, size_t len) \
1645: { \
1646: struct f_uac1_opts *opts = to_f_uac1_opts(item); \
1647: int ret = 0; \
1648: \
1649: mutex_lock(&opts->lock); \
1650: if (opts->refcnt) { \
1651: ret = -EBUSY; \
1652: goto end; \
1653: } \
1654: \
1655: ret = scnprintf(opts->name, min(sizeof(opts->name), len), \
1656: "%s", page); \
1657: \
1658: end: \
1659: mutex_unlock(&opts->lock); \
1660: return ret; \
1661: } \
1662: \
1663: CONFIGFS_ATTR(f_uac1_opts_, name)
1664:
1665: UAC1_ATTRIBUTE(u32, c_chmask);
1666: UAC1_RATE_ATTRIBUTE(c_srate);
1667: UAC1_ATTRIBUTE(u32, c_ssize);
1668: UAC1_ATTRIBUTE(u32, p_chmask);
1669: UAC1_RATE_ATTRIBUTE(p_srate);
]
|
| 14/2 |
2026/07/31 01:55 |
llm |
expert |
0m
Model:
Tokens:
input: 12604
output: 32
thoughts: 1016
Thoughts: **Invalid Free Bug in UAC1 Rate Attribute Store**
Okay, so I've pinpointed a bug. It seems to be an invalid-free issue within `f_uac1_opts_c_srate_store`, which is generated by the `UAC1_RATE_ATTRIBUTE(c_srate)` macro. Let's dive into that macro definition to understand the root cause.
Looking at the code, specifically the `f_uac1_opts_##name##_store` function, the issue jumps out immediately. I see a `kstrdup(page, GFP_KERNEL)` allocating memory, and then `strsep(&split_page, ",")` is used to tokenize the input string. The crucial point is that `strsep` modifies the `split_page` pointer in place, advancing it to the next token. Then, at the end of the function, `kfree(split_page)` is called.
The problem? `kfree` is being called on the *modified* `split_page` pointer, not the original pointer returned by `kstrdup`. If the input string contains a comma, `strsep` will advance `split_page` to point to a location *within* the allocated memory region, not at the beginning. This is a classic invalid free situation. This explains the KASAN report perfectly: it shows a free attempt on an address within an allocated region. The report confirms that the address being freed is inside the allocation from `kstrdup` called from `f_uac1_opts_c_srate_store`.
This bug resides within the configfs attribute store for the USB gadget function `f_uac1`. Configfs is typically mounted at `/sys/kernel/config`. Now, a key question becomes how accessible this code path is. USB gadget configuration usually requires root privileges or equivalent access to the configfs mount. Typically, configfs is restricted to root or, under certain circumstances, user namespaces. However, the USB gadget subsystem isn't designed to be user namespace-aware in a way that allows unprivileged users to mount or interact with it.
So, I need to confirm: can unprivileged users mount configfs? No. Can unprivileged users write to `/sys/kernel/config/usb_gadget/...`? Also no, because the files are owned by root. Is it accessible from user namespaces? I need to check whether configfs is `FS_USERNS_MOUNT`? No, it's not.
|
| 15/2 |
2026/07/31 01:55 |
tool |
grepper |
0m
Error:
Args: map[Expression:FS_USERNS_MOUNT PathPrefix:fs/configfs/]
Results:
|
| 16/2 |
2026/07/31 01:55 |
llm |
expert |
0m
Model:
Tokens:
input: 13178
output: 29
thoughts: 0
|
| 17/2 |
2026/07/31 01:55 |
tool |
grepper |
0m
Args: map[Expression:configfs_init PathPrefix:fs/configfs/]
Results: map[Output:fs/configfs/dir.c=68=const struct dentry_operations configfs_dentry_ops = {
--
fs/configfs/dir.c-92-
fs/configfs/dir.c:93:static void configfs_init_dirent_depth(struct configfs_dirent *sd)
fs/configfs/dir.c-94-{
--
fs/configfs/dir.c=129=configfs_adjust_dir_dirent_depth_after_populate(struct configfs_dirent *sd)
--
fs/configfs/dir.c-136-
fs/configfs/dir.c:137:static void configfs_init_dirent_depth(struct configfs_dirent *sd)
fs/configfs/dir.c-138-{
--
fs/configfs/dir.c=187=static struct configfs_dirent *configfs_new_dirent(struct configfs_dirent *parent_sd,
--
fs/configfs/dir.c-200- sd->s_type = type;
fs/configfs/dir.c:201: configfs_init_dirent_depth(sd);
fs/configfs/dir.c-202- spin_lock(&configfs_dirent_lock);
--
fs/configfs/mount.c=105=static const struct fs_context_operations configfs_context_ops = {
--
fs/configfs/mount.c-108-
fs/configfs/mount.c:109:static int configfs_init_fs_context(struct fs_context *fc)
fs/configfs/mount.c-110-{
--
fs/configfs/mount.c=115=static struct file_system_type configfs_fs_type = {
--
fs/configfs/mount.c-117- .name = "configfs",
fs/configfs/mount.c:118: .init_fs_context = configfs_init_fs_context,
fs/configfs/mount.c-119- .kill_sb = kill_anon_super,
--
fs/configfs/mount.c=130=void configfs_release_fs(void)
--
fs/configfs/mount.c-135-
fs/configfs/mount.c:136:static int __init configfs_init(void)
fs/configfs/mount.c-137-{
--
fs/configfs/mount.c=176=MODULE_DESCRIPTION("Simple RAM filesystem for user driven kernel subsystem configuration.");
fs/configfs/mount.c-177-
fs/configfs/mount.c:178:core_initcall(configfs_init);
fs/configfs/mount.c-179-module_exit(configfs_exit);
]
|
| 18/2 |
2026/07/31 01:55 |
llm |
expert |
0m
Model:
Tokens:
input: 13917
output: 36
thoughts: 0
|
| 19/2 |
2026/07/31 01:55 |
tool |
read-file |
0m
Args: map[File:fs/configfs/mount.c FirstLine:115 LineCount:20]
Results: map[Contents: 115: static struct file_system_type configfs_fs_type = {
116: .owner = THIS_MODULE,
117: .name = "configfs",
118: .init_fs_context = configfs_init_fs_context,
119: .kill_sb = kill_anon_super,
120: };
121: MODULE_ALIAS_FS("configfs");
122:
123: struct dentry *configfs_pin_fs(void)
124: {
125: int err = simple_pin_fs(&configfs_fs_type, &configfs_mount,
126: &configfs_mnt_count);
127: return err ? ERR_PTR(err) : configfs_mount->mnt_root;
128: }
129:
130: void configfs_release_fs(void)
131: {
132: simple_release_fs(&configfs_mount, &configfs_mnt_count);
133: }
134:
]
|
| 20/2 |
2026/07/31 01:55 |
llm |
expert |
0m
Model:
Tokens:
input: 14271
output: 22
thoughts: 0
|
| 21/2 |
2026/07/31 01:55 |
tool |
codeexpert |
0m
Args: map[Question:Can unprivileged users mount configfs?]
Results: map[Answer:No, unprivileged users cannot mount configfs.
### Explanation:
1. **Privilege Requirement for Mounting**: In the Linux kernel, mounting a filesystem generally requires the `CAP_SYS_ADMIN` capability in the initial user namespace (i.e., root privileges).
2. **User Namespace Exception**: Filesystems can allow unprivileged users to mount them inside a user namespace (where the user has `CAP_SYS_ADMIN` for that namespace) if and only if the filesystem's registration structure (`struct file_system_type`) has the `FS_USERNS_MOUNT` flag set in its `.fs_flags`.
3. **ConfigFS Registration**: The `configfs` filesystem is defined in `fs/configfs/mount.c` as:
```c
static struct file_system_type configfs_fs_type = {
.owner = THIS_MODULE,
.name = "configfs",
.init_fs_context = configfs_init_fs_context,
.kill_sb = kill_anon_super,
};
```
As shown above, `configfs_fs_type` does **not** set the `FS_USERNS_MOUNT` flag.
4. **VFS Enforcement**: When a mount request is made, the VFS layer checks permissions using `mount_capable()` in `fs/super.c`:
```c
bool mount_capable(struct fs_context *fc)
{
if (!(fc->fs_type->fs_flags & FS_USERNS_MOUNT))
return capable(CAP_SYS_ADMIN);
else
return ns_capable(fc->user_ns, CAP_SYS_ADMIN);
}
```
Because `configfs` lacks `FS_USERNS_MOUNT`, the kernel falls back to `capable(CAP_SYS_ADMIN)`, which checks for the capability in the initial user namespace. This restricts mounting of configfs strictly to privileged users.]
|
| 22/3 |
2026/07/31 01:55 |
agent |
codeexpert |
0m
Model:
Results: map[Answer:No, unprivileged users cannot mount configfs.
### Explanation:
1. **Privilege Requirement for Mounting**: In the Linux kernel, mounting a filesystem generally requires the `CAP_SYS_ADMIN` capability in the initial user namespace (i.e., root privileges).
2. **User Namespace Exception**: Filesystems can allow unprivileged users to mount them inside a user namespace (where the user has `CAP_SYS_ADMIN` for that namespace) if and only if the filesystem's registration structure (`struct file_system_type`) has the `FS_USERNS_MOUNT` flag set in its `.fs_flags`.
3. **ConfigFS Registration**: The `configfs` filesystem is defined in `fs/configfs/mount.c` as:
```c
static struct file_system_type configfs_fs_type = {
.owner = THIS_MODULE,
.name = "configfs",
.init_fs_context = configfs_init_fs_context,
.kill_sb = kill_anon_super,
};
```
As shown above, `configfs_fs_type` does **not** set the `FS_USERNS_MOUNT` flag.
4. **VFS Enforcement**: When a mount request is made, the VFS layer checks permissions using `mount_capable()` in `fs/super.c`:
```c
bool mount_capable(struct fs_context *fc)
{
if (!(fc->fs_type->fs_flags & FS_USERNS_MOUNT))
return capable(CAP_SYS_ADMIN);
else
return ns_capable(fc->user_ns, CAP_SYS_ADMIN);
}
```
Because `configfs` lacks `FS_USERNS_MOUNT`, the kernel falls back to `capable(CAP_SYS_ADMIN)`, which checks for the capability in the initial user namespace. This restricts mounting of configfs strictly to privileged users.]
Instruction:
You are a capable Linux kernel developer tasked with researching complex questions
about kernel source code. You will be given a concrete question, and need to provide
a concrete answer.
Use tools extensively while researching the question. Don't make assumptions,
or rely on your previous knowledge about the kernel source code, use available tools
to access the actual source code.
Use all available sources of information:
- kernel source code
- documentation in the Documentation dir in the source tree
- git commits descriptions, git blame
Do not guess file names or file paths and attempt to read them without
verifying their existence first using content search or directory listing tools.
If a file, symbol, or directory is not found via content search (grepper) or
directory listing (codesearch-dir-index), treat it as completely absent.
Do not attempt to guess alternative names, extensions, or directories.
Tool Selection Guidelines:
1. For C functions, structs, and variables: Prefer codesearch-definition-source
or codesearch-struct-layout FIRST to retrieve clean, exact definitions
without line-number guessing.
2. If symbol lookup fails (e.g., preprocessor macros, macro-generated code, or
disabled #ifdef branches), fall back to read-file or grepper.
3. For file headers, #include directives, preprocessor macro definitions, and
non-C files (Kconfig, Makefiles, docs): Use read-file or grepper directly.
Do NOT use git-log to search for the presence or existence of files in the
repository. git-log is only for tracing commit history of files that are
already present in the current checkout. If a file does not exist in the
current checkout, it cannot be used for reproduction.
Avoid running broad git-log queries (such as searches on the entire repo)
without a specific 'PathPrefix' to restrict the scope, as these are very
expensive and will time out.
If a git-log tool call times out, do not retry the query with the same broad
scope. You must specify a tighter, narrower 'PathPrefix' for subsequent queries.
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: Can unprivileged users mount configfs?
|
| 23/4 |
2026/07/31 01:55 |
llm |
codeexpert |
0m
Model:
Tokens:
input: 3869
output: 23
thoughts: 159
Thoughts: **Investigating Configfs Mountability for Unprivileged Users**
Okay, so the question is: can a regular, unprivileged user mount `configfs`? That's the crux of it. My initial thought is to dive right into the kernel source or documentation. I need to figure out *how* `configfs` is registered, specifically. The key, as I understand it, is whether or not it's registered with the `FS_USERNS_MOUNT` flag. If it *is*, then unprivileged mounting within a user namespace is allowed. If not, it's a no-go.
So, the plan is to hunt down the filesystem registration. I'll be looking for something like the `configfs_fs_type` struct, or the code where `configfs` is registered, directly. I'll need to check if the flag is set. To make this efficient, I should be able to use a tool like `grepper` to search the codebase. That should give me a solid answer quickly, by digging up the relevant source code.
|
| 24/4 |
2026/07/31 01:55 |
tool |
grepper |
0m
Args: map[Expression:struct file_system_type.*configfs]
Results: map[Output:fs/configfs/mount.c=109=static int configfs_init_fs_context(struct fs_context *fc)
--
fs/configfs/mount.c-114-
fs/configfs/mount.c:115:static struct file_system_type configfs_fs_type = {
fs/configfs/mount.c-116- .owner = THIS_MODULE,
]
|
| 25/4 |
2026/07/31 01:55 |
llm |
codeexpert |
0m
Model:
Tokens:
input: 4389
output: 38
thoughts: 94
Thoughts: **Examining the ConfigFS Filesystem Type**
Alright, let's dive into the core of how ConfigFS is defined. I'm going to take a look at `fs/configfs/mount.c`, specifically around line 115, as that seems like a good place to start for understanding the `configfs_fs_type` structure.
I've got a couple of options for finding the definition – `codesearch-definition-source` is probably the most direct way to get the source code quickly, or I could use `read-file` if that fails. I'll start with `codesearch-definition-source` first; it should get me straight to the relevant bit of code. That's usually the fastest way to get to the definition, rather than manually locating the file.
|
| 26/4 |
2026/07/31 01:55 |
tool |
codesearch-definition-source |
0m
Args: map[ContextFile:fs/configfs/mount.c Name:configfs_fs_type]
Results: map[SourceCode: 115: static struct file_system_type configfs_fs_type = {
116: .owner = THIS_MODULE,
117: .name = "configfs",
118: .init_fs_context = configfs_init_fs_context,
119: .kill_sb = kill_anon_super,
120: };
SourceFile:fs/configfs/mount.c]
|
| 27/4 |
2026/07/31 01:55 |
llm |
codeexpert |
0m
Model:
Tokens:
input: 4826
output: 36
thoughts: 189
Thoughts: **Investigating Configfs and User Namespaces**
Okay, so I'm looking at the `configfs_fs_type` structure definition. I see the standard fields: `.owner`, `.name`, `.init_fs_context`, and `.kill_sb`. Nothing immediately jumps out, but I need to make sure I'm not missing anything crucial, especially regarding user namespaces. The key question is whether `configfs` explicitly supports mounting within user namespaces.
Alright, a quick check... no, `configfs_fs_type` *doesn't* have `.fs_flags = FS_USERNS_MOUNT` directly set. That's the typical flag to enable user namespace mounting. Now, just because it's not set here doesn't mean it's *not* supported. I need to be thorough. I should check if `configfs_fs_type` is modified elsewhere, or if `FS_USERNS_MOUNT` is set through some other mechanism. Let's delve into the `fs/configfs/mount.c` file, specifically around line 115. That seems like a logical place to start looking for any relevant mounting logic or flags being applied. Let's see what's there.
|
| 28/4 |
2026/07/31 01:55 |
tool |
read-file |
0m
Args: map[File:fs/configfs/mount.c FirstLine:100 LineCount:50]
Results: map[Contents: 100: static int configfs_get_tree(struct fs_context *fc)
101: {
102: return get_tree_single(fc, configfs_fill_super);
103: }
104:
105: static const struct fs_context_operations configfs_context_ops = {
106: .get_tree = configfs_get_tree,
107: };
108:
109: static int configfs_init_fs_context(struct fs_context *fc)
110: {
111: fc->ops = &configfs_context_ops;
112: return 0;
113: }
114:
115: static struct file_system_type configfs_fs_type = {
116: .owner = THIS_MODULE,
117: .name = "configfs",
118: .init_fs_context = configfs_init_fs_context,
119: .kill_sb = kill_anon_super,
120: };
121: MODULE_ALIAS_FS("configfs");
122:
123: struct dentry *configfs_pin_fs(void)
124: {
125: int err = simple_pin_fs(&configfs_fs_type, &configfs_mount,
126: &configfs_mnt_count);
127: return err ? ERR_PTR(err) : configfs_mount->mnt_root;
128: }
129:
130: void configfs_release_fs(void)
131: {
132: simple_release_fs(&configfs_mount, &configfs_mnt_count);
133: }
134:
135:
136: static int __init configfs_init(void)
137: {
138: int err = -ENOMEM;
139:
140: configfs_dir_cachep = kmem_cache_create("configfs_dir_cache",
141: sizeof(struct configfs_dirent),
142: 0, 0, NULL);
143: if (!configfs_dir_cachep)
144: goto out;
145:
146: err = sysfs_create_mount_point(kernel_kobj, "config");
147: if (err)
148: goto out2;
149:
]
|
| 29/4 |
2026/07/31 01:55 |
llm |
codeexpert |
0m
Model:
Tokens:
input: 6041
output: 36
thoughts: 19
Thoughts: Here's my attempt at summarizing the thought process, tailored for an expert audience:
**Diving Deeper into `configfs_init`**
Okay, so we've looked at the basic structure of `configfs_init`. But I'm feeling a niggling sense of needing to verify a few assumptions. The core functionality seems straightforward, but experience has taught me that initial setup routines are often where subtle dependencies or edge-case handling can hide.
I'm thinking, "Let's just quickly scan the remaining code within `configfs_init`." I don't want to get blindsided by something seemingly unrelated that's actually crucial to understanding the full context. It's about building a solid, reliable mental model of the initialization process. Better safe than sorry when dealing with core infrastructure code. Time to eliminate any lingering uncertainties.
|
| 30/4 |
2026/07/31 01:55 |
tool |
read-file |
0m
Args: map[File:fs/configfs/mount.c FirstLine:150 LineCount:30]
Results: map[Contents: 150: err = register_filesystem(&configfs_fs_type);
151: if (err)
152: goto out3;
153:
154: return 0;
155: out3:
156: pr_err("Unable to register filesystem!\n");
157: sysfs_remove_mount_point(kernel_kobj, "config");
158: out2:
159: kmem_cache_destroy(configfs_dir_cachep);
160: configfs_dir_cachep = NULL;
161: out:
162: return err;
163: }
164:
165: static void __exit configfs_exit(void)
166: {
167: unregister_filesystem(&configfs_fs_type);
168: sysfs_remove_mount_point(kernel_kobj, "config");
169: kmem_cache_destroy(configfs_dir_cachep);
170: configfs_dir_cachep = NULL;
171: }
172:
173: MODULE_AUTHOR("Oracle");
174: MODULE_LICENSE("GPL");
175: MODULE_VERSION("0.0.2");
176: MODULE_DESCRIPTION("Simple RAM filesystem for user driven kernel subsystem configuration.");
177:
178: core_initcall(configfs_init);
179: module_exit(configfs_exit);
]
|
| 31/4 |
2026/07/31 01:55 |
llm |
codeexpert |
0m
Model:
Tokens:
input: 6709
output: 21
thoughts: 258
Thoughts: **My Analysis of Filesystem Mounting Privileges**
Okay, so I see that `configfs_fs_type` is registered using `register_filesystem(&configfs_fs_type)`, but it doesn't have the `FS_USERNS_MOUNT` flag set. That immediately makes me wonder about the broader implications for unprivileged users and filesystem mounting. Is this the *only* mechanism, or are there other ways around this?
Generally speaking, mounting a filesystem requires `CAP_SYS_ADMIN` in the initial user namespace. That's the baseline. However, the system bypasses this check if the filesystem is registered with the `FS_USERNS_MOUNT` flag. If a filesystem *does* have this flag, then an unprivileged user, *provided* they've created a new user namespace, can mount it. Because in this new namespace, they'll effectively have `CAP_SYS_ADMIN`, or at least the relevant powers.
Without that `FS_USERNS_MOUNT` flag, only a user with `CAP_SYS_ADMIN` in the *initial* user namespace can mount the filesystem. That's, for all intents and purposes, a privileged user, root, or someone with elevated capabilities.
Just to be absolutely sure, I'm going to double-check my assumptions here. I'll search the kernel documentation and codebase for *all* instances of `FS_USERNS_MOUNT`. I need to verify that there aren't any other subtle mechanisms or exceptions to this rule, or, you know, some detailed documentation on its behavior. Gotta be thorough.
|
| 32/4 |
2026/07/31 01:55 |
tool |
grepper |
0m
Args: map[Expression:FS_USERNS_MOUNT]
Results: map[Output:drivers/android/binder/rust_binderfs.c=753=static struct file_system_type binder_fs_type = {
--
drivers/android/binder/rust_binderfs.c-757- .kill_sb = binderfs_kill_super,
drivers/android/binder/rust_binderfs.c:758: .fs_flags = FS_USERNS_MOUNT,
drivers/android/binder/rust_binderfs.c-759-};
--
drivers/android/binderfs.c=749=static struct file_system_type binder_fs_type = {
--
drivers/android/binderfs.c-753- .kill_sb = binderfs_kill_super,
drivers/android/binderfs.c:754: .fs_flags = FS_USERNS_MOUNT,
drivers/android/binderfs.c-755-};
--
fs/binfmt_misc.c=1020=static struct file_system_type bm_fs_type = {
--
fs/binfmt_misc.c-1023- .init_fs_context = bm_init_fs_context,
fs/binfmt_misc.c:1024: .fs_flags = FS_USERNS_MOUNT,
fs/binfmt_misc.c-1025- .kill_sb = kill_anon_super,
--
fs/devpts/inode.c=453=static struct file_system_type devpts_fs_type = {
--
fs/devpts/inode.c-457- .kill_sb = devpts_kill_sb,
fs/devpts/inode.c:458: .fs_flags = FS_USERNS_MOUNT,
fs/devpts/inode.c-459-};
--
fs/fuse/inode.c=2001=static struct file_system_type fuse_fs_type = {
--
fs/fuse/inode.c-2003- .name = "fuse",
fs/fuse/inode.c:2004: .fs_flags = FS_HAS_SUBTYPE | FS_USERNS_MOUNT | FS_ALLOW_IDMAP,
fs/fuse/inode.c-2005- .init_fs_context = fuse_init_fs_context,
--
fs/namespace.c=1076=static void mnt_add_to_ns(struct mnt_namespace *ns, struct mount *mnt)
--
fs/namespace.c-1101-
fs/namespace.c:1102: if ((mnt->mnt.mnt_sb->s_type->fs_flags & FS_USERNS_MOUNT_RESTRICTED) &&
fs/namespace.c-1103- mnt->mnt.mnt_root == mnt->mnt.mnt_sb->s_root)
--
fs/namespace.c=6415=static bool mount_too_revealing(const struct super_block *sb, int *new_mnt_flags)
--
fs/namespace.c-6424- /* Can this filesystem be too revealing? */
fs/namespace.c:6425: if (!(sb->s_type->fs_flags & FS_USERNS_MOUNT_RESTRICTED))
fs/namespace.c-6426- return false;
--
fs/overlayfs/params.c=782=static const struct fs_context_operations ovl_context_ops = {
--
fs/overlayfs/params.c-791- * This is called during fsopen() and will record the user namespace of
fs/overlayfs/params.c:792: * the caller in fc->user_ns since we've raised FS_USERNS_MOUNT. We'll
fs/overlayfs/params.c-793- * need it when we actually create the superblock to verify that the
--
fs/overlayfs/super.c=1571=struct file_system_type ovl_fs_type = {
--
fs/overlayfs/super.c-1575- .parameters = ovl_parameter_spec,
fs/overlayfs/super.c:1576: .fs_flags = FS_USERNS_MOUNT,
fs/overlayfs/super.c-1577- .kill_sb = kill_anon_super,
--
fs/proc/root.c=369=static struct file_system_type proc_fs_type = {
--
fs/proc/root.c-373- .kill_sb = proc_kill_sb,
fs/proc/root.c:374: .fs_flags = FS_USERNS_MOUNT | FS_USERNS_MOUNT_RESTRICTED | FS_DISALLOW_NOTIFY_PERM,
fs/proc/root.c-375-};
--
fs/ramfs/inode.c=317=static struct file_system_type ramfs_fs_type = {
--
fs/ramfs/inode.c-321- .kill_sb = ramfs_kill_sb,
fs/ramfs/inode.c:322: .fs_flags = FS_USERNS_MOUNT,
fs/ramfs/inode.c-323-};
--
fs/super.c=695=bool mount_capable(struct fs_context *fc)
fs/super.c-696-{
fs/super.c:697: if (!(fc->fs_type->fs_flags & FS_USERNS_MOUNT))
fs/super.c-698- return capable(CAP_SYS_ADMIN);
--
fs/super.c=735=struct super_block *sget_fc(struct fs_context *fc,
--
fs/super.c-744- /*
fs/super.c:745: * Never allow s_user_ns != &init_user_ns when FS_USERNS_MOUNT or
fs/super.c-746- * FS_USERNS_DELEGATABLE is not set, as the filesystem is likely
--
fs/super.c-750- if (user_ns != &init_user_ns &&
fs/super.c:751: !(fc->fs_type->fs_flags & (FS_USERNS_MOUNT | FS_USERNS_DELEGATABLE))) {
fs/super.c-752- errorfc(fc, "VFS: Mounting from non-initial user namespace is not allowed");
--
fs/sysfs/mount.c=78=static struct file_system_type sysfs_fs_type = {
--
fs/sysfs/mount.c-81- .kill_sb = sysfs_kill_sb,
fs/sysfs/mount.c:82: .fs_flags = FS_USERNS_MOUNT | FS_USERNS_MOUNT_RESTRICTED,
fs/sysfs/mount.c-83-};
--
include/linux/fs.h=2280=struct file_system_type {
--
include/linux/fs.h-2285-#define FS_HAS_SUBTYPE 4
include/linux/fs.h:2286:#define FS_USERNS_MOUNT 8 /* Can be mounted by userns root */
include/linux/fs.h-2287-#define FS_DISALLOW_NOTIFY_PERM 16 /* Disable fanotify permission events */
--
include/linux/fs.h-2291-#define FS_POWER_FREEZE 256 /* Always freeze on suspend/hibernate */
include/linux/fs.h:2292:#define FS_USERNS_MOUNT_RESTRICTED 512 /* Restrict mount in userns if not already visible */
include/linux/fs.h-2293-#define FS_USERNS_DELEGATABLE 1024 /* Can be mounted inside userns from outside */
--
ipc/mqueue.c=1613=static struct file_system_type mqueue_fs_type = {
--
ipc/mqueue.c-1616- .kill_sb = kill_anon_super,
ipc/mqueue.c:1617: .fs_flags = FS_USERNS_MOUNT,
ipc/mqueue.c-1618-};
--
kernel/acct.c=216=static int acct_on(const char __user *name)
--
kernel/acct.c-251- /* Exclude procfs and sysfs. */
kernel/acct.c:252: if (file_inode(file)->i_sb->s_type->fs_flags & FS_USERNS_MOUNT_RESTRICTED)
kernel/acct.c-253- return -EINVAL;
--
kernel/bpf/inode.c=1277=static struct file_system_type bpf_fs_type = {
--
kernel/bpf/inode.c-1282- .kill_sb = bpf_kill_super,
kernel/bpf/inode.c:1283: .fs_flags = FS_USERNS_MOUNT,
kernel/bpf/inode.c-1284-};
--
kernel/cgroup/cgroup.c=2388=struct file_system_type cgroup_fs_type = {
--
kernel/cgroup/cgroup.c-2392- .kill_sb = cgroup_kill_sb,
kernel/cgroup/cgroup.c:2393: .fs_flags = FS_USERNS_MOUNT,
kernel/cgroup/cgroup.c-2394-};
--
kernel/cgroup/cgroup.c=2396=static struct file_system_type cgroup2_fs_type = {
--
kernel/cgroup/cgroup.c-2400- .kill_sb = cgroup_kill_sb,
kernel/cgroup/cgroup.c:2401: .fs_flags = FS_USERNS_MOUNT,
kernel/cgroup/cgroup.c-2402-};
--
kernel/cgroup/cgroup.c=2469=static struct file_system_type cpuset_fs_type = {
--
kernel/cgroup/cgroup.c-2472- .parameters = cpuset_fs_parameters,
kernel/cgroup/cgroup.c:2473: .fs_flags = FS_USERNS_MOUNT,
kernel/cgroup/cgroup.c-2474-};
--
mm/shmem.c=5280=static struct file_system_type shmem_fs_type = {
--
mm/shmem.c-5287- .kill_sb = kill_anon_super,
mm/shmem.c:5288: .fs_flags = FS_USERNS_MOUNT | FS_ALLOW_IDMAP | FS_MGTIME,
mm/shmem.c-5289-};
--
mm/shmem.c=5712=static struct file_system_type shmem_fs_type = {
--
mm/shmem.c-5716- .kill_sb = ramfs_kill_sb,
mm/shmem.c:5717: .fs_flags = FS_USERNS_MOUNT,
mm/shmem.c-5718-};
]
|
| 33/4 |
2026/07/31 01:55 |
llm |
codeexpert |
0m
Model:
Tokens:
input: 10167
output: 32
thoughts: 45
Thoughts: Okay, let's dive into this.
**Investigating the `mount_capable` Function**
Right, so I need to understand how the `mount_capable` function actually functions. My first instinct is to go straight to the source, and I'm pretty sure it's located in `fs/super.c`. Specifically, I seem to recall it being around line 695 in that file. That seems like a reasonable starting point.
To get a clear grasp of its implementation and usage, I think the quickest way is going to be using `codesearch-definition-source` on `mount_capable`. That should give me the core definition and also show me where it's being called from and what context it operates in. I need the full picture to understand this effectively.
|
| 34/4 |
2026/07/31 01:55 |
tool |
codesearch-definition-source |
0m
Args: map[ContextFile:fs/super.c Name:mount_capable]
Results: map[SourceCode: 695: bool mount_capable(struct fs_context *fc)
696: {
697: if (!(fc->fs_type->fs_flags & FS_USERNS_MOUNT))
698: return capable(CAP_SYS_ADMIN);
699: else
700: return ns_capable(fc->user_ns, CAP_SYS_ADMIN);
701: }
SourceFile:fs/super.c]
|
| 35/4 |
2026/07/31 01:55 |
llm |
codeexpert |
0m
Model:
Tokens:
input: 10549
output: 26
thoughts: 276
Thoughts: **Analyzing Configfs Mounting Permissions**
Okay, this is pretty straightforward. The logic here is crystal clear. Basically, if the `FS_USERNS_MOUNT` flag isn't set in the filesystem type's `fs_flags`, then the `mount_capable` function defaults to checking for the `CAP_SYS_ADMIN` capability in the *initial* user namespace. That's a root/privileged check, plain and simple.
However, if `FS_USERNS_MOUNT` *is* set, then `mount_capable` calls `ns_capable` to check for `CAP_SYS_ADMIN` within the filesystem context's user namespace. That's the mechanism that allows unprivileged users to mount things if they're within their own new user namespace.
Now, considering `configfs` specifically, it doesn't have the `FS_USERNS_MOUNT` flag set in its `configfs_fs_type` struct. Therefore, it's inherently restricted to privileged users for mounting. Makes sense; this is the expected behavior.
I'm quickly scanning `fs/namespace.c` to see if there are any other exceptions or nuances related to `configfs`, any specific mentions, or any deviations from this rule. Nope, looks clean.
To be thorough, I'll also do a quick dive into the `Documentation/` directory. Let's search for `configfs` and see if there are any specific notes or documentation about the mounting permissions or if there's any mention of allowing unprivileged mounts somewhere else. A brief search of the kernel documentation should reveal any caveats or special cases.
|
| 36/4 |
2026/07/31 01:55 |
tool |
grepper |
0m
Args: map[Expression:mount.*configfs PathPrefix:Documentation/]
Results: map[Output:Documentation/ABI/testing/configfs-most=4=Description: Interface is used to configure and connect device channels
--
Documentation/ABI/testing/configfs-most-8- configfs in /sys/kernel/config directory use:
Documentation/ABI/testing/configfs-most:9: # mount -t configfs none /sys/kernel/config/
Documentation/ABI/testing/configfs-most-10-
--
Documentation/ABI/testing/configfs-rdma_cm=4=Description: Interface is used to configure RDMA-cable HCAs in respect to
--
Documentation/ABI/testing/configfs-rdma_cm-8- configfs in /config directory use:
Documentation/ABI/testing/configfs-rdma_cm:9: # mount -t configfs none /config/
Documentation/ABI/testing/configfs-rdma_cm-10-
--
Documentation/ABI/testing/configfs-spear-pcie-gadget=5=Description:
--
Documentation/ABI/testing/configfs-spear-pcie-gadget-11-
Documentation/ABI/testing/configfs-spear-pcie-gadget:12: Nodes are only visible when configfs is mounted. To mount configfs
Documentation/ABI/testing/configfs-spear-pcie-gadget-13- in /config directory use::
Documentation/ABI/testing/configfs-spear-pcie-gadget-14-
Documentation/ABI/testing/configfs-spear-pcie-gadget:15: # mount -t configfs none /config/
Documentation/ABI/testing/configfs-spear-pcie-gadget-16-
--
Documentation/PCI/endpoint/pci-endpoint-cfs.rst=14=Mounting configfs
--
Documentation/PCI/endpoint/pci-endpoint-cfs.rst-16-
Documentation/PCI/endpoint/pci-endpoint-cfs.rst:17:The PCI Endpoint Core layer creates pci_ep directory in the mounted configfs
Documentation/PCI/endpoint/pci-endpoint-cfs.rst-18-directory. configfs can be mounted using the following command::
Documentation/PCI/endpoint/pci-endpoint-cfs.rst-19-
Documentation/PCI/endpoint/pci-endpoint-cfs.rst:20: mount -t configfs none /sys/kernel/config
Documentation/PCI/endpoint/pci-endpoint-cfs.rst-21-
--
Documentation/PCI/endpoint/pci-ntb-howto.rst=53=pci-epf-ntb device, the following commands can be used::
Documentation/PCI/endpoint/pci-ntb-howto.rst-54-
Documentation/PCI/endpoint/pci-ntb-howto.rst:55: # mount -t configfs none /sys/kernel/config
Documentation/PCI/endpoint/pci-ntb-howto.rst-56- # cd /sys/kernel/config/pci_ep/
--
Documentation/PCI/endpoint/pci-test-howto.rst=48=pci-epf-test device, the following commands can be used::
Documentation/PCI/endpoint/pci-test-howto.rst-49-
Documentation/PCI/endpoint/pci-test-howto.rst:50: # mount -t configfs none /sys/kernel/config
Documentation/PCI/endpoint/pci-test-howto.rst-51- # cd /sys/kernel/config/pci_ep/
--
Documentation/PCI/endpoint/pci-vntb-howto.rst=49=pci-epf-vntb device, the following commands can be used::
Documentation/PCI/endpoint/pci-vntb-howto.rst-50-
Documentation/PCI/endpoint/pci-vntb-howto.rst:51: # mount -t configfs none /sys/kernel/config
Documentation/PCI/endpoint/pci-vntb-howto.rst-52- # cd /sys/kernel/config/pci_ep/
--
Documentation/admin-guide/acpi/ssdt-overlays.rst=172=interface. The CONFIG_ACPI_CONFIGFS option must be select and configfs must be
Documentation/admin-guide/acpi/ssdt-overlays.rst:173:mounted. In the following examples, we assume that configfs has been mounted in
Documentation/admin-guide/acpi/ssdt-overlays.rst-174-/sys/kernel/config.
--
Documentation/filesystems/configfs.rst=44=it by doing::
Documentation/filesystems/configfs.rst-45-
Documentation/filesystems/configfs.rst:46: mount -t configfs none /config
Documentation/filesystems/configfs.rst-47-
--
Documentation/gpu/vkms.rst=57=It is possible to create and configure multiple VKMS instances via configfs.
Documentation/gpu/vkms.rst-58-
Documentation/gpu/vkms.rst:59:Start by mounting configfs and loading VKMS::
Documentation/gpu/vkms.rst-60-
Documentation/gpu/vkms.rst:61: sudo mount -t configfs none /config
Documentation/gpu/vkms.rst-62- sudo modprobe vkms
--
Documentation/iio/iio_configfs.rst=19=time via CONFIG_IIO_CONFIGFS config option.
Documentation/iio/iio_configfs.rst-20-
Documentation/iio/iio_configfs.rst:21:Then, mount the configfs filesystem (usually under /config directory)::
Documentation/iio/iio_configfs.rst-22-
Documentation/iio/iio_configfs.rst-23- $ mkdir /config
Documentation/iio/iio_configfs.rst:24: $ mount -t configfs none /config
Documentation/iio/iio_configfs.rst-25-
--
Documentation/misc-devices/spear-pcie-gadget.rst=79=to the PCIe host, then host sees this device as 1MB RAM.
--
Documentation/misc-devices/spear-pcie-gadget.rst-82-
Documentation/misc-devices/spear-pcie-gadget.rst:83: #mount -t configfs none /Config
Documentation/misc-devices/spear-pcie-gadget.rst-84-
--
Documentation/nvme/nvme-pci-endpoint-target.rst=191=First, make sure that configfs is enabled::
Documentation/nvme/nvme-pci-endpoint-target.rst-192-
Documentation/nvme/nvme-pci-endpoint-target.rst:193: # mount -t configfs none /sys/kernel/config
Documentation/nvme/nvme-pci-endpoint-target.rst-194-
--
Documentation/target/target-export-device=42=lsmod | grep -q target_core_mod || modprobe target_core_mod
Documentation/target/target-export-device:43:mount | grep -q ^configfs || mount -t configfs none $CONFIGFS
Documentation/target/target-export-device-44-mkdir -p $ISCSI_DIR
--
Documentation/translations/zh_CN/iio/iio_configfs.rst=19=configfs轻松配置的对象(例如:设备,触发器)。
--
Documentation/translations/zh_CN/iio/iio_configfs.rst-31- $ mkdir/config
Documentation/translations/zh_CN/iio/iio_configfs.rst:32: $ mount -t configfs none/config
Documentation/translations/zh_CN/iio/iio_configfs.rst-33-
--
Documentation/usb/gadget_configfs.rst=50=http://www.spinics.net/lists/linux-usb/msg76388.html)
--
Documentation/usb/gadget_configfs.rst-54- $ modprobe libcomposite
Documentation/usb/gadget_configfs.rst:55: $ mount none $CONFIGFS_HOME -t configfs
Documentation/usb/gadget_configfs.rst-56-
Documentation/usb/gadget_configfs.rst:57:where CONFIGFS_HOME is the mount point for configfs
Documentation/usb/gadget_configfs.rst-58-
--
Documentation/usb/gadget_uvc.rst=37=Assumptions
Documentation/usb/gadget_uvc.rst-38-~~~~~~~~~~~
Documentation/usb/gadget_uvc.rst:39:This section assumes that you have mounted configfs at `/sys/kernel/config` and
Documentation/usb/gadget_uvc.rst-40-created a gadget as `/sys/kernel/config/usb_gadget/g1`.
]
|
| 37/4 |
2026/07/31 01:55 |
llm |
codeexpert |
0m
Model:
Tokens:
input: 13354
output: 456
thoughts: 357
Thoughts: **My Analysis of Configfs Mounting Restrictions**
Okay, let's break down why I'm seeing this consistent requirement for root or `sudo` when mounting `configfs` in the documentation. I can see the pattern: always `#` or using `sudo`. I need to figure out the "why" so that I can provide an appropriate solution.
First, the core of the issue is around file system mounting. Mounting any filesystem in Linux *generally* requires the `CAP_SYS_ADMIN` capability. This is pretty standard practice.
However, there's a specific exception. Some filesystems, when they're *registered* with the kernel, can be flagged with `FS_USERNS_MOUNT`. This flag lets unprivileged users mount them, but crucially, only *within* a user namespace. The unprivileged user holds `CAP_SYS_ADMIN` *within that namespace*, which gives them the necessary privileges. That's the workaround.
Now, let's examine the specifics of `configfs`. Looking at the definition of `configfs_fs_type` in `fs/configfs/mount.c`, I see:
```c
static struct file_system_type configfs_fs_type = {
.owner = THIS_MODULE,
.name = "configfs",
.init_fs_context = configfs_init_fs_context,
.kill_sb = kill_anon_super,
};
```
I can see what's going on here: It **doesn't** have `FS_USERNS_MOUNT` set! It's *missing* that critical flag.
Therefore, the VFS layer in the kernel kicks in and, specifically, the `mount_capable()` function in `fs/super.c` checks for the root, or, `CAP_SYS_ADMIN` in the *initial* user namespace, which only privileged users (root) have. So, because the flag isn't set, any unprivileged user is going to be blocked at the VFS level.
In conclusion: Unprivileged users cannot mount `configfs` because the `configfs` filesystem type is not registered with the `FS_USERNS_MOUNT` flag. It must be mounted by a user with root privileges. I am going to call `set-results` to record this conclusion.
|
| 38/4 |
2026/07/31 01:55 |
tool |
set-results |
0m
Args: map[Answer:No, unprivileged users cannot mount configfs.
### Explanation:
1. **Privilege Requirement for Mounting**: In the Linux kernel, mounting a filesystem generally requires the `CAP_SYS_ADMIN` capability in the initial user namespace (i.e., root privileges).
2. **User Namespace Exception**: Filesystems can allow unprivileged users to mount them inside a user namespace (where the user has `CAP_SYS_ADMIN` for that namespace) if and only if the filesystem's registration structure (`struct file_system_type`) has the `FS_USERNS_MOUNT` flag set in its `.fs_flags`.
3. **ConfigFS Registration**: The `configfs` filesystem is defined in `fs/configfs/mount.c` as:
```c
static struct file_system_type configfs_fs_type = {
.owner = THIS_MODULE,
.name = "configfs",
.init_fs_context = configfs_init_fs_context,
.kill_sb = kill_anon_super,
};
```
As shown above, `configfs_fs_type` does **not** set the `FS_USERNS_MOUNT` flag.
4. **VFS Enforcement**: When a mount request is made, the VFS layer checks permissions using `mount_capable()` in `fs/super.c`:
```c
bool mount_capable(struct fs_context *fc)
{
if (!(fc->fs_type->fs_flags & FS_USERNS_MOUNT))
return capable(CAP_SYS_ADMIN);
else
return ns_capable(fc->user_ns, CAP_SYS_ADMIN);
}
```
Because `configfs` lacks `FS_USERNS_MOUNT`, the kernel falls back to `capable(CAP_SYS_ADMIN)`, which checks for the capability in the initial user namespace. This restricts mounting of configfs strictly to privileged users.]
Results: map[Answer:No, unprivileged users cannot mount configfs.
### Explanation:
1. **Privilege Requirement for Mounting**: In the Linux kernel, mounting a filesystem generally requires the `CAP_SYS_ADMIN` capability in the initial user namespace (i.e., root privileges).
2. **User Namespace Exception**: Filesystems can allow unprivileged users to mount them inside a user namespace (where the user has `CAP_SYS_ADMIN` for that namespace) if and only if the filesystem's registration structure (`struct file_system_type`) has the `FS_USERNS_MOUNT` flag set in its `.fs_flags`.
3. **ConfigFS Registration**: The `configfs` filesystem is defined in `fs/configfs/mount.c` as:
```c
static struct file_system_type configfs_fs_type = {
.owner = THIS_MODULE,
.name = "configfs",
.init_fs_context = configfs_init_fs_context,
.kill_sb = kill_anon_super,
};
```
As shown above, `configfs_fs_type` does **not** set the `FS_USERNS_MOUNT` flag.
4. **VFS Enforcement**: When a mount request is made, the VFS layer checks permissions using `mount_capable()` in `fs/super.c`:
```c
bool mount_capable(struct fs_context *fc)
{
if (!(fc->fs_type->fs_flags & FS_USERNS_MOUNT))
return capable(CAP_SYS_ADMIN);
else
return ns_capable(fc->user_ns, CAP_SYS_ADMIN);
}
```
Because `configfs` lacks `FS_USERNS_MOUNT`, the kernel falls back to `capable(CAP_SYS_ADMIN)`, which checks for the capability in the initial user namespace. This restricts mounting of configfs strictly to privileged users.]
|
| 39/2 |
2026/07/31 01:55 |
llm |
expert |
0m
Model:
Tokens:
input: 14746
output: 34
thoughts: 3380
Thoughts: **Analysis of Invalid Free in `f_uac1_opts_c_srate_store`**
Okay, here's what I'm seeing. There's a potential heap corruption vulnerability in the `f_uac1_opts_c_srate_store` function, which is generated by the `UAC1_RATE_ATTRIBUTE(c_srate)` macro. This function is designed to handle the storage of sample rate settings within a USB Audio Class 1 (UAC1) gadget.
The core issue is a classic "invalid free" scenario arising from the use of `strsep` to parse a comma-separated string provided as input. Essentially, `strsep` modifies the pointer (`split_page`) *in-place*. This means that after `strsep` advances the pointer to the next token, the subsequent call to `kfree(split_page)` attempts to free a memory address *within* the originally allocated buffer, not the start.
This is a problem because, in SLUB (the Slab allocator), `kfree` doesn't automatically align the pointer unless security hardening features like KASAN are enabled. Instead, it treats the provided pointer as the object to be freed. The `kfree` function calls `slab_free`, which eventually calls `do_slab_free`, which in turn, sets a `freepointer` with an unaligned address in the freelist. This allows an unaligned pointer to be added to the free list. Subsequent allocations might then return this unaligned pointer, leading to overlapping allocations and memory corruption.
Specifically, if the comma in the string is at a position that's not aligned with the SLUB alignment (typically 8 bytes), `slab_free_freelist_hook` might catch it. However, if `CONFIG_SLAB_FREELIST_HARDENED` is not enabled, the interior pointer gets added to the freelist, which leads to the memory corruption.
Now, for exploitability: this could be bad. This invalid free is a primitive that allows for arbitrary memory corruption, specifically overlapping allocations and controlled out-of-bounds writes. By carefully crafting the input string, an attacker could potentially overwrite adjacent objects in memory.
However, the real question is how to *trigger* it. The `f_uac1_opts_c_srate_store` function is part of configfs, a virtual filesystem used for kernel configuration. This means you need root privileges to modify anything in `/sys/kernel/config`. Further, triggering this requires both `libcomposite` and `usb_f_uac1` modules to be loaded and a gadget configured within configfs. The exploit requires the root user to create the necessary directories and files. While this vulnerability is *technically* exploitable, it's not directly accessible to unprivileged users or within user namespaces due to the configfs permissions. The syzbot bug report also runs as root.
While a VM guest running the same kernel could trigger it (if the host mounts configfs and grants access or by running a nested hypervisor), that's less a "guest escape" and more "root in the guest can crash the guest/host". The issue is the attack surface.
Therefore, although this bug is exploitable, it is *only* exploitable by a root user in a privileged context, limiting its severity for most typical threat models, unless the root user in the guest is considered to be a threat. There are no other ways this is reachable.
|
| 40/2 |
2026/07/31 01:56 |
tool |
grepper |
0m
Args: map[Expression:slab_free_freelist_hook PathPrefix:mm/slub.c]
Results: map[Output:mm/slub.c=2680=static __fastpath_inline
mm/slub.c:2681:bool slab_free_freelist_hook(struct kmem_cache *s, void **head, void **tail,
mm/slub.c-2682- int *cnt)
--
mm/slub.c=6402=void slab_free_bulk(struct kmem_cache *s, struct slab *slab, void *head,
--
mm/slub.c-6407- /*
mm/slub.c:6408: * With KASAN enabled slab_free_freelist_hook modifies the freelist
mm/slub.c-6409- * to remove objects, whose reuse must be delayed.
mm/slub.c-6410- */
mm/slub.c:6411: if (likely(slab_free_freelist_hook(s, &head, &tail, &cnt))) {
mm/slub.c-6412- __slab_free(s, slab, head, tail, cnt, addr);
]
|
| 41/2 |
2026/07/31 01:56 |
llm |
expert |
0m
Model:
Tokens:
input: 15809
output: 136
thoughts: 0
|
| 42/2 |
2026/07/31 01:56 |
tool |
codeexpert |
1h35m
Args: map[Question:Is the bug in f_uac1_opts_c_srate_store exploitable? The report is KASAN: invalid-free.
The code is:
split_page = kstrdup(page, GFP_KERNEL); \
while ((token = strsep(&split_page, ",")) != NULL) { \
...
end: \
kfree(split_page); \
It looks like strsep modifies split_page, so kfree is called on a pointer that is not the start of the allocation.]
Results: map[Answer:Yes, the bug in `f_uac1_opts_c_srate_store` (and its counterpart `f_uac1_opts_p_srate_store`, as well as the corresponding functions in `f_uac2.c`) is technically exploitable under specific conditions, though it has some practical limitations.
### 1. Bug Mechanism
The bug is a classic **invalid-free (unaligned free)** caused by the misuse of `strsep()`.
In `f_uac1.c` (and `f_uac2.c`), the store macro is defined as:
```c
split_page = kstrdup(page, GFP_KERNEL); \
while ((token = strsep(&split_page, ",")) != NULL) { \
ret = kstrtou32(token, 0, &num); \
if (ret) \
goto end; \
...
}; \
end: \
kfree(split_page); \
```
`strsep()` takes a pointer to a string pointer (`char **stringp`) and updates it to point past the token delimiter.
- If the loop runs to completion (reaching the end of the string), `split_page` becomes `NULL`. Calling `kfree(NULL)` is safe and does nothing.
- However, if an invalid rate string is passed (e.g., `"invalid_rate,44100"`), `strsep()` extracts `"invalid_rate"`, updates `split_page` to point to `"44100"`, and then `kstrtou32()` fails. This triggers `goto end;`.
- At `end:`, `kfree(split_page)` is called on the updated pointer, which points to an offset within the allocated buffer rather than the start of the allocation.
---
### 2. Exploitability Analysis
#### A. Attack Surface & Access Requirements
- **Privilege Requirement:** Writing to configfs attributes (typically mounted at `/sys/kernel/config/usb-gadget/`) generally requires `root` privileges or specific capabilities (e.g., `CAP_SYS_ADMIN`). Thus, this bug cannot be triggered by an unprivileged local user unless configfs permissions have been explicitly relaxed.
- **Context:** It is relevant for local privilege escalation from a partially privileged process (e.g., a container escape or a daemon with configfs write access) or for bypassing kernel lockdown/secure boot restrictions.
#### B. Impact on the SLUB Allocator (Without `SLUB_DEBUG`)
If `CONFIG_SLUB_DEBUG` is disabled (as is common in production kernels for performance reasons), the SLUB allocator does not perform consistency checks on the freed pointer:
1. **Page Resolution:** `virt_to_page(split_page)` resolves the unaligned pointer to the correct `struct slab` page.
2. **Freelist Corruption:** `__slab_free()` links the unaligned pointer (`split_page`) directly into the slab's freelist. The freepointer of this unaligned "object" (located at `split_page + s->offset`) is written with the previous freelist head.
3. **Unaligned Allocation:** The next allocation request from the same `kmalloc` cache will return the unaligned pointer `split_page`.
4. **Object Overlap (OOB Write):** Because the allocated object is shifted/unaligned, writing to it will overflow into the adjacent active objects in the same slab. For example, if the allocation is shifted by 16 bytes, writing to the new object will overwrite the first 16 bytes of the next neighboring object.
5. **Privilege Escalation:** An attacker can use this overlap to overwrite critical fields (such as function pointers, object pointers, or reference counters) of neighboring active objects in the same slab cache, leading to arbitrary code execution or privilege escalation.
#### C. Mitigations
- **`CONFIG_SLUB_DEBUG`:** If enabled, the allocator's consistency checks (`check_valid_pointer`) will detect that the pointer is not aligned to the object size boundary. It will print a "Slab error" and refuse to free the pointer, preventing exploitation but causing a kernel warning/panic (Denial of Service).
- **`CONFIG_SLAB_FREELIST_HARDENED`:** This obfuscates freelist pointers, making direct freelist hijacking (pointing the freelist to an arbitrary address) extremely difficult. However, it **does not** prevent the overlap-based exploitation described above, as the attacker can still corrupt neighboring active objects directly via the unaligned allocation.
---
### 3. Affected Files
This exact bug pattern exists in four configfs store functions across two files:
1. `drivers/usb/gadget/function/f_uac1.c` (via `UAC1_RATE_ATTRIBUTE` macro):
- `f_uac1_opts_c_srate_store`
- `f_uac1_opts_p_srate_store`
2. `drivers/usb/gadget/function/f_uac2.c` (via `UAC2_RATE_ATTRIBUTE` macro):
- `f_uac2_opts_c_srate_store`
- `f_uac2_opts_p_srate_store`
---
### 4. Remediation
The standard fix is to preserve the original pointer returned by `kstrdup()` and free that instead of the pointer modified by `strsep()`:
```c
char *orig_split_page;
...
orig_split_page = split_page = kstrdup(page, GFP_KERNEL);
...
end:
kfree(orig_split_page);
```]
|
| 43/3 |
2026/07/31 01:56 |
agent |
codeexpert |
1h35m
Model:
Results: map[Answer:Yes, the bug in `f_uac1_opts_c_srate_store` (and its counterpart `f_uac1_opts_p_srate_store`, as well as the corresponding functions in `f_uac2.c`) is technically exploitable under specific conditions, though it has some practical limitations.
### 1. Bug Mechanism
The bug is a classic **invalid-free (unaligned free)** caused by the misuse of `strsep()`.
In `f_uac1.c` (and `f_uac2.c`), the store macro is defined as:
```c
split_page = kstrdup(page, GFP_KERNEL); \
while ((token = strsep(&split_page, ",")) != NULL) { \
ret = kstrtou32(token, 0, &num); \
if (ret) \
goto end; \
...
}; \
end: \
kfree(split_page); \
```
`strsep()` takes a pointer to a string pointer (`char **stringp`) and updates it to point past the token delimiter.
- If the loop runs to completion (reaching the end of the string), `split_page` becomes `NULL`. Calling `kfree(NULL)` is safe and does nothing.
- However, if an invalid rate string is passed (e.g., `"invalid_rate,44100"`), `strsep()` extracts `"invalid_rate"`, updates `split_page` to point to `"44100"`, and then `kstrtou32()` fails. This triggers `goto end;`.
- At `end:`, `kfree(split_page)` is called on the updated pointer, which points to an offset within the allocated buffer rather than the start of the allocation.
---
### 2. Exploitability Analysis
#### A. Attack Surface & Access Requirements
- **Privilege Requirement:** Writing to configfs attributes (typically mounted at `/sys/kernel/config/usb-gadget/`) generally requires `root` privileges or specific capabilities (e.g., `CAP_SYS_ADMIN`). Thus, this bug cannot be triggered by an unprivileged local user unless configfs permissions have been explicitly relaxed.
- **Context:** It is relevant for local privilege escalation from a partially privileged process (e.g., a container escape or a daemon with configfs write access) or for bypassing kernel lockdown/secure boot restrictions.
#### B. Impact on the SLUB Allocator (Without `SLUB_DEBUG`)
If `CONFIG_SLUB_DEBUG` is disabled (as is common in production kernels for performance reasons), the SLUB allocator does not perform consistency checks on the freed pointer:
1. **Page Resolution:** `virt_to_page(split_page)` resolves the unaligned pointer to the correct `struct slab` page.
2. **Freelist Corruption:** `__slab_free()` links the unaligned pointer (`split_page`) directly into the slab's freelist. The freepointer of this unaligned "object" (located at `split_page + s->offset`) is written with the previous freelist head.
3. **Unaligned Allocation:** The next allocation request from the same `kmalloc` cache will return the unaligned pointer `split_page`.
4. **Object Overlap (OOB Write):** Because the allocated object is shifted/unaligned, writing to it will overflow into the adjacent active objects in the same slab. For example, if the allocation is shifted by 16 bytes, writing to the new object will overwrite the first 16 bytes of the next neighboring object.
5. **Privilege Escalation:** An attacker can use this overlap to overwrite critical fields (such as function pointers, object pointers, or reference counters) of neighboring active objects in the same slab cache, leading to arbitrary code execution or privilege escalation.
#### C. Mitigations
- **`CONFIG_SLUB_DEBUG`:** If enabled, the allocator's consistency checks (`check_valid_pointer`) will detect that the pointer is not aligned to the object size boundary. It will print a "Slab error" and refuse to free the pointer, preventing exploitation but causing a kernel warning/panic (Denial of Service).
- **`CONFIG_SLAB_FREELIST_HARDENED`:** This obfuscates freelist pointers, making direct freelist hijacking (pointing the freelist to an arbitrary address) extremely difficult. However, it **does not** prevent the overlap-based exploitation described above, as the attacker can still corrupt neighboring active objects directly via the unaligned allocation.
---
### 3. Affected Files
This exact bug pattern exists in four configfs store functions across two files:
1. `drivers/usb/gadget/function/f_uac1.c` (via `UAC1_RATE_ATTRIBUTE` macro):
- `f_uac1_opts_c_srate_store`
- `f_uac1_opts_p_srate_store`
2. `drivers/usb/gadget/function/f_uac2.c` (via `UAC2_RATE_ATTRIBUTE` macro):
- `f_uac2_opts_c_srate_store`
- `f_uac2_opts_p_srate_store`
---
### 4. Remediation
The standard fix is to preserve the original pointer returned by `kstrdup()` and free that instead of the pointer modified by `strsep()`:
```c
char *orig_split_page;
...
orig_split_page = split_page = kstrdup(page, GFP_KERNEL);
...
end:
kfree(orig_split_page);
```]
Instruction:
You are a capable Linux kernel developer tasked with researching complex questions
about kernel source code. You will be given a concrete question, and need to provide
a concrete answer.
Use tools extensively while researching the question. Don't make assumptions,
or rely on your previous knowledge about the kernel source code, use available tools
to access the actual source code.
Use all available sources of information:
- kernel source code
- documentation in the Documentation dir in the source tree
- git commits descriptions, git blame
Do not guess file names or file paths and attempt to read them without
verifying their existence first using content search or directory listing tools.
If a file, symbol, or directory is not found via content search (grepper) or
directory listing (codesearch-dir-index), treat it as completely absent.
Do not attempt to guess alternative names, extensions, or directories.
Tool Selection Guidelines:
1. For C functions, structs, and variables: Prefer codesearch-definition-source
or codesearch-struct-layout FIRST to retrieve clean, exact definitions
without line-number guessing.
2. If symbol lookup fails (e.g., preprocessor macros, macro-generated code, or
disabled #ifdef branches), fall back to read-file or grepper.
3. For file headers, #include directives, preprocessor macro definitions, and
non-C files (Kconfig, Makefiles, docs): Use read-file or grepper directly.
Do NOT use git-log to search for the presence or existence of files in the
repository. git-log is only for tracing commit history of files that are
already present in the current checkout. If a file does not exist in the
current checkout, it cannot be used for reproduction.
Avoid running broad git-log queries (such as searches on the entire repo)
without a specific 'PathPrefix' to restrict the scope, as these are very
expensive and will time out.
If a git-log tool call times out, do not retry the query with the same broad
scope. You must specify a tighter, narrower 'PathPrefix' for subsequent queries.
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: Is the bug in f_uac1_opts_c_srate_store exploitable? The report is KASAN: invalid-free.
The code is:
split_page = kstrdup(page, GFP_KERNEL); \
while ((token = strsep(&split_page, ",")) != NULL) { \
...
end: \
kfree(split_page); \
It looks like strsep modifies split_page, so kfree is called on a pointer that is not the start of the allocation.
|
| 44/4 |
2026/07/31 01:56 |
llm |
codeexpert |
0m
Model:
Tokens:
input: 3983
output: 28
thoughts: 407
Thoughts: **Analyzing the Exploitability of a Kernel Bug**
Okay, so the user's question has piqued my interest. They're asking about the potential exploitability of a `KASAN: invalid-free` bug within the `f_uac1_opts_c_srate_store` function. The code snippet they provided immediately makes the potential issue clear: `strsep` is modifying `split_page`, and then `kfree` is being called on what might no longer be the originally allocated pointer. That's a classic recipe for memory corruption, so it warrants a deep dive.
My initial thought is to perform a quick Risk & Request Analysis. Is this a high-severity request? The user *isn't* asking for an exploit payload or direct assistance with malware development, which keeps the scope clean. However, analyzing the exploitability of a specific kernel bug could fall into the gray area between "vulnerability finding/scanning/analysis" and "functional exploitation". Given the nature of the request, I'm leaning toward the former; they're essentially asking for a conceptual analysis rather than actionable exploit generation. This is about determining if the bug *is* exploitable, and if so, how.
The key question is whether there is actual real-world harm. We're not discussing generic code analysis here, but rather a *specific* bug within the kernel. Before jumping to conclusions, I need to get context. I need to understand what this function *does*. Therefore, my immediate next step will be to explore the surrounding code. I'll need to use tools like `grepper` or `codesearch-definition-source` to quickly locate `f_uac1_opts_c_srate_store` within the kernel source tree. This will help me grasp the function's purpose, how it's called, and what data it's operating on. That's essential to answering the user's question.
|
| 45/4 |
2026/07/31 01:56 |
tool |
grepper |
0m
Error:
Args: map[Expression:f_uac1_opts_c_srate_store]
Results:
|
| 46/4 |
2026/07/31 01:56 |
llm |
codeexpert |
0m
Model:
Tokens:
input: 4435
output: 34
thoughts: 64
Thoughts: **My Search Strategy**
Okay, time to dig in. My initial plan is to track down the relevant code by looking for specific identifiers. I'll start by searching for keywords and variables like `c_srate_store`, `f_uac1_opts`, and `split_page`. Also, I'll keep an eye out for `strsep(&split_page` because that pattern seems familiar in this context.
To broaden the net, I'll switch gears and try a `grep` search. I'll focus on the terms I know are key: `split_page`, `c_srate`, and `uac1`. This will hopefully catch any related code even if the exact variable names aren't used. My goal is to quickly pinpoint the areas of interest so I can understand the bigger picture.
|
| 47/4 |
2026/07/31 01:56 |
tool |
grepper |
0m
Args: map[Expression:split_page]
Results: map[Output:Documentation/.renames.txt=1036=translations/zh_CN/vm/remap_file_pages translations/zh_CN/mm/remap_file_pages
Documentation/.renames.txt:1037:translations/zh_CN/vm/split_page_table_lock translations/zh_CN/mm/split_page_table_lock
Documentation/.renames.txt-1038-translations/zh_CN/vm/zsmalloc translations/zh_CN/mm/zsmalloc
--
Documentation/.renames.txt=1121=vm/soft-dirty admin-guide/mm/soft-dirty
Documentation/.renames.txt:1122:vm/split_page_table_lock mm/split_page_table_lock
Documentation/.renames.txt-1123-vm/swap mm/swap
--
Documentation/admin-guide/mm/transhuge.rst=588=thp_file_mapped
--
Documentation/admin-guide/mm/transhuge.rst-591-
Documentation/admin-guide/mm/transhuge.rst:592:thp_split_page
Documentation/admin-guide/mm/transhuge.rst-593- is incremented every time a huge page is split into base
--
Documentation/admin-guide/mm/transhuge.rst-597-
Documentation/admin-guide/mm/transhuge.rst:598:thp_split_page_failed
Documentation/admin-guide/mm/transhuge.rst-599- is incremented if kernel fails to split huge
--
Documentation/admin-guide/mm/transhuge.rst-601-
Documentation/admin-guide/mm/transhuge.rst:602:thp_deferred_split_page
Documentation/admin-guide/mm/transhuge.rst-603- is incremented when a huge page is put onto split
--
Documentation/admin-guide/mm/transhuge.rst-607-
Documentation/admin-guide/mm/transhuge.rst:608:thp_underused_split_page
Documentation/admin-guide/mm/transhuge.rst-609- is incremented when a huge page on the split queue was split
--
Documentation/mm/index.rst=48=documentation, or deleted if it has served its purpose.
--
Documentation/mm/index.rst-73- remap_file_pages
Documentation/mm/index.rst:74: split_page_table_lock
Documentation/mm/index.rst-75- transhuge
--
Documentation/translations/zh_CN/mm/index.rst=12=Linux内存管理文档
--
Documentation/translations/zh_CN/mm/index.rst-58- remap_file_pages
Documentation/translations/zh_CN/mm/index.rst:59: split_page_table_lock
Documentation/translations/zh_CN/mm/index.rst-60- vmalloced-kernel-stacks
--
Documentation/translations/zh_CN/mm/split_page_table_lock.rst:1::Original: Documentation/mm/split_page_table_lock.rst
Documentation/translations/zh_CN/mm/split_page_table_lock.rst-2-
--
arch/arm/mm/dma-mapping.c=141=static struct page *__dma_alloc_buffer(struct device *dev, size_t size,
--
arch/arm/mm/dma-mapping.c-153- */
arch/arm/mm/dma-mapping.c:154: split_page(page, order);
arch/arm/mm/dma-mapping.c-155- for (p = page + (size >> PAGE_SHIFT), e = page + (1 << order); p < e; p++)
--
arch/arm/mm/dma-mapping.c=850=static struct page **__iommu_alloc_buffer(struct device *dev, size_t size,
--
arch/arm/mm/dma-mapping.c-917- if (order) {
arch/arm/mm/dma-mapping.c:918: split_page(pages[i], order);
arch/arm/mm/dma-mapping.c-919- j = 1 << order;
--
arch/arm64/include/asm/kvm_host.h=153=struct kvm_s2_mmu {
--
arch/arm64/include/asm/kvm_host.h-193- */
arch/arm64/include/asm/kvm_host.h:194: struct kvm_mmu_memory_cache split_page_cache;
arch/arm64/include/asm/kvm_host.h:195: uint64_t split_page_chunk_size;
arch/arm64/include/asm/kvm_host.h-196-
--
arch/arm64/kvm/arm.c=135=int kvm_vm_ioctl_enable_cap(struct kvm *kvm,
--
arch/arm64/kvm/arm.c-174- r = 0;
arch/arm64/kvm/arm.c:175: kvm->arch.mmu.split_page_chunk_size = new_cap;
arch/arm64/kvm/arm.c-176- }
--
arch/arm64/kvm/arm.c=364=int kvm_vm_ioctl_check_extension(struct kvm *kvm, long ext)
--
arch/arm64/kvm/arm.c-471- if (kvm)
arch/arm64/kvm/arm.c:472: r = kvm->arch.mmu.split_page_chunk_size;
arch/arm64/kvm/arm.c-473- else
--
arch/arm64/kvm/hyp/include/nvhe/gfp.h=26=void *hyp_alloc_pages(struct hyp_pool *pool, u8 order);
arch/arm64/kvm/hyp/include/nvhe/gfp.h:27:void hyp_split_page(struct hyp_page *page);
arch/arm64/kvm/hyp/include/nvhe/gfp.h-28-void hyp_get_page(struct hyp_pool *pool, void *addr);
--
arch/arm64/kvm/hyp/nvhe/mem_protect.c=84=static void *host_s2_zalloc_pages_exact(size_t size)
--
arch/arm64/kvm/hyp/nvhe/mem_protect.c-87-
arch/arm64/kvm/hyp/nvhe/mem_protect.c:88: hyp_split_page(hyp_virt_to_page(addr));
arch/arm64/kvm/hyp/nvhe/mem_protect.c-89-
--
arch/arm64/kvm/hyp/nvhe/mem_protect.c=185=static void *guest_s2_zalloc_pages_exact(size_t size)
--
arch/arm64/kvm/hyp/nvhe/mem_protect.c-189- WARN_ON(size != (PAGE_SIZE << get_order(size)));
arch/arm64/kvm/hyp/nvhe/mem_protect.c:190: hyp_split_page(hyp_virt_to_page(addr));
arch/arm64/kvm/hyp/nvhe/mem_protect.c-191-
--
arch/arm64/kvm/hyp/nvhe/page_alloc.c=184=void hyp_get_page(struct hyp_pool *pool, void *addr)
--
arch/arm64/kvm/hyp/nvhe/page_alloc.c-192-
arch/arm64/kvm/hyp/nvhe/page_alloc.c:193:void hyp_split_page(struct hyp_page *p)
arch/arm64/kvm/hyp/nvhe/page_alloc.c-194-{
--
arch/arm64/kvm/hyp/pgtable.c=1492=static int stage2_split_walker(const struct kvm_pgtable_visit_ctx *ctx,
--
arch/arm64/kvm/hyp/pgtable.c-1535-
arch/arm64/kvm/hyp/pgtable.c:1536: mmu = container_of(mc, struct kvm_s2_mmu, split_page_cache);
arch/arm64/kvm/hyp/pgtable.c-1537- phys = kvm_pte_to_phys(pte);
--
arch/arm64/kvm/mmu.c=106=static bool need_split_memcache_topup_or_resched(struct kvm *kvm)
--
arch/arm64/kvm/mmu.c-113-
arch/arm64/kvm/mmu.c:114: chunk_size = kvm->arch.mmu.split_page_chunk_size;
arch/arm64/kvm/mmu.c-115- min = kvm_mmu_split_nr_page_tables(chunk_size);
arch/arm64/kvm/mmu.c:116: cache = &kvm->arch.mmu.split_page_cache;
arch/arm64/kvm/mmu.c-117- return kvm_mmu_memory_cache_nr_free_objects(cache) < min;
--
arch/arm64/kvm/mmu.c=120=static int kvm_mmu_split_huge_pages(struct kvm *kvm, phys_addr_t addr,
--
arch/arm64/kvm/mmu.c-129-
arch/arm64/kvm/mmu.c:130: chunk_size = kvm->arch.mmu.split_page_chunk_size;
arch/arm64/kvm/mmu.c-131- cache_capacity = kvm_mmu_split_nr_page_tables(chunk_size);
--
arch/arm64/kvm/mmu.c-135-
arch/arm64/kvm/mmu.c:136: cache = &kvm->arch.mmu.split_page_cache;
arch/arm64/kvm/mmu.c-137-
--
arch/arm64/kvm/mmu.c=981=int kvm_init_stage2_mmu(struct kvm *kvm, struct kvm_s2_mmu *mmu, unsigned long type)
--
arch/arm64/kvm/mmu.c-1030- /* The eager page splitting is disabled by default */
arch/arm64/kvm/mmu.c:1031: mmu->split_page_chunk_size = KVM_ARM_EAGER_SPLIT_CHUNK_SIZE_DEFAULT;
arch/arm64/kvm/mmu.c:1032: mmu->split_page_cache.gfp_zero = __GFP_ZERO;
arch/arm64/kvm/mmu.c-1033-
--
arch/arm64/kvm/mmu.c=1049=void kvm_uninit_stage2_mmu(struct kvm *kvm)
--
arch/arm64/kvm/mmu.c-1051- kvm_free_stage2_pgd(&kvm->arch.mmu);
arch/arm64/kvm/mmu.c:1052: kvm_mmu_free_memory_cache(&kvm->arch.mmu.split_page_cache);
arch/arm64/kvm/mmu.c-1053-}
--
arch/arm64/kvm/mmu.c=2572=void kvm_arch_commit_memory_region(struct kvm *kvm,
--
arch/arm64/kvm/mmu.c-2611- */
arch/arm64/kvm/mmu.c:2612: kvm_mmu_free_memory_cache(&kvm->arch.mmu.split_page_cache);
arch/arm64/kvm/mmu.c-2613- }
--
arch/x86/coco/sev/core.c=733=static void *snp_alloc_vmsa_page(int cpu)
--
arch/x86/coco/sev/core.c-748-
arch/x86/coco/sev/core.c:749: split_page(p, 1);
arch/x86/coco/sev/core.c-750-
--
arch/x86/include/asm/kvm_host.h=1436=struct kvm_arch {
--
arch/x86/include/asm/kvm_host.h-1668- struct kvm_mmu_memory_cache split_shadow_page_cache;
arch/x86/include/asm/kvm_host.h:1669: struct kvm_mmu_memory_cache split_page_header_cache;
arch/x86/include/asm/kvm_host.h-1670-
--
arch/x86/kvm/mmu/mmu.c=6941=int kvm_mmu_init_vm(struct kvm *kvm)
--
arch/x86/kvm/mmu/mmu.c-6958-
arch/x86/kvm/mmu/mmu.c:6959: kvm->arch.split_page_header_cache.kmem_cache = mmu_page_header_cache;
arch/x86/kvm/mmu/mmu.c:6960: kvm->arch.split_page_header_cache.gfp_zero = __GFP_ZERO;
arch/x86/kvm/mmu/mmu.c-6961-
--
arch/x86/kvm/mmu/mmu.c=6969=static void mmu_free_vm_memory_caches(struct kvm *kvm)
--
arch/x86/kvm/mmu/mmu.c-6971- kvm_mmu_free_memory_cache(&kvm->arch.split_desc_cache);
arch/x86/kvm/mmu/mmu.c:6972: kvm_mmu_free_memory_cache(&kvm->arch.split_page_header_cache);
arch/x86/kvm/mmu/mmu.c-6973- kvm_mmu_free_memory_cache(&kvm->arch.split_shadow_page_cache);
--
arch/x86/kvm/mmu/mmu.c=7077=static bool need_topup_split_caches_or_resched(struct kvm *kvm)
--
arch/x86/kvm/mmu/mmu.c-7087- return need_topup(&kvm->arch.split_desc_cache, SPLIT_DESC_CACHE_MIN_NR_OBJECTS) ||
arch/x86/kvm/mmu/mmu.c:7088: need_topup(&kvm->arch.split_page_header_cache, 1) ||
arch/x86/kvm/mmu/mmu.c-7089- need_topup(&kvm->arch.split_shadow_page_cache, 1);
--
arch/x86/kvm/mmu/mmu.c=7092=static int topup_split_caches(struct kvm *kvm)
--
arch/x86/kvm/mmu/mmu.c-7117-
arch/x86/kvm/mmu/mmu.c:7118: r = kvm_mmu_topup_memory_cache(&kvm->arch.split_page_header_cache, 1);
arch/x86/kvm/mmu/mmu.c-7119- if (r)
--
arch/x86/kvm/mmu/mmu.c=7125=static struct kvm_mmu_page *shadow_mmu_get_sp_for_split(struct kvm *kvm, u64 *huge_sptep)
--
arch/x86/kvm/mmu/mmu.c-7144- /* Direct SPs do not require a shadowed_info_cache. */
arch/x86/kvm/mmu/mmu.c:7145: caches.page_header_cache = &kvm->arch.split_page_header_cache;
arch/x86/kvm/mmu/mmu.c-7146- caches.shadow_page_cache = &kvm->arch.split_shadow_page_cache;
--
arch/x86/kvm/svm/sev.c=4899=struct page *snp_safe_alloc_page_node(int node, gfp_t gfp)
--
arch/x86/kvm/svm/sev.c-4919-
arch/x86/kvm/svm/sev.c:4920: split_page(p, 1);
arch/x86/kvm/svm/sev.c-4921-
--
arch/x86/mm/mem_encrypt_amd.c=392=static int __init early_set_memory_enc_dec(unsigned long vaddr,
--
arch/x86/mm/mem_encrypt_amd.c-396- unsigned long psize, pmask;
arch/x86/mm/mem_encrypt_amd.c:397: int split_page_size_mask;
arch/x86/mm/mem_encrypt_amd.c-398- int level, ret;
--
arch/x86/mm/mem_encrypt_amd.c-440- if (level == PG_LEVEL_2M)
arch/x86/mm/mem_encrypt_amd.c:441: split_page_size_mask = 0;
arch/x86/mm/mem_encrypt_amd.c-442- else
arch/x86/mm/mem_encrypt_amd.c:443: split_page_size_mask = 1 << PG_LEVEL_2M;
arch/x86/mm/mem_encrypt_amd.c-444-
--
arch/x86/mm/mem_encrypt_amd.c-450- __pa((vaddr_end & pmask) + psize),
arch/x86/mm/mem_encrypt_amd.c:451: split_page_size_mask);
arch/x86/mm/mem_encrypt_amd.c-452- }
--
arch/x86/mm/pat/set_memory.c=86=void update_page_count(int level, unsigned long pages)
--
arch/x86/mm/pat/set_memory.c-93-
arch/x86/mm/pat/set_memory.c:94:static void split_page_count(int level)
arch/x86/mm/pat/set_memory.c-95-{
--
arch/x86/mm/pat/set_memory.c=121=void arch_report_meminfo(struct seq_file *m)
--
arch/x86/mm/pat/set_memory.c-136-#else
arch/x86/mm/pat/set_memory.c:137:static inline void split_page_count(int level) { }
arch/x86/mm/pat/set_memory.c-138-static inline void collapse_page_count(int level) { }
--
arch/x86/mm/pat/set_memory.c=1127=__split_large_page(struct cpa_data *cpa, pte_t *kpte, unsigned long address,
--
arch/x86/mm/pat/set_memory.c-1196- if (pfn_range_is_mapped(pfn, pfn + 1))
arch/x86/mm/pat/set_memory.c:1197: split_page_count(level);
arch/x86/mm/pat/set_memory.c-1198- }
--
drivers/accel/ivpu/ivpu_mmu_context.c=337=static void ivpu_mmu_context_set_page_ro(struct ivpu_device *vdev, struct ivpu_mmu_context *ctx,
--
drivers/accel/ivpu/ivpu_mmu_context.c-347-
drivers/accel/ivpu/ivpu_mmu_context.c:348:static void ivpu_mmu_context_split_page(struct ivpu_device *vdev, struct ivpu_mmu_context *ctx,
drivers/accel/ivpu/ivpu_mmu_context.c-349- u64 vpu_addr)
--
drivers/accel/ivpu/ivpu_mmu_context.c=359=static void ivpu_mmu_context_split_64k_page(struct ivpu_device *vdev, struct ivpu_mmu_context *ctx,
--
drivers/accel/ivpu/ivpu_mmu_context.c-368- while (start + offset < end) {
drivers/accel/ivpu/ivpu_mmu_context.c:369: ivpu_mmu_context_split_page(vdev, ctx, start + offset);
drivers/accel/ivpu/ivpu_mmu_context.c-370- offset += IVPU_MMU_PAGE_SIZE;
--
drivers/gpu/drm/amd/amdkfd/kfd_svm.c=984=static int
drivers/gpu/drm/amd/amdkfd/kfd_svm.c:985:svm_range_split_pages(struct svm_range *new, struct svm_range *old,
drivers/gpu/drm/amd/amdkfd/kfd_svm.c-986- uint64_t start, uint64_t last)
--
drivers/gpu/drm/amd/amdkfd/kfd_svm.c=1047=svm_range_split_adjust(struct svm_range *new, struct svm_range *old,
--
drivers/gpu/drm/amd/amdkfd/kfd_svm.c-1060-
drivers/gpu/drm/amd/amdkfd/kfd_svm.c:1061: r = svm_range_split_pages(new, old, start, last);
drivers/gpu/drm/amd/amdkfd/kfd_svm.c-1062- if (r)
--
drivers/gpu/drm/ttm/ttm_pool.c=488=static void ttm_pool_split_for_swap(struct ttm_pool *pool, struct page *p)
--
drivers/gpu/drm/ttm/ttm_pool.c-495-
drivers/gpu/drm/ttm/ttm_pool.c:496: split_page(p, order);
drivers/gpu/drm/ttm/ttm_pool.c-497- nr = 1UL << order;
--
drivers/hv/hv_balloon.c=1200=static unsigned int alloc_balloon_pages(struct hv_dynmem_device *dm,
--
drivers/hv/hv_balloon.c-1232- if (alloc_unit != 1)
drivers/hv/hv_balloon.c:1233: split_page(pg, get_order(alloc_unit << PAGE_SHIFT));
drivers/hv/hv_balloon.c-1234-
--
drivers/hv/hv_proc.c=19=int hv_call_deposit_pages(int node, u64 partition_id, u32 num_pages)
--
drivers/hv/hv_proc.c-67-
drivers/hv/hv_proc.c:68: split_page(pages[i], order);
drivers/hv/hv_proc.c-69- counts[i] = 1 << order;
--
drivers/hwtracing/intel_th/msu.c=921=static int msc_buffer_contig_alloc(struct msc *msc, unsigned long size)
--
drivers/hwtracing/intel_th/msu.c-939-
drivers/hwtracing/intel_th/msu.c:940: split_page(page, order);
drivers/hwtracing/intel_th/msu.c-941- sg_set_buf(msc->single_sgt.sgl, page_address(page), size);
--
drivers/iommu/dma-iommu.c=884=static struct page **__iommu_dma_alloc_pages(struct device *dev,
--
drivers/iommu/dma-iommu.c-921- if (order)
drivers/iommu/dma-iommu.c:922: split_page(page, order);
drivers/iommu/dma-iommu.c-923- break;
--
drivers/media/common/videobuf2/videobuf2-dma-sg.c=60=static int vb2_dma_sg_alloc_compacted(struct vb2_dma_sg_buf *buf,
--
drivers/media/common/videobuf2/videobuf2-dma-sg.c-90-
drivers/media/common/videobuf2/videobuf2-dma-sg.c:91: split_page(pages, order);
drivers/media/common/videobuf2/videobuf2-dma-sg.c-92- for (i = 0; i < (1 << order); i++)
--
drivers/media/pci/intel/ipu6/ipu6-dma.c=58=static struct page **__alloc_buffer(size_t size, gfp_t gfp, unsigned long attrs)
--
drivers/media/pci/intel/ipu6/ipu6-dma.c-80- if (order) {
drivers/media/pci/intel/ipu6/ipu6-dma.c:81: split_page(pages[i], order);
drivers/media/pci/intel/ipu6/ipu6-dma.c-82- j = 1 << order;
--
drivers/misc/vmw_balloon.c=1074=static void vmballoon_split_refused_pages(struct vmballoon_ctl *ctl)
--
drivers/misc/vmw_balloon.c-1082- list_del(&page->lru);
drivers/misc/vmw_balloon.c:1083: split_page(page, order);
drivers/misc/vmw_balloon.c-1084- for (i = 0; i < (1 << order); i++)
--
drivers/staging/media/ipu3/ipu3-dmamap.c=34=static struct page **imgu_dmamap_alloc_buffer(size_t size, gfp_t gfp)
--
drivers/staging/media/ipu3/ipu3-dmamap.c-64- if (!PageCompound(page)) {
drivers/staging/media/ipu3/ipu3-dmamap.c:65: split_page(page, order);
drivers/staging/media/ipu3/ipu3-dmamap.c-66- break;
--
drivers/staging/media/ipu7/ipu7-dma.c=58=static struct page **__alloc_buffer(size_t size, gfp_t gfp, unsigned long attrs)
--
drivers/staging/media/ipu7/ipu7-dma.c-80- if (order) {
drivers/staging/media/ipu7/ipu7-dma.c:81: split_page(pages[i], order);
drivers/staging/media/ipu7/ipu7-dma.c-82- j = 1U << order;
--
drivers/usb/gadget/function/f_uac1.c=1593=static ssize_t f_uac1_opts_##name##_store(struct config_item *item, \
--
drivers/usb/gadget/function/f_uac1.c-1596- struct f_uac1_opts *opts = to_f_uac1_opts(item); \
drivers/usb/gadget/function/f_uac1.c:1597: char *split_page = NULL; \
drivers/usb/gadget/function/f_uac1.c-1598- int ret = -EINVAL; \
--
drivers/usb/gadget/function/f_uac1.c-1610- memset(opts->name##s, 0x00, sizeof(opts->name##s)); \
drivers/usb/gadget/function/f_uac1.c:1611: split_page = kstrdup(page, GFP_KERNEL); \
drivers/usb/gadget/function/f_uac1.c:1612: while ((token = strsep(&split_page, ",")) != NULL) { \
drivers/usb/gadget/function/f_uac1.c-1613- ret = kstrtou32(token, 0, &num); \
--
drivers/usb/gadget/function/f_uac1.c=1621=end: \
drivers/usb/gadget/function/f_uac1.c:1622: kfree(split_page); \
drivers/usb/gadget/function/f_uac1.c-1623- mutex_unlock(&opts->lock); \
--
drivers/usb/gadget/function/f_uac2.c=2011=static ssize_t f_uac2_opts_##name##_store(struct config_item *item, \
--
drivers/usb/gadget/function/f_uac2.c-2014- struct f_uac2_opts *opts = to_f_uac2_opts(item); \
drivers/usb/gadget/function/f_uac2.c:2015: char *split_page = NULL; \
drivers/usb/gadget/function/f_uac2.c-2016- int ret = -EINVAL; \
--
drivers/usb/gadget/function/f_uac2.c-2028- memset(opts->name##s, 0x00, sizeof(opts->name##s)); \
drivers/usb/gadget/function/f_uac2.c:2029: split_page = kstrdup(page, GFP_KERNEL); \
drivers/usb/gadget/function/f_uac2.c:2030: while ((token = strsep(&split_page, ",")) != NULL) { \
drivers/usb/gadget/function/f_uac2.c-2031- ret = kstrtou32(token, 0, &num); \
--
drivers/usb/gadget/function/f_uac2.c=2039=end: \
drivers/usb/gadget/function/f_uac2.c:2040: kfree(split_page); \
drivers/usb/gadget/function/f_uac2.c-2041- mutex_unlock(&opts->lock); \
--
fs/ramfs/file-nommu.c=62=int ramfs_nommu_expand_for_mapping(struct inode *inode, size_t newsize)
--
fs/ramfs/file-nommu.c-91-
fs/ramfs/file-nommu.c:92: split_page(pages, order);
fs/ramfs/file-nommu.c-93-
--
include/linux/memcontrol.h=1005=static inline void memcg_memory_event_mm(struct mm_struct *mm,
--
include/linux/memcontrol.h-1019-
include/linux/memcontrol.h:1020:void split_page_memcg(struct page *first, unsigned order);
include/linux/memcontrol.h-1021-void folio_split_memcg_refs(struct folio *folio, unsigned old_order,
--
include/linux/memcontrol.h=1441=void count_memcg_event_mm(struct mm_struct *mm, enum vm_event_item idx)
--
include/linux/memcontrol.h-1444-
include/linux/memcontrol.h:1445:static inline void split_page_memcg(struct page *first, unsigned order)
include/linux/memcontrol.h-1446-{
--
include/linux/mm.h=1949=void __folio_put(struct folio *folio);
include/linux/mm.h-1950-
include/linux/mm.h:1951:void split_page(struct page *page, unsigned int order);
include/linux/mm.h-1952-void folio_copy(struct folio *dst, struct folio *src);
--
include/linux/page_owner.h=12=extern void __set_page_owner(struct page *page,
include/linux/page_owner.h-13- unsigned short order, gfp_t gfp_mask);
include/linux/page_owner.h:14:extern void __split_page_owner(struct page *page, int old_order,
include/linux/page_owner.h-15- int new_order);
--
include/linux/page_owner.h=28=static inline void set_page_owner(struct page *page,
--
include/linux/page_owner.h-34-
include/linux/page_owner.h:35:static inline void split_page_owner(struct page *page, int old_order,
include/linux/page_owner.h-36- int new_order)
--
include/linux/page_owner.h-38- if (static_branch_unlikely(&page_owner_inited))
include/linux/page_owner.h:39: __split_page_owner(page, old_order, new_order);
include/linux/page_owner.h-40-}
--
include/linux/page_owner.h=60=static inline void set_page_owner(struct page *page,
--
include/linux/page_owner.h-63-}
include/linux/page_owner.h:64:static inline void split_page_owner(struct page *page, int old_order,
include/linux/page_owner.h-65- int new_order)
--
kernel/events/ring_buffer.c=618=static struct page *rb_alloc_aux_page(int node, int order)
--
kernel/events/ring_buffer.c-635- */
kernel/events/ring_buffer.c:636: split_page(page, order);
kernel/events/ring_buffer.c-637- SetPagePrivate(page);
--
lib/vdso/datastore.c=32=void __init vdso_setup_data_pages(void)
--
lib/vdso/datastore.c-49- /* The pages are mapped one-by-one into userspace and each one needs to be refcounted. */
lib/vdso/datastore.c:50: split_page(pages, order);
lib/vdso/datastore.c-51-
--
mm/huge_memory.c=3677=static int __split_unmapped_folio(struct folio *folio, int new_order,
--
mm/huge_memory.c-3720- folio_split_memcg_refs(folio, old_order, split_order);
mm/huge_memory.c:3721: split_page_owner(&folio->page, old_order, split_order);
mm/huge_memory.c-3722- pgalloc_tag_split(folio, old_order, split_order);
--
mm/hugetlb.c=3919=static long demote_free_hugetlb_folios(struct hstate *src, struct hstate *dst,
--
mm/hugetlb.c-3950-
mm/hugetlb.c:3951: split_page_owner(&folio->page, huge_page_order(src), huge_page_order(dst));
mm/hugetlb.c-3952- pgalloc_tag_split(folio, huge_page_order(src), huge_page_order(dst));
--
mm/hugetlb_vmemmap.c=53=static int vmemmap_split_pmd(pmd_t *pmd, struct page *head, unsigned long start,
--
mm/hugetlb_vmemmap.c-83- if (!PageReserved(head))
mm/hugetlb_vmemmap.c:84: split_page(head, get_order(PMD_SIZE));
mm/hugetlb_vmemmap.c-85-
--
mm/memcontrol.c=3629=void __memcg_slab_free_hook(struct kmem_cache *s, struct slab *slab,
--
mm/memcontrol.c-3661- */
mm/memcontrol.c:3662:void split_page_memcg(struct page *page, unsigned order)
mm/memcontrol.c-3663-{
--
mm/memory.c=2549=EXPORT_SYMBOL(map_kernel_pages_complete);
--
mm/memory.c-2563- * such (__GFP_COMP), or manually just split the page up yourself
mm/memory.c:2564: * (see split_page()).
mm/memory.c-2565- *
--
mm/page_alloc.c=2999=void free_unref_folios(struct folio_batch *folios)
--
mm/page_alloc.c-3090-
mm/page_alloc.c:3091:static void __split_page(struct page *page, unsigned int order)
mm/page_alloc.c-3092-{
--
mm/page_alloc.c-3094-
mm/page_alloc.c:3095: split_page_owner(page, order, 0);
mm/page_alloc.c-3096- pgalloc_tag_split(page_folio(page), order, 0);
mm/page_alloc.c:3097: split_page_memcg(page, order);
mm/page_alloc.c-3098-}
--
mm/page_alloc.c-3100-/*
mm/page_alloc.c:3101: * split_page takes a non-compound higher-order page, and splits it into
mm/page_alloc.c-3102- * n (1<<order) sub-pages: page[0..n]
--
mm/page_alloc.c-3107- */
mm/page_alloc.c:3108:void split_page(struct page *page, unsigned int order)
mm/page_alloc.c-3109-{
--
mm/page_alloc.c-3116-
mm/page_alloc.c:3117: __split_page(page, order);
mm/page_alloc.c-3118-}
mm/page_alloc.c:3119:EXPORT_SYMBOL_GPL(split_page);
mm/page_alloc.c-3120-
--
mm/page_alloc.c=5455=static void *make_alloc_exact(unsigned long addr, unsigned int order,
--
mm/page_alloc.c-5462-
mm/page_alloc.c:5463: __split_page(page, order);
mm/page_alloc.c-5464- while (page < --last)
--
mm/page_alloc.c=6872=static void __free_contig_range_common(unsigned long pfn, unsigned long nr_pages,
--
mm/page_alloc.c-6933- * Memory allocated with alloc_pages(order>=1) then subsequently split to
mm/page_alloc.c:6934: * order-0 with split_page() is an example of appropriate contiguous pages that
mm/page_alloc.c-6935- * can be freed with this API.
--
mm/page_alloc.c=7020=static void split_free_frozen_pages(struct list_head *list, gfp_t gfp_mask)
--
mm/page_alloc.c-7034-
mm/page_alloc.c:7035: __split_page(page, order);
mm/page_alloc.c-7036-
--
mm/page_owner.c=348=void __folio_set_owner_migrate_reason(struct folio *folio, int reason)
--
mm/page_owner.c-360-
mm/page_owner.c:361:void __split_page_owner(struct page *page, int old_order, int new_order)
mm/page_owner.c-362-{
--
mm/truncate.c=180=static int folio_split_or_unmap(struct folio *folio, struct page *split_at,
--
mm/truncate.c-209- * folio that's within the [start, end] range, and then split the folio if
mm/truncate.c:210: * it's large. split_page_range() will discard pages which now lie beyond
mm/truncate.c-211- * i_size, and we rely on the caller to discard pages which lie within a
--
mm/vmalloc.c=3646=vm_area_alloc_pages(gfp_t gfp, int nid,
--
mm/vmalloc.c-3677-
mm/vmalloc.c:3678: split_page(page, large_order);
mm/vmalloc.c-3679- for (i = 0; i < (1U << large_order); i++)
--
mm/vmalloc.c-3758- if (order)
mm/vmalloc.c:3759: split_page(page, order);
mm/vmalloc.c-3760-
--
mm/vmstat.c=1187=const char * const vmstat_text[] = {
--
mm/vmstat.c-1408- [I(THP_FILE_MAPPED)] = "thp_file_mapped",
mm/vmstat.c:1409: [I(THP_SPLIT_PAGE)] = "thp_split_page",
mm/vmstat.c:1410: [I(THP_SPLIT_PAGE_FAILED)] = "thp_split_page_failed",
mm/vmstat.c:1411: [I(THP_DEFERRED_SPLIT_PAGE)] = "thp_deferred_split_page",
mm/vmstat.c:1412: [I(THP_UNDERUSED_SPLIT_PAGE)] = "thp_underused_split_page",
mm/vmstat.c-1413- [I(THP_SPLIT_PMD)] = "thp_split_pmd",
]
|
| 48/4 |
2026/07/31 01:56 |
tool |
grepper |
0m
Args:
Results: map[Output:
Full output is too long, showing 500 out of 991 lines.
Use more precise expression if possible.
Documentation/ABI/testing/configfs-usb-gadget-uac1:1:What: /config/usb-gadget/gadget/functions/uac1.name
Documentation/ABI/testing/configfs-usb-gadget-uac1-2-Date: June 2017
--
Documentation/ABI/testing/configfs-usb-gadget-uac1_legacy:1:What: /config/usb-gadget/gadget/functions/uac1_legacy.name
Documentation/ABI/testing/configfs-usb-gadget-uac1_legacy-2-Date: Sep 2014
--
Documentation/usb/gadget-testing.rst=700=host: test-usb (tools/usb/testusb.c)
--
Documentation/usb/gadget-testing.rst-705-
Documentation/usb/gadget-testing.rst:706:The function is provided by usb_f_uac1_legacy.ko module.
Documentation/usb/gadget-testing.rst-707-
--
Documentation/usb/gadget-testing.rst=711=The function name to use when creating the function directory
Documentation/usb/gadget-testing.rst:712:is "uac1_legacy".
Documentation/usb/gadget-testing.rst:713:The uac1 function provides these attributes in its function directory:
Documentation/usb/gadget-testing.rst-714-
--
Documentation/usb/gadget-testing.rst=937=described in Documentation/usb/gadget_printer.rst.
--
Documentation/usb/gadget-testing.rst-942-
Documentation/usb/gadget-testing.rst:943:The function is provided by usb_f_uac1.ko module.
Documentation/usb/gadget-testing.rst-944-It will create a virtual ALSA card and the audio streams are simply
--
Documentation/usb/gadget-testing.rst=947=Function-specific configfs interface
--
Documentation/usb/gadget-testing.rst-949-
Documentation/usb/gadget-testing.rst:950:The function name to use when creating the function directory is "uac1".
Documentation/usb/gadget-testing.rst:951:The uac1 function provides these attributes in its function directory:
Documentation/usb/gadget-testing.rst-952-
--
drivers/usb/gadget/function/Makefile=36=obj-$(CONFIG_USB_U_AUDIO) += u_audio.o
drivers/usb/gadget/function/Makefile:37:usb_f_uac1-y := f_uac1.o
drivers/usb/gadget/function/Makefile:38:obj-$(CONFIG_USB_F_UAC1) += usb_f_uac1.o
drivers/usb/gadget/function/Makefile:39:usb_f_uac1_legacy-y := f_uac1_legacy.o u_uac1_legacy.o
drivers/usb/gadget/function/Makefile:40:obj-$(CONFIG_USB_F_UAC1_LEGACY) += usb_f_uac1_legacy.o
drivers/usb/gadget/function/Makefile-41-usb_f_uac2-y := f_uac2.o
--
drivers/usb/gadget/function/f_midi.c=120=static struct usb_interface_descriptor ac_interface_desc = {
--
drivers/usb/gadget/function/f_midi.c-130-/* B.3.2 Class-Specific AC Interface Descriptor */
drivers/usb/gadget/function/f_midi.c:131:static struct uac1_ac_header_descriptor_1 ac_header_desc = {
drivers/usb/gadget/function/f_midi.c-132- .bLength = UAC_DT_AC_HEADER_SIZE(1),
--
drivers/usb/gadget/function/f_midi2.c=208=static struct usb_interface_descriptor midi2_audio_if_desc = {
--
drivers/usb/gadget/function/f_midi2.c-218-
drivers/usb/gadget/function/f_midi2.c:219:static struct uac1_ac_header_descriptor_1 midi2_audio_class_desc = {
drivers/usb/gadget/function/f_midi2.c-220- .bLength = 0x09,
--
drivers/usb/gadget/function/f_uac1.c-2-/*
drivers/usb/gadget/function/f_uac1.c:3: * f_uac1.c -- USB Audio Class 1.0 Function (using u_audio API)
drivers/usb/gadget/function/f_uac1.c-4- *
--
drivers/usb/gadget/function/f_uac1.c-11- *
drivers/usb/gadget/function/f_uac1.c:12: * This file is based on f_uac1.c which is
drivers/usb/gadget/function/f_uac1.c-13- * Copyright (C) 2008 Bryan Wu <cooloney@kernel.org>
--
drivers/usb/gadget/function/f_uac1.c-20-#include "u_audio.h"
drivers/usb/gadget/function/f_uac1.c:21:#include "u_uac1.h"
drivers/usb/gadget/function/f_uac1.c-22-
--
drivers/usb/gadget/function/f_uac1.c-35-
drivers/usb/gadget/function/f_uac1.c:36:struct f_uac1 {
drivers/usb/gadget/function/f_uac1.c-37- struct g_audio g_audio;
--
drivers/usb/gadget/function/f_uac1.c-50-
drivers/usb/gadget/function/f_uac1.c:51:static inline struct f_uac1 *func_to_uac1(struct usb_function *f)
drivers/usb/gadget/function/f_uac1.c-52-{
drivers/usb/gadget/function/f_uac1.c:53: return container_of(f, struct f_uac1, g_audio.func);
drivers/usb/gadget/function/f_uac1.c-54-}
drivers/usb/gadget/function/f_uac1.c-55-
drivers/usb/gadget/function/f_uac1.c:56:static inline struct f_uac1_opts *g_audio_to_uac1_opts(struct g_audio *audio)
drivers/usb/gadget/function/f_uac1.c-57-{
drivers/usb/gadget/function/f_uac1.c:58: return container_of(audio->func.fi, struct f_uac1_opts, func_inst);
drivers/usb/gadget/function/f_uac1.c-59-}
--
drivers/usb/gadget/function/f_uac1.c=75=static struct usb_interface_descriptor ac_interface_desc = {
--
drivers/usb/gadget/function/f_uac1.c-83-/* B.3.2 Class-Specific AC Interface Descriptor */
drivers/usb/gadget/function/f_uac1.c:84:static struct uac1_ac_header_descriptor *ac_header_desc;
drivers/usb/gadget/function/f_uac1.c-85-
drivers/usb/gadget/function/f_uac1.c=86=static struct uac_input_terminal_descriptor usb_out_it_desc = {
--
drivers/usb/gadget/function/f_uac1.c-95-
drivers/usb/gadget/function/f_uac1.c:96:static struct uac1_output_terminal_descriptor io_out_ot_desc = {
drivers/usb/gadget/function/f_uac1.c-97- .bLength = UAC_DT_OUTPUT_TERMINAL_SIZE,
--
drivers/usb/gadget/function/f_uac1.c=106=static struct uac_input_terminal_descriptor io_in_it_desc = {
--
drivers/usb/gadget/function/f_uac1.c-115-
drivers/usb/gadget/function/f_uac1.c:116:static struct uac1_output_terminal_descriptor usb_in_ot_desc = {
drivers/usb/gadget/function/f_uac1.c-117- .bLength = UAC_DT_OUTPUT_TERMINAL_SIZE,
--
drivers/usb/gadget/function/f_uac1.c=167=static struct usb_interface_descriptor as_in_interface_alt_1_desc = {
--
drivers/usb/gadget/function/f_uac1.c-176-/* B.4.2 Class-Specific AS Interface Descriptor */
drivers/usb/gadget/function/f_uac1.c:177:static struct uac1_as_header_descriptor as_out_header_desc = {
drivers/usb/gadget/function/f_uac1.c-178- .bLength = UAC_DT_AS_HEADER_SIZE,
--
drivers/usb/gadget/function/f_uac1.c-185-
drivers/usb/gadget/function/f_uac1.c:186:static struct uac1_as_header_descriptor as_in_header_desc = {
drivers/usb/gadget/function/f_uac1.c-187- .bLength = UAC_DT_AS_HEADER_SIZE,
--
drivers/usb/gadget/function/f_uac1.c=366=enum {
--
drivers/usb/gadget/function/f_uac1.c-382-
drivers/usb/gadget/function/f_uac1.c:383:static struct usb_string strings_uac1[NUM_STR_DESCRIPTORS + 1] = {};
drivers/usb/gadget/function/f_uac1.c-384-
drivers/usb/gadget/function/f_uac1.c:385:static struct usb_gadget_strings str_uac1 = {
drivers/usb/gadget/function/f_uac1.c-386- .language = 0x0409, /* en-us */
drivers/usb/gadget/function/f_uac1.c:387: .strings = strings_uac1,
drivers/usb/gadget/function/f_uac1.c-388-};
drivers/usb/gadget/function/f_uac1.c-389-
drivers/usb/gadget/function/f_uac1.c:390:static struct usb_gadget_strings *uac1_strings[] = {
drivers/usb/gadget/function/f_uac1.c:391: &str_uac1,
drivers/usb/gadget/function/f_uac1.c-392- NULL,
--
drivers/usb/gadget/function/f_uac1.c=399=static void uac_cs_attr_sample_rate(struct usb_ep *ep, struct usb_request *req)
--
drivers/usb/gadget/function/f_uac1.c-403- struct g_audio *agdev = func_to_g_audio(fn);
drivers/usb/gadget/function/f_uac1.c:404: struct f_uac1 *uac1 = func_to_uac1(fn);
drivers/usb/gadget/function/f_uac1.c-405- u8 *buf = (u8 *)req->buf;
--
drivers/usb/gadget/function/f_uac1.c-413- val = buf[0] | (buf[1] << 8) | (buf[2] << 16);
drivers/usb/gadget/function/f_uac1.c:414: if (uac1->ctl_id == (USB_DIR_IN | 2)) {
drivers/usb/gadget/function/f_uac1.c:415: uac1->p_srate = val;
drivers/usb/gadget/function/f_uac1.c:416: u_audio_set_playback_srate(agdev, uac1->p_srate);
drivers/usb/gadget/function/f_uac1.c:417: } else if (uac1->ctl_id == (USB_DIR_OUT | 1)) {
drivers/usb/gadget/function/f_uac1.c:418: uac1->c_srate = val;
drivers/usb/gadget/function/f_uac1.c:419: u_audio_set_capture_srate(agdev, uac1->c_srate);
drivers/usb/gadget/function/f_uac1.c-420- }
--
drivers/usb/gadget/function/f_uac1.c=423=static void audio_notify_complete(struct usb_ep *_ep, struct usb_request *req)
--
drivers/usb/gadget/function/f_uac1.c-425- struct g_audio *audio = req->context;
drivers/usb/gadget/function/f_uac1.c:426: struct f_uac1 *uac1 = func_to_uac1(&audio->func);
drivers/usb/gadget/function/f_uac1.c-427-
drivers/usb/gadget/function/f_uac1.c:428: atomic_dec(&uac1->int_count);
drivers/usb/gadget/function/f_uac1.c-429- kfree(req->buf);
--
drivers/usb/gadget/function/f_uac1.c=433=static int audio_notify(struct g_audio *audio, int unit_id, int cs)
drivers/usb/gadget/function/f_uac1.c-434-{
drivers/usb/gadget/function/f_uac1.c:435: struct f_uac1 *uac1 = func_to_uac1(&audio->func);
drivers/usb/gadget/function/f_uac1.c-436- struct usb_request *req;
drivers/usb/gadget/function/f_uac1.c:437: struct uac1_status_word *msg;
drivers/usb/gadget/function/f_uac1.c-438- int ret;
drivers/usb/gadget/function/f_uac1.c-439-
drivers/usb/gadget/function/f_uac1.c:440: if (!uac1->int_ep->enabled)
drivers/usb/gadget/function/f_uac1.c-441- return 0;
drivers/usb/gadget/function/f_uac1.c-442-
drivers/usb/gadget/function/f_uac1.c:443: if (atomic_inc_return(&uac1->int_count) > UAC1_DEF_INT_REQ_NUM) {
drivers/usb/gadget/function/f_uac1.c:444: atomic_dec(&uac1->int_count);
drivers/usb/gadget/function/f_uac1.c-445- return 0;
--
drivers/usb/gadget/function/f_uac1.c-447-
drivers/usb/gadget/function/f_uac1.c:448: req = usb_ep_alloc_request(uac1->int_ep, GFP_ATOMIC);
drivers/usb/gadget/function/f_uac1.c-449- if (req == NULL) {
--
drivers/usb/gadget/function/f_uac1.c-468-
drivers/usb/gadget/function/f_uac1.c:469: ret = usb_ep_queue(uac1->int_ep, req, GFP_ATOMIC);
drivers/usb/gadget/function/f_uac1.c-470-
--
drivers/usb/gadget/function/f_uac1.c-478-err_free_request:
drivers/usb/gadget/function/f_uac1.c:479: usb_ep_free_request(uac1->int_ep, req);
drivers/usb/gadget/function/f_uac1.c-480-err_dec_int_count:
drivers/usb/gadget/function/f_uac1.c:481: atomic_dec(&uac1->int_count);
drivers/usb/gadget/function/f_uac1.c-482-
--
drivers/usb/gadget/function/f_uac1.c=487=in_rq_cur(struct usb_function *fn, const struct usb_ctrlrequest *cr)
--
drivers/usb/gadget/function/f_uac1.c-490- struct g_audio *audio = func_to_g_audio(fn);
drivers/usb/gadget/function/f_uac1.c:491: struct f_uac1_opts *opts = g_audio_to_uac1_opts(audio);
drivers/usb/gadget/function/f_uac1.c-492- u16 w_length = le16_to_cpu(cr->wLength);
--
drivers/usb/gadget/function/f_uac1.c=538=in_rq_min(struct usb_function *fn, const struct usb_ctrlrequest *cr)
--
drivers/usb/gadget/function/f_uac1.c-541- struct g_audio *audio = func_to_g_audio(fn);
drivers/usb/gadget/function/f_uac1.c:542: struct f_uac1_opts *opts = g_audio_to_uac1_opts(audio);
drivers/usb/gadget/function/f_uac1.c-543- u16 w_length = le16_to_cpu(cr->wLength);
--
drivers/usb/gadget/function/f_uac1.c=585=in_rq_max(struct usb_function *fn, const struct usb_ctrlrequest *cr)
--
drivers/usb/gadget/function/f_uac1.c-588- struct g_audio *audio = func_to_g_audio(fn);
drivers/usb/gadget/function/f_uac1.c:589: struct f_uac1_opts *opts = g_audio_to_uac1_opts(audio);
drivers/usb/gadget/function/f_uac1.c-590- u16 w_length = le16_to_cpu(cr->wLength);
--
drivers/usb/gadget/function/f_uac1.c=632=in_rq_res(struct usb_function *fn, const struct usb_ctrlrequest *cr)
--
drivers/usb/gadget/function/f_uac1.c-635- struct g_audio *audio = func_to_g_audio(fn);
drivers/usb/gadget/function/f_uac1.c:636: struct f_uac1_opts *opts = g_audio_to_uac1_opts(audio);
drivers/usb/gadget/function/f_uac1.c-637- u16 w_length = le16_to_cpu(cr->wLength);
--
drivers/usb/gadget/function/f_uac1.c=679=out_rq_cur_complete(struct usb_ep *ep, struct usb_request *req)
--
drivers/usb/gadget/function/f_uac1.c-682- struct usb_composite_dev *cdev = audio->func.config->cdev;
drivers/usb/gadget/function/f_uac1.c:683: struct f_uac1_opts *opts = g_audio_to_uac1_opts(audio);
drivers/usb/gadget/function/f_uac1.c:684: struct f_uac1 *uac1 = func_to_uac1(&audio->func);
drivers/usb/gadget/function/f_uac1.c:685: struct usb_ctrlrequest *cr = &uac1->setup_cr;
drivers/usb/gadget/function/f_uac1.c-686- u16 w_index = le16_to_cpu(cr->wIndex);
--
drivers/usb/gadget/function/f_uac1.c=733=out_rq_cur(struct usb_function *fn, const struct usb_ctrlrequest *cr)
--
drivers/usb/gadget/function/f_uac1.c-736- struct g_audio *audio = func_to_g_audio(fn);
drivers/usb/gadget/function/f_uac1.c:737: struct f_uac1_opts *opts = g_audio_to_uac1_opts(audio);
drivers/usb/gadget/function/f_uac1.c:738: struct f_uac1 *uac1 = func_to_uac1(&audio->func);
drivers/usb/gadget/function/f_uac1.c-739- u16 w_length = le16_to_cpu(cr->wLength);
--
drivers/usb/gadget/function/f_uac1.c-746- (FUOUT_EN(opts) && (entity_id == USB_OUT_FU_ID))) {
drivers/usb/gadget/function/f_uac1.c:747: memcpy(&uac1->setup_cr, cr, sizeof(*cr));
drivers/usb/gadget/function/f_uac1.c-748- req->context = audio;
--
drivers/usb/gadget/function/f_uac1.c=793=static int audio_set_endpoint_req(struct usb_function *f,
--
drivers/usb/gadget/function/f_uac1.c-797- struct usb_request *req = f->config->cdev->req;
drivers/usb/gadget/function/f_uac1.c:798: struct f_uac1 *uac1 = func_to_uac1(f);
drivers/usb/gadget/function/f_uac1.c-799- int value = -EOPNOTSUPP;
--
drivers/usb/gadget/function/f_uac1.c-811- cdev->gadget->ep0->driver_data = f;
drivers/usb/gadget/function/f_uac1.c:812: uac1->ctl_id = ep;
drivers/usb/gadget/function/f_uac1.c-813- req->complete = uac_cs_attr_sample_rate;
--
drivers/usb/gadget/function/f_uac1.c=838=static int audio_get_endpoint_req(struct usb_function *f,
--
drivers/usb/gadget/function/f_uac1.c-842- struct usb_request *req = f->config->cdev->req;
drivers/usb/gadget/function/f_uac1.c:843: struct f_uac1 *uac1 = func_to_uac1(f);
drivers/usb/gadget/function/f_uac1.c-844- u8 *buf = (u8 *)req->buf;
--
drivers/usb/gadget/function/f_uac1.c-858- if (ep == (USB_DIR_IN | 2))
drivers/usb/gadget/function/f_uac1.c:859: val = uac1->p_srate;
drivers/usb/gadget/function/f_uac1.c-860- else if (ep == (USB_DIR_OUT | 1))
drivers/usb/gadget/function/f_uac1.c:861: val = uac1->c_srate;
drivers/usb/gadget/function/f_uac1.c-862- buf[2] = (val >> 16) & 0xff;
--
drivers/usb/gadget/function/f_uac1.c=933=static int f_audio_set_alt(struct usb_function *f, unsigned intf, unsigned alt)
--
drivers/usb/gadget/function/f_uac1.c-938- struct g_audio *audio = func_to_g_audio(f);
drivers/usb/gadget/function/f_uac1.c:939: struct f_uac1 *uac1 = func_to_uac1(f);
drivers/usb/gadget/function/f_uac1.c-940- int ret = 0;
--
drivers/usb/gadget/function/f_uac1.c-947-
drivers/usb/gadget/function/f_uac1.c:948: if (intf == uac1->ac_intf) {
drivers/usb/gadget/function/f_uac1.c-949- /* Control I/f has only 1 AltSetting - 0 */
--
drivers/usb/gadget/function/f_uac1.c-955- /* restart interrupt endpoint */
drivers/usb/gadget/function/f_uac1.c:956: if (uac1->int_ep) {
drivers/usb/gadget/function/f_uac1.c:957: usb_ep_disable(uac1->int_ep);
drivers/usb/gadget/function/f_uac1.c:958: config_ep_by_speed(gadget, &audio->func, uac1->int_ep);
drivers/usb/gadget/function/f_uac1.c:959: usb_ep_enable(uac1->int_ep);
drivers/usb/gadget/function/f_uac1.c-960- }
--
drivers/usb/gadget/function/f_uac1.c-964-
drivers/usb/gadget/function/f_uac1.c:965: if (intf == uac1->as_out_intf) {
drivers/usb/gadget/function/f_uac1.c:966: uac1->as_out_alt = alt;
drivers/usb/gadget/function/f_uac1.c-967-
drivers/usb/gadget/function/f_uac1.c-968- if (alt)
drivers/usb/gadget/function/f_uac1.c:969: ret = u_audio_start_capture(&uac1->g_audio);
drivers/usb/gadget/function/f_uac1.c-970- else
drivers/usb/gadget/function/f_uac1.c:971: u_audio_stop_capture(&uac1->g_audio);
drivers/usb/gadget/function/f_uac1.c:972: } else if (intf == uac1->as_in_intf) {
drivers/usb/gadget/function/f_uac1.c:973: uac1->as_in_alt = alt;
drivers/usb/gadget/function/f_uac1.c-974-
drivers/usb/gadget/function/f_uac1.c-975- if (alt)
drivers/usb/gadget/function/f_uac1.c:976: ret = u_audio_start_playback(&uac1->g_audio);
drivers/usb/gadget/function/f_uac1.c-977- else
drivers/usb/gadget/function/f_uac1.c:978: u_audio_stop_playback(&uac1->g_audio);
drivers/usb/gadget/function/f_uac1.c-979- } else {
--
drivers/usb/gadget/function/f_uac1.c=987=static int f_audio_get_alt(struct usb_function *f, unsigned intf)
--
drivers/usb/gadget/function/f_uac1.c-991- struct device *dev = &gadget->dev;
drivers/usb/gadget/function/f_uac1.c:992: struct f_uac1 *uac1 = func_to_uac1(f);
drivers/usb/gadget/function/f_uac1.c-993-
drivers/usb/gadget/function/f_uac1.c:994: if (intf == uac1->ac_intf)
drivers/usb/gadget/function/f_uac1.c:995: return uac1->ac_alt;
drivers/usb/gadget/function/f_uac1.c:996: else if (intf == uac1->as_out_intf)
drivers/usb/gadget/function/f_uac1.c:997: return uac1->as_out_alt;
drivers/usb/gadget/function/f_uac1.c:998: else if (intf == uac1->as_in_intf)
drivers/usb/gadget/function/f_uac1.c:999: return uac1->as_in_alt;
drivers/usb/gadget/function/f_uac1.c-1000- else
--
drivers/usb/gadget/function/f_uac1.c=1008=static void f_audio_disable(struct usb_function *f)
drivers/usb/gadget/function/f_uac1.c-1009-{
drivers/usb/gadget/function/f_uac1.c:1010: struct f_uac1 *uac1 = func_to_uac1(f);
drivers/usb/gadget/function/f_uac1.c-1011-
drivers/usb/gadget/function/f_uac1.c:1012: uac1->as_out_alt = 0;
drivers/usb/gadget/function/f_uac1.c:1013: uac1->as_in_alt = 0;
drivers/usb/gadget/function/f_uac1.c-1014-
drivers/usb/gadget/function/f_uac1.c:1015: u_audio_stop_playback(&uac1->g_audio);
drivers/usb/gadget/function/f_uac1.c:1016: u_audio_stop_capture(&uac1->g_audio);
drivers/usb/gadget/function/f_uac1.c:1017: if (uac1->int_ep)
drivers/usb/gadget/function/f_uac1.c:1018: usb_ep_disable(uac1->int_ep);
drivers/usb/gadget/function/f_uac1.c-1019-}
--
drivers/usb/gadget/function/f_uac1.c=1022=f_audio_suspend(struct usb_function *f)
drivers/usb/gadget/function/f_uac1.c-1023-{
drivers/usb/gadget/function/f_uac1.c:1024: struct f_uac1 *uac1 = func_to_uac1(f);
drivers/usb/gadget/function/f_uac1.c-1025-
drivers/usb/gadget/function/f_uac1.c:1026: u_audio_suspend(&uac1->g_audio);
drivers/usb/gadget/function/f_uac1.c-1027-}
--
drivers/usb/gadget/function/f_uac1.c=1052=static struct
drivers/usb/gadget/function/f_uac1.c:1053:uac1_ac_header_descriptor *build_ac_header_desc(struct f_uac1_opts *opts)
drivers/usb/gadget/function/f_uac1.c-1054-{
drivers/usb/gadget/function/f_uac1.c:1055: struct uac1_ac_header_descriptor *ac_desc;
drivers/usb/gadget/function/f_uac1.c-1056- int ac_header_desc_size;
--
drivers/usb/gadget/function/f_uac1.c-1083-
drivers/usb/gadget/function/f_uac1.c:1084:static void setup_descriptor(struct f_uac1_opts *opts)
drivers/usb/gadget/function/f_uac1.c-1085-{
--
drivers/usb/gadget/function/f_uac1.c=1179=static int f_audio_validate_opts(struct g_audio *audio, struct device *dev)
drivers/usb/gadget/function/f_uac1.c-1180-{
drivers/usb/gadget/function/f_uac1.c:1181: struct f_uac1_opts *opts = g_audio_to_uac1_opts(audio);
drivers/usb/gadget/function/f_uac1.c-1182-
--
drivers/usb/gadget/function/f_uac1.c=1232=static int f_audio_bind(struct usb_configuration *c, struct usb_function *f)
--
drivers/usb/gadget/function/f_uac1.c-1236- struct device *dev = &gadget->dev;
drivers/usb/gadget/function/f_uac1.c:1237: struct f_uac1 *uac1 = func_to_uac1(f);
drivers/usb/gadget/function/f_uac1.c-1238- struct g_audio *audio = func_to_g_audio(f);
drivers/usb/gadget/function/f_uac1.c:1239: struct f_uac1_opts *audio_opts;
drivers/usb/gadget/function/f_uac1.c-1240- struct usb_ep *ep = NULL;
--
drivers/usb/gadget/function/f_uac1.c-1249-
drivers/usb/gadget/function/f_uac1.c:1250: audio_opts = container_of(f->fi, struct f_uac1_opts, func_inst);
drivers/usb/gadget/function/f_uac1.c-1251-
drivers/usb/gadget/function/f_uac1.c:1252: strings_uac1[STR_AC_IF].s = audio_opts->function_name;
drivers/usb/gadget/function/f_uac1.c-1253-
drivers/usb/gadget/function/f_uac1.c:1254: strings_uac1[STR_USB_OUT_IT].s = audio_opts->c_it_name;
drivers/usb/gadget/function/f_uac1.c:1255: strings_uac1[STR_USB_OUT_IT_CH_NAMES].s = audio_opts->c_it_ch_name;
drivers/usb/gadget/function/f_uac1.c:1256: strings_uac1[STR_IO_OUT_OT].s = audio_opts->c_ot_name;
drivers/usb/gadget/function/f_uac1.c:1257: strings_uac1[STR_FU_OUT].s = audio_opts->c_fu_vol_name;
drivers/usb/gadget/function/f_uac1.c:1258: strings_uac1[STR_AS_OUT_IF_ALT0].s = "Playback Inactive";
drivers/usb/gadget/function/f_uac1.c:1259: strings_uac1[STR_AS_OUT_IF_ALT1].s = "Playback Active";
drivers/usb/gadget/function/f_uac1.c-1260-
drivers/usb/gadget/function/f_uac1.c:1261: strings_uac1[STR_IO_IN_IT].s = audio_opts->p_it_name;
drivers/usb/gadget/function/f_uac1.c:1262: strings_uac1[STR_IO_IN_IT_CH_NAMES].s = audio_opts->p_it_ch_name;
drivers/usb/gadget/function/f_uac1.c:1263: strings_uac1[STR_USB_IN_OT].s = audio_opts->p_ot_name;
drivers/usb/gadget/function/f_uac1.c:1264: strings_uac1[STR_FU_IN].s = audio_opts->p_fu_vol_name;
drivers/usb/gadget/function/f_uac1.c:1265: strings_uac1[STR_AS_IN_IF_ALT0].s = "Capture Inactive";
drivers/usb/gadget/function/f_uac1.c:1266: strings_uac1[STR_AS_IN_IF_ALT1].s = "Capture Active";
drivers/usb/gadget/function/f_uac1.c-1267-
drivers/usb/gadget/function/f_uac1.c:1268: us = usb_gstrings_attach(cdev, uac1_strings, ARRAY_SIZE(strings_uac1));
drivers/usb/gadget/function/f_uac1.c-1269- if (IS_ERR(us))
--
drivers/usb/gadget/function/f_uac1.c-1368- as_in_type_i_desc.bSamFreqType = idx;
drivers/usb/gadget/function/f_uac1.c:1369: uac1->p_srate = audio_opts->p_srates[0];
drivers/usb/gadget/function/f_uac1.c:1370: uac1->c_srate = audio_opts->c_srates[0];
drivers/usb/gadget/function/f_uac1.c-1371-
--
drivers/usb/gadget/function/f_uac1.c-1376- ac_interface_desc.bInterfaceNumber = status;
drivers/usb/gadget/function/f_uac1.c:1377: uac1->ac_intf = status;
drivers/usb/gadget/function/f_uac1.c:1378: uac1->ac_alt = 0;
drivers/usb/gadget/function/f_uac1.c-1379-
--
drivers/usb/gadget/function/f_uac1.c-1388- ac_header_desc->baInterfaceNr[ba_iface_id++] = status;
drivers/usb/gadget/function/f_uac1.c:1389: uac1->as_out_intf = status;
drivers/usb/gadget/function/f_uac1.c:1390: uac1->as_out_alt = 0;
drivers/usb/gadget/function/f_uac1.c-1391- }
--
drivers/usb/gadget/function/f_uac1.c-1399- ac_header_desc->baInterfaceNr[ba_iface_id++] = status;
drivers/usb/gadget/function/f_uac1.c:1400: uac1->as_in_intf = status;
drivers/usb/gadget/function/f_uac1.c:1401: uac1->as_in_alt = 0;
drivers/usb/gadget/function/f_uac1.c-1402- }
--
drivers/usb/gadget/function/f_uac1.c-1414- goto err_free_fu;
drivers/usb/gadget/function/f_uac1.c:1415: uac1->int_ep = ep;
drivers/usb/gadget/function/f_uac1.c:1416: uac1->int_ep->desc = &ac_int_ep_desc;
drivers/usb/gadget/function/f_uac1.c-1417-
--
drivers/usb/gadget/function/f_uac1.c-1501-
drivers/usb/gadget/function/f_uac1.c:1502:static inline struct f_uac1_opts *to_f_uac1_opts(struct config_item *item)
drivers/usb/gadget/function/f_uac1.c-1503-{
drivers/usb/gadget/function/f_uac1.c:1504: return container_of(to_config_group(item), struct f_uac1_opts,
drivers/usb/gadget/function/f_uac1.c-1505- func_inst.group);
--
drivers/usb/gadget/function/f_uac1.c-1507-
drivers/usb/gadget/function/f_uac1.c:1508:static void f_uac1_attr_release(struct config_item *item)
drivers/usb/gadget/function/f_uac1.c-1509-{
drivers/usb/gadget/function/f_uac1.c:1510: struct f_uac1_opts *opts = to_f_uac1_opts(item);
drivers/usb/gadget/function/f_uac1.c-1511-
--
drivers/usb/gadget/function/f_uac1.c-1514-
drivers/usb/gadget/function/f_uac1.c:1515:static const struct configfs_item_operations f_uac1_item_ops = {
drivers/usb/gadget/function/f_uac1.c:1516: .release = f_uac1_attr_release,
drivers/usb/gadget/function/f_uac1.c-1517-};
drivers/usb/gadget/function/f_uac1.c-1518-
drivers/usb/gadget/function/f_uac1.c:1519:#define uac1_kstrtou32 kstrtou32
drivers/usb/gadget/function/f_uac1.c:1520:#define uac1_kstrtos16 kstrtos16
drivers/usb/gadget/function/f_uac1.c:1521:#define uac1_kstrtobool(s, base, res) kstrtobool((s), (res))
drivers/usb/gadget/function/f_uac1.c-1522-
--
drivers/usb/gadget/function/f_uac1.c=1525=static const char *bool_fmt = "%u\n";
--
drivers/usb/gadget/function/f_uac1.c-1527-#define UAC1_ATTRIBUTE(type, name) \
drivers/usb/gadget/function/f_uac1.c:1528:static ssize_t f_uac1_opts_##name##_show( \
drivers/usb/gadget/function/f_uac1.c-1529- struct config_item *item, \
--
drivers/usb/gadget/function/f_uac1.c-1531-{ \
drivers/usb/gadget/function/f_uac1.c:1532: struct f_uac1_opts *opts = to_f_uac1_opts(item); \
drivers/usb/gadget/function/f_uac1.c-1533- int result; \
--
drivers/usb/gadget/function/f_uac1.c-1541- \
drivers/usb/gadget/function/f_uac1.c:1542:static ssize_t f_uac1_opts_##name##_store( \
drivers/usb/gadget/function/f_uac1.c-1543- struct config_item *item, \
--
drivers/usb/gadget/function/f_uac1.c-1545-{ \
drivers/usb/gadget/function/f_uac1.c:1546: struct f_uac1_opts *opts = to_f_uac1_opts(item); \
drivers/usb/gadget/function/f_uac1.c-1547- int ret; \
--
drivers/usb/gadget/function/f_uac1.c-1555- \
drivers/usb/gadget/function/f_uac1.c:1556: ret = uac1_kstrto##type(page, 0, &num); \
drivers/usb/gadget/function/f_uac1.c-1557- if (ret) \
--
drivers/usb/gadget/function/f_uac1.c=1563=end: \
--
drivers/usb/gadget/function/f_uac1.c-1567- \
drivers/usb/gadget/function/f_uac1.c:1568:CONFIGFS_ATTR(f_uac1_opts_, name)
drivers/usb/gadget/function/f_uac1.c-1569-
drivers/usb/gadget/function/f_uac1.c-1570-#define UAC1_RATE_ATTRIBUTE(name) \
drivers/usb/gadget/function/f_uac1.c:1571:static ssize_t f_uac1_opts_##name##_show(struct config_item *item, \
drivers/usb/gadget/function/f_uac1.c-1572- char *page) \
drivers/usb/gadget/function/f_uac1.c-1573-{ \
drivers/usb/gadget/function/f_uac1.c:1574: struct f_uac1_opts *opts = to_f_uac1_opts(item); \
drivers/usb/gadget/function/f_uac1.c-1575- int result = 0; \
--
drivers/usb/gadget/function/f_uac1.c-1592- \
drivers/usb/gadget/function/f_uac1.c:1593:static ssize_t f_uac1_opts_##name##_store(struct config_item *item, \
drivers/usb/gadget/function/f_uac1.c-1594- const char *page, size_t len) \
drivers/usb/gadget/function/f_uac1.c-1595-{ \
drivers/usb/gadget/function/f_uac1.c:1596: struct f_uac1_opts *opts = to_f_uac1_opts(item); \
drivers/usb/gadget/function/f_uac1.c-1597- char *split_page = NULL; \
--
drivers/usb/gadget/function/f_uac1.c=1621=end: \
--
drivers/usb/gadget/function/f_uac1.c-1626- \
drivers/usb/gadget/function/f_uac1.c:1627:CONFIGFS_ATTR(f_uac1_opts_, name)
drivers/usb/gadget/function/f_uac1.c-1628-
drivers/usb/gadget/function/f_uac1.c-1629-#define UAC1_ATTRIBUTE_STRING(name) \
drivers/usb/gadget/function/f_uac1.c:1630:static ssize_t f_uac1_opts_##name##_show(struct config_item *item, \
drivers/usb/gadget/function/f_uac1.c-1631- char *page) \
drivers/usb/gadget/function/f_uac1.c-1632-{ \
drivers/usb/gadget/function/f_uac1.c:1633: struct f_uac1_opts *opts = to_f_uac1_opts(item); \
drivers/usb/gadget/function/f_uac1.c-1634- int result; \
--
drivers/usb/gadget/function/f_uac1.c-1642- \
drivers/usb/gadget/function/f_uac1.c:1643:static ssize_t f_uac1_opts_##name##_store(struct config_item *item, \
drivers/usb/gadget/function/f_uac1.c-1644- const char *page, size_t len) \
drivers/usb/gadget/function/f_uac1.c-1645-{ \
drivers/usb/gadget/function/f_uac1.c:1646: struct f_uac1_opts *opts = to_f_uac1_opts(item); \
drivers/usb/gadget/function/f_uac1.c-1647- int ret = 0; \
--
drivers/usb/gadget/function/f_uac1.c=1658=end: \
--
drivers/usb/gadget/function/f_uac1.c-1662- \
drivers/usb/gadget/function/f_uac1.c:1663:CONFIGFS_ATTR(f_uac1_opts_, name)
drivers/usb/gadget/function/f_uac1.c-1664-
--
drivers/usb/gadget/function/f_uac1.c=1695=UAC1_ATTRIBUTE_STRING(c_fu_vol_name);
drivers/usb/gadget/function/f_uac1.c-1696-
drivers/usb/gadget/function/f_uac1.c:1697:static struct configfs_attribute *f_uac1_attrs[] = {
drivers/usb/gadget/function/f_uac1.c:1698: &f_uac1_opts_attr_c_chmask,
drivers/usb/gadget/function/f_uac1.c:1699: &f_uac1_opts_attr_c_srate,
drivers/usb/gadget/function/f_uac1.c:1700: &f_uac1_opts_attr_c_ssize,
drivers/usb/gadget/function/f_uac1.c:1701: &f_uac1_opts_attr_p_chmask,
drivers/usb/gadget/function/f_uac1.c:1702: &f_uac1_opts_attr_p_srate,
drivers/usb/gadget/function/f_uac1.c:1703: &f_uac1_opts_attr_p_ssize,
drivers/usb/gadget/function/f_uac1.c:1704: &f_uac1_opts_attr_req_number,
drivers/usb/gadget/function/f_uac1.c-1705-
drivers/usb/gadget/function/f_uac1.c:1706: &f_uac1_opts_attr_p_mute_present,
drivers/usb/gadget/function/f_uac1.c:1707: &f_uac1_opts_attr_p_volume_present,
drivers/usb/gadget/function/f_uac1.c:1708: &f_uac1_opts_attr_p_volume_min,
drivers/usb/gadget/function/f_uac1.c:1709: &f_uac1_opts_attr_p_volume_max,
drivers/usb/gadget/function/f_uac1.c:1710: &f_uac1_opts_attr_p_volume_res,
drivers/usb/gadget/function/f_uac1.c-1711-
drivers/usb/gadget/function/f_uac1.c:1712: &f_uac1_opts_attr_c_mute_present,
drivers/usb/gadget/function/f_uac1.c:1713: &f_uac1_opts_attr_c_volume_present,
drivers/usb/gadget/function/f_uac1.c:1714: &f_uac1_opts_attr_c_volume_min,
drivers/usb/gadget/function/f_uac1.c:1715: &f_uac1_opts_attr_c_volume_max,
drivers/usb/gadget/function/f_uac1.c:1716: &f_uac1_opts_attr_c_volume_res,
drivers/usb/gadget/function/f_uac1.c-1717-
drivers/usb/gadget/function/f_uac1.c:1718: &f_uac1_opts_attr_function_name,
drivers/usb/gadget/function/f_uac1.c-1719-
drivers/usb/gadget/function/f_uac1.c:1720: &f_uac1_opts_attr_p_it_name,
drivers/usb/gadget/function/f_uac1.c:1721: &f_uac1_opts_attr_p_it_ch_name,
drivers/usb/gadget/function/f_uac1.c:1722: &f_uac1_opts_attr_p_ot_name,
drivers/usb/gadget/function/f_uac1.c:1723: &f_uac1_opts_attr_p_fu_vol_name,
drivers/usb/gadget/function/f_uac1.c-1724-
drivers/usb/gadget/function/f_uac1.c:1725: &f_uac1_opts_attr_c_it_name,
drivers/usb/gadget/function/f_uac1.c:1726: &f_uac1_opts_attr_c_it_ch_name,
drivers/usb/gadget/function/f_uac1.c:1727: &f_uac1_opts_attr_c_ot_name,
drivers/usb/gadget/function/f_uac1.c:1728: &f_uac1_opts_attr_c_fu_vol_name,
drivers/usb/gadget/function/f_uac1.c-1729-
--
drivers/usb/gadget/function/f_uac1.c-1732-
drivers/usb/gadget/function/f_uac1.c:1733:static const struct config_item_type f_uac1_func_type = {
drivers/usb/gadget/function/f_uac1.c:1734: .ct_item_ops = &f_uac1_item_ops,
drivers/usb/gadget/function/f_uac1.c:1735: .ct_attrs = f_uac1_attrs,
drivers/usb/gadget/function/f_uac1.c-1736- .ct_owner = THIS_MODULE,
--
]
|
| 49/4 |
2026/07/31 01:56 |
llm |
codeexpert |
0m
Model:
Tokens:
input: 30095
output: 45
thoughts: 54
Thoughts: Okay, here's my take on those thoughts, framed as my own:
**Analyzing the UAC1 Rate Attribute Macro**
Right, let's see... the code's using `UAC1_RATE_ATTRIBUTE(name)`. I recognize that, of course. I need to get a clear picture of how this attribute is constructed and utilized within the UAC1 gadget driver. Time to dig into the source. Specifically, I should look at `drivers/usb/gadget/function/f_uac1.c`. I'll focus on around line 1590 to 1630. Hopefully, the surrounding code will give me the context I need to properly understand how it's defined and what the `name` parameter signifies in this macro. Let's see if this macro is just for rate settings, or if it covers other USB audio control aspects.
|
| 50/4 |
2026/07/31 01:56 |
tool |
read-file |
0m
Args: map[File:drivers/usb/gadget/function/f_uac1.c FirstLine:1570 LineCount:60]
Results: map[Contents:1570: #define UAC1_RATE_ATTRIBUTE(name) \
1571: static ssize_t f_uac1_opts_##name##_show(struct config_item *item, \
1572: char *page) \
1573: { \
1574: struct f_uac1_opts *opts = to_f_uac1_opts(item); \
1575: int result = 0; \
1576: int i; \
1577: \
1578: mutex_lock(&opts->lock); \
1579: page[0] = '\0'; \
1580: for (i = 0; i < UAC_MAX_RATES; i++) { \
1581: if (opts->name##s[i] == 0) \
1582: break; \
1583: result += sprintf(page + strlen(page), "%u,", \
1584: opts->name##s[i]); \
1585: } \
1586: if (strlen(page) > 0) \
1587: page[strlen(page) - 1] = '\n'; \
1588: mutex_unlock(&opts->lock); \
1589: \
1590: return result; \
1591: } \
1592: \
1593: static ssize_t f_uac1_opts_##name##_store(struct config_item *item, \
1594: const char *page, size_t len) \
1595: { \
1596: struct f_uac1_opts *opts = to_f_uac1_opts(item); \
1597: char *split_page = NULL; \
1598: int ret = -EINVAL; \
1599: char *token; \
1600: u32 num; \
1601: int i; \
1602: \
1603: mutex_lock(&opts->lock); \
1604: if (opts->refcnt) { \
1605: ret = -EBUSY; \
1606: goto end; \
1607: } \
1608: \
1609: i = 0; \
1610: memset(opts->name##s, 0x00, sizeof(opts->name##s)); \
1611: split_page = kstrdup(page, GFP_KERNEL); \
1612: while ((token = strsep(&split_page, ",")) != NULL) { \
1613: ret = kstrtou32(token, 0, &num); \
1614: if (ret) \
1615: goto end; \
1616: \
1617: opts->name##s[i++] = num; \
1618: ret = len; \
1619: }; \
1620: \
1621: end: \
1622: kfree(split_page); \
1623: mutex_unlock(&opts->lock); \
1624: return ret; \
1625: } \
1626: \
1627: CONFIGFS_ATTR(f_uac1_opts_, name)
1628:
1629: #define UAC1_ATTRIBUTE_STRING(name) \
]
|
| 51/4 |
2026/07/31 01:56 |
llm |
codeexpert |
0m
Model:
Tokens:
input: 31311
output: 35
thoughts: 1371
Thoughts: **Analyzing a Potential Memory Corruption Bug**
Okay, let's dive into this `f_uac1_opts_##name##_store` function, instantiated as `f_uac1_opts_c_srate_store` and `f_uac1_opts_p_srate_store`. I'm focusing on how it handles input from the user, specifically the string parsing and conversion to numerical values. The function's logic is concerning, and I believe there's a serious vulnerability here.
I'm seeing that the input `page`, a string, is duplicated using `kstrdup`. Then, this duplicate, `split_page`, is tokenized using `strsep`. My initial thought is the usual checks are there; the code locks a mutex, checks a reference counter, and zeroes the name array. The problem arises in how `strsep` is used, especially in conjunction with error handling.
The while loop iterates through the comma-separated tokens. The code calls `kstrtou32` to convert each token to an unsigned integer. If `kstrtou32` fails, a `goto end;` is executed. The problem is, during the parsing loop, `strsep` modifies the `split_page` pointer. If an error occurs in the *middle* of processing, `split_page` no longer points to the *beginning* of the allocated string. It might point to an offset within that string, or even `NULL` if it was the last token.
Now, if `split_page` is `NULL` at the `end:` label, then `kfree(NULL)` is safe. However, I realized the problem when I imagined `page` being `"invalid_rate,44100"`. In this case, `split_page` would be modified by `strsep` to point to the substring "44100". The `kstrtou32` would fail, and control would jump to `end:`. The `kfree(split_page)` there would be called with a pointer to the middle of the allocated buffer, an invalid-free. This is a serious memory corruption bug; likely a heap overflow.
To be sure, I walked through two different input scenarios to be very sure, and my analysis holds. I'm now certain this is an issue. Next step, I'm going to look at the kernel source history using `git-log` to see if this has been fixed, and how. I will filter the commit log focusing on changes to the `f_uac1.c` and `f_uac2.c` files.
|
| 52/4 |
2026/07/31 01:56 |
tool |
git-log |
0m
Args: map[CodeRegexp:split_page PathPrefix:drivers/usb/gadget/function/]
Results: map[Output:695d39ffc2b5 usb: gadget: f_uac1: Support multiple sampling rates
a7339e4f5788 usb: gadget: f_uac2: Support multiple sampling rates
]
|
| 53/4 |
2026/07/31 01:57 |
llm |
codeexpert |
0m
Model:
Tokens:
input: 31979
output: 26
thoughts: 0
|
| 54/4 |
2026/07/31 01:57 |
tool |
git-show |
0m
Args:
Results: map[Output:commit 695d39ffc2b59b8333ff85724619514f98613205
Author: Julian Scheel <julian@jusst.de>
Date: Fri Jan 21 16:53:04 2022 +0100
usb: gadget: f_uac1: Support multiple sampling rates
A list of sampling rates can be specified via configfs. All enabled
sampling rates are sent to the USB host on request. When the host
selects a sampling rate the internal active rate is updated.
Config strings with single value stay compatible with the previous version.
Multiple samplerates passed as configuration arrays to g_audio module
when built for f_uac1.
Signed-off-by: Julian Scheel <julian@jusst.de>
Signed-off-by: Pavel Hofman <pavel.hofman@ivitera.com>
Link: https://lore.kernel.org/r/20220121155308.48794-7-pavel.hofman@ivitera.com
Signed-off-by: Greg Kroah-Hartman <gregkh@linuxfoundation.org>
diff --git a/Documentation/ABI/testing/configfs-usb-gadget-uac1 b/Documentation/ABI/testing/configfs-usb-gadget-uac1
index d4b8cf40a9e4..09725e273e9b 100644
--- a/Documentation/ABI/testing/configfs-usb-gadget-uac1
+++ b/Documentation/ABI/testing/configfs-usb-gadget-uac1
@@ -6,7 +6,7 @@ Description:
===================== =======================================
c_chmask capture channel mask
- c_srate capture sampling rate
+ c_srate list of capture sampling rates (comma-separated)
c_ssize capture sample size (bytes)
c_mute_present capture mute control enable
c_volume_present capture volume control enable
@@ -17,7 +17,7 @@ Description:
c_volume_res capture volume control resolution
(in 1/256 dB)
p_chmask playback channel mask
- p_srate playback sampling rate
+ p_srate list of playback sampling rates (comma-separated)
p_ssize playback sample size (bytes)
p_mute_present playback mute control enable
p_volume_present playback volume control enable
diff --git a/Documentation/usb/gadget-testing.rst b/Documentation/usb/gadget-testing.rst
index 419f6e5e890a..046842b00c89 100644
--- a/Documentation/usb/gadget-testing.rst
+++ b/Documentation/usb/gadget-testing.rst
@@ -916,7 +916,7 @@ The uac1 function provides these attributes in its function directory:
================ ====================================================
c_chmask capture channel mask
- c_srate capture sampling rate
+ c_srate list of capture sampling rates (comma-separated)
c_ssize capture sample size (bytes)
c_mute_present capture mute control enable
c_volume_present capture volume control enable
@@ -924,7 +924,7 @@ The uac1 function provides these attributes in its function directory:
c_volume_max capture volume control max value (in 1/256 dB)
c_volume_res capture volume control resolution (in 1/256 dB)
p_chmask playback channel mask
- p_srate playback sampling rate
+ p_srate list of playback sampling rates (comma-separated)
p_ssize playback sample size (bytes)
p_mute_present playback mute control enable
p_volume_present playback volume control enable
diff --git a/drivers/usb/gadget/function/f_uac1.c b/drivers/usb/gadget/function/f_uac1.c
index 0397b27df42e..73df76a6fbe0 100644
--- a/drivers/usb/gadget/function/f_uac1.c
+++ b/drivers/usb/gadget/function/f_uac1.c
@@ -3,6 +3,7 @@
* f_uac1.c -- USB Audio Class 1.0 Function (using u_audio API)
*
* Copyright (C) 2016 Ruslan Bilovol <ruslan.bilovol@gmail.com>
+ * Copyright (C) 2021 Julian Scheel <julian@jusst.de>
*
* This driver doesn't expect any real Audio codec to be present
* on the device - the audio streams are simply sinked to and
@@ -42,6 +43,9 @@ struct f_uac1 {
/* Interrupt IN endpoint of AC interface */
struct usb_ep *int_ep;
atomic_t int_count;
+ int ctl_id; /* EP id */
+ int c_srate; /* current capture srate */
+ int p_srate; /* current playback prate */
};
static inline struct f_uac1 *func_to_uac1(struct usb_function *f)
@@ -188,16 +192,18 @@ static struct uac1_as_header_descriptor as_in_header_desc = {
.wFormatTag = cpu_to_le16(UAC_FORMAT_TYPE_I_PCM),
};
-DECLARE_UAC_FORMAT_TYPE_I_DISCRETE_DESC(1);
+DECLARE_UAC_FORMAT_TYPE_I_DISCRETE_DESC(UAC_MAX_RATES);
+#define uac_format_type_i_discrete_descriptor \
+ uac_format_type_i_discrete_descriptor_##UAC_MAX_RATES
-static struct uac_format_type_i_discrete_descriptor_1 as_out_type_i_desc = {
- .bLength = UAC_FORMAT_TYPE_I_DISCRETE_DESC_SIZE(1),
+static struct uac_format_type_i_discrete_descriptor as_out_type_i_desc = {
+ .bLength = 0, /* filled on rate setup */
.bDescriptorType = USB_DT_CS_INTERFACE,
.bDescriptorSubtype = UAC_FORMAT_TYPE,
.bFormatType = UAC_FORMAT_TYPE_I,
.bSubframeSize = 2,
.bBitResolution = 16,
- .bSamFreqType = 1,
+ .bSamFreqType = 0, /* filled on rate setup */
};
/* Standard ISO OUT Endpoint Descriptor */
@@ -221,14 +227,14 @@ static struct uac_iso_endpoint_descriptor as_iso_out_desc = {
.wLockDelay = cpu_to_le16(1),
};
-static struct uac_format_type_i_discrete_descriptor_1 as_in_type_i_desc = {
- .bLength = UAC_FORMAT_TYPE_I_DISCRETE_DESC_SIZE(1),
+static struct uac_format_type_i_discrete_descriptor as_in_type_i_desc = {
+ .bLength = 0, /* filled on rate setup */
.bDescriptorType = USB_DT_CS_INTERFACE,
.bDescriptorSubtype = UAC_FORMAT_TYPE,
.bFormatType = UAC_FORMAT_TYPE_I,
.bSubframeSize = 2,
.bBitResolution = 16,
- .bSamFreqType = 1,
+ .bSamFreqType = 0, /* filled on rate setup */
};
/* Standard ISO OUT Endpoint Descriptor */
@@ -333,6 +339,30 @@ static struct usb_gadget_strings *uac1_strings[] = {
* This function is an ALSA sound card following USB Audio Class Spec 1.0.
*/
+static void uac_cs_attr_sample_rate(struct usb_ep *ep, struct usb_request *req)
+{
+ struct usb_function *fn = ep->driver_data;
+ struct usb_composite_dev *cdev = fn->config->cdev;
+ struct g_audio *agdev = func_to_g_audio(fn);
+ struct f_uac1 *uac1 = func_to_uac1(fn);
+ u8 *buf = (u8 *)req->buf;
+ u32 val = 0;
+
+ if (req->actual != 3) {
+ WARN(cdev, "Invalid data size for UAC_EP_CS_ATTR_SAMPLE_RATE.\n");
+ return;
+ }
+
+ val = buf[0] | (buf[1] << 8) | (buf[2] << 16);
+ if (uac1->ctl_id == (USB_DIR_IN | 2)) {
+ uac1->p_srate = val;
+ u_audio_set_playback_srate(agdev, uac1->p_srate);
+ } else if (uac1->ctl_id == (USB_DIR_OUT | 1)) {
+ uac1->c_srate = val;
+ u_audio_set_capture_srate(agdev, uac1->c_srate);
+ }
+}
+
static void audio_notify_complete(struct usb_ep *_ep, struct usb_request *req)
{
struct g_audio *audio = req->context;
@@ -707,18 +737,27 @@ static int audio_set_endpoint_req(struct usb_function *f,
const struct usb_ctrlrequest *ctrl)
{
struct usb_composite_dev *cdev = f->config->cdev;
+ struct usb_request *req = f->config->cdev->req;
+ struct f_uac1 *uac1 = func_to_uac1(f);
int value = -EOPNOTSUPP;
u16 ep = le16_to_cpu(ctrl->wIndex);
u16 len = le16_to_cpu(ctrl->wLength);
u16 w_value = le16_to_cpu(ctrl->wValue);
+ u8 cs = w_value >> 8;
DBG(cdev, "bRequest 0x%x, w_value 0x%04x, len %d, endpoint %d\n",
ctrl->bRequest, w_value, len, ep);
switch (ctrl->bRequest) {
- case UAC_SET_CUR:
+ case UAC_SET_CUR: {
+ if (cs == UAC_EP_CS_ATTR_SAMPLE_RATE) {
+ cdev->gadget->ep0->driver_data = f;
+ uac1->ctl_id = ep;
+ req->complete = uac_cs_attr_sample_rate;
+ }
value = len;
break;
+ }
case UAC_SET_MIN:
break;
@@ -743,16 +782,33 @@ static int audio_get_endpoint_req(struct usb_function *f,
const struct usb_ctrlrequest *ctrl)
{
struct usb_composite_dev *cdev = f->config->cdev;
+ struct usb_request *req = f->config->cdev->req;
+ struct f_uac1 *uac1 = func_to_uac1(f);
+ u8 *buf = (u8 *)req->buf;
int value = -EOPNOTSUPP;
- u8 ep = ((le16_to_cpu(ctrl->wIndex) >> 8) & 0xFF);
+ u8 ep = le16_to_cpu(ctrl->wIndex);
u16 len = le16_to_cpu(ctrl->wLength);
u16 w_value = le16_to_cpu(ctrl->wValue);
+ u8 cs = w_value >> 8;
+ u32 val = 0;
DBG(cdev, "bRequest 0x%x, w_value 0x%04x, len %d, endpoint %d\n",
ctrl->bRequest, w_value, len, ep);
switch (ctrl->bRequest) {
- case UAC_GET_CUR:
+ case UAC_GET_CUR: {
+ if (cs == UAC_EP_CS_ATTR_SAMPLE_RATE) {
+ if (ep == (USB_DIR_IN | 2))
+ val = uac1->p_srate;
+ else if (ep == (USB_DIR_OUT | 1))
+ val = uac1->c_srate;
+ buf[2] = (val >> 16) & 0xff;
+ buf[1] = (val >> 8) & 0xff;
+ buf[0] = val & 0xff;
+ }
+ value = len;
+ break;
+ }
case UAC_GET_MIN:
case UAC_GET_MAX:
case UAC_GET_RES:
@@ -1074,10 +1130,10 @@ static int f_audio_validate_opts(struct g_audio *audio, struct device *dev)
} else if ((opts->c_ssize < 1) || (opts->c_ssize > 4)) {
dev_err(dev, "Error: incorrect capture sample size\n");
return -EINVAL;
- } else if (!opts->p_srate) {
+ } else if (!opts->p_srates[0]) {
dev_err(dev, "Error: incorrect playback sampling rate\n");
return -EINVAL;
- } else if (!opts->c_srate) {
+ } else if (!opts->c_srates[0]) {
dev_err(dev, "Error: incorrect capture sampling rate\n");
return -EINVAL;
}
@@ -1118,10 +1174,9 @@ static int f_audio_bind(struct usb_configuration *c, struct usb_function *f)
struct f_uac1_opts *audio_opts;
struct usb_ep *ep = NULL;
struct usb_string *us;
- u8 *sam_freq;
- int rate;
int ba_iface_id;
int status;
+ int idx, i;
status = f_audio_validate_opts(audio, dev);
if (status)
@@ -1213,12 +1268,25 @@ static int f_audio_bind(struct usb_configuration *c, struct usb_function *f)
}
/* Set sample rates */
- rate = audio_opts->c_srate;
- sam_freq = as_out_type_i_desc.tSamFreq[0];
- memcpy(sam_freq, &rate, 3);
- rate = audio_opts->p_srate;
- sam_freq = as_in_type_i_desc.tSamFreq[0];
- memcpy(sam_freq, &rate, 3);
+ for (i = 0, idx = 0; i < UAC_MAX_RATES; i++) {
+ if (audio_opts->c_srates[i] == 0)
+ break;
+ memcpy(as_out_type_i_desc.tSamFreq[idx++],
+ &audio_opts->c_srates[i], 3);
+ }
+ as_out_type_i_desc.bLength = UAC_FORMAT_TYPE_I_DISCRETE_DESC_SIZE(idx);
+ as_out_type_i_desc.bSamFreqType = idx;
+
+ for (i = 0, idx = 0; i < UAC_MAX_RATES; i++) {
+ if (audio_opts->p_srates[i] == 0)
+ break;
+ memcpy(as_in_type_i_desc.tSamFreq[idx++],
+ &audio_opts->p_srates[i], 3);
+ }
+ as_in_type_i_desc.bLength = UAC_FORMAT_TYPE_I_DISCRETE_DESC_SIZE(idx);
+ as_in_type_i_desc.bSamFreqType = idx;
+ uac1->p_srate = audio_opts->p_srates[0];
+ uac1->c_srate = audio_opts->c_srates[0];
/* allocate instance-specific interface IDs, and patch descriptors */
status = usb_interface_id(c, f);
@@ -1297,7 +1365,8 @@ static int f_audio_bind(struct usb_configuration *c, struct usb_function *f)
audio->out_ep_maxpsize = le16_to_cpu(as_out_ep_desc.wMaxPacketSize);
audio->in_ep_maxpsize = le16_to_cpu(as_in_ep_desc.wMaxPacketSize);
audio->params.c_chmask = audio_opts->c_chmask;
- audio->params.c_srates[0] = audio_opts->c_srate;
+ memcpy(audio->params.c_srates, audio_opts->c_srates,
+ sizeof(audio->params.c_srates));
audio->params.c_ssize = audio_opts->c_ssize;
if (FUIN_EN(audio_opts)) {
audio->params.p_fu.id = USB_IN_FU_ID;
@@ -1309,7 +1378,8 @@ static int f_audio_bind(struct usb_configuration *c, struct usb_function *f)
audio->params.p_fu.volume_res = audio_opts->p_volume_res;
}
audio->params.p_chmask = audio_opts->p_chmask;
- audio->params.p_srates[0] = audio_opts->p_srate;
+ memcpy(audio->params.p_srates, audio_opts->p_srates,
+ sizeof(audio->params.p_srates));
audio->params.p_ssize = audio_opts->p_ssize;
if (FUOUT_EN(audio_opts)) {
audio->params.c_fu.id = USB_OUT_FU_ID;
@@ -1414,11 +1484,70 @@ end: \
\
CONFIGFS_ATTR(f_uac1_opts_, name)
+#define UAC1_RATE_ATTRIBUTE(name) \
+static ssize_t f_uac1_opts_##name##_show(struct config_item *item, \
+ char *page) \
+{ \
+ struct f_uac1_opts *opts = to_f_uac1_opts(item); \
+ int result = 0; \
+ int i; \
+ \
+ mutex_lock(&opts->lock); \
+ page[0] = '\0'; \
+ for (i = 0; i < UAC_MAX_RATES; i++) { \
+ if (opts->name##s[i] == 0) \
+ break; \
+ result += sprintf(page + strlen(page), "%u,", \
+ opts->name##s[i]); \
+ } \
+ if (strlen(page) > 0) \
+ page[strlen(page) - 1] = '\n'; \
+ mutex_unlock(&opts->lock); \
+ \
+ return result; \
+} \
+ \
+static ssize_t f_uac1_opts_##name##_store(struct config_item *item, \
+ const char *page, size_t len) \
+{ \
+ struct f_uac1_opts *opts = to_f_uac1_opts(item); \
+ char *split_page = NULL; \
+ int ret = -EINVAL; \
+ char *token; \
+ u32 num; \
+ int i; \
+ \
+ mutex_lock(&opts->lock); \
+ if (opts->refcnt) { \
+ ret = -EBUSY; \
+ goto end; \
+ } \
+ \
+ i = 0; \
+ memset(opts->name##s, 0x00, sizeof(opts->name##s)); \
+ split_page = kstrdup(page, GFP_KERNEL); \
+ while ((token = strsep(&split_page, ",")) != NULL) { \
+ ret = kstrtou32(token, 0, &num); \
+ if (ret) \
+ goto end; \
+ \
+ opts->name##s[i++] = num; \
+ ret = len; \
+ }; \
+ \
+end: \
+ kfree(split_page); \
+ mutex_unlock(&opts->lock); \
+ return ret; \
+} \
+ \
+CONFIGFS_ATTR(f_uac1_opts_, name)
+
UAC1_ATTRIBUTE(u32, c_chmask);
-UAC1_ATTRIBUTE(u32, c_srate);
+UAC1_RATE_ATTRIBUTE(c_srate);
UAC1_ATTRIBUTE(u32, c_ssize);
UAC1_ATTRIBUTE(u32, p_chmask);
-UAC1_ATTRIBUTE(u32, p_srate);
+UAC1_RATE_ATTRIBUTE(p_srate);
UAC1_ATTRIBUTE(u32, p_ssize);
UAC1_ATTRIBUTE(u32, req_number);
@@ -1487,10 +1616,10 @@ static struct usb_function_instance *f_audio_alloc_inst(void)
&f_uac1_func_type);
opts->c_chmask = UAC1_DEF_CCHMASK;
- opts->c_srate = UAC1_DEF_CSRATE;
+ opts->c_srates[0] = UAC1_DEF_CSRATE;
opts->c_ssize = UAC1_DEF_CSSIZE;
opts->p_chmask = UAC1_DEF_PCHMASK;
- opts->p_srate = UAC1_DEF_PSRATE;
+ opts->p_srates[0] = UAC1_DEF_PSRATE;
opts->p_ssize = UAC1_DEF_PSSIZE;
opts->p_mute_present = UAC1_DEF_MUTE_PRESENT;
diff --git a/drivers/usb/gadget/function/u_uac1.h b/drivers/usb/gadget/function/u_uac1.h
index 589fae861141..b6cd6171d306 100644
--- a/drivers/usb/gadget/function/u_uac1.h
+++ b/drivers/usb/gadget/function/u_uac1.h
@@ -9,6 +9,7 @@
#define __U_UAC1_H
#include <linux/usb/composite.h>
+#include "uac_common.h"
#define UAC1_OUT_EP_MAX_PACKET_SIZE 200
#define UAC1_DEF_CCHMASK 0x3
@@ -30,10 +31,10 @@
struct f_uac1_opts {
struct usb_function_instance func_inst;
int c_chmask;
- int c_srate;
+ int c_srates[UAC_MAX_RATES];
int c_ssize;
int p_chmask;
- int p_srate;
+ int p_srates[UAC_MAX_RATES];
int p_ssize;
bool p_mute_present;
diff --git a/drivers/usb/gadget/legacy/audio.c b/drivers/usb/gadget/legacy/audio.c
index d14b9f2d4c07..c89c777a1aa3 100644
--- a/drivers/usb/gadget/legacy/audio.c
+++ b/drivers/usb/gadget/legacy/audio.c
@@ -61,9 +61,10 @@ module_param(p_chmask, uint, 0444);
MODULE_PARM_DESC(p_chmask, "Playback Channel Mask");
/* Playback Default 48 KHz */
-static int p_srate = UAC1_DEF_PSRATE;
-module_param(p_srate, uint, 0444);
-MODULE_PARM_DESC(p_srate, "Playback Sampling Rate");
+static int p_srates[UAC_MAX_RATES] = {UAC1_DEF_PSRATE};
+static int p_srates_cnt = 1;
+module_param_array_named(p_srate, p_srates, uint, &p_srates_cnt, 0444);
+MODULE_PARM_DESC(p_srate, "Playback Sampling Rates (array)");
/* Playback Default 16bits/sample */
static int p_ssize = UAC1_DEF_PSSIZE;
@@ -76,9 +77,10 @@ module_param(c_chmask, uint, 0444);
MODULE_PARM_DESC(c_chmask, "Capture Channel Mask");
/* Capture Default 48 KHz */
-static int c_srate = UAC1_DEF_CSRATE;
-module_param(c_srate, uint, 0444);
-MODULE_PARM_DESC(c_srate, "Capture Sampling Rate");
+static int c_srates[UAC_MAX_RATES] = {UAC1_DEF_CSRATE};
+static int c_srates_cnt = 1;
+module_param_array_named(c_srate, c_srates, uint, &c_srates_cnt, 0444);
+MODULE_PARM_DESC(c_srate, "Capture Sampling Rates (array)");
/* Capture Default 16bits/sample */
static int c_ssize = UAC1_DEF_CSSIZE;
@@ -243,6 +245,7 @@ static int audio_bind(struct usb_composite_dev *cdev)
#else
#ifndef CONFIG_GADGET_UAC1_LEGACY
struct f_uac1_opts *uac1_opts;
+ int i;
#else
struct f_uac1_legacy_opts *uac1_opts;
#endif
@@ -282,10 +285,16 @@ static int audio_bind(struct usb_composite_dev *cdev)
#ifndef CONFIG_GADGET_UAC1_LEGACY
uac1_opts = container_of(fi_uac1, struct f_uac1_opts, func_inst);
uac1_opts->p_chmask = p_chmask;
- uac1_opts->p_srate = p_srate;
+
+ for (i = 0; i < p_srates_cnt; ++i)
+ uac1_opts->p_srates[i] = p_srates[i];
+
uac1_opts->p_ssize = p_ssize;
uac1_opts->c_chmask = c_chmask;
- uac1_opts->c_srate = c_srate;
+
+ for (i = 0; i < c_srates_cnt; ++i)
+ uac1_opts->c_srates[i] = c_srates[i];
+
uac1_opts->c_ssize = c_ssize;
uac1_opts->req_number = UAC1_DEF_REQ_NUM;
#else /* CONFIG_GADGET_UAC1_LEGACY */
]
|
| 55/4 |
2026/07/31 01:57 |
llm |
codeexpert |
0m
Model:
Tokens:
input: 39318
output: 40
thoughts: 726
Thoughts: **Analyzing the Invalid-Free Bug in `f_uac1_opts_c_srate_store`**
Okay, let's break this down. The user's query centers around a KASAN: invalid-free report within the `f_uac1_opts_c_srate_store` function, specifically tied to how `strsep` is used with `kstrdup`. The code snippet clearly shows a potential issue.
First, I need to confirm the core problem. The code does this:
```c
split_page = kstrdup(page, GFP_KERNEL); \
while ((token = strsep(&split_page, ",")) != NULL) { \
...
end: \
kfree(split_page); \
```
So, `strsep` is modifying `split_page`. We're calling `kfree` on a pointer that *could* be anywhere inside the original allocation. That's a huge red flag. Let's make sure I understand how `strsep` works, though. It modifies the provided pointer, `stringp`, to point to the character *after* the delimiter. If there's no delimiter, the pointer becomes NULL.
Okay, inside the loop: if there's a comma, `split_page` is advanced. If there are no more commas, `split_page` becomes NULL, and `kfree(NULL)` is called, which is harmless, but... a memory leak. Now, here's where it gets interesting: what if we exit the loop early via `goto end` due to an error? Or what if we exited via error during the loop?
Wait, if we exit early – say, `ret` is non-zero after `kstrtou32` - `split_page` has already been updated by `strsep` to point somewhere *within* the original allocation. Then `kfree(split_page)` is called with an invalid pointer, leading to the KASAN: invalid-free. That is indeed the root cause of the bug.
And, as I thought, if we *don't* exit early, the loop finishes, `split_page` becomes NULL, and we call `kfree(NULL)`, and leak the original memory. So, we have two issues:
1. **Memory Leak:** If the loop completes successfully, a memory leak occurs because `kfree(NULL)` is called.
2. **Invalid-Free:** If the loop exits early (e.g., parsing error), `kfree` is called on an invalid pointer, leading to a memory corruption.
Alright, now let's see if this has been addressed. Time to check the git log. I need to search for commits that modify `f_uac1.c` or `f_uac2.c` and contain keywords like "invalid-free," "strsep," or "split_page" in their commit messages.
|
| 56/4 |
2026/07/31 01:57 |
tool |
git-log |
0m
Args: map[CodeRegexp:split_page Count:10 PathPrefix:drivers/usb/gadget/function/]
Results: map[Output:695d39ffc2b5 usb: gadget: f_uac1: Support multiple sampling rates
a7339e4f5788 usb: gadget: f_uac2: Support multiple sampling rates
]
|
| 57/4 |
2026/07/31 01:57 |
llm |
codeexpert |
0m
Model:
Tokens:
input: 40063
output: 31
thoughts: 0
|
| 58/4 |
2026/07/31 01:57 |
tool |
git-log |
10m
Error:
Args: map[CodeRegexp:f_uac1_opts_c_srate_store]
Results:
|
| 59/4 |
2026/07/31 02:07 |
llm |
codeexpert |
0m
Model:
Tokens:
input: 40129
output: 46
thoughts: 0
|
| 60/4 |
2026/07/31 02:07 |
tool |
git-log |
0m
Args: map[CodeRegexp:f_uac1_opts_c_srate_store PathPrefix:drivers/usb/gadget/function/]
Results:
|
| 61/4 |
2026/07/31 02:07 |
llm |
codeexpert |
0m
Model:
Tokens:
input: 40188
output: 40
thoughts: 0
|
| 62/4 |
2026/07/31 02:07 |
tool |
git-log |
0m
Args: map[MessageRegexps:[uac1 srate] PathPrefix:drivers/usb/gadget/function/]
Results: map[Output:8fe9a03f4331 usb: gadget: u_audio: Rate ctl notifies about current srate (0=stopped)
eb3a1ce6f5ed usb: gadget: u_audio: Add capture/playback srate getter
]
|
| 63/4 |
2026/07/31 02:07 |
llm |
codeexpert |
0m
Model:
Tokens:
input: 40299
output: 39
thoughts: 0
|
| 64/4 |
2026/07/31 02:07 |
tool |
git-log |
0m
Args: map[MessageRegexps:[uac1 free] PathPrefix:drivers/usb/gadget/function/]
Results: map[Output:6c67ed9ad9b8 usb: gadget: u_audio: don't let userspace block driver unbind
c76abecc42b6 usb: gadget: u_uac1: fix one code style problem
3703cfe78295 usb: gadget: uac1: struct gaudio is useless for struct f_uac1_opts
8d252db174ab usb: gadget: f_uac1: update Class-Specific AudioControl Interface Header Descriptor
625763d101c7 usb: gadget: f_uac1: configure endpoint before using it
4fde6204df05 usb: gadget: f_uac1: access freed memory at f_audio_free_inst
]
|
| 65/4 |
2026/07/31 02:07 |
llm |
codeexpert |
0m
Model:
Tokens:
input: 40528
output: 39
thoughts: 0
|
| 66/4 |
2026/07/31 02:07 |
tool |
git-log |
0m
Args: map[MessageRegexps:[uac2 free] PathPrefix:drivers/usb/gadget/function/]
Results: map[Output:a2cf936ebef2 usb: gadget: u_audio: Clear uac pointer when freed.
6c67ed9ad9b8 usb: gadget: u_audio: don't let userspace block driver unbind
75432ba583a8 usb: gadget: f_uac2: fixup feedback endpoint stop
40c73b30546e usb: gadget: f_uac2: add adaptive sync support for capture
789ea77310f0 usb: gadget: f_uac2: always increase endpoint max_packet_size by one audio slot
7de8681be2cd usb: gadget: u_audio: Free requests only after callback
f1d3861d63a5 usb: gadget: f_uac2: fix error handling at afunc_bind
0f4315a8f1a7 usb: gadget: f_uac2: fix build warning
913e4a90b6f9 usb: gadget: f_uac2: finalize wMaxPacketSize according to bandwidth
c41b7767673c usb: gadget: f_uac2: fix calculation of uac2->p_interval
]
|
| 67/4 |
2026/07/31 02:07 |
llm |
codeexpert |
0m
Model:
Tokens:
input: 40868
output: 20
thoughts: 172
Thoughts: **My Approach to Debugging this USB Gadget Issue**
Okay, here's how I'm going to tackle this. My initial instinct is to run `git log` on the entire repository, looking for commits related to `split_page`. But wait, that might be too broad. Let's focus our search. I need to pinpoint commits that actually *fix* this particular bug.
First, I think I need to define what the bug is that I am solving. It seems to be related to `f_uac1_opts_c_srate_store` or `f_uac2_opts_c_srate_store`, or something similar to that naming convention. Now I have a search target.
I'll refine the search. My instinct says I should target the USB gadget files themselves. So, I'll restrict my search to `drivers/usb/gadget/function/f_uac1.c` and `f_uac2.c`. I need to look for commits containing either "split_page" or "strsep" within those files. That's a good starting point.
But, a more focused approach is preferable. Let's make sure the search is really useful. A more efficient strategy is to execute `git log` with more precision. I can use `CodeRegexp: "split_page"` to focus on code changes involving that function, and/or `MessageRegexps: ["invalid-free"]` to search commit messages for related errors. The combination of these techniques should quickly get me to the relevant commits!
|
| 68/4 |
2026/07/31 02:07 |
tool |
git-log |
0m
Args: map[MessageRegexps:[invalid-free]]
Results: map[Output:37a23d6f1193 bus: mhi: host: Use kzalloc_flex
8ab3b09755d9 io_uring: use mempool KASAN hook
413643f3a3e2 mempool: use new mempool KASAN hooks
f129c3103928 kasan: introduce kasan_mempool_poison_pages
2e7c954c11af kasan: add return value for kasan_mempool_poison_object
280ec6ccb642 kasan: rename kasan_slab_free_mempool to kasan_mempool_poison_object
6e2bda2c192d jfs: fix invalid free of JFS_IP(ipimap)->i_imap in diUnmount
ef4c5afc783d net/mlx5: DR, Fix wrong action data allocation in decap action
98bea253aa28 fs/ntfs3: Validate MFT flags before replaying logs
880ce5f20033 net: avoid skb end_offset change in __skb_unclone_keeptruesize()
]
|
| 69/4 |
2026/07/31 02:08 |
llm |
codeexpert |
0m
Model:
Tokens:
input: 41520
output: 34
thoughts: 2719
Thoughts: **Analyzing the Kernel Bug: An Invalid Free in f_uac1/f_uac2**
Okay, so the user reports a KASAN invalid-free in `f_uac1_opts_c_srate_store`, and it looks like the `strsep` function is the culprit. The code allocates memory with `kstrdup`, then uses `strsep` to tokenize the string. The problem is that `strsep` modifies the input pointer. Let's dig in.
I need to confirm if this is a known issue. Let's check the current codebase. Ah, the macro `UAC1_RATE_ATTRIBUTE(name)` is the key. It defines the store function. The bug looks like it's right around line 1593 in `f_uac1.c`. Yes, I'm pretty sure I've seen it before: the `split_page` pointer is advanced by `strsep`.
Here's the scenario: `strsep` updates the pointer to the *remaining* part of the string. The code does `kfree(split_page)`. But, if the loop doesn't finish normally (e.g., `kstrtou32` fails), the pointer passed to `kfree` is now pointing *inside* the originally allocated buffer, not the start.
If `kstrtou32` fails, `goto end` is executed. The pointer `split_page` will point after the parsed token, if the token was at the start of the string, or after a comma if the token was not at the start. So, if `split_page` initially points to `"invalid,123"`, the first `strsep` sets `split_page` to point to `"123"`. When `kstrtou32` on "invalid" fails, and `goto end` is executed, then `kfree` is called on the `"123"` pointer, which is 8 bytes into the original allocation! That's our invalid free.
I'm pretty confident we have an invalid-free here. Let's see if this flaw is also present in `f_uac2.c`. Yes, it looks like it's in the same exact code block in `f_uac2.c`.
Now the important question: has this been fixed? Let's check the git log. I need to search for commits related to `f_uac1_opts_`, `f_uac2_opts_`, `split_page`, or `strsep`. Let's refine the search for commits mentioning `strsep` within the gadget function directory.
|
| 70/4 |
2026/07/31 02:08 |
tool |
git-log |
0m
Args: map[CodeRegexp:strsep PathPrefix:drivers/usb/gadget/function/]
Results: map[Output:695d39ffc2b5 usb: gadget: f_uac1: Support multiple sampling rates
a7339e4f5788 usb: gadget: f_uac2: Support multiple sampling rates
]
|
| 71/4 |
2026/07/31 02:08 |
llm |
codeexpert |
0m
Model:
Tokens:
input: 42196
output: 20
thoughts: 1725
Thoughts: **Analyzing a Potential Invalid-Free in `f_uac1_opts_##name##_store`**
Okay, so I'm looking at this `f_uac1_opts_##name##_store` function, and it's taking a string, `page`, that presumably contains a comma-separated list of numbers. The goal is to parse these numbers and store them in an `opts->name##s` array. I see they are using `kstrdup` to make a copy of the input, which is good to avoid messing with the original data. Then, it iterates through the copied string using `strsep` to tokenize it. `strsep` updates the pointer to the *next* part of the string after the comma.
Now, this is where it gets interesting. `strsep` modifies the input string in place, which is why it takes a pointer to a pointer. It replaces the comma with a null terminator. I'm going through the loop, and it calls `kstrtou32` to convert the token to a number. If that fails, it `goto end;`. At the end, there's a `kfree(split_page)`.
Initially, it seems fine. If `strsep` returns NULL, it's because there are no more tokens, and `split_page` is NULL at that point, so `kfree(NULL)` is a safe no-op. But let me trace a failure scenario. What if `kstrtou32` fails? Let's say the input is "123,abc". The first call to `strsep` gets "123", and it updates `split_page` to point to "abc". Then `kstrtou32` fails, `goto end;`, and `kfree(split_page)` is called. But at this point, `split_page` points to "abc", which is *within* the allocated buffer, not the start of it! **This is an invalid-free!** I'm calling `kfree` on a pointer to the middle of the allocated block.
Now, I need to think about exploitability. This is a configfs attribute, so it's likely accessible only to root, which limits the attack surface. Let me quickly check the permissions. I need to make sure this isn't something that can be accessed by a user in a container or namespace. Is it possible for a non-root user in a user namespace to mount configfs and write to it? I should check the kernel source for configfs mount flags and see if `FS_USERNS_MOUNT` is set for configfs.
|
| 72/4 |
2026/07/31 02:08 |
tool |
grepper |
0m
Args: map[Expression:configfs_fs_type]
Results: map[Output:fs/configfs/mount.c=109=static int configfs_init_fs_context(struct fs_context *fc)
--
fs/configfs/mount.c-114-
fs/configfs/mount.c:115:static struct file_system_type configfs_fs_type = {
fs/configfs/mount.c-116- .owner = THIS_MODULE,
--
fs/configfs/mount.c=123=struct dentry *configfs_pin_fs(void)
fs/configfs/mount.c-124-{
fs/configfs/mount.c:125: int err = simple_pin_fs(&configfs_fs_type, &configfs_mount,
fs/configfs/mount.c-126- &configfs_mnt_count);
--
fs/configfs/mount.c=136=static int __init configfs_init(void)
--
fs/configfs/mount.c-149-
fs/configfs/mount.c:150: err = register_filesystem(&configfs_fs_type);
fs/configfs/mount.c-151- if (err)
--
fs/configfs/mount.c=165=static void __exit configfs_exit(void)
fs/configfs/mount.c-166-{
fs/configfs/mount.c:167: unregister_filesystem(&configfs_fs_type);
fs/configfs/mount.c-168- sysfs_remove_mount_point(kernel_kobj, "config");
]
|
| 73/4 |
2026/07/31 02:08 |
llm |
codeexpert |
0m
Model:
Tokens:
input: 43193
output: 20
thoughts: 63
Thoughts: **Investigating the Configuration File System (configfs) Attribute Write Process**
Okay, so I'm diving into the `configfs` framework. My immediate focus is on how writes to attributes within the filesystem are handled – specifically, how the data from the user gets passed to the relevant `store` callback function. I'm starting with `configfs_register_subsystem` and the broader context it provides. This is my entry point. I need to understand the data flow, from the user writing to an attribute file, all the way down to the internal storage mechanism. This is key for understanding its security, and performance characteristics in relation to my specialty. Let's trace through the write implementation and dissect that process.
|
| 74/4 |
2026/07/31 02:08 |
tool |
grepper |
0m
Error:
Args: map[Expression:configfs_write_file]
Results:
|
| 75/4 |
2026/07/31 02:08 |
llm |
codeexpert |
0m
Model:
Tokens:
input: 43377
output: 20
thoughts: 0
|
| 76/4 |
2026/07/31 02:08 |
tool |
grepper |
0m
Args: map[Expression:configfs_item_operations]
Results: map[Output:
Full output is too long, showing 500 out of 651 lines.
Use more precise expression if possible.
Documentation/filesystems/configfs.rst=179=struct config_item_type
--
Documentation/filesystems/configfs.rst-183-
Documentation/filesystems/configfs.rst:184: struct configfs_item_operations {
Documentation/filesystems/configfs.rst-185- void (*release)(struct config_item *);
--
Documentation/filesystems/configfs.rst-193- struct module *ct_owner;
Documentation/filesystems/configfs.rst:194: struct configfs_item_operations *ct_item_ops;
Documentation/filesystems/configfs.rst-195- struct configfs_group_operations *ct_group_ops;
--
drivers/block/null_blk/main.c=637=static void nullb_device_release(struct config_item *item)
--
drivers/block/null_blk/main.c-644-
drivers/block/null_blk/main.c:645:static const struct configfs_item_operations nullb_device_ops = {
drivers/block/null_blk/main.c-646- .release = nullb_device_release,
--
drivers/gpio/gpio-aggregator.c=1232=gpio_aggregator_line_release(struct config_item *item)
--
drivers/gpio/gpio-aggregator.c-1244-
drivers/gpio/gpio-aggregator.c:1245:static const struct configfs_item_operations gpio_aggregator_line_item_ops = {
drivers/gpio/gpio-aggregator.c-1246- .release = gpio_aggregator_line_release,
--
drivers/gpio/gpio-aggregator.c=1255=static void gpio_aggregator_device_release(struct config_item *item)
--
drivers/gpio/gpio-aggregator.c-1265-
drivers/gpio/gpio-aggregator.c:1266:static const struct configfs_item_operations gpio_aggregator_device_item_ops = {
drivers/gpio/gpio-aggregator.c-1267- .release = gpio_aggregator_device_release,
--
drivers/gpio/gpio-sim.c=1375=static void gpio_sim_hog_config_item_release(struct config_item *item)
--
drivers/gpio/gpio-sim.c-1387-
drivers/gpio/gpio-sim.c:1388:static const struct configfs_item_operations gpio_sim_hog_config_item_ops = {
drivers/gpio/gpio-sim.c-1389- .release = gpio_sim_hog_config_item_release,
--
drivers/gpio/gpio-sim.c=1425=static void gpio_sim_line_config_group_release(struct config_item *item)
--
drivers/gpio/gpio-sim.c-1436-
drivers/gpio/gpio-sim.c:1437:static const struct configfs_item_operations gpio_sim_line_config_item_ops = {
drivers/gpio/gpio-sim.c-1438- .release = gpio_sim_line_config_group_release,
--
drivers/gpio/gpio-sim.c=1486=static void gpio_sim_bank_config_group_release(struct config_item *item)
--
drivers/gpio/gpio-sim.c-1497-
drivers/gpio/gpio-sim.c:1498:static const struct configfs_item_operations gpio_sim_bank_config_item_ops = {
drivers/gpio/gpio-sim.c-1499- .release = gpio_sim_bank_config_group_release,
--
drivers/gpio/gpio-sim.c=1539=static void gpio_sim_device_config_group_release(struct config_item *item)
--
drivers/gpio/gpio-sim.c-1552-
drivers/gpio/gpio-sim.c:1553:static const struct configfs_item_operations gpio_sim_device_config_item_ops = {
drivers/gpio/gpio-sim.c-1554- .release = gpio_sim_device_config_group_release,
--
drivers/gpio/gpio-virtuser.c=1595=static struct
drivers/gpio/gpio-virtuser.c:1596:configfs_item_operations gpio_virtuser_lookup_entry_config_item_ops = {
drivers/gpio/gpio-virtuser.c-1597- .release = gpio_virtuser_lookup_entry_config_group_release,
--
drivers/gpio/gpio-virtuser.c=1634=static void gpio_virtuser_lookup_config_group_release(struct config_item *item)
--
drivers/gpio/gpio-virtuser.c-1646-
drivers/gpio/gpio-virtuser.c:1647:static const struct configfs_item_operations gpio_virtuser_lookup_config_item_ops = {
drivers/gpio/gpio-virtuser.c-1648- .release = gpio_virtuser_lookup_config_group_release,
--
drivers/gpio/gpio-virtuser.c=1694=static void gpio_virtuser_device_config_group_release(struct config_item *item)
--
drivers/gpio/gpio-virtuser.c-1707-
drivers/gpio/gpio-virtuser.c:1708:static const struct configfs_item_operations gpio_virtuser_device_config_item_ops = {
drivers/gpio/gpio-virtuser.c-1709- .release = gpio_virtuser_device_config_group_release,
--
drivers/gpu/drm/vkms/vkms_configfs.c=178=static void crtc_release(struct config_item *item)
--
drivers/gpu/drm/vkms/vkms_configfs.c-191-
drivers/gpu/drm/vkms/vkms_configfs.c:192:static struct configfs_item_operations crtc_item_operations = {
drivers/gpu/drm/vkms/vkms_configfs.c-193- .release = &crtc_release,
--
drivers/gpu/drm/vkms/vkms_configfs.c=266=static void plane_possible_crtcs_drop_link(struct config_item *src,
--
drivers/gpu/drm/vkms/vkms_configfs.c-278-
drivers/gpu/drm/vkms/vkms_configfs.c:279:static struct configfs_item_operations plane_possible_crtcs_item_operations = {
drivers/gpu/drm/vkms/vkms_configfs.c-280- .allow_link = plane_possible_crtcs_allow_link,
--
drivers/gpu/drm/vkms/vkms_configfs.c=334=static void plane_release(struct config_item *item)
--
drivers/gpu/drm/vkms/vkms_configfs.c-347-
drivers/gpu/drm/vkms/vkms_configfs.c:348:static struct configfs_item_operations plane_item_operations = {
drivers/gpu/drm/vkms/vkms_configfs.c-349- .release = &plane_release,
--
drivers/gpu/drm/vkms/vkms_configfs.c=428=static void encoder_possible_crtcs_drop_link(struct config_item *src,
--
drivers/gpu/drm/vkms/vkms_configfs.c-440-
drivers/gpu/drm/vkms/vkms_configfs.c:441:static struct configfs_item_operations encoder_possible_crtcs_item_operations = {
drivers/gpu/drm/vkms/vkms_configfs.c-442- .allow_link = encoder_possible_crtcs_allow_link,
--
drivers/gpu/drm/vkms/vkms_configfs.c=451=static void encoder_release(struct config_item *item)
--
drivers/gpu/drm/vkms/vkms_configfs.c-464-
drivers/gpu/drm/vkms/vkms_configfs.c:465:static struct configfs_item_operations encoder_item_operations = {
drivers/gpu/drm/vkms/vkms_configfs.c-466- .release = &encoder_release,
--
drivers/gpu/drm/vkms/vkms_configfs.c=568=static void connector_release(struct config_item *item)
--
drivers/gpu/drm/vkms/vkms_configfs.c-581-
drivers/gpu/drm/vkms/vkms_configfs.c:582:static struct configfs_item_operations connector_item_operations = {
drivers/gpu/drm/vkms/vkms_configfs.c-583- .release = &connector_release,
--
drivers/gpu/drm/vkms/vkms_configfs.c=616=static void connector_possible_encoders_drop_link(struct config_item *src,
--
drivers/gpu/drm/vkms/vkms_configfs.c-630-
drivers/gpu/drm/vkms/vkms_configfs.c:631:static struct configfs_item_operations connector_possible_encoders_item_operations = {
drivers/gpu/drm/vkms/vkms_configfs.c-632- .allow_link = connector_possible_encoders_allow_link,
--
drivers/gpu/drm/vkms/vkms_configfs.c=739=static void device_release(struct config_item *item)
--
drivers/gpu/drm/vkms/vkms_configfs.c-752-
drivers/gpu/drm/vkms/vkms_configfs.c:753:static struct configfs_item_operations device_item_operations = {
drivers/gpu/drm/vkms/vkms_configfs.c-754- .release = &device_release,
--
drivers/gpu/drm/xe/xe_configfs.c=830=static void xe_config_device_release(struct config_item *item)
--
drivers/gpu/drm/xe/xe_configfs.c-840-
drivers/gpu/drm/xe/xe_configfs.c:841:static struct configfs_item_operations xe_config_device_ops = {
drivers/gpu/drm/xe/xe_configfs.c-842- .release = xe_config_device_release,
--
drivers/hwtracing/stm/policy.c=177=static void stp_policy_node_release(struct config_item *item)
--
drivers/hwtracing/stm/policy.c-183-
drivers/hwtracing/stm/policy.c:184:static struct configfs_item_operations stp_policy_node_item_ops = {
drivers/hwtracing/stm/policy.c-185- .release = stp_policy_node_release,
--
drivers/hwtracing/stm/policy.c=351=static void stp_policy_release(struct config_item *item)
--
drivers/hwtracing/stm/policy.c-366-
drivers/hwtracing/stm/policy.c:367:static struct configfs_item_operations stp_policy_item_ops = {
drivers/hwtracing/stm/policy.c-368- .release = stp_policy_release,
--
drivers/infiniband/core/cma_configfs.c=246=static void release_cma_ports_group(struct config_item *item)
--
drivers/infiniband/core/cma_configfs.c-257-
drivers/infiniband/core/cma_configfs.c:258:static const struct configfs_item_operations cma_ports_item_ops = {
drivers/infiniband/core/cma_configfs.c-259- .release = release_cma_ports_group
--
drivers/infiniband/core/cma_configfs.c=262=static const struct config_item_type cma_ports_group_type = {
--
drivers/infiniband/core/cma_configfs.c-266-
drivers/infiniband/core/cma_configfs.c:267:static const struct configfs_item_operations cma_device_item_ops = {
drivers/infiniband/core/cma_configfs.c-268- .release = release_cma_dev
--
drivers/most/configfs.c=380=static void mdev_link_release(struct config_item *item)
--
drivers/most/configfs.c-401-
drivers/most/configfs.c:402:static struct configfs_item_operations mdev_link_item_ops = {
drivers/most/configfs.c-403- .release = mdev_link_release,
--
drivers/most/configfs.c=450=static void most_common_release(struct config_item *item)
--
drivers/most/configfs.c-456-
drivers/most/configfs.c:457:static struct configfs_item_operations most_common_item_ops = {
drivers/most/configfs.c-458- .release = most_common_release,
--
drivers/most/configfs.c=566=static void most_snd_grp_release(struct config_item *item)
--
drivers/most/configfs.c-573-
drivers/most/configfs.c:574:static struct configfs_item_operations most_snd_grp_item_ops = {
drivers/most/configfs.c-575- .release = most_snd_grp_release,
--
drivers/net/netconsole.c=1288=static void userdatum_release(struct config_item *item)
--
drivers/net/netconsole.c-1292-
drivers/net/netconsole.c:1293:static const struct configfs_item_operations userdatum_ops = {
drivers/net/netconsole.c-1294- .release = userdatum_release,
--
drivers/net/netconsole.c=1391=static void netconsole_target_release(struct config_item *item)
--
drivers/net/netconsole.c-1398-
drivers/net/netconsole.c:1399:static const struct configfs_item_operations netconsole_target_item_ops = {
drivers/net/netconsole.c-1400- .release = netconsole_target_release,
--
drivers/nvme/target/configfs.c=840=static void nvmet_ns_release(struct config_item *item)
--
drivers/nvme/target/configfs.c-846-
drivers/nvme/target/configfs.c:847:static struct configfs_item_operations nvmet_ns_item_ops = {
drivers/nvme/target/configfs.c-848- .release = nvmet_ns_release,
--
drivers/nvme/target/configfs.c=1097=static void nvmet_port_subsys_drop_link(struct config_item *parent,
--
drivers/nvme/target/configfs.c-1122-
drivers/nvme/target/configfs.c:1123:static struct configfs_item_operations nvmet_port_subsys_item_ops = {
drivers/nvme/target/configfs.c-1124- .allow_link = nvmet_port_subsys_allow_link,
--
drivers/nvme/target/configfs.c=1175=static void nvmet_allowed_hosts_drop_link(struct config_item *parent,
--
drivers/nvme/target/configfs.c-1197-
drivers/nvme/target/configfs.c:1198:static struct configfs_item_operations nvmet_allowed_hosts_item_ops = {
drivers/nvme/target/configfs.c-1199- .allow_link = nvmet_allowed_hosts_allow_link,
--
drivers/nvme/target/configfs.c=1716=static void nvmet_subsys_release(struct config_item *item)
--
drivers/nvme/target/configfs.c-1723-
drivers/nvme/target/configfs.c:1724:static struct configfs_item_operations nvmet_subsys_item_ops = {
drivers/nvme/target/configfs.c-1725- .release = nvmet_subsys_release,
--
drivers/nvme/target/configfs.c=1830=static void nvmet_referral_release(struct config_item *item)
--
drivers/nvme/target/configfs.c-1836-
drivers/nvme/target/configfs.c:1837:static struct configfs_item_operations nvmet_referral_item_ops = {
drivers/nvme/target/configfs.c-1838- .release = nvmet_referral_release,
--
drivers/nvme/target/configfs.c=1927=static void nvmet_ana_group_release(struct config_item *item)
--
drivers/nvme/target/configfs.c-1942-
drivers/nvme/target/configfs.c:1943:static struct configfs_item_operations nvmet_ana_group_item_ops = {
drivers/nvme/target/configfs.c-1944- .release = nvmet_ana_group_release,
--
drivers/nvme/target/configfs.c=2013=static struct configfs_attribute *nvmet_port_attrs[] = {
--
drivers/nvme/target/configfs.c-2028-
drivers/nvme/target/configfs.c:2029:static struct configfs_item_operations nvmet_port_item_ops = {
drivers/nvme/target/configfs.c-2030- .release = nvmet_port_release,
--
drivers/nvme/target/configfs.c=2248=static void nvmet_host_release(struct config_item *item)
--
drivers/nvme/target/configfs.c-2258-
drivers/nvme/target/configfs.c:2259:static struct configfs_item_operations nvmet_host_item_ops = {
drivers/nvme/target/configfs.c-2260- .release = nvmet_host_release,
--
drivers/pci/endpoint/pci-ep-cfs.c=71=static void pci_secondary_epc_epf_unlink(struct config_item *epf_item,
--
drivers/pci/endpoint/pci-ep-cfs.c-86-
drivers/pci/endpoint/pci-ep-cfs.c:87:static const struct configfs_item_operations pci_secondary_epc_item_ops = {
drivers/pci/endpoint/pci-ep-cfs.c-88- .allow_link = pci_secondary_epc_epf_link,
--
drivers/pci/endpoint/pci-ep-cfs.c=135=static void pci_primary_epc_epf_unlink(struct config_item *epf_item,
--
drivers/pci/endpoint/pci-ep-cfs.c-150-
drivers/pci/endpoint/pci-ep-cfs.c:151:static const struct configfs_item_operations pci_primary_epc_item_ops = {
drivers/pci/endpoint/pci-ep-cfs.c-152- .allow_link = pci_primary_epc_epf_link,
--
drivers/pci/endpoint/pci-ep-cfs.c=243=static void pci_epc_epf_unlink(struct config_item *epc_item,
--
drivers/pci/endpoint/pci-ep-cfs.c-258-
drivers/pci/endpoint/pci-ep-cfs.c:259:static const struct configfs_item_operations pci_epc_item_ops = {
drivers/pci/endpoint/pci-ep-cfs.c-260- .allow_link = pci_epc_epf_link,
--
drivers/pci/endpoint/pci-ep-cfs.c=499=static void pci_epf_release(struct config_item *item)
--
drivers/pci/endpoint/pci-ep-cfs.c-509-
drivers/pci/endpoint/pci-ep-cfs.c:510:static const struct configfs_item_operations pci_epf_ops = {
drivers/pci/endpoint/pci-ep-cfs.c-511- .allow_link = pci_epf_vepf_link,
--
drivers/target/target_core_configfs.c=2772=static void target_core_dev_release(struct config_item *item)
--
drivers/target/target_core_configfs.c-2784- */
drivers/target/target_core_configfs.c:2785:struct configfs_item_operations target_core_dev_item_ops = {
drivers/target/target_core_configfs.c-2786- .release = target_core_dev_release,
--
drivers/target/target_core_configfs.c=2874=static void target_core_alua_lu_gp_release(struct config_item *item)
--
drivers/target/target_core_configfs.c-2881-
drivers/target/target_core_configfs.c:2882:static const struct configfs_item_operations target_core_alua_lu_gp_ops = {
drivers/target/target_core_configfs.c-2883- .release = target_core_alua_lu_gp_release,
--
drivers/target/target_core_configfs.c=3304=static void target_core_alua_tg_pt_gp_release(struct config_item *item)
--
drivers/target/target_core_configfs.c-3311-
drivers/target/target_core_configfs.c:3312:static const struct configfs_item_operations target_core_alua_tg_pt_gp_ops = {
drivers/target/target_core_configfs.c-3313- .release = target_core_alua_tg_pt_gp_release,
--
drivers/target/target_core_configfs.c=3611=static struct configfs_attribute *target_core_hba_attrs[] = {
--
drivers/target/target_core_configfs.c-3616-
drivers/target/target_core_configfs.c:3617:static const struct configfs_item_operations target_core_hba_item_ops = {
drivers/target/target_core_configfs.c-3618- .release = target_core_hba_release,
--
drivers/target/target_core_fabric_configfs.c=50=static void target_fabric_setup_##_name##_cit(struct target_fabric_configfs *tf) \
--
drivers/target/target_core_fabric_configfs.c-61-
drivers/target/target_core_fabric_configfs.c:62:static const struct configfs_item_operations target_fabric_port_item_ops;
drivers/target/target_core_fabric_configfs.c-63-
--
drivers/target/target_core_fabric_configfs.c=213=static void target_fabric_mappedlun_release(struct config_item *item)
--
drivers/target/target_core_fabric_configfs.c-221-
drivers/target/target_core_fabric_configfs.c:222:static const struct configfs_item_operations target_fabric_mappedlun_item_ops = {
drivers/target/target_core_fabric_configfs.c-223- .release = target_fabric_mappedlun_release,
--
drivers/target/target_core_fabric_configfs.c=339=static void target_fabric_nacl_base_release(struct config_item *item)
--
drivers/target/target_core_fabric_configfs.c-347-
drivers/target/target_core_fabric_configfs.c:348:static const struct configfs_item_operations target_fabric_nacl_base_item_ops = {
drivers/target/target_core_fabric_configfs.c-349- .release = target_fabric_nacl_base_release,
--
drivers/target/target_core_fabric_configfs.c=447=static void target_fabric_np_base_release(struct config_item *item)
--
drivers/target/target_core_fabric_configfs.c-456-
drivers/target/target_core_fabric_configfs.c:457:static const struct configfs_item_operations target_fabric_np_base_item_ops = {
drivers/target/target_core_fabric_configfs.c-458- .release = target_fabric_np_base_release,
--
drivers/target/target_core_fabric_configfs.c=695=static void target_fabric_port_release(struct config_item *item)
--
drivers/target/target_core_fabric_configfs.c-702-
drivers/target/target_core_fabric_configfs.c:703:static const struct configfs_item_operations target_fabric_port_item_ops = {
drivers/target/target_core_fabric_configfs.c-704- .release = target_fabric_port_release,
--
drivers/target/target_core_fabric_configfs.c=805=static void target_fabric_tpg_release(struct config_item *item)
--
drivers/target/target_core_fabric_configfs.c-814-
drivers/target/target_core_fabric_configfs.c:815:static const struct configfs_item_operations target_fabric_tpg_base_item_ops = {
drivers/target/target_core_fabric_configfs.c-816- .release = target_fabric_tpg_release,
--
drivers/target/target_core_fabric_configfs.c=990=static void target_fabric_release_wwn(struct config_item *item)
--
drivers/target/target_core_fabric_configfs.c-1000-
drivers/target/target_core_fabric_configfs.c:1001:static const struct configfs_item_operations target_fabric_tpg_item_ops = {
drivers/target/target_core_fabric_configfs.c-1002- .release = target_fabric_release_wwn,
--
drivers/target/target_core_internal.h=91=void target_dev_ua_allocate(struct se_device *dev, u8 asc, u8 ascq);
--
drivers/target/target_core_internal.h-93-/* target_core_configfs.c */
drivers/target/target_core_internal.h:94:extern struct configfs_item_operations target_core_dev_item_ops;
drivers/target/target_core_internal.h-95-void target_setup_backend_cits(struct target_backend *);
--
drivers/thunderbolt/stream.c=1219=static void tbstream_dev_item_release(struct config_item *item)
--
drivers/thunderbolt/stream.c-1227-
drivers/thunderbolt/stream.c:1228:static struct configfs_item_operations tbstream_dev_item_ops = {
drivers/thunderbolt/stream.c-1229- .release = tbstream_dev_item_release,
--
drivers/thunderbolt/stream.c=1410=static void tbstream_item_release(struct config_item *item)
--
drivers/thunderbolt/stream.c-1418-
drivers/thunderbolt/stream.c:1419:static struct configfs_item_operations tbstream_item_ops = {
drivers/thunderbolt/stream.c-1420- .release = tbstream_item_release,
--
drivers/usb/gadget/configfs.c=400=static void gadget_info_attr_release(struct config_item *item)
--
drivers/usb/gadget/configfs.c-411-
drivers/usb/gadget/configfs.c:412:static const struct configfs_item_operations gadget_root_item_ops = {
drivers/usb/gadget/configfs.c-413- .release = gadget_info_attr_release,
--
drivers/usb/gadget/configfs.c=483=static void config_usb_cfg_unlink(
--
drivers/usb/gadget/configfs.c-516-
drivers/usb/gadget/configfs.c:517:static const struct configfs_item_operations gadget_config_item_ops = {
drivers/usb/gadget/configfs.c-518- .release = gadget_config_attr_release,
--
drivers/usb/gadget/configfs.c=790=static void gadget_language_attr_release(struct config_item *item)
--
drivers/usb/gadget/configfs.c-801-
drivers/usb/gadget/configfs.c:802:static const struct configfs_item_operations gadget_language_langid_item_ops = {
drivers/usb/gadget/configfs.c-803- .release = gadget_language_attr_release,
--
drivers/usb/gadget/configfs.c=848=static void gadget_string_release(struct config_item *item)
--
drivers/usb/gadget/configfs.c-854-
drivers/usb/gadget/configfs.c:855:static const struct configfs_item_operations gadget_string_item_ops = {
drivers/usb/gadget/configfs.c-856- .release = gadget_string_release,
--
drivers/usb/gadget/configfs.c=1255=static void os_desc_unlink(struct config_item *os_desc_ci,
--
drivers/usb/gadget/configfs.c-1268-
drivers/usb/gadget/configfs.c:1269:static const struct configfs_item_operations os_desc_ops = {
drivers/usb/gadget/configfs.c-1270- .allow_link = os_desc_link,
--
drivers/usb/gadget/configfs.c=1387=static void usb_os_desc_ext_prop_release(struct config_item *item)
--
drivers/usb/gadget/configfs.c-1393-
drivers/usb/gadget/configfs.c:1394:static const struct configfs_item_operations ext_prop_ops = {
drivers/usb/gadget/configfs.c-1395- .release = usb_os_desc_ext_prop_release,
--
drivers/usb/gadget/function/f_acm.c=789=static void acm_attr_release(struct config_item *item)
--
drivers/usb/gadget/function/f_acm.c-795-
drivers/usb/gadget/function/f_acm.c:796:static const struct configfs_item_operations acm_item_ops = {
drivers/usb/gadget/function/f_acm.c-797- .release = acm_attr_release,
--
drivers/usb/gadget/function/f_fs.c=4011=static void ffs_attr_release(struct config_item *item)
--
drivers/usb/gadget/function/f_fs.c-4017-
drivers/usb/gadget/function/f_fs.c:4018:static const struct configfs_item_operations ffs_item_ops = {
drivers/usb/gadget/function/f_fs.c-4019- .release = ffs_attr_release,
--
drivers/usb/gadget/function/f_hid.c=1326=static void hid_attr_release(struct config_item *item)
--
drivers/usb/gadget/function/f_hid.c-1332-
drivers/usb/gadget/function/f_hid.c:1333:static const struct configfs_item_operations hidg_item_ops = {
drivers/usb/gadget/function/f_hid.c-1334- .release = hid_attr_release,
--
drivers/usb/gadget/function/f_loopback.c=460=static void lb_attr_release(struct config_item *item)
--
drivers/usb/gadget/function/f_loopback.c-466-
drivers/usb/gadget/function/f_loopback.c:467:static const struct configfs_item_operations lb_item_ops = {
drivers/usb/gadget/function/f_loopback.c-468- .release = lb_attr_release,
--
drivers/usb/gadget/function/f_mass_storage.c=3156=static void fsg_lun_attr_release(struct config_item *item)
--
drivers/usb/gadget/function/f_mass_storage.c-3163-
drivers/usb/gadget/function/f_mass_storage.c:3164:static const struct configfs_item_operations fsg_lun_item_ops = {
drivers/usb/gadget/function/f_mass_storage.c-3165- .release = fsg_lun_attr_release,
--
drivers/usb/gadget/function/f_mass_storage.c=3373=static void fsg_attr_release(struct config_item *item)
--
drivers/usb/gadget/function/f_mass_storage.c-3379-
drivers/usb/gadget/function/f_mass_storage.c:3380:static const struct configfs_item_operations fsg_item_ops = {
drivers/usb/gadget/function/f_mass_storage.c-3381- .release = fsg_attr_release,
--
drivers/usb/gadget/function/f_midi.c=1086=static void midi_attr_release(struct config_item *item)
--
drivers/usb/gadget/function/f_midi.c-1092-
drivers/usb/gadget/function/f_midi.c:1093:static const struct configfs_item_operations midi_item_ops = {
drivers/usb/gadget/function/f_midi.c-1094- .release = midi_attr_release,
--
drivers/usb/gadget/function/f_midi2.c=2310=static void f_midi2_block_opts_release(struct config_item *item)
--
drivers/usb/gadget/function/f_midi2.c-2317-
drivers/usb/gadget/function/f_midi2.c:2318:static const struct configfs_item_operations f_midi2_block_item_ops = {
drivers/usb/gadget/function/f_midi2.c-2319- .release = f_midi2_block_opts_release,
--
drivers/usb/gadget/function/f_midi2.c=2472=static void f_midi2_ep_opts_release(struct config_item *item)
--
drivers/usb/gadget/function/f_midi2.c-2480-
drivers/usb/gadget/function/f_midi2.c:2481:static const struct configfs_item_operations f_midi2_ep_item_ops = {
drivers/usb/gadget/function/f_midi2.c-2482- .release = f_midi2_ep_opts_release,
--
drivers/usb/gadget/function/f_midi2.c=2613=static void f_midi2_opts_release(struct config_item *item)
--
drivers/usb/gadget/function/f_midi2.c-2619-
drivers/usb/gadget/function/f_midi2.c:2620:static const struct configfs_item_operations f_midi2_item_ops = {
drivers/usb/gadget/function/f_midi2.c-2621- .release = f_midi2_opts_release,
--
drivers/usb/gadget/function/f_obex.c=386=static void obex_attr_release(struct config_item *item)
--
drivers/usb/gadget/function/f_obex.c-392-
drivers/usb/gadget/function/f_obex.c:393:static const struct configfs_item_operations obex_item_ops = {
drivers/usb/gadget/function/f_obex.c-394- .release = obex_attr_release,
--
drivers/usb/gadget/function/f_phonet.c=590=static void phonet_attr_release(struct config_item *item)
--
drivers/usb/gadget/function/f_phonet.c-596-
drivers/usb/gadget/function/f_phonet.c:597:static const struct configfs_item_operations phonet_item_ops = {
drivers/usb/gadget/function/f_phonet.c-598- .release = phonet_attr_release,
--
drivers/usb/gadget/function/f_printer.c=1228=static void printer_attr_release(struct config_item *item)
--
drivers/usb/gadget/function/f_printer.c-1234-
drivers/usb/gadget/function/f_printer.c:1235:static const struct configfs_item_operations printer_item_ops = {
drivers/usb/gadget/function/f_printer.c-1236- .release = printer_attr_release,
--
drivers/usb/gadget/function/f_serial.c=256=static void serial_attr_release(struct config_item *item)
--
drivers/usb/gadget/function/f_serial.c-262-
drivers/usb/gadget/function/f_serial.c:263:static const struct configfs_item_operations serial_item_ops = {
drivers/usb/gadget/function/f_serial.c-264- .release = serial_attr_release,
--
drivers/usb/gadget/function/f_sourcesink.c=886=static void ss_attr_release(struct config_item *item)
--
drivers/usb/gadget/function/f_sourcesink.c-892-
drivers/usb/gadget/function/f_sourcesink.c:893:static const struct configfs_item_operations ss_item_ops = {
drivers/usb/gadget/function/f_sourcesink.c-894- .release = ss_attr_release,
--
drivers/usb/gadget/function/f_tcm.c=2587=static void tcm_attr_release(struct config_item *item)
--
drivers/usb/gadget/function/f_tcm.c-2593-
drivers/usb/gadget/function/f_tcm.c:2594:static const struct configfs_item_operations tcm_item_ops = {
drivers/usb/gadget/function/f_tcm.c-2595- .release = tcm_attr_release,
--
drivers/usb/gadget/function/f_uac1.c=1508=static void f_uac1_attr_release(struct config_item *item)
--
drivers/usb/gadget/function/f_uac1.c-1514-
drivers/usb/gadget/function/f_uac1.c:1515:static const struct configfs_item_operations f_uac1_item_ops = {
drivers/usb/gadget/function/f_uac1.c-1516- .release = f_uac1_attr_release,
--
drivers/usb/gadget/function/f_uac1_legacy.c=835=static void f_uac1_attr_release(struct config_item *item)
--
drivers/usb/gadget/function/f_uac1_legacy.c-841-
drivers/usb/gadget/function/f_uac1_legacy.c:842:static const struct configfs_item_operations f_uac1_item_ops = {
drivers/usb/gadget/function/f_uac1_legacy.c-843- .release = f_uac1_attr_release,
--
drivers/usb/gadget/function/f_uac2.c=1870=static void f_uac2_attr_release(struct config_item *item)
--
drivers/usb/gadget/function/f_uac2.c-1876-
drivers/usb/gadget/function/f_uac2.c:1877:static const struct configfs_item_operations f_uac2_item_ops = {
drivers/usb/gadget/function/f_uac2.c-1878- .release = f_uac2_attr_release,
--
drivers/usb/gadget/function/u_ether_configfs.h-23- \
drivers/usb/gadget/function/u_ether_configfs.h:24: static const struct configfs_item_operations _f_##_item_ops = { \
drivers/usb/gadget/function/u_ether_configfs.h-25- .release = _f_##_attr_release, \
--
drivers/usb/gadget/function/uvc_configfs.c=124=static void uvcg_config_item_release(struct config_item *item)
--
drivers/usb/gadget/function/uvc_configfs.c-130-
drivers/usb/gadget/function/uvc_configfs.c:131:static const struct configfs_item_operations uvcg_config_item_ops = {
drivers/usb/gadget/function/uvc_configfs.c-132- .release = uvcg_config_item_release,
--
drivers/usb/gadget/function/uvc_configfs.c=1215=static void uvcg_extension_drop_link(struct config_item *src, struct config_item *tgt)
--
drivers/usb/gadget/function/uvc_configfs.c-1235-
drivers/usb/gadget/function/uvc_configfs.c:1236:static const struct configfs_item_operations uvcg_extension_item_ops = {
drivers/usb/gadget/function/uvc_configfs.c-1237- .release = uvcg_extension_release,
--
drivers/usb/gadget/function/uvc_configfs.c=1382=static void uvcg_control_class_drop_link(struct config_item *src,
--
]
|
| 77/4 |
2026/07/31 02:08 |
llm |
codeexpert |
0m
Model:
Tokens:
input: 54439
output: 20
thoughts: 0
|
| 78/4 |
2026/07/31 02:08 |
tool |
grepper |
0m
Args: map[Expression:configfs_write_iter]
Results: map[Output:fs/configfs/file.c=200=flush_write_buffer(struct file *file, struct configfs_buffer *buffer, size_t count)
--
fs/configfs/file.c-219- */
fs/configfs/file.c:220:static ssize_t configfs_write_iter(struct kiocb *iocb, struct iov_iter *from)
fs/configfs/file.c-221-{
--
fs/configfs/file.c=424=const struct file_operations configfs_file_operations = {
fs/configfs/file.c-425- .read_iter = configfs_read_iter,
fs/configfs/file.c:426: .write_iter = configfs_write_iter,
fs/configfs/file.c-427- .llseek = generic_file_llseek,
]
|
| 79/4 |
2026/07/31 02:08 |
llm |
codeexpert |
0m
Model:
Tokens:
input: 54693
output: 37
thoughts: 0
|
| 80/4 |
2026/07/31 02:08 |
tool |
read-file |
0m
Args: map[File:fs/configfs/file.c FirstLine:200 LineCount:100]
Results: map[Contents: 200: flush_write_buffer(struct file *file, struct configfs_buffer *buffer, size_t count)
201: {
202: struct configfs_fragment *frag = to_frag(file);
203: int res = -ENOENT;
204:
205: down_read(&frag->frag_sem);
206: if (!frag->frag_dead)
207: res = buffer->attr->store(buffer->item, buffer->page, count);
208: up_read(&frag->frag_sem);
209: return res;
210: }
211:
212:
213: /*
214: * There is no easy way for us to know if userspace is only doing a partial
215: * write, so we don't support them. We expect the entire buffer to come on the
216: * first write.
217: * Hint: if you're writing a value, first read the file, modify only the value
218: * you're changing, then write entire buffer back.
219: */
220: static ssize_t configfs_write_iter(struct kiocb *iocb, struct iov_iter *from)
221: {
222: struct file *file = iocb->ki_filp;
223: struct configfs_buffer *buffer = file->private_data;
224: int len;
225:
226: mutex_lock(&buffer->mutex);
227: len = fill_write_buffer(buffer, from);
228: if (len > 0)
229: len = flush_write_buffer(file, buffer, len);
230: if (len > 0)
231: iocb->ki_pos += len;
232: mutex_unlock(&buffer->mutex);
233: return len;
234: }
235:
236: static ssize_t configfs_bin_write_iter(struct kiocb *iocb,
237: struct iov_iter *from)
238: {
239: struct file *file = iocb->ki_filp;
240: struct configfs_buffer *buffer = file->private_data;
241: void *tbuf = NULL;
242: size_t end_offset;
243: ssize_t len;
244:
245: mutex_lock(&buffer->mutex);
246:
247: /* we don't support switching read/write modes */
248: if (buffer->read_in_progress) {
249: len = -ETXTBSY;
250: goto out;
251: }
252: buffer->write_in_progress = true;
253:
254: /* buffer grows? */
255: end_offset = iocb->ki_pos + iov_iter_count(from);
256: if (end_offset > buffer->bin_buffer_size) {
257: if (buffer->cb_max_size && end_offset > buffer->cb_max_size) {
258: len = -EFBIG;
259: goto out;
260: }
261:
262: tbuf = vmalloc(end_offset);
263: if (tbuf == NULL) {
264: len = -ENOMEM;
265: goto out;
266: }
267:
268: /* copy old contents */
269: if (buffer->bin_buffer) {
270: memcpy(tbuf, buffer->bin_buffer,
271: buffer->bin_buffer_size);
272: vfree(buffer->bin_buffer);
273: }
274:
275: /* clear the new area */
276: memset(tbuf + buffer->bin_buffer_size, 0,
277: end_offset - buffer->bin_buffer_size);
278: buffer->bin_buffer = tbuf;
279: buffer->bin_buffer_size = end_offset;
280: }
281:
282: len = copy_from_iter(buffer->bin_buffer + iocb->ki_pos,
283: buffer->bin_buffer_size - iocb->ki_pos, from);
284: iocb->ki_pos += len;
285: out:
286: mutex_unlock(&buffer->mutex);
287: return len ? : -EFAULT;
288: }
289:
290: static int __configfs_open_file(struct inode *inode, struct file *file, int type)
291: {
292: struct dentry *dentry = file->f_path.dentry;
293: struct configfs_fragment *frag = to_frag(file);
294: struct configfs_attribute *attr;
295: struct configfs_buffer *buffer;
296: int error;
297:
298: error = -ENOMEM;
299: buffer = kzalloc_obj(struct configfs_buffer);
]
|
| 81/4 |
2026/07/31 02:08 |
llm |
codeexpert |
0m
Model:
Tokens:
input: 56239
output: 37
thoughts: 49
Thoughts: Okay, here's my interpretation of that thought process, assuming I'm an expert in file system internals:
**Diving into `read-file` in `configfs`**
Right, so `call:default_api:read-file{File:fs/configfs/file.c,FirstLine:100,LineCount:100}`... that's my target. I need to understand how `configfs` handles file reading through its default API. Specifically, I'm zeroing in on lines 100 through 200 within `fs/configfs/file.c`. This suggests a crucial piece of the file reading puzzle is handled in that section of code. I'm going to look at that region and see how `buffer->page` is actually allocated and populated with data, because that's often where the core file data handling happens. Understanding that `buffer->page` allocation will tell me the method used to move file contents for retrieval, and, the most interesting parts: if this allocation is happening efficiently, and if the method used is optimized for this particular configuration file system. I'm looking for memory management, and how this file system handles any potential errors that might arise with read requests.
|
| 82/4 |
2026/07/31 02:09 |
tool |
read-file |
0m
Args: map[File:fs/configfs/file.c FirstLine:100 LineCount:100]
Results: map[Contents: 100: retval = -EFAULT;
101: out:
102: mutex_unlock(&buffer->mutex);
103: return retval;
104: }
105:
106: static ssize_t configfs_bin_read_iter(struct kiocb *iocb, struct iov_iter *to)
107: {
108: struct file *file = iocb->ki_filp;
109: struct configfs_fragment *frag = to_frag(file);
110: struct configfs_buffer *buffer = file->private_data;
111: ssize_t retval = 0;
112: ssize_t len;
113:
114: mutex_lock(&buffer->mutex);
115:
116: /* we don't support switching read/write modes */
117: if (buffer->write_in_progress) {
118: retval = -ETXTBSY;
119: goto out;
120: }
121: buffer->read_in_progress = true;
122:
123: if (buffer->needs_read_fill) {
124: /* perform first read with buf == NULL to get extent */
125: down_read(&frag->frag_sem);
126: if (!frag->frag_dead)
127: len = buffer->bin_attr->read(buffer->item, NULL, 0);
128: else
129: len = -ENOENT;
130: up_read(&frag->frag_sem);
131: if (len <= 0) {
132: retval = len;
133: goto out;
134: }
135:
136: /* do not exceed the maximum value */
137: if (buffer->cb_max_size && len > buffer->cb_max_size) {
138: retval = -EFBIG;
139: goto out;
140: }
141:
142: buffer->bin_buffer = vmalloc(len);
143: if (buffer->bin_buffer == NULL) {
144: retval = -ENOMEM;
145: goto out;
146: }
147: buffer->bin_buffer_size = len;
148:
149: /* perform second read to fill buffer */
150: down_read(&frag->frag_sem);
151: if (!frag->frag_dead)
152: len = buffer->bin_attr->read(buffer->item,
153: buffer->bin_buffer, len);
154: else
155: len = -ENOENT;
156: up_read(&frag->frag_sem);
157: if (len < 0) {
158: retval = len;
159: vfree(buffer->bin_buffer);
160: buffer->bin_buffer_size = 0;
161: buffer->bin_buffer = NULL;
162: goto out;
163: }
164:
165: buffer->needs_read_fill = 0;
166: }
167:
168: if (iocb->ki_pos >= buffer->bin_buffer_size)
169: goto out;
170: retval = copy_to_iter(buffer->bin_buffer + iocb->ki_pos,
171: buffer->bin_buffer_size - iocb->ki_pos, to);
172: iocb->ki_pos += retval;
173: if (retval == 0)
174: retval = -EFAULT;
175: out:
176: mutex_unlock(&buffer->mutex);
177: return retval;
178: }
179:
180: /* Fill @buffer with data coming from @from. */
181: static int fill_write_buffer(struct configfs_buffer *buffer,
182: struct iov_iter *from)
183: {
184: int copied;
185:
186: if (!buffer->page)
187: buffer->page = kmalloc(PAGE_SIZE, GFP_KERNEL);
188: if (!buffer->page)
189: return -ENOMEM;
190:
191: copied = copy_from_iter(buffer->page, SIMPLE_ATTR_SIZE - 1, from);
192: buffer->needs_read_fill = 1;
193: /* if buf is assumed to contain a string, terminate it by \0,
194: * so e.g. sscanf() can scan the string easily */
195: buffer->page[copied] = 0;
196: return copied ? : -EFAULT;
197: }
198:
199: static int
]
|
| 83/4 |
2026/07/31 02:09 |
llm |
codeexpert |
0m
Model:
Tokens:
input: 57966
output: 38
thoughts: 0
|
| 84/4 |
2026/07/31 02:09 |
tool |
git-log |
6m
Args: map[CodeRegexp:f_uac1_opts_c_srate_store Since:3 years]
Results:
|
| 85/4 |
2026/07/31 02:15 |
llm |
codeexpert |
0m
Model:
Tokens:
input: 58017
output: 43
thoughts: 0
|
| 86/4 |
2026/07/31 02:15 |
tool |
git-log |
0m
Args: map[CodeRegexp:f_uac1_opts_.*_store PathPrefix:drivers/usb/gadget/function/]
Results: map[Output:dfb05b5dc3af usb: gadget: f_uac1: allow changing interface name via configfs
695d39ffc2b5 usb: gadget: f_uac1: Support multiple sampling rates
0591bc236015 usb: gadget: add f_uac1 variant based on a new u_audio api
c6f89f1cca1c usb-gadget/f_uac1: use per-attribute show and store methods
0854611a19ae usb: gadget: f_uac1: add configfs support
]
|
| 87/4 |
2026/07/31 02:15 |
llm |
codeexpert |
0m
Model:
Tokens:
input: 58213
output: 43
thoughts: 0
|
| 88/4 |
2026/07/31 02:15 |
tool |
git-show |
0m
Args: map[Commit:695d39ffc2b5:drivers/usb/gadget/function/f_uac1.c]
Results: map[Output:
Full output is too long, showing 1000 out of 1701 lines.
[// SPDX-License-Identifier: GPL-2.0+
/*
* f_uac1.c -- USB Audio Class 1.0 Function (using u_audio API)
*
* Copyright (C) 2016 Ruslan Bilovol <ruslan.bilovol@gmail.com>
* Copyright (C) 2021 Julian Scheel <julian@jusst.de>
*
* This driver doesn't expect any real Audio codec to be present
* on the device - the audio streams are simply sinked to and
* sourced from a virtual ALSA sound card created.
*
* This file is based on f_uac1.c which is
* Copyright (C) 2008 Bryan Wu <cooloney@kernel.org>
* Copyright (C) 2008 Analog Devices, Inc
*/
#include <linux/usb/audio.h>
#include <linux/module.h>
#include "u_audio.h"
#include "u_uac1.h"
/* UAC1 spec: 3.7.2.3 Audio Channel Cluster Format */
#define UAC1_CHANNEL_MASK 0x0FFF
#define USB_OUT_FU_ID (out_feature_unit_desc->bUnitID)
#define USB_IN_FU_ID (in_feature_unit_desc->bUnitID)
#define EPIN_EN(_opts) ((_opts)->p_chmask != 0)
#define EPOUT_EN(_opts) ((_opts)->c_chmask != 0)
#define FUIN_EN(_opts) ((_opts)->p_mute_present \
|| (_opts)->p_volume_present)
#define FUOUT_EN(_opts) ((_opts)->c_mute_present \
|| (_opts)->c_volume_present)
struct f_uac1 {
struct g_audio g_audio;
u8 ac_intf, as_in_intf, as_out_intf;
u8 ac_alt, as_in_alt, as_out_alt; /* needed for get_alt() */
struct usb_ctrlrequest setup_cr; /* will be used in data stage */
/* Interrupt IN endpoint of AC interface */
struct usb_ep *int_ep;
atomic_t int_count;
int ctl_id; /* EP id */
int c_srate; /* current capture srate */
int p_srate; /* current playback prate */
};
static inline struct f_uac1 *func_to_uac1(struct usb_function *f)
{
return container_of(f, struct f_uac1, g_audio.func);
}
static inline struct f_uac1_opts *g_audio_to_uac1_opts(struct g_audio *audio)
{
return container_of(audio->func.fi, struct f_uac1_opts, func_inst);
}
/*
* DESCRIPTORS ... most are static, but strings and full
* configuration descriptors are built on demand.
*/
/*
* We have three interfaces - one AudioControl and two AudioStreaming
*
* The driver implements a simple UAC_1 topology.
* USB-OUT -> IT_1 -> OT_2 -> ALSA_Capture
* ALSA_Playback -> IT_3 -> OT_4 -> USB-IN
*/
/* B.3.1 Standard AC Interface Descriptor */
static struct usb_interface_descriptor ac_interface_desc = {
.bLength = USB_DT_INTERFACE_SIZE,
.bDescriptorType = USB_DT_INTERFACE,
/* .bNumEndpoints = DYNAMIC */
.bInterfaceClass = USB_CLASS_AUDIO,
.bInterfaceSubClass = USB_SUBCLASS_AUDIOCONTROL,
};
/* B.3.2 Class-Specific AC Interface Descriptor */
static struct uac1_ac_header_descriptor *ac_header_desc;
static struct uac_input_terminal_descriptor usb_out_it_desc = {
.bLength = UAC_DT_INPUT_TERMINAL_SIZE,
.bDescriptorType = USB_DT_CS_INTERFACE,
.bDescriptorSubtype = UAC_INPUT_TERMINAL,
/* .bTerminalID = DYNAMIC */
.wTerminalType = cpu_to_le16(UAC_TERMINAL_STREAMING),
.bAssocTerminal = 0,
.wChannelConfig = cpu_to_le16(0x3),
};
static struct uac1_output_terminal_descriptor io_out_ot_desc = {
.bLength = UAC_DT_OUTPUT_TERMINAL_SIZE,
.bDescriptorType = USB_DT_CS_INTERFACE,
.bDescriptorSubtype = UAC_OUTPUT_TERMINAL,
/* .bTerminalID = DYNAMIC */
.wTerminalType = cpu_to_le16(UAC_OUTPUT_TERMINAL_SPEAKER),
.bAssocTerminal = 0,
/* .bSourceID = DYNAMIC */
};
static struct uac_input_terminal_descriptor io_in_it_desc = {
.bLength = UAC_DT_INPUT_TERMINAL_SIZE,
.bDescriptorType = USB_DT_CS_INTERFACE,
.bDescriptorSubtype = UAC_INPUT_TERMINAL,
/* .bTerminalID = DYNAMIC */
.wTerminalType = cpu_to_le16(UAC_INPUT_TERMINAL_MICROPHONE),
.bAssocTerminal = 0,
.wChannelConfig = cpu_to_le16(0x3),
};
static struct uac1_output_terminal_descriptor usb_in_ot_desc = {
.bLength = UAC_DT_OUTPUT_TERMINAL_SIZE,
.bDescriptorType = USB_DT_CS_INTERFACE,
.bDescriptorSubtype = UAC_OUTPUT_TERMINAL,
/* .bTerminalID = DYNAMIC */
.wTerminalType = cpu_to_le16(UAC_TERMINAL_STREAMING),
.bAssocTerminal = 0,
/* .bSourceID = DYNAMIC */
};
static struct uac_feature_unit_descriptor *in_feature_unit_desc;
static struct uac_feature_unit_descriptor *out_feature_unit_desc;
/* AC IN Interrupt Endpoint */
static struct usb_endpoint_descriptor ac_int_ep_desc = {
.bLength = USB_DT_ENDPOINT_SIZE,
.bDescriptorType = USB_DT_ENDPOINT,
.bEndpointAddress = USB_DIR_IN,
.bmAttributes = USB_ENDPOINT_XFER_INT,
.wMaxPacketSize = cpu_to_le16(2),
.bInterval = 4,
};
/* B.4.1 Standard AS Interface Descriptor */
static struct usb_interface_descriptor as_out_interface_alt_0_desc = {
.bLength = USB_DT_INTERFACE_SIZE,
.bDescriptorType = USB_DT_INTERFACE,
.bAlternateSetting = 0,
.bNumEndpoints = 0,
.bInterfaceClass = USB_CLASS_AUDIO,
.bInterfaceSubClass = USB_SUBCLASS_AUDIOSTREAMING,
};
static struct usb_interface_descriptor as_out_interface_alt_1_desc = {
.bLength = USB_DT_INTERFACE_SIZE,
.bDescriptorType = USB_DT_INTERFACE,
.bAlternateSetting = 1,
.bNumEndpoints = 1,
.bInterfaceClass = USB_CLASS_AUDIO,
.bInterfaceSubClass = USB_SUBCLASS_AUDIOSTREAMING,
};
static struct usb_interface_descriptor as_in_interface_alt_0_desc = {
.bLength = USB_DT_INTERFACE_SIZE,
.bDescriptorType = USB_DT_INTERFACE,
.bAlternateSetting = 0,
.bNumEndpoints = 0,
.bInterfaceClass = USB_CLASS_AUDIO,
.bInterfaceSubClass = USB_SUBCLASS_AUDIOSTREAMING,
};
static struct usb_interface_descriptor as_in_interface_alt_1_desc = {
.bLength = USB_DT_INTERFACE_SIZE,
.bDescriptorType = USB_DT_INTERFACE,
.bAlternateSetting = 1,
.bNumEndpoints = 1,
.bInterfaceClass = USB_CLASS_AUDIO,
.bInterfaceSubClass = USB_SUBCLASS_AUDIOSTREAMING,
};
/* B.4.2 Class-Specific AS Interface Descriptor */
static struct uac1_as_header_descriptor as_out_header_desc = {
.bLength = UAC_DT_AS_HEADER_SIZE,
.bDescriptorType = USB_DT_CS_INTERFACE,
.bDescriptorSubtype = UAC_AS_GENERAL,
/* .bTerminalLink = DYNAMIC */
.bDelay = 1,
.wFormatTag = cpu_to_le16(UAC_FORMAT_TYPE_I_PCM),
};
static struct uac1_as_header_descriptor as_in_header_desc = {
.bLength = UAC_DT_AS_HEADER_SIZE,
.bDescriptorType = USB_DT_CS_INTERFACE,
.bDescriptorSubtype = UAC_AS_GENERAL,
/* .bTerminalLink = DYNAMIC */
.bDelay = 1,
.wFormatTag = cpu_to_le16(UAC_FORMAT_TYPE_I_PCM),
};
DECLARE_UAC_FORMAT_TYPE_I_DISCRETE_DESC(UAC_MAX_RATES);
#define uac_format_type_i_discrete_descriptor \
uac_format_type_i_discrete_descriptor_##UAC_MAX_RATES
static struct uac_format_type_i_discrete_descriptor as_out_type_i_desc = {
.bLength = 0, /* filled on rate setup */
.bDescriptorType = USB_DT_CS_INTERFACE,
.bDescriptorSubtype = UAC_FORMAT_TYPE,
.bFormatType = UAC_FORMAT_TYPE_I,
.bSubframeSize = 2,
.bBitResolution = 16,
.bSamFreqType = 0, /* filled on rate setup */
};
/* Standard ISO OUT Endpoint Descriptor */
static struct usb_endpoint_descriptor as_out_ep_desc = {
.bLength = USB_DT_ENDPOINT_AUDIO_SIZE,
.bDescriptorType = USB_DT_ENDPOINT,
.bEndpointAddress = USB_DIR_OUT,
.bmAttributes = USB_ENDPOINT_SYNC_ADAPTIVE
| USB_ENDPOINT_XFER_ISOC,
.wMaxPacketSize = cpu_to_le16(UAC1_OUT_EP_MAX_PACKET_SIZE),
.bInterval = 4,
};
/* Class-specific AS ISO OUT Endpoint Descriptor */
static struct uac_iso_endpoint_descriptor as_iso_out_desc = {
.bLength = UAC_ISO_ENDPOINT_DESC_SIZE,
.bDescriptorType = USB_DT_CS_ENDPOINT,
.bDescriptorSubtype = UAC_EP_GENERAL,
.bmAttributes = 1,
.bLockDelayUnits = 1,
.wLockDelay = cpu_to_le16(1),
};
static struct uac_format_type_i_discrete_descriptor as_in_type_i_desc = {
.bLength = 0, /* filled on rate setup */
.bDescriptorType = USB_DT_CS_INTERFACE,
.bDescriptorSubtype = UAC_FORMAT_TYPE,
.bFormatType = UAC_FORMAT_TYPE_I,
.bSubframeSize = 2,
.bBitResolution = 16,
.bSamFreqType = 0, /* filled on rate setup */
};
/* Standard ISO OUT Endpoint Descriptor */
static struct usb_endpoint_descriptor as_in_ep_desc = {
.bLength = USB_DT_ENDPOINT_AUDIO_SIZE,
.bDescriptorType = USB_DT_ENDPOINT,
.bEndpointAddress = USB_DIR_IN,
.bmAttributes = USB_ENDPOINT_SYNC_ASYNC
| USB_ENDPOINT_XFER_ISOC,
.wMaxPacketSize = cpu_to_le16(UAC1_OUT_EP_MAX_PACKET_SIZE),
.bInterval = 4,
};
/* Class-specific AS ISO OUT Endpoint Descriptor */
static struct uac_iso_endpoint_descriptor as_iso_in_desc = {
.bLength = UAC_ISO_ENDPOINT_DESC_SIZE,
.bDescriptorType = USB_DT_CS_ENDPOINT,
.bDescriptorSubtype = UAC_EP_GENERAL,
.bmAttributes = 1,
.bLockDelayUnits = 0,
.wLockDelay = 0,
};
static struct usb_descriptor_header *f_audio_desc[] = {
(struct usb_descriptor_header *)&ac_interface_desc,
(struct usb_descriptor_header *)&ac_header_desc,
(struct usb_descriptor_header *)&usb_out_it_desc,
(struct usb_descriptor_header *)&io_out_ot_desc,
(struct usb_descriptor_header *)&out_feature_unit_desc,
(struct usb_descriptor_header *)&io_in_it_desc,
(struct usb_descriptor_header *)&usb_in_ot_desc,
(struct usb_descriptor_header *)&in_feature_unit_desc,
(struct usb_descriptor_header *)&ac_int_ep_desc,
(struct usb_descriptor_header *)&as_out_interface_alt_0_desc,
(struct usb_descriptor_header *)&as_out_interface_alt_1_desc,
(struct usb_descriptor_header *)&as_out_header_desc,
(struct usb_descriptor_header *)&as_out_type_i_desc,
(struct usb_descriptor_header *)&as_out_ep_desc,
(struct usb_descriptor_header *)&as_iso_out_desc,
(struct usb_descriptor_header *)&as_in_interface_alt_0_desc,
(struct usb_descriptor_header *)&as_in_interface_alt_1_desc,
(struct usb_descriptor_header *)&as_in_header_desc,
(struct usb_descriptor_header *)&as_in_type_i_desc,
(struct usb_descriptor_header *)&as_in_ep_desc,
(struct usb_descriptor_header *)&as_iso_in_desc,
NULL,
};
enum {
STR_AC_IF,
STR_USB_OUT_IT,
STR_USB_OUT_IT_CH_NAMES,
STR_IO_OUT_OT,
STR_IO_IN_IT,
STR_IO_IN_IT_CH_NAMES,
STR_USB_IN_OT,
STR_FU_IN,
STR_FU_OUT,
STR_AS_OUT_IF_ALT0,
STR_AS_OUT_IF_ALT1,
STR_AS_IN_IF_ALT0,
STR_AS_IN_IF_ALT1,
};
static struct usb_string strings_uac1[] = {
[STR_AC_IF].s = "AC Interface",
[STR_USB_OUT_IT].s = "Playback Input terminal",
[STR_USB_OUT_IT_CH_NAMES].s = "Playback Channels",
[STR_IO_OUT_OT].s = "Playback Output terminal",
[STR_IO_IN_IT].s = "Capture Input terminal",
[STR_IO_IN_IT_CH_NAMES].s = "Capture Channels",
[STR_USB_IN_OT].s = "Capture Output terminal",
[STR_FU_IN].s = "Capture Volume",
[STR_FU_OUT].s = "Playback Volume",
[STR_AS_OUT_IF_ALT0].s = "Playback Inactive",
[STR_AS_OUT_IF_ALT1].s = "Playback Active",
[STR_AS_IN_IF_ALT0].s = "Capture Inactive",
[STR_AS_IN_IF_ALT1].s = "Capture Active",
{ },
};
static struct usb_gadget_strings str_uac1 = {
.language = 0x0409, /* en-us */
.strings = strings_uac1,
};
static struct usb_gadget_strings *uac1_strings[] = {
&str_uac1,
NULL,
};
/*
* This function is an ALSA sound card following USB Audio Class Spec 1.0.
*/
static void uac_cs_attr_sample_rate(struct usb_ep *ep, struct usb_request *req)
{
struct usb_function *fn = ep->driver_data;
struct usb_composite_dev *cdev = fn->config->cdev;
struct g_audio *agdev = func_to_g_audio(fn);
struct f_uac1 *uac1 = func_to_uac1(fn);
u8 *buf = (u8 *)req->buf;
u32 val = 0;
if (req->actual != 3) {
WARN(cdev, "Invalid data size for UAC_EP_CS_ATTR_SAMPLE_RATE.\n");
return;
}
val = buf[0] | (buf[1] << 8) | (buf[2] << 16);
if (uac1->ctl_id == (USB_DIR_IN | 2)) {
uac1->p_srate = val;
u_audio_set_playback_srate(agdev, uac1->p_srate);
} else if (uac1->ctl_id == (USB_DIR_OUT | 1)) {
uac1->c_srate = val;
u_audio_set_capture_srate(agdev, uac1->c_srate);
}
}
static void audio_notify_complete(struct usb_ep *_ep, struct usb_request *req)
{
struct g_audio *audio = req->context;
struct f_uac1 *uac1 = func_to_uac1(&audio->func);
atomic_dec(&uac1->int_count);
kfree(req->buf);
usb_ep_free_request(_ep, req);
}
static int audio_notify(struct g_audio *audio, int unit_id, int cs)
{
struct f_uac1 *uac1 = func_to_uac1(&audio->func);
struct usb_request *req;
struct uac1_status_word *msg;
int ret;
if (!uac1->int_ep->enabled)
return 0;
if (atomic_inc_return(&uac1->int_count) > UAC1_DEF_INT_REQ_NUM) {
atomic_dec(&uac1->int_count);
return 0;
}
req = usb_ep_alloc_request(uac1->int_ep, GFP_ATOMIC);
if (req == NULL) {
ret = -ENOMEM;
goto err_dec_int_count;
}
msg = kmalloc(sizeof(*msg), GFP_ATOMIC);
if (msg == NULL) {
ret = -ENOMEM;
goto err_free_request;
}
msg->bStatusType = UAC1_STATUS_TYPE_IRQ_PENDING
| UAC1_STATUS_TYPE_ORIG_AUDIO_CONTROL_IF;
msg->bOriginator = unit_id;
req->length = sizeof(*msg);
req->buf = msg;
req->context = audio;
req->complete = audio_notify_complete;
ret = usb_ep_queue(uac1->int_ep, req, GFP_ATOMIC);
if (ret)
goto err_free_msg;
return 0;
err_free_msg:
kfree(msg);
err_free_request:
usb_ep_free_request(uac1->int_ep, req);
err_dec_int_count:
atomic_dec(&uac1->int_count);
return ret;
}
static int
in_rq_cur(struct usb_function *fn, const struct usb_ctrlrequest *cr)
{
struct usb_request *req = fn->config->cdev->req;
struct g_audio *audio = func_to_g_audio(fn);
struct f_uac1_opts *opts = g_audio_to_uac1_opts(audio);
u16 w_length = le16_to_cpu(cr->wLength);
u16 w_index = le16_to_cpu(cr->wIndex);
u16 w_value = le16_to_cpu(cr->wValue);
u8 entity_id = (w_index >> 8) & 0xff;
u8 control_selector = w_value >> 8;
int value = -EOPNOTSUPP;
if ((FUIN_EN(opts) && (entity_id == USB_IN_FU_ID)) ||
(FUOUT_EN(opts) && (entity_id == USB_OUT_FU_ID))) {
unsigned int is_playback = 0;
if (FUIN_EN(opts) && (entity_id == USB_IN_FU_ID))
is_playback = 1;
if (control_selector == UAC_FU_MUTE) {
unsigned int mute;
u_audio_get_mute(audio, is_playback, &mute);
*(u8 *)req->buf = mute;
value = min_t(unsigned int, w_length, 1);
} else if (control_selector == UAC_FU_VOLUME) {
__le16 c;
s16 volume;
u_audio_get_volume(audio, is_playback, &volume);
c = cpu_to_le16(volume);
value = min_t(unsigned int, w_length, sizeof(c));
memcpy(req->buf, &c, value);
} else {
dev_err(&audio->gadget->dev,
"%s:%d control_selector=%d TODO!\n",
__func__, __LINE__, control_selector);
}
} else {
dev_err(&audio->gadget->dev,
"%s:%d entity_id=%d control_selector=%d TODO!\n",
__func__, __LINE__, entity_id, control_selector);
}
return value;
}
static int
in_rq_min(struct usb_function *fn, const struct usb_ctrlrequest *cr)
{
struct usb_request *req = fn->config->cdev->req;
struct g_audio *audio = func_to_g_audio(fn);
struct f_uac1_opts *opts = g_audio_to_uac1_opts(audio);
u16 w_length = le16_to_cpu(cr->wLength);
u16 w_index = le16_to_cpu(cr->wIndex);
u16 w_value = le16_to_cpu(cr->wValue);
u8 entity_id = (w_index >> 8) & 0xff;
u8 control_selector = w_value >> 8;
int value = -EOPNOTSUPP;
if ((FUIN_EN(opts) && (entity_id == USB_IN_FU_ID)) ||
(FUOUT_EN(opts) && (entity_id == USB_OUT_FU_ID))) {
unsigned int is_playback = 0;
if (FUIN_EN(opts) && (entity_id == USB_IN_FU_ID))
is_playback = 1;
if (control_selector == UAC_FU_VOLUME) {
__le16 r;
s16 min_db;
if (is_playback)
min_db = opts->p_volume_min;
else
min_db = opts->c_volume_min;
r = cpu_to_le16(min_db);
value = min_t(unsigned int, w_length, sizeof(r));
memcpy(req->buf, &r, value);
} else {
dev_err(&audio->gadget->dev,
"%s:%d control_selector=%d TODO!\n",
__func__, __LINE__, control_selector);
}
} else {
dev_err(&audio->gadget->dev,
"%s:%d entity_id=%d control_selector=%d TODO!\n",
__func__, __LINE__, entity_id, control_selector);
}
return value;
}
static int
in_rq_max(struct usb_function *fn, const struct usb_ctrlrequest *cr)
{
struct usb_request *req = fn->config->cdev->req;
struct g_audio *audio = func_to_g_audio(fn);
struct f_uac1_opts *opts = g_audio_to_uac1_opts(audio);
u16 w_length = le16_to_cpu(cr->wLength);
u16 w_index = le16_to_cpu(cr->wIndex);
u16 w_value = le16_to_cpu(cr->wValue);
u8 entity_id = (w_index >> 8) & 0xff;
u8 control_selector = w_value >> 8;
int value = -EOPNOTSUPP;
if ((FUIN_EN(opts) && (entity_id == USB_IN_FU_ID)) ||
(FUOUT_EN(opts) && (entity_id == USB_OUT_FU_ID))) {
unsigned int is_playback = 0;
if (FUIN_EN(opts) && (entity_id == USB_IN_FU_ID))
is_playback = 1;
if (control_selector == UAC_FU_VOLUME) {
__le16 r;
s16 max_db;
if (is_playback)
max_db = opts->p_volume_max;
else
max_db = opts->c_volume_max;
r = cpu_to_le16(max_db);
value = min_t(unsigned int, w_length, sizeof(r));
memcpy(req->buf, &r, value);
} else {
dev_err(&audio->gadget->dev,
"%s:%d control_selector=%d TODO!\n",
__func__, __LINE__, control_selector);
}
} else {
dev_err(&audio->gadget->dev,
"%s:%d entity_id=%d control_selector=%d TODO!\n",
__func__, __LINE__, entity_id, control_selector);
}
return value;
}
static int
in_rq_res(struct usb_function *fn, const struct usb_ctrlrequest *cr)
{
struct usb_request *req = fn->config->cdev->req;
struct g_audio *audio = func_to_g_audio(fn);
struct f_uac1_opts *opts = g_audio_to_uac1_opts(audio);
u16 w_length = le16_to_cpu(cr->wLength);
u16 w_index = le16_to_cpu(cr->wIndex);
u16 w_value = le16_to_cpu(cr->wValue);
u8 entity_id = (w_index >> 8) & 0xff;
u8 control_selector = w_value >> 8;
int value = -EOPNOTSUPP;
if ((FUIN_EN(opts) && (entity_id == USB_IN_FU_ID)) ||
(FUOUT_EN(opts) && (entity_id == USB_OUT_FU_ID))) {
unsigned int is_playback = 0;
if (FUIN_EN(opts) && (entity_id == USB_IN_FU_ID))
is_playback = 1;
if (control_selector == UAC_FU_VOLUME) {
__le16 r;
s16 res_db;
if (is_playback)
res_db = opts->p_volume_res;
else
res_db = opts->c_volume_res;
r = cpu_to_le16(res_db);
value = min_t(unsigned int, w_length, sizeof(r));
memcpy(req->buf, &r, value);
} else {
dev_err(&audio->gadget->dev,
"%s:%d control_selector=%d TODO!\n",
__func__, __LINE__, control_selector);
}
} else {
dev_err(&audio->gadget->dev,
"%s:%d entity_id=%d control_selector=%d TODO!\n",
__func__, __LINE__, entity_id, control_selector);
}
return value;
}
static void
out_rq_cur_complete(struct usb_ep *ep, struct usb_request *req)
{
struct g_audio *audio = req->context;
struct usb_composite_dev *cdev = audio->func.config->cdev;
struct f_uac1_opts *opts = g_audio_to_uac1_opts(audio);
struct f_uac1 *uac1 = func_to_uac1(&audio->func);
struct usb_ctrlrequest *cr = &uac1->setup_cr;
u16 w_index = le16_to_cpu(cr->wIndex);
u16 w_value = le16_to_cpu(cr->wValue);
u8 entity_id = (w_index >> 8) & 0xff;
u8 control_selector = w_value >> 8;
if (req->status != 0) {
dev_dbg(&cdev->gadget->dev, "completion err %d\n", req->status);
return;
}
if ((FUIN_EN(opts) && (entity_id == USB_IN_FU_ID)) ||
(FUOUT_EN(opts) && (entity_id == USB_OUT_FU_ID))) {
unsigned int is_playback = 0;
if (FUIN_EN(opts) && (entity_id == USB_IN_FU_ID))
is_playback = 1;
if (control_selector == UAC_FU_MUTE) {
u8 mute = *(u8 *)req->buf;
u_audio_set_mute(audio, is_playback, mute);
return;
} else if (control_selector == UAC_FU_VOLUME) {
__le16 *c = req->buf;
s16 volume;
volume = le16_to_cpu(*c);
u_audio_set_volume(audio, is_playback, volume);
return;
} else {
dev_err(&audio->gadget->dev,
"%s:%d control_selector=%d TODO!\n",
__func__, __LINE__, control_selector);
usb_ep_set_halt(ep);
}
} else {
dev_err(&audio->gadget->dev,
"%s:%d entity_id=%d control_selector=%d TODO!\n",
__func__, __LINE__, entity_id, control_selector);
usb_ep_set_halt(ep);
}
}
static int
out_rq_cur(struct usb_function *fn, const struct usb_ctrlrequest *cr)
{
struct usb_request *req = fn->config->cdev->req;
struct g_audio *audio = func_to_g_audio(fn);
struct f_uac1_opts *opts = g_audio_to_uac1_opts(audio);
struct f_uac1 *uac1 = func_to_uac1(&audio->func);
u16 w_length = le16_to_cpu(cr->wLength);
u16 w_index = le16_to_cpu(cr->wIndex);
u16 w_value = le16_to_cpu(cr->wValue);
u8 entity_id = (w_index >> 8) & 0xff;
u8 control_selector = w_value >> 8;
if ((FUIN_EN(opts) && (entity_id == USB_IN_FU_ID)) ||
(FUOUT_EN(opts) && (entity_id == USB_OUT_FU_ID))) {
memcpy(&uac1->setup_cr, cr, sizeof(*cr));
req->context = audio;
req->complete = out_rq_cur_complete;
return w_length;
} else {
dev_err(&audio->gadget->dev,
"%s:%d entity_id=%d control_selector=%d TODO!\n",
__func__, __LINE__, entity_id, control_selector);
}
return -EOPNOTSUPP;
}
static int ac_rq_in(struct usb_function *f,
const struct usb_ctrlrequest *ctrl)
{
struct usb_composite_dev *cdev = f->config->cdev;
int value = -EOPNOTSUPP;
u8 ep = ((le16_to_cpu(ctrl->wIndex) >> 8) & 0xFF);
u16 len = le16_to_cpu(ctrl->wLength);
u16 w_value = le16_to_cpu(ctrl->wValue);
DBG(cdev, "bRequest 0x%x, w_value 0x%04x, len %d, endpoint %d\n",
ctrl->bRequest, w_value, len, ep);
switch (ctrl->bRequest) {
case UAC_GET_CUR:
return in_rq_cur(f, ctrl);
case UAC_GET_MIN:
return in_rq_min(f, ctrl);
case UAC_GET_MAX:
return in_rq_max(f, ctrl);
case UAC_GET_RES:
return in_rq_res(f, ctrl);
case UAC_GET_MEM:
break;
case UAC_GET_STAT:
value = len;
break;
default:
break;
}
return value;
}
static int audio_set_endpoint_req(struct usb_function *f,
const struct usb_ctrlrequest *ctrl)
{
struct usb_composite_dev *cdev = f->config->cdev;
struct usb_request *req = f->config->cdev->req;
struct f_uac1 *uac1 = func_to_uac1(f);
int value = -EOPNOTSUPP;
u16 ep = le16_to_cpu(ctrl->wIndex);
u16 len = le16_to_cpu(ctrl->wLength);
u16 w_value = le16_to_cpu(ctrl->wValue);
u8 cs = w_value >> 8;
DBG(cdev, "bRequest 0x%x, w_value 0x%04x, len %d, endpoint %d\n",
ctrl->bRequest, w_value, len, ep);
switch (ctrl->bRequest) {
case UAC_SET_CUR: {
if (cs == UAC_EP_CS_ATTR_SAMPLE_RATE) {
cdev->gadget->ep0->driver_data = f;
uac1->ctl_id = ep;
req->complete = uac_cs_attr_sample_rate;
}
value = len;
break;
}
case UAC_SET_MIN:
break;
case UAC_SET_MAX:
break;
case UAC_SET_RES:
break;
case UAC_SET_MEM:
break;
default:
break;
}
return value;
}
static int audio_get_endpoint_req(struct usb_function *f,
const struct usb_ctrlrequest *ctrl)
{
struct usb_composite_dev *cdev = f->config->cdev;
struct usb_request *req = f->config->cdev->req;
struct f_uac1 *uac1 = func_to_uac1(f);
u8 *buf = (u8 *)req->buf;
int value = -EOPNOTSUPP;
u8 ep = le16_to_cpu(ctrl->wIndex);
u16 len = le16_to_cpu(ctrl->wLength);
u16 w_value = le16_to_cpu(ctrl->wValue);
u8 cs = w_value >> 8;
u32 val = 0;
DBG(cdev, "bRequest 0x%x, w_value 0x%04x, len %d, endpoint %d\n",
ctrl->bRequest, w_value, len, ep);
switch (ctrl->bRequest) {
case UAC_GET_CUR: {
if (cs == UAC_EP_CS_ATTR_SAMPLE_RATE) {
if (ep == (USB_DIR_IN | 2))
val = uac1->p_srate;
else if (ep == (USB_DIR_OUT | 1))
val = uac1->c_srate;
buf[2] = (val >> 16) & 0xff;
buf[1] = (val >> 8) & 0xff;
buf[0] = val & 0xff;
}
value = len;
break;
}
case UAC_GET_MIN:
case UAC_GET_MAX:
case UAC_GET_RES:
value = len;
break;
case UAC_GET_MEM:
break;
default:
break;
}
return value;
}
static int
f_audio_setup(struct usb_function *f, const struct usb_ctrlrequest *ctrl)
{
struct usb_composite_dev *cdev = f->config->cdev;
struct usb_request *req = cdev->req;
int value = -EOPNOTSUPP;
u16 w_index = le16_to_cpu(ctrl->wIndex);
u16 w_value = le16_to_cpu(ctrl->wValue);
u16 w_length = le16_to_cpu(ctrl->wLength);
/* composite driver infrastructure handles everything; interface
* activation uses set_alt().
*/
switch (ctrl->bRequestType) {
case USB_DIR_OUT | USB_TYPE_CLASS | USB_RECIP_ENDPOINT:
value = audio_set_endpoint_req(f, ctrl);
break;
case USB_DIR_IN | USB_TYPE_CLASS | USB_RECIP_ENDPOINT:
value = audio_get_endpoint_req(f, ctrl);
break;
case USB_DIR_OUT | USB_TYPE_CLASS | USB_RECIP_INTERFACE:
if (ctrl->bRequest == UAC_SET_CUR)
value = out_rq_cur(f, ctrl);
break;
case USB_DIR_IN | USB_TYPE_CLASS | USB_RECIP_INTERFACE:
value = ac_rq_in(f, ctrl);
break;
default:
ERROR(cdev, "invalid control req%02x.%02x v%04x i%04x l%d\n",
ctrl->bRequestType, ctrl->bRequest,
w_value, w_index, w_length);
}
/* respond with data transfer or status phase? */
if (value >= 0) {
DBG(cdev, "audio req%02x.%02x v%04x i%04x l%d\n",
ctrl->bRequestType, ctrl->bRequest,
w_value, w_index, w_length);
req->zero = 0;
req->length = value;
value = usb_ep_queue(cdev->gadget->ep0, req, GFP_ATOMIC);
if (value < 0)
ERROR(cdev, "audio response on err %d\n", value);
}
/* device either stalls (value < 0) or reports success */
return value;
}
static int f_audio_set_alt(struct usb_function *f, unsigned intf, unsigned alt)
{
struct usb_composite_dev *cdev = f->config->cdev;
struct usb_gadget *gadget = cdev->gadget;
struct device *dev = &gadget->dev;
struct g_audio *audio = func_to_g_audio(f);
struct f_uac1 *uac1 = func_to_uac1(f);
int ret = 0;
/* No i/f has more than 2 alt settings */
if (alt > 1) {
dev_err(dev, "%s:%d Error!\n", __func__, __LINE__);
return -EINVAL;
}
if (intf == uac1->ac_intf) {
/* Control I/f has only 1 AltSetting - 0 */
if (alt) {
dev_err(dev, "%s:%d Error!\n", __func__, __LINE__);
return -EINVAL;
}
/* restart interrupt endpoint */
if (uac1->int_ep) {
usb_ep_disable(uac1->int_ep);
config_ep_by_speed(gadget, &audio->func, uac1->int_ep);
usb_ep_enable(uac1->int_ep);
}
return 0;
}
if (intf == uac1->as_out_intf) {
uac1->as_out_alt = alt;
if (alt)
ret = u_audio_start_capture(&uac1->g_audio);
else
u_audio_stop_capture(&uac1->g_audio);
} else if (intf == uac1->as_in_intf) {
uac1->as_in_alt = alt;
if (alt)
ret = u_audio_start_playback(&uac1->g_audio);
else
u_audio_stop_playback(&uac1->g_audio);
} else {
dev_err(dev, "%s:%d Error!\n", __func__, __LINE__);
return -EINVAL;
}
return ret;
}
static int f_audio_get_alt(struct usb_function *f, unsigned intf)
{
struct usb_composite_dev *cdev = f->config->cdev;
struct usb_gadget *gadget = cdev->gadget;
struct device *dev = &gadget->dev;
struct f_uac1 *uac1 = func_to_uac1(f);
if (intf == uac1->ac_intf)
return uac1->ac_alt;
else if (intf == uac1->as_out_intf)
return uac1->as_out_alt;
else if (intf == uac1->as_in_intf)
return uac1->as_in_alt;
else
dev_err(dev, "%s:%d Invalid Interface %d!\n",
__func__, __LINE__, intf);
return -EINVAL;
}
static void f_audio_disable(struct usb_function *f)
{
struct f_uac1 *uac1 = func_to_uac1(f);
uac1->as_out_alt = 0;
uac1->as_in_alt = 0;
u_audio_stop_playback(&uac1->g_audio);
u_audio_stop_capture(&uac1->g_audio);
if (uac1->int_ep)
usb_ep_disable(uac1->int_ep);
}
/*-------------------------------------------------------------------------*/
static struct uac_feature_unit_descriptor *build_fu_desc(int chmask)
{
struct uac_feature_unit_descriptor *fu_desc;
int channels = num_channels(chmask);
int fu_desc_size = UAC_DT_FEATURE_UNIT_SIZE(channels);
fu_desc = kzalloc(fu_desc_size, GFP_KERNEL);
if (!fu_desc)
return NULL;
fu_desc->bLength = fu_desc_size;
fu_desc->bDescriptorType = USB_DT_CS_INTERFACE;
fu_desc->bDescriptorSubtype = UAC_FEATURE_UNIT;
fu_desc->bControlSize = 2;
/* bUnitID, bSourceID and bmaControls will be defined later */
return fu_desc;
}
/* B.3.2 Class-Specific AC Interface Descriptor */
static struct
uac1_ac_header_descriptor *build_ac_header_desc(struct f_uac1_opts *opts)
{
struct uac1_ac_header_descriptor *ac_desc;
int ac_header_desc_size;
int num_ifaces = 0;
if (EPOUT_EN(opts))
num_ifaces++;
if (EPIN_EN(opts))
num_ifaces++;
ac_header_desc_size = UAC_DT_AC_HEADER_SIZE(num_ifaces);
]
]
|
| 89/4 |
2026/07/31 02:15 |
llm |
codeexpert |
0m
Model:
Tokens:
input: 70619
output: 39
thoughts: 0
|
| 90/4 |
2026/07/31 02:15 |
tool |
git-log |
0m
Args: map[CodeRegexp:kfree\(split_page\) PathPrefix:drivers/usb/gadget/function/]
Results: map[Output:695d39ffc2b5 usb: gadget: f_uac1: Support multiple sampling rates
a7339e4f5788 usb: gadget: f_uac2: Support multiple sampling rates
]
|
| 91/4 |
2026/07/31 02:15 |
llm |
codeexpert |
0m
Model:
Tokens:
input: 70724
output: 31
thoughts: 0
|
| 92/4 |
2026/07/31 02:15 |
tool |
git-log |
0m
Args: map[MessageRegexps:[f_uac1_opts_c_srate_store]]
Results:
|
| 93/4 |
2026/07/31 02:15 |
llm |
codeexpert |
0m
Model:
Tokens:
input: 70768
output: 27
thoughts: 0
|
| 94/4 |
2026/07/31 02:16 |
tool |
git-log |
0m
Args: map[MessageRegexps:[uac1_opts srate]]
Results:
|
| 95/4 |
2026/07/31 02:16 |
llm |
codeexpert |
0m
Model:
Tokens:
input: 70808
output: 21
thoughts: 1491
Thoughts: **Bug Analysis: f_uac1_opts_c_srate_store and Family**
Okay, let's dive into this bug. It seems we have an exploitable issue in `f_uac1_opts_c_srate_store`, and by extension, the related `p` and `uac2` versions of this function. Looking at the code:
```c
static ssize_t f_uac1_opts_##name##_store(struct config_item *item, \
const char *page, size_t len) \
{ \
struct f_uac1_opts *opts = to_f_uac1_opts(item); \
char *split_page = NULL; \
int ret = -EINVAL; \
char *token; \
u32 num; \
int i; \
\
mutex_lock(&opts->lock); \
if (opts->refcnt) { \
ret = -EBUSY; \
goto end; \
} \
\
i = 0; \
memset(opts->name##s, 0x00, sizeof(opts->name##s)); \
split_page = kstrdup(page, GFP_KERNEL); \
while ((token = strsep(&split_page, ",")) != NULL) { \
ret = kstrtou32(token, 0, &num); \
if (ret) \
goto end; \
\
opts->name##s[i++] = num; \
ret = len; \
}; \
\
end: \
kfree(split_page); \
mutex_unlock(&opts->lock); \
return ret; \
} \
```
Alright, the vulnerable spot seems to be the use of `strsep`. Let's refresh: `strsep` modifies the input string pointer, which could be an issue. It takes a `char **` and splits the string based on a delimiter. It modifies the string in place!
Looking at the loop:
```c
split_page = kstrdup(page, GFP_KERNEL); \
while ((token = strsep(&split_page, ",")) != NULL) { \
ret = kstrtou32(token, 0, &num); \
if (ret) \
goto end; \
\
opts->name##s[i++] = num; \
ret = len; \
}; \
```
`split_page` is allocated via `kstrdup`. The problem emerges here: In the first iteration, `strsep` updates `split_page`. If a comma exists, it points to the character *after* the comma. `token` points to the start of the current token. If there's *no* comma, `split_page` becomes NULL, and `token` points to the entire string. Crucially, regardless, `split_page` is modified.
Now, if `split_page` becomes NULL in the loop, after the loop, we call `kfree(NULL)`. That's safe, but... memory leak! We leaked the buffer!
However, the more dangerous case appears if `kstrtou32` fails (returns non-zero). We jump to `end:`, and we call `kfree(split_page)`. *But `split_page` has been modified by strsep!* This is an invalid free, a KASAN trigger! We are freeing a pointer that wasn't the original allocation.
Let's trace a successful parsing scenario: For input like "44100,48000"
1. `split_page` points to "44100,48000". `strsep` extracts "44100". `split_page` is updated to point after the comma (to "48000"). `kstrtou32` succeeds, `opts->name##s[0]` = 44100.
2. `split_page` points to "48000". `strsep` extracts "48000". `split_page` becomes NULL. `kstrtou32` succeeds, `opts->name##s[1]` = 48000.
3.
|
| 96/4 |
2026/07/31 02:16 |
tool |
grepper |
0m
Args: map[Expression:UAC_MAX_RATES]
Results: map[Output:drivers/usb/gadget/function/f_uac1.c=186=static struct uac1_as_header_descriptor as_in_header_desc = {
--
drivers/usb/gadget/function/f_uac1.c-194-
drivers/usb/gadget/function/f_uac1.c:195:DECLARE_UAC_FORMAT_TYPE_I_DISCRETE_DESC(UAC_MAX_RATES);
drivers/usb/gadget/function/f_uac1.c-196-#define uac_format_type_i_discrete_descriptor \
drivers/usb/gadget/function/f_uac1.c:197: uac_format_type_i_discrete_descriptor_##UAC_MAX_RATES
drivers/usb/gadget/function/f_uac1.c-198-
--
drivers/usb/gadget/function/f_uac1.c=1232=static int f_audio_bind(struct usb_configuration *c, struct usb_function *f)
--
drivers/usb/gadget/function/f_uac1.c-1351- /* Set sample rates */
drivers/usb/gadget/function/f_uac1.c:1352: for (i = 0, idx = 0; i < UAC_MAX_RATES; i++) {
drivers/usb/gadget/function/f_uac1.c-1353- if (audio_opts->c_srates[i] == 0)
--
drivers/usb/gadget/function/f_uac1.c-1360-
drivers/usb/gadget/function/f_uac1.c:1361: for (i = 0, idx = 0; i < UAC_MAX_RATES; i++) {
drivers/usb/gadget/function/f_uac1.c-1362- if (audio_opts->p_srates[i] == 0)
--
drivers/usb/gadget/function/f_uac1.c=1571=static ssize_t f_uac1_opts_##name##_show(struct config_item *item, \
--
drivers/usb/gadget/function/f_uac1.c-1579- page[0] = '\0'; \
drivers/usb/gadget/function/f_uac1.c:1580: for (i = 0; i < UAC_MAX_RATES; i++) { \
drivers/usb/gadget/function/f_uac1.c-1581- if (opts->name##s[i] == 0) \
--
drivers/usb/gadget/function/f_uac2.c=640=struct cntrl_subrange_lay3 {
--
drivers/usb/gadget/function/f_uac2.c-655-
drivers/usb/gadget/function/f_uac2.c:656:DECLARE_UAC2_CNTRL_RANGES_LAY3(srates, UAC_MAX_RATES);
drivers/usb/gadget/function/f_uac2.c-657-
drivers/usb/gadget/function/f_uac2.c=658=static int get_max_srate(const int *srates)
--
drivers/usb/gadget/function/f_uac2.c-661-
drivers/usb/gadget/function/f_uac2.c:662: for (i = 0; i < UAC_MAX_RATES; i++) {
drivers/usb/gadget/function/f_uac2.c-663- if (srates[i] == 0)
--
drivers/usb/gadget/function/f_uac2.c=1601=in_rq_range(struct usb_function *fn, const struct usb_ctrlrequest *cr)
--
drivers/usb/gadget/function/f_uac2.c-1626- return -EOPNOTSUPP;
drivers/usb/gadget/function/f_uac2.c:1627: for (i = 0; i < UAC_MAX_RATES; i++) {
drivers/usb/gadget/function/f_uac2.c-1628- srate = srates[i];
--
drivers/usb/gadget/function/f_uac2.c=1989=static ssize_t f_uac2_opts_##name##_show(struct config_item *item, \
--
drivers/usb/gadget/function/f_uac2.c-1997- page[0] = '\0'; \
drivers/usb/gadget/function/f_uac2.c:1998: for (i = 0; i < UAC_MAX_RATES; i++) { \
drivers/usb/gadget/function/f_uac2.c-1999- if (opts->name##s[i] == 0) \
--
drivers/usb/gadget/function/u_audio.c=508=int u_audio_set_capture_srate(struct g_audio *audio_dev, int srate)
--
drivers/usb/gadget/function/u_audio.c-517- prm = &uac->c_prm;
drivers/usb/gadget/function/u_audio.c:518: for (i = 0; i < UAC_MAX_RATES; i++) {
drivers/usb/gadget/function/u_audio.c-519- if (params->c_srates[i] == srate) {
--
drivers/usb/gadget/function/u_audio.c=547=int u_audio_set_playback_srate(struct g_audio *audio_dev, int srate)
--
drivers/usb/gadget/function/u_audio.c-556- prm = &uac->p_prm;
drivers/usb/gadget/function/u_audio.c:557: for (i = 0; i < UAC_MAX_RATES; i++) {
drivers/usb/gadget/function/u_audio.c-558- if (params->p_srates[i] == srate) {
--
drivers/usb/gadget/function/u_audio.c=1079=static int get_max_srate(const int *srates)
--
drivers/usb/gadget/function/u_audio.c-1082-
drivers/usb/gadget/function/u_audio.c:1083: for (i = 0; i < UAC_MAX_RATES; i++) {
drivers/usb/gadget/function/u_audio.c-1084- if (srates[i] == 0)
--
drivers/usb/gadget/function/u_audio.c=1092=static int get_min_srate(const int *srates)
--
drivers/usb/gadget/function/u_audio.c-1095-
drivers/usb/gadget/function/u_audio.c:1096: for (i = 0; i < UAC_MAX_RATES; i++) {
drivers/usb/gadget/function/u_audio.c-1097- if (srates[i] == 0)
--
drivers/usb/gadget/function/u_audio.h=41=struct uac_params {
--
drivers/usb/gadget/function/u_audio.h-43- int p_chmask; /* channel mask */
drivers/usb/gadget/function/u_audio.h:44: int p_srates[UAC_MAX_RATES]; /* available rates in Hz (0 terminated list) */
drivers/usb/gadget/function/u_audio.h-45- int p_ssize; /* sample size */
--
drivers/usb/gadget/function/u_audio.h-49- int c_chmask; /* channel mask */
drivers/usb/gadget/function/u_audio.h:50: int c_srates[UAC_MAX_RATES]; /* available rates in Hz (0 terminated list) */
drivers/usb/gadget/function/u_audio.h-51- int c_ssize; /* sample size */
--
drivers/usb/gadget/function/u_uac1.h=31=struct f_uac1_opts {
--
drivers/usb/gadget/function/u_uac1.h-33- int c_chmask;
drivers/usb/gadget/function/u_uac1.h:34: int c_srates[UAC_MAX_RATES];
drivers/usb/gadget/function/u_uac1.h-35- int c_ssize;
drivers/usb/gadget/function/u_uac1.h-36- int p_chmask;
drivers/usb/gadget/function/u_uac1.h:37: int p_srates[UAC_MAX_RATES];
drivers/usb/gadget/function/u_uac1.h-38- int p_ssize;
--
drivers/usb/gadget/function/u_uac2.h=43=struct f_uac2_opts {
--
drivers/usb/gadget/function/u_uac2.h-45- int p_chmask;
drivers/usb/gadget/function/u_uac2.h:46: int p_srates[UAC_MAX_RATES];
drivers/usb/gadget/function/u_uac2.h-47- int p_ssize;
--
drivers/usb/gadget/function/u_uac2.h-49- int c_chmask;
drivers/usb/gadget/function/u_uac2.h:50: int c_srates[UAC_MAX_RATES];
drivers/usb/gadget/function/u_uac2.h-51- int c_ssize;
--
drivers/usb/gadget/function/uac_common.h-7-
drivers/usb/gadget/function/uac_common.h:8:#define UAC_MAX_RATES 10 /* maximum number of rates configurable by f_uac1/2 */
drivers/usb/gadget/function/uac_common.h-9-#endif
--
drivers/usb/gadget/legacy/audio.c=26=MODULE_PARM_DESC(p_chmask, "Playback Channel Mask");
--
drivers/usb/gadget/legacy/audio.c-28-/* Playback Default 48 KHz */
drivers/usb/gadget/legacy/audio.c:29:static int p_srates[UAC_MAX_RATES] = {UAC2_DEF_PSRATE};
drivers/usb/gadget/legacy/audio.c-30-static int p_srates_cnt = 1;
--
drivers/usb/gadget/legacy/audio.c=48=MODULE_PARM_DESC(c_chmask, "Capture Channel Mask");
--
drivers/usb/gadget/legacy/audio.c-50-/* Capture Default 64 KHz */
drivers/usb/gadget/legacy/audio.c:51:static int c_srates[UAC_MAX_RATES] = {UAC2_DEF_CSRATE};
drivers/usb/gadget/legacy/audio.c-52-static int c_srates_cnt = 1;
--
drivers/usb/gadget/legacy/audio.c=74=MODULE_PARM_DESC(p_chmask, "Playback Channel Mask");
--
drivers/usb/gadget/legacy/audio.c-76-/* Playback Default 48 KHz */
drivers/usb/gadget/legacy/audio.c:77:static int p_srates[UAC_MAX_RATES] = {UAC1_DEF_PSRATE};
drivers/usb/gadget/legacy/audio.c-78-static int p_srates_cnt = 1;
--
drivers/usb/gadget/legacy/audio.c=90=MODULE_PARM_DESC(c_chmask, "Capture Channel Mask");
--
drivers/usb/gadget/legacy/audio.c-92-/* Capture Default 48 KHz */
drivers/usb/gadget/legacy/audio.c:93:static int c_srates[UAC_MAX_RATES] = {UAC1_DEF_CSRATE};
drivers/usb/gadget/legacy/audio.c-94-static int c_srates_cnt = 1;
]
|
| 97/4 |
2026/07/31 02:16 |
llm |
codeexpert |
0m
Model:
Tokens:
input: 75058
output: 24
thoughts: 0
|
| 98/4 |
2026/07/31 02:16 |
tool |
git-log |
10m
Error:
Args: map[CodeRegexp:kfree\(split_page\)]
Results:
|
| 99/4 |
2026/07/31 02:26 |
llm |
codeexpert |
0m
Model:
Tokens:
input: 75117
output: 34
thoughts: 0
|
| 100/4 |
2026/07/31 02:26 |
tool |
git-log |
0m
Args: map[CodeRegexp:kfree\(split_page\) PathPrefix:drivers/usb/]
Results: map[Output:695d39ffc2b5 usb: gadget: f_uac1: Support multiple sampling rates
a7339e4f5788 usb: gadget: f_uac2: Support multiple sampling rates
]
|
| 101/4 |
2026/07/31 02:27 |
llm |
codeexpert |
0m
Model:
Tokens:
input: 75217
output: 38
thoughts: 0
|
| 102/4 |
2026/07/31 02:27 |
tool |
git-log |
0m
Args: map[MessageRegexps:[f_uac1_opts_c_srate_store] Since:3 years]
Results:
|
| 103/4 |
2026/07/31 02:27 |
llm |
codeexpert |
0m
Model:
Tokens:
input: 75268
output: 27
thoughts: 0
|
| 104/4 |
2026/07/31 02:27 |
tool |
git-log |
0m
Args: map[MessageRegexps:[invalid-free] Since:3 years]
Results: map[Output:37a23d6f1193 bus: mhi: host: Use kzalloc_flex
8ab3b09755d9 io_uring: use mempool KASAN hook
413643f3a3e2 mempool: use new mempool KASAN hooks
f129c3103928 kasan: introduce kasan_mempool_poison_pages
2e7c954c11af kasan: add return value for kasan_mempool_poison_object
280ec6ccb642 kasan: rename kasan_slab_free_mempool to kasan_mempool_poison_object
]
|
| 105/4 |
2026/07/31 02:27 |
llm |
codeexpert |
0m
Model:
Tokens:
input: 75472
output: 25
thoughts: 1912
Thoughts: **Analyzing a Potential Kernel Bug**
Okay, so the `git-log` search is done; time to dive into this bug report. The user's asking if the `f_uac1_opts_c_srate_store` bug, flagged as a KASAN `invalid-free`, is exploitable. The code snippet given uses `strsep` to tokenize a comma-separated string, but then frees the potentially modified pointer. I remember we have seen similar bugs before. Let's dig in.
The code's structure looks straightforward: allocate a copy of the input string (`kstrdup`), use `strsep` to extract tokens, parse each token with `kstrtou32`, and then `kfree` the allocated string. The critical part is that `strsep` modifies the input string in place, which is the giveaway.
So, `strsep` is declared as taking a pointer to a string pointer. That makes sense; it needs to update the pointer to the rest of the string after each token. If the original `*stringp` is NULL, it returns NULL, which makes the whole thing NULL. No harm, no foul. If it finds a delimiter, it overwrites the delimiter with a null terminator and updates `*stringp` to point to the character *after* the delimiter. If it *doesn't* find a delimiter, it sets `*stringp` to NULL.
Now, let's analyze the loop's execution paths, particularly how `split_page` evolves: if the input doesn't contain a comma, `strsep` returns the whole string as the first token, sets `split_page` to NULL, and the loop body executes once. In the next iteration, the check becomes `strsep(&NULL, ",")` and it returns NULL, and the loop terminates. The `kfree(NULL)` at the end is perfectly safe; it's a no-op.
Even if there *are* commas, say the input is "44100,48000", `split_page` gets allocated, we tokenize "44100", `split_page` points to "48000". `kfree(NULL)` is again executed. No issue.
Ah, but the crucial point is what happens if `kstrtou32` *fails* while parsing a token. Let's say we have "44100,invalid". `strsep` parses "44100", moves `split_page` to "invalid". `kstrtou32` succeeds for "44100", then fails on the "invalid" token and hits the `goto end`. At the `end` label, it calls `kfree(split_page)`. Since `split_page` now points to "invalid" which means it's pointing to *inside* an already allocated memory area, it is safe.
Now, what about that initial parse failure? Suppose the input is "invalid,48000". The first call to `strsep` successfully splits the string into "invalid" (token) and "48000" (remainder) while `split_page` points to `original_pointer + 8` bytes. `kstrtou32` fails, hitting the `goto end`. But now, at the `end` label, `kfree(split_page)` is called with a pointer to the *middle* of the original allocated buffer! That's the KASAN `invalid-free` right there. We are freeing a pointer *inside* the allocated block, not at the beginning. That's a classic off-by-a-pointer bug.
So, this is indeed an invalid free, and we need to consider the exploitability. Who can write to this configfs attribute? Typically, configfs is root-only, and thus, this would be a local privilege escalation or kernel crash/denial of service. However, in environments with less restrictive permissions (e.g., some Android devices or containers) a normal user or compromised system service (like a USB gadget daemon) might be able to write to configfs attributes. Thus, this may not necessarily need root to be exploitable.
Now, is the invalid free exploitable for arbitrary code execution? `kfree` checks for the pointer's alignment and whether it points to a valid slab page. If the pointer isn't aligned or is just some offset in a slab, KASAN will trigger, as we're seeing. Without KASAN, though... what will the SLUB allocator do? I should check `mm/slub.c` to see what happens when it tries to `kfree` a pointer that's offset within a slab. It's time to dig into the SLUB implementation
|
| 106/4 |
2026/07/31 02:27 |
tool |
grepper |
0m
Args: map[Expression:void kfree PathPrefix:mm/]
Results: map[Output:mm/kasan/kasan_test_c.c=983=static void workqueue_uaf(struct kunit *test)
--
mm/kasan/kasan_test_c.c-1001-
mm/kasan/kasan_test_c.c:1002:static void kfree_via_page(struct kunit *test)
mm/kasan/kasan_test_c.c-1003-{
--
mm/kasan/kasan_test_c.c-1016-
mm/kasan/kasan_test_c.c:1017:static void kfree_via_phys(struct kunit *test)
mm/kasan/kasan_test_c.c-1018-{
--
mm/slab_common.c=1229=module_init(slab_proc_init);
--
mm/slab_common.c-1243- */
mm/slab_common.c:1244:void kfree_sensitive(const void *p)
mm/slab_common.c-1245-{
--
mm/slab_common.c=1543=kvfree_rcu_list(struct rcu_head *head)
--
mm/slab_common.c-1566- */
mm/slab_common.c:1567:static void kfree_rcu_work(struct work_struct *work)
mm/slab_common.c-1568-{
--
mm/slab_common.c=1732=kvfree_rcu_queue_batch(struct kfree_rcu_cpu *krcp)
--
mm/slab_common.c-1787- */
mm/slab_common.c:1788:static void kfree_rcu_monitor(struct work_struct *work)
mm/slab_common.c-1789-{
--
mm/slub.c=6624=void kvfree_rcu_cb(struct rcu_head *head)
--
mm/slub.c-6670- */
mm/slub.c:6671:void kfree(const void *object)
mm/slub.c-6672-{
--
mm/slub.c=6694=EXPORT_SYMBOL(kfree);
--
mm/slub.c-6704- */
mm/slub.c:6705:void kfree_nolock(const void *object)
mm/slub.c-6706-{
--
mm/util.c-42- */
mm/util.c:43:void kfree_const(const void *x)
mm/util.c-44-{
]
|
| 107/4 |
2026/07/31 02:27 |
llm |
codeexpert |
0m
Model:
Tokens:
input: 77201
output: 35
thoughts: 0
|
| 108/4 |
2026/07/31 02:27 |
tool |
read-file |
0m
Args: map[File:mm/slub.c FirstLine:6671 LineCount:30]
Results: map[Contents:6671: void kfree(const void *object)
6672: {
6673: struct page *page;
6674: struct slab *slab;
6675: struct kmem_cache *s;
6676: void *x = (void *)object;
6677:
6678: trace_kfree(_RET_IP_, object);
6679:
6680: if (unlikely(ZERO_OR_NULL_PTR(object)))
6681: return;
6682:
6683: page = virt_to_page(object);
6684: slab = page_slab(page);
6685: if (!slab) {
6686: /* kmalloc_nolock() doesn't support large kmalloc */
6687: free_large_kmalloc(page, (void *)object);
6688: return;
6689: }
6690:
6691: s = slab->slab_cache;
6692: slab_free(s, slab, x, _RET_IP_);
6693: }
6694: EXPORT_SYMBOL(kfree);
6695:
6696: /*
6697: * Can be called while holding raw_spinlock_t or from IRQ and NMI,
6698: * but ONLY for objects allocated by kmalloc_nolock().
6699: * Debug checks (like kmemleak and kfence) were skipped on allocation,
6700: * hence
]
|
| 109/4 |
2026/07/31 02:27 |
llm |
codeexpert |
0m
Model:
Tokens:
input: 77662
output: 35
thoughts: 0
|
| 110/4 |
2026/07/31 02:27 |
tool |
git-log |
2m
Args: map[CodeRegexp:f_uac1_opts_.*_store Since:1 year]
Results:
|
| 111/4 |
2026/07/31 02:29 |
llm |
codeexpert |
0m
Model:
Tokens:
input: 77710
output: 35
thoughts: 0
|
| 112/4 |
2026/07/31 02:29 |
tool |
git-log |
6m
Args: map[CodeRegexp:f_uac1_opts_.*_store Since:5 years]
Results: map[Output:dfb05b5dc3af usb: gadget: f_uac1: allow changing interface name via configfs
695d39ffc2b5 usb: gadget: f_uac1: Support multiple sampling rates
]
|
| 113/4 |
2026/07/31 02:36 |
llm |
codeexpert |
0m
Model:
Tokens:
input: 77811
output: 35
thoughts: 0
|
| 114/4 |
2026/07/31 02:36 |
tool |
git-log |
6m
Args: map[CodeRegexp:f_uac2_opts_.*_store Since:5 years]
Results: map[Output:993a44fa85c1 usb: gadget: f_uac2: allow changing interface name via configfs
a7339e4f5788 usb: gadget: f_uac2: Support multiple sampling rates
]
|
| 115/4 |
2026/07/31 02:43 |
llm |
codeexpert |
0m
Model:
Tokens:
input: 77915
output: 27
thoughts: 0
|
| 116/4 |
2026/07/31 02:43 |
tool |
git-show |
0m
Args:
Results: map[Output:commit a7339e4f5788bd088bb0be1f96a6cce459676ed0
Author: Julian Scheel <julian@jusst.de>
Date: Fri Jan 21 16:53:03 2022 +0100
usb: gadget: f_uac2: Support multiple sampling rates
A list of sampling rates can be specified via configfs. All enabled
sampling rates are sent to the USB host on request. When the host
selects a sampling rate, the internal active rate (stored in
struct f_uac2) is updated.
The gadget no longer supports only one frequency. Therefore USB strings
corresponding to the clock sources are renamed from specific Hz value to
general names Input clock/Output clock.
Config strings with single value stay compatible with the previous
version.
Multiple samplerates passed as configuration arrays to g_audio module
when built for f_uac2.
Signed-off-by: Julian Scheel <julian@jusst.de>
Signed-off-by: Pavel Hofman <pavel.hofman@ivitera.com>
Link: https://lore.kernel.org/r/20220121155308.48794-6-pavel.hofman@ivitera.com
Signed-off-by: Greg Kroah-Hartman <gregkh@linuxfoundation.org>
diff --git a/Documentation/ABI/testing/configfs-usb-gadget-uac2 b/Documentation/ABI/testing/configfs-usb-gadget-uac2
index 7fb3dbe26857..9d2f59ab9701 100644
--- a/Documentation/ABI/testing/configfs-usb-gadget-uac2
+++ b/Documentation/ABI/testing/configfs-usb-gadget-uac2
@@ -6,7 +6,7 @@ Description:
===================== =======================================
c_chmask capture channel mask
- c_srate capture sampling rate
+ c_srate list of capture sampling rates (comma-separated)
c_ssize capture sample size (bytes)
c_sync capture synchronization type
(async/adaptive)
@@ -20,7 +20,7 @@ Description:
(in 1/256 dB)
fb_max maximum extra bandwidth in async mode
p_chmask playback channel mask
- p_srate playback sampling rate
+ p_srate list of playback sampling rates (comma-separated)
p_ssize playback sample size (bytes)
p_mute_present playback mute control enable
p_volume_present playback volume control enable
diff --git a/Documentation/usb/gadget-testing.rst b/Documentation/usb/gadget-testing.rst
index cbbd948c626f..419f6e5e890a 100644
--- a/Documentation/usb/gadget-testing.rst
+++ b/Documentation/usb/gadget-testing.rst
@@ -726,7 +726,7 @@ The uac2 function provides these attributes in its function directory:
================ ====================================================
c_chmask capture channel mask
- c_srate capture sampling rate
+ c_srate list of capture sampling rates (comma-separated)
c_ssize capture sample size (bytes)
c_sync capture synchronization type (async/adaptive)
c_mute_present capture mute control enable
@@ -736,7 +736,7 @@ The uac2 function provides these attributes in its function directory:
c_volume_res capture volume control resolution (in 1/256 dB)
fb_max maximum extra bandwidth in async mode
p_chmask playback channel mask
- p_srate playback sampling rate
+ p_srate list of playback sampling rates (comma-separated)
p_ssize playback sample size (bytes)
p_mute_present playback mute control enable
p_volume_present playback volume control enable
diff --git a/drivers/usb/gadget/function/f_uac2.c b/drivers/usb/gadget/function/f_uac2.c
index e518f210968c..5ee5314780a6 100644
--- a/drivers/usb/gadget/function/f_uac2.c
+++ b/drivers/usb/gadget/function/f_uac2.c
@@ -70,6 +70,8 @@ struct f_uac2 {
/* Interrupt IN endpoint of AC interface */
struct usb_ep *int_ep;
atomic_t int_count;
+ /* transient state, only valid during handling of a single control request */
+ int clock_id;
};
static inline struct f_uac2 *func_to_uac2(struct usb_function *f)
@@ -104,14 +106,11 @@ enum {
STR_AS_IN_ALT1,
};
-static char clksrc_in[8];
-static char clksrc_out[8];
-
static struct usb_string strings_fn[] = {
[STR_ASSOC].s = "Source/Sink",
[STR_IF_CTRL].s = "Topology Control",
- [STR_CLKSRC_IN].s = clksrc_in,
- [STR_CLKSRC_OUT].s = clksrc_out,
+ [STR_CLKSRC_IN].s = "Input Clock",
+ [STR_CLKSRC_OUT].s = "Output Clock",
[STR_USB_IT].s = "USBH Out",
[STR_IO_IT].s = "USBD Out",
[STR_USB_OT].s = "USBH In",
@@ -166,7 +165,7 @@ static struct uac_clock_source_descriptor in_clk_src_desc = {
.bDescriptorSubtype = UAC2_CLOCK_SOURCE,
/* .bClockID = DYNAMIC */
.bmAttributes = UAC_CLOCK_SOURCE_TYPE_INT_FIXED,
- .bmControls = (CONTROL_RDONLY << CLK_FREQ_CTRL),
+ .bmControls = (CONTROL_RDWR << CLK_FREQ_CTRL),
.bAssocTerminal = 0,
};
@@ -178,7 +177,7 @@ static struct uac_clock_source_descriptor out_clk_src_desc = {
.bDescriptorSubtype = UAC2_CLOCK_SOURCE,
/* .bClockID = DYNAMIC */
.bmAttributes = UAC_CLOCK_SOURCE_TYPE_INT_FIXED,
- .bmControls = (CONTROL_RDONLY << CLK_FREQ_CTRL),
+ .bmControls = (CONTROL_RDWR << CLK_FREQ_CTRL),
.bAssocTerminal = 0,
};
@@ -634,13 +633,37 @@ struct cntrl_cur_lay3 {
__le32 dCUR;
};
-struct cntrl_range_lay3 {
- __le16 wNumSubRanges;
+struct cntrl_subrange_lay3 {
__le32 dMIN;
__le32 dMAX;
__le32 dRES;
} __packed;
+#define ranges_lay3_size(c) (sizeof(c.wNumSubRanges) \
+ + le16_to_cpu(c.wNumSubRanges) \
+ * sizeof(struct cntrl_subrange_lay3))
+
+#define DECLARE_UAC2_CNTRL_RANGES_LAY3(k, n) \
+ struct cntrl_ranges_lay3_##k { \
+ __le16 wNumSubRanges; \
+ struct cntrl_subrange_lay3 r[n]; \
+} __packed
+
+DECLARE_UAC2_CNTRL_RANGES_LAY3(srates, UAC_MAX_RATES);
+
+static int get_max_srate(const int *srates)
+{
+ int i, max_srate = 0;
+
+ for (i = 0; i < UAC_MAX_RATES; i++) {
+ if (srates[i] == 0)
+ break;
+ if (srates[i] > max_srate)
+ max_srate = srates[i];
+ }
+ return max_srate;
+}
+
static int set_ep_max_packet_size(const struct f_uac2_opts *uac2_opts,
struct usb_endpoint_descriptor *ep_desc,
enum usb_device_speed speed, bool is_playback)
@@ -667,11 +690,11 @@ static int set_ep_max_packet_size(const struct f_uac2_opts *uac2_opts,
if (is_playback) {
chmask = uac2_opts->p_chmask;
- srate = uac2_opts->p_srate;
+ srate = get_max_srate(uac2_opts->p_srates);
ssize = uac2_opts->p_ssize;
} else {
chmask = uac2_opts->c_chmask;
- srate = uac2_opts->c_srate;
+ srate = get_max_srate(uac2_opts->c_srates);
ssize = uac2_opts->c_ssize;
}
@@ -912,10 +935,10 @@ static int afunc_validate_opts(struct g_audio *agdev, struct device *dev)
} else if ((opts->c_ssize < 1) || (opts->c_ssize > 4)) {
dev_err(dev, "Error: incorrect capture sample size\n");
return -EINVAL;
- } else if (!opts->p_srate) {
+ } else if (!opts->p_srates[0]) {
dev_err(dev, "Error: incorrect playback sampling rate\n");
return -EINVAL;
- } else if (!opts->c_srate) {
+ } else if (!opts->c_srates[0]) {
dev_err(dev, "Error: incorrect capture sampling rate\n");
return -EINVAL;
}
@@ -1037,9 +1060,6 @@ afunc_bind(struct usb_configuration *cfg, struct usb_function *fn)
*bma = cpu_to_le32(control);
}
- snprintf(clksrc_in, sizeof(clksrc_in), "%uHz", uac2_opts->p_srate);
- snprintf(clksrc_out, sizeof(clksrc_out), "%uHz", uac2_opts->c_srate);
-
ret = usb_interface_id(cfg, fn);
if (ret < 0) {
dev_err(dev, "%s:%d Error!\n", __func__, __LINE__);
@@ -1209,7 +1229,8 @@ afunc_bind(struct usb_configuration *cfg, struct usb_function *fn)
agdev->gadget = gadget;
agdev->params.p_chmask = uac2_opts->p_chmask;
- agdev->params.p_srates[0] = uac2_opts->p_srate;
+ memcpy(agdev->params.p_srates, uac2_opts->p_srates,
+ sizeof(agdev->params.p_srates));
agdev->params.p_ssize = uac2_opts->p_ssize;
if (FUIN_EN(uac2_opts)) {
agdev->params.p_fu.id = USB_IN_FU_ID;
@@ -1220,7 +1241,8 @@ afunc_bind(struct usb_configuration *cfg, struct usb_function *fn)
agdev->params.p_fu.volume_res = uac2_opts->p_volume_res;
}
agdev->params.c_chmask = uac2_opts->c_chmask;
- agdev->params.c_srates[0] = uac2_opts->c_srate;
+ memcpy(agdev->params.c_srates, uac2_opts->c_srates,
+ sizeof(agdev->params.c_srates));
agdev->params.c_ssize = uac2_opts->c_ssize;
if (FUOUT_EN(uac2_opts)) {
agdev->params.c_fu.id = USB_OUT_FU_ID;
@@ -1423,10 +1445,10 @@ in_rq_cur(struct usb_function *fn, const struct usb_ctrlrequest *cr)
u8 entity_id = (w_index >> 8) & 0xff;
u8 control_selector = w_value >> 8;
int value = -EOPNOTSUPP;
- int p_srate, c_srate;
+ u32 p_srate, c_srate;
- p_srate = opts->p_srate;
- c_srate = opts->c_srate;
+ u_audio_get_playback_srate(agdev, &p_srate);
+ u_audio_get_capture_srate(agdev, &c_srate);
if ((entity_id == USB_IN_CLK_ID) || (entity_id == USB_OUT_CLK_ID)) {
if (control_selector == UAC2_CS_CONTROL_SAM_FREQ) {
@@ -1500,28 +1522,39 @@ in_rq_range(struct usb_function *fn, const struct usb_ctrlrequest *cr)
u8 entity_id = (w_index >> 8) & 0xff;
u8 control_selector = w_value >> 8;
int value = -EOPNOTSUPP;
- int p_srate, c_srate;
-
- p_srate = opts->p_srate;
- c_srate = opts->c_srate;
if ((entity_id == USB_IN_CLK_ID) || (entity_id == USB_OUT_CLK_ID)) {
if (control_selector == UAC2_CS_CONTROL_SAM_FREQ) {
- struct cntrl_range_lay3 r;
+ struct cntrl_ranges_lay3_srates rs;
+ int i;
+ int wNumSubRanges = 0;
+ int srate;
+ int *srates;
if (entity_id == USB_IN_CLK_ID)
- r.dMIN = cpu_to_le32(p_srate);
+ srates = opts->p_srates;
else if (entity_id == USB_OUT_CLK_ID)
- r.dMIN = cpu_to_le32(c_srate);
+ srates = opts->c_srates;
else
return -EOPNOTSUPP;
-
- r.dMAX = r.dMIN;
- r.dRES = 0;
- r.wNumSubRanges = cpu_to_le16(1);
-
- value = min_t(unsigned int, w_length, sizeof(r));
- memcpy(req->buf, &r, value);
+ for (i = 0; i < UAC_MAX_RATES; i++) {
+ srate = srates[i];
+ if (srate == 0)
+ break;
+
+ rs.r[wNumSubRanges].dMIN = cpu_to_le32(srate);
+ rs.r[wNumSubRanges].dMAX = cpu_to_le32(srate);
+ rs.r[wNumSubRanges].dRES = 0;
+ wNumSubRanges++;
+ dev_dbg(&agdev->gadget->dev,
+ "%s(): clk %d: rate ID %d: %d\n",
+ __func__, entity_id, wNumSubRanges, srate);
+ }
+ rs.wNumSubRanges = cpu_to_le16(wNumSubRanges);
+ value = min_t(unsigned int, w_length, ranges_lay3_size(rs));
+ dev_dbg(&agdev->gadget->dev, "%s(): sending %d rates, size %d\n",
+ __func__, rs.wNumSubRanges, value);
+ memcpy(req->buf, &rs, value);
} else {
dev_err(&agdev->gadget->dev,
"%s:%d control_selector=%d TODO!\n",
@@ -1580,6 +1613,25 @@ ac_rq_in(struct usb_function *fn, const struct usb_ctrlrequest *cr)
return -EOPNOTSUPP;
}
+static void uac2_cs_control_sam_freq(struct usb_ep *ep, struct usb_request *req)
+{
+ struct usb_function *fn = ep->driver_data;
+ struct g_audio *agdev = func_to_g_audio(fn);
+ struct f_uac2 *uac2 = func_to_uac2(fn);
+ u32 val;
+
+ if (req->actual != 4)
+ return;
+
+ val = le32_to_cpu(*((__le32 *)req->buf));
+ dev_dbg(&agdev->gadget->dev, "%s val: %d.\n", __func__, val);
+ if (uac2->clock_id == USB_IN_CLK_ID) {
+ u_audio_set_playback_srate(agdev, val);
+ } else if (uac2->clock_id == USB_OUT_CLK_ID) {
+ u_audio_set_capture_srate(agdev, val);
+ }
+}
+
static void
out_rq_cur_complete(struct usb_ep *ep, struct usb_request *req)
{
@@ -1631,6 +1683,7 @@ out_rq_cur_complete(struct usb_ep *ep, struct usb_request *req)
static int
out_rq_cur(struct usb_function *fn, const struct usb_ctrlrequest *cr)
{
+ struct usb_composite_dev *cdev = fn->config->cdev;
struct usb_request *req = fn->config->cdev->req;
struct g_audio *agdev = func_to_g_audio(fn);
struct f_uac2_opts *opts = g_audio_to_uac2_opts(agdev);
@@ -1640,10 +1693,17 @@ out_rq_cur(struct usb_function *fn, const struct usb_ctrlrequest *cr)
u16 w_value = le16_to_cpu(cr->wValue);
u8 entity_id = (w_index >> 8) & 0xff;
u8 control_selector = w_value >> 8;
+ u8 clock_id = w_index >> 8;
if ((entity_id == USB_IN_CLK_ID) || (entity_id == USB_OUT_CLK_ID)) {
- if (control_selector == UAC2_CS_CONTROL_SAM_FREQ)
+ if (control_selector == UAC2_CS_CONTROL_SAM_FREQ) {
+ dev_dbg(&agdev->gadget->dev,
+ "control_selector UAC2_CS_CONTROL_SAM_FREQ, clock: %d\n", clock_id);
+ cdev->gadget->ep0->driver_data = fn;
+ uac2->clock_id = clock_id;
+ req->complete = uac2_cs_control_sam_freq;
return w_length;
+ }
} else if ((FUIN_EN(opts) && (entity_id == USB_IN_FU_ID)) ||
(FUOUT_EN(opts) && (entity_id == USB_OUT_FU_ID))) {
memcpy(&uac2->setup_cr, cr, sizeof(*cr));
@@ -1836,11 +1896,70 @@ end: \
\
CONFIGFS_ATTR(f_uac2_opts_, name)
+#define UAC2_RATE_ATTRIBUTE(name) \
+static ssize_t f_uac2_opts_##name##_show(struct config_item *item, \
+ char *page) \
+{ \
+ struct f_uac2_opts *opts = to_f_uac2_opts(item); \
+ int result = 0; \
+ int i; \
+ \
+ mutex_lock(&opts->lock); \
+ page[0] = '\0'; \
+ for (i = 0; i < UAC_MAX_RATES; i++) { \
+ if (opts->name##s[i] == 0) \
+ break; \
+ result += sprintf(page + strlen(page), "%u,", \
+ opts->name##s[i]); \
+ } \
+ if (strlen(page) > 0) \
+ page[strlen(page) - 1] = '\n'; \
+ mutex_unlock(&opts->lock); \
+ \
+ return result; \
+} \
+ \
+static ssize_t f_uac2_opts_##name##_store(struct config_item *item, \
+ const char *page, size_t len) \
+{ \
+ struct f_uac2_opts *opts = to_f_uac2_opts(item); \
+ char *split_page = NULL; \
+ int ret = -EINVAL; \
+ char *token; \
+ u32 num; \
+ int i; \
+ \
+ mutex_lock(&opts->lock); \
+ if (opts->refcnt) { \
+ ret = -EBUSY; \
+ goto end; \
+ } \
+ \
+ i = 0; \
+ memset(opts->name##s, 0x00, sizeof(opts->name##s)); \
+ split_page = kstrdup(page, GFP_KERNEL); \
+ while ((token = strsep(&split_page, ",")) != NULL) { \
+ ret = kstrtou32(token, 0, &num); \
+ if (ret) \
+ goto end; \
+ \
+ opts->name##s[i++] = num; \
+ ret = len; \
+ }; \
+ \
+end: \
+ kfree(split_page); \
+ mutex_unlock(&opts->lock); \
+ return ret; \
+} \
+ \
+CONFIGFS_ATTR(f_uac2_opts_, name)
+
UAC2_ATTRIBUTE(u32, p_chmask);
-UAC2_ATTRIBUTE(u32, p_srate);
+UAC2_RATE_ATTRIBUTE(p_srate);
UAC2_ATTRIBUTE(u32, p_ssize);
UAC2_ATTRIBUTE(u32, c_chmask);
-UAC2_ATTRIBUTE(u32, c_srate);
+UAC2_RATE_ATTRIBUTE(c_srate);
UAC2_ATTRIBUTE_SYNC(c_sync);
UAC2_ATTRIBUTE(u32, c_ssize);
UAC2_ATTRIBUTE(u32, req_number);
@@ -1913,10 +2032,10 @@ static struct usb_function_instance *afunc_alloc_inst(void)
&f_uac2_func_type);
opts->p_chmask = UAC2_DEF_PCHMASK;
- opts->p_srate = UAC2_DEF_PSRATE;
+ opts->p_srates[0] = UAC2_DEF_PSRATE;
opts->p_ssize = UAC2_DEF_PSSIZE;
opts->c_chmask = UAC2_DEF_CCHMASK;
- opts->c_srate = UAC2_DEF_CSRATE;
+ opts->c_srates[0] = UAC2_DEF_CSRATE;
opts->c_ssize = UAC2_DEF_CSSIZE;
opts->c_sync = UAC2_DEF_CSYNC;
diff --git a/drivers/usb/gadget/function/u_uac2.h b/drivers/usb/gadget/function/u_uac2.h
index e0c8e3513bfd..6bfcf6d0e863 100644
--- a/drivers/usb/gadget/function/u_uac2.h
+++ b/drivers/usb/gadget/function/u_uac2.h
@@ -14,6 +14,7 @@
#define U_UAC2_H
#include <linux/usb/composite.h>
+#include "uac_common.h"
#define UAC2_DEF_PCHMASK 0x3
#define UAC2_DEF_PSRATE 48000
@@ -35,10 +36,10 @@
struct f_uac2_opts {
struct usb_function_instance func_inst;
int p_chmask;
- int p_srate;
+ int p_srates[UAC_MAX_RATES];
int p_ssize;
int c_chmask;
- int c_srate;
+ int c_srates[UAC_MAX_RATES];
int c_ssize;
int c_sync;
diff --git a/drivers/usb/gadget/legacy/audio.c b/drivers/usb/gadget/legacy/audio.c
index 5ec477ffab7f..d14b9f2d4c07 100644
--- a/drivers/usb/gadget/legacy/audio.c
+++ b/drivers/usb/gadget/legacy/audio.c
@@ -26,9 +26,10 @@ module_param(p_chmask, uint, 0444);
MODULE_PARM_DESC(p_chmask, "Playback Channel Mask");
/* Playback Default 48 KHz */
-static int p_srate = UAC2_DEF_PSRATE;
-module_param(p_srate, uint, 0444);
-MODULE_PARM_DESC(p_srate, "Playback Sampling Rate");
+static int p_srates[UAC_MAX_RATES] = {UAC2_DEF_PSRATE};
+static int p_srates_cnt = 1;
+module_param_array_named(p_srate, p_srates, uint, &p_srates_cnt, 0444);
+MODULE_PARM_DESC(p_srate, "Playback Sampling Rates (array)");
/* Playback Default 16bits/sample */
static int p_ssize = UAC2_DEF_PSSIZE;
@@ -41,9 +42,10 @@ module_param(c_chmask, uint, 0444);
MODULE_PARM_DESC(c_chmask, "Capture Channel Mask");
/* Capture Default 64 KHz */
-static int c_srate = UAC2_DEF_CSRATE;
-module_param(c_srate, uint, 0444);
-MODULE_PARM_DESC(c_srate, "Capture Sampling Rate");
+static int c_srates[UAC_MAX_RATES] = {UAC2_DEF_CSRATE};
+static int c_srates_cnt = 1;
+module_param_array_named(c_srate, c_srates, uint, &c_srates_cnt, 0444);
+MODULE_PARM_DESC(c_srate, "Capture Sampling Rates (array)");
/* Capture Default 16bits/sample */
static int c_ssize = UAC2_DEF_CSSIZE;
@@ -237,6 +239,7 @@ static int audio_bind(struct usb_composite_dev *cdev)
{
#ifndef CONFIG_GADGET_UAC1
struct f_uac2_opts *uac2_opts;
+ int i;
#else
#ifndef CONFIG_GADGET_UAC1_LEGACY
struct f_uac1_opts *uac1_opts;
@@ -263,10 +266,16 @@ static int audio_bind(struct usb_composite_dev *cdev)
#ifndef CONFIG_GADGET_UAC1
uac2_opts = container_of(fi_uac2, struct f_uac2_opts, func_inst);
uac2_opts->p_chmask = p_chmask;
- uac2_opts->p_srate = p_srate;
+
+ for (i = 0; i < p_srates_cnt; ++i)
+ uac2_opts->p_srates[i] = p_srates[i];
+
uac2_opts->p_ssize = p_ssize;
uac2_opts->c_chmask = c_chmask;
- uac2_opts->c_srate = c_srate;
+
+ for (i = 0; i < c_srates_cnt; ++i)
+ uac2_opts->c_srates[i] = c_srates[i];
+
uac2_opts->c_ssize = c_ssize;
uac2_opts->req_number = UAC2_DEF_REQ_NUM;
#else
]
|
| 117/4 |
2026/07/31 02:43 |
llm |
codeexpert |
0m
Model:
Tokens:
input: 85660
output: 27
thoughts: 2806
Thoughts: **Bug Analysis and Exploitability Assessment**
Okay, let's break this down. My initial observation is that this `f_uac1_opts_##name##_store` function, and its `UAC1_RATE_ATTRIBUTE` and `UAC2_RATE_ATTRIBUTE` counterparts, contain a potential vulnerability. Specifically, I'm focusing on the usage of `strsep` and subsequent `kfree` of the allocated `split_page`.
Here's the sequence: The function receives a comma-separated string `page`. `kstrdup` allocates memory for `split_page` and copies the string. Then `strsep` is used in a loop to parse the string. If `kstrtou32` fails, the code jumps to `end` and `kfree(split_page)` is called. The crucial point is that if `kstrtou32` fails *mid-way* through parsing (i.e., after at least one successful parse), `split_page` will point to a location *within* the allocated buffer, but not at the beginning of it. `kfree` is then called on this invalid pointer. This is a classic "invalid-free" or "bad-free" bug.
I need to confirm this. Let's trace through a few examples. If the input is "44100,48000", `split_page` gets `NULL` at the end and `kfree(NULL)` is safe. If the input is "44100,abc", `kstrtou32` on "abc" fails; `split_page` is not NULL and, thus, points to inside the buffer. `kfree(split_page)` will attempt to free an invalid pointer. This is the bad case! Finally, an input like "abc,44100" demonstrates the same scenario.
So, the vulnerability is triggered whenever an invalid token is encountered after at least one valid token. The function will attempt to free a pointer that is not the start of the allocated memory block, leading to memory corruption.
Now, is this exploitable? First, I need to consider who can write to configfs. Usually, only root can write to `/config` or `/sys/kernel/config`. However, configfs is a RAM filesystem, and the permissions depend on the subsystem. For USB gadget configuration (as in the code), the files might be under `/config/usb-gadget/`. Typically, these files are root-owned and are not writable by unprivileged users. But, on some Android systems, a restricted user (like `system` or a USB daemon) might have access.
If the root user triggers this, it's not a privilege escalation. But for systems with lockdown or in containers, where root's privileges might be restricted, the issue is critical.
How is this memory corruption exploited in the kernel allocator? `kfree(split_page)` will trigger `virt_to_page` to resolve the pointer to the page that contains the invalid object, then, the `slab_free` function will be called on an invalid pointer. The object itself points to an offset inside an allocation. This is a serious problem because the incorrect pointer is passed to `slab_free`. The allocator is likely to corrupt either adjacent metadata, other objects on the slab, or trigger a crash (KASAN invalid-free). This could lead to arbitrary code execution, kernel panic, or denial-of-service, depending on how the allocator is used afterwards.
|
| 118/4 |
2026/07/31 02:43 |
tool |
grepper |
0m
Error:
Args: map[Expression:do_slab_free PathPrefix:mm/]
Results:
|
| 119/4 |
2026/07/31 02:43 |
llm |
codeexpert |
0m
Model:
Tokens:
input: 86461
output: 25
thoughts: 0
|
| 120/4 |
2026/07/31 02:43 |
tool |
grepper |
0m
Args: map[Expression:slab_free PathPrefix:mm/]
Results: map[Output:mm/kasan/common.c=256=bool __kasan_slab_pre_free(struct kmem_cache *cache, void *object,
--
mm/kasan/common.c-263-
mm/kasan/common.c:264:bool __kasan_slab_free(struct kmem_cache *cache, void *object, bool init,
mm/kasan/common.c-265- bool still_accessible, bool no_quarantine)
--
mm/kfence/core.c=611=static unsigned long kfence_init_pool(void)
--
mm/kfence/core.c-628- * fast-path in SLUB, and therefore need to ensure kfree() correctly
mm/kfence/core.c:629: * enters __slab_free() slow-path.
mm/kfence/core.c-630- */
--
mm/kfence/report.c=73=static int get_stack_skipnr(const unsigned long stack_entries[], int num_entries,
--
mm/kfence/report.c-102- str_has_prefix(buf, ARCH_FUNC_PREFIX "__kmem_cache_free") ||
mm/kfence/report.c:103: !strncmp(buf, ARCH_FUNC_PREFIX "__slab_free", len)) {
mm/kfence/report.c-104- /*
--
mm/kmsan/hooks.c=48=void kmsan_slab_alloc(struct kmem_cache *s, void *object, gfp_t flags)
--
mm/kmsan/hooks.c-70-
mm/kmsan/hooks.c:71:void kmsan_slab_free(struct kmem_cache *s, void *object)
mm/kmsan/hooks.c-72-{
--
mm/ksm.c=490=static int __init ksm_slab_init(void)
--
mm/ksm.c-513-
mm/ksm.c:514:static void __init ksm_slab_free(void)
mm/ksm.c-515-{
--
mm/ksm.c=3977=static int __init ksm_init(void)
--
mm/ksm.c-4016-out_free:
mm/ksm.c:4017: ksm_slab_free();
mm/ksm.c-4018-out:
--
mm/memcontrol.c=3537=bool __memcg_slab_post_alloc_hook(struct kmem_cache *s, struct list_lru *lru,
--
mm/memcontrol.c-3628-
mm/memcontrol.c:3629:void __memcg_slab_free_hook(struct kmem_cache *s, struct slab *slab,
mm/memcontrol.c-3630- void **p, int objects, unsigned long obj_exts)
--
mm/slab.h=685=bool __memcg_slab_post_alloc_hook(struct kmem_cache *s, struct list_lru *lru,
--
mm/slab.h-687- size_t size, void **p);
mm/slab.h:688:void __memcg_slab_free_hook(struct kmem_cache *s, struct slab *slab,
mm/slab.h-689- void **p, int objects, unsigned long obj_exts);
--
mm/slub.c-92- * This clearing also exempts them from list management. Please see
mm/slub.c:93: * __slab_free() for more details.
mm/slub.c-94- *
--
mm/slub.c-98- * slab->objects and slab->freelist == NULL) are not placed on any list.
mm/slub.c:99: * The __slab_free() freeing the first object from such a slab will place
mm/slub.c-100- * it on the partial list. Caches with debugging enabled place such slab
--
mm/slub.c=2383=static noinline void
mm/slub.c:2384:__alloc_tagging_slab_free_hook(struct kmem_cache *s, struct slab *slab, void **p,
mm/slub.c-2385- int objects)
--
mm/slub.c=2407=static inline void
mm/slub.c:2408:alloc_tagging_slab_free_hook(struct kmem_cache *s, struct slab *slab, void **p,
mm/slub.c-2409- int objects)
--
mm/slub.c-2411- if (mem_alloc_profiling_enabled())
mm/slub.c:2412: __alloc_tagging_slab_free_hook(s, slab, p, objects);
mm/slub.c-2413-}
--
mm/slub.c=2423=static inline void
mm/slub.c:2424:alloc_tagging_slab_free_hook(struct kmem_cache *s, struct slab *slab, void **p,
mm/slub.c-2425- int objects)
--
mm/slub.c=2461=static __fastpath_inline
mm/slub.c:2462:void memcg_slab_free_hook(struct kmem_cache *s, struct slab *slab, void **p,
mm/slub.c-2463- int objects)
--
mm/slub.c-2474- get_slab_obj_exts(obj_exts);
mm/slub.c:2475: __memcg_slab_free_hook(s, slab, p, objects, obj_exts);
mm/slub.c-2476- put_slab_obj_exts(obj_exts);
--
mm/slub.c=2541=static inline bool memcg_slab_post_alloc_hook(struct kmem_cache *s,
--
mm/slub.c-2548-
mm/slub.c:2549:static inline void memcg_slab_free_hook(struct kmem_cache *s, struct slab *slab,
mm/slub.c-2550- void **p, int objects)
--
mm/slub.c=2554=static inline bool memcg_slab_post_charge(void *p, gfp_t flags)
--
mm/slub.c-2560-#ifdef CONFIG_SLUB_RCU_DEBUG
mm/slub.c:2561:static void slab_free_after_rcu_debug(struct rcu_head *rcu_head);
mm/slub.c-2562-
--
mm/slub.c=2595=static __always_inline
mm/slub.c:2596:bool slab_free_hook(struct kmem_cache *s, void *x, bool init,
mm/slub.c-2597- bool after_rcu_delay)
--
mm/slub.c-2602- kmemleak_free_recursive(x, s->flags);
mm/slub.c:2603: kmsan_slab_free(s, x);
mm/slub.c-2604-
--
mm/slub.c-2640- delayed_free->object = x;
mm/slub.c:2641: call_rcu(&delayed_free->head, slab_free_after_rcu_debug);
mm/slub.c-2642- return false;
--
mm/slub.c-2648- * As memory initialization might be integrated into KASAN,
mm/slub.c:2649: * kasan_slab_free and initialization memset's must be
mm/slub.c-2650- * kept together to avoid discrepancies in behavior.
--
mm/slub.c-2676- /* KASAN might put x into memory quarantine, delaying its reuse. */
mm/slub.c:2677: return !kasan_slab_free(s, x, init, still_accessible, false);
mm/slub.c-2678-}
--
mm/slub.c=2680=static __fastpath_inline
mm/slub.c:2681:bool slab_free_freelist_hook(struct kmem_cache *s, void **head, void **tail,
mm/slub.c-2682- int *cnt)
--
mm/slub.c-2690- if (is_kfence_address(next)) {
mm/slub.c:2691: slab_free_hook(s, next, false, false);
mm/slub.c-2692- return false;
--
mm/slub.c-2705- /* If object's reuse doesn't have to be delayed */
mm/slub.c:2706: if (likely(slab_free_hook(s, object, init, false))) {
mm/slub.c-2707- /* Move object to the new freelist */
--
mm/slub.c=2914=static bool __rcu_free_sheaf_prepare(struct kmem_cache *s,
--
mm/slub.c-2924-
mm/slub.c:2925: memcg_slab_free_hook(s, slab, p + i, 1);
mm/slub.c:2926: alloc_tagging_slab_free_hook(s, slab, p + i, 1);
mm/slub.c-2927-
mm/slub.c:2928: if (unlikely(!slab_free_hook(s, p[i], init, true))) {
mm/slub.c-2929- p[i] = p[--sheaf->size];
--
mm/slub.c=3581=static inline void *next_slab_obj(struct kmem_cache *s,
--
mm/slub.c-3605-/* Build a freelist from the objects not yet allocated from a fresh slab. */
mm/slub.c:3606:static inline void build_slab_freelist(struct kmem_cache *s, struct slab *slab,
mm/slub.c-3607- struct slab_obj_iter *iter)
--
mm/slub.c=3667=static void *alloc_single_from_new_slab(struct kmem_cache *s, struct slab *slab,
--
mm/slub.c-3681- needs_add_partial = (slab->objects > 1);
mm/slub.c:3682: build_slab_freelist(s, slab, &iter);
mm/slub.c-3683-
--
mm/slub.c=3727=static bool get_partial_node_bulk(struct kmem_cache *s,
--
mm/slub.c-3749- struct freelist_counters flc;
mm/slub.c:3750: unsigned int slab_free;
mm/slub.c-3751-
--
mm/slub.c-3764- *
mm/slub.c:3765: * slab_free is a lower bound due to possible subsequent
mm/slub.c-3766- * concurrent freeing, so the caller may get more objects than
--
mm/slub.c-3769- flc.counters = data_race(READ_ONCE(slab->counters));
mm/slub.c:3770: slab_free = flc.objects - flc.inuse;
mm/slub.c-3771-
--
mm/slub.c-3773- if (total_free >= pc->min_objects
mm/slub.c:3774: && total_free + slab_free > pc->max_objects)
mm/slub.c-3775- break;
--
mm/slub.c-3781-
mm/slub.c:3782: total_free += slab_free;
mm/slub.c-3783- if (total_free >= pc->max_objects)
--
mm/slub.c=3798=static void *get_from_partial_node(struct kmem_cache *s,
--
mm/slub.c-3836- * get a single object from the slab. This might race against
mm/slub.c:3837: * __slab_free(), which however has to take the list_lock if
mm/slub.c-3838- * it's about to make the slab fully free.
--
mm/slub.c=4373=static unsigned int alloc_from_new_slab(struct kmem_cache *s, struct slab *slab,
--
mm/slub.c-4396- slab->inuse = count;
mm/slub.c:4397: build_slab_freelist(s, slab, &iter);
mm/slub.c-4398-
--
mm/slub.c=5537=static noinline void free_to_partial_list(
--
mm/slub.c-5542- struct kmem_cache_node *n = get_node(s, slab_nid(slab));
mm/slub.c:5543: struct slab *slab_free = NULL;
mm/slub.c-5544- int cnt = bulk_cnt;
--
mm/slub.c-5570- if (slab->inuse == 0 && n->nr_partial >= s->min_partial)
mm/slub.c:5571: slab_free = slab;
mm/slub.c-5572-
--
mm/slub.c-5575- remove_full(s, n, slab);
mm/slub.c:5576: if (!slab_free) {
mm/slub.c-5577- add_partial(n, slab, ADD_TO_TAIL);
--
mm/slub.c-5579- }
mm/slub.c:5580: } else if (slab_free) {
mm/slub.c-5581- remove_partial(n, slab);
--
mm/slub.c-5585-
mm/slub.c:5586: if (slab_free) {
mm/slub.c-5587- /*
--
mm/slub.c-5590- */
mm/slub.c:5591: dec_slabs_node(s, slab_nid(slab_free), slab_free->objects);
mm/slub.c-5592- }
--
mm/slub.c-5595-
mm/slub.c:5596: if (slab_free) {
mm/slub.c-5597- stat(s, FREE_SLAB);
mm/slub.c:5598: free_slab(s, slab_free);
mm/slub.c-5599- }
--
mm/slub.c=5609=static bool __slab_try_return_freelist(struct kmem_cache *s, struct slab *slab,
--
mm/slub.c-5637- */
mm/slub.c:5638:static void __slab_free(struct kmem_cache *s, struct slab *slab,
mm/slub.c-5639- void *head, void *tail, int cnt,
--
mm/slub.c-5691-
mm/slub.c:5692: } while (!slab_update_freelist(s, slab, &old, &new, "__slab_free"));
mm/slub.c-5693-
--
mm/slub.c=5809=__pcs_replace_full_main(struct kmem_cache *s, struct slub_percpu_sheaves *pcs,
--
mm/slub.c-5928- * Free an object to the percpu sheaves.
mm/slub.c:5929: * The object is expected to have passed slab_free_hook() already.
mm/slub.c-5930- */
--
mm/slub.c=5957=static void rcu_free_sheaf(struct rcu_head *head)
--
mm/slub.c-5967- /*
mm/slub.c:5968: * This may remove some objects due to slab_free_hook() returning false,
mm/slub.c-5969- * so that the sheaf might no longer be completely full. But it's easier
--
mm/slub.c-5984-
mm/slub.c:5985: /* due to slab_free_hook() */
mm/slub.c-5986- if (unlikely(sheaf->size == 0))
--
mm/slub.c=6184=static void free_to_pcs_bulk(struct kmem_cache *s, size_t size, void **p)
--
mm/slub.c-6196-
mm/slub.c:6197: memcg_slab_free_hook(s, slab, p + i, 1);
mm/slub.c:6198: alloc_tagging_slab_free_hook(s, slab, p + i, 1);
mm/slub.c-6199-
mm/slub.c:6200: if (unlikely(!slab_free_hook(s, p[i], init, false))) {
mm/slub.c-6201- p[i] = p[--size];
--
mm/slub.c=6306=static DEFINE_PER_CPU(struct defer_free, defer_free_objects) = {
--
mm/slub.c-6312- * In PREEMPT_RT irq_work runs in per-cpu kthread, so it's safe
mm/slub.c:6313: * to take sleeping spin_locks from __slab_free().
mm/slub.c-6314- * In !PREEMPT_RT irq_work will run after local_unlock_irqrestore().
--
mm/slub.c=6316=static void free_deferred_objects(struct irq_work *work)
--
mm/slub.c-6343-
mm/slub.c:6344: __slab_free(s, slab, x, x, 1, _THIS_IP_);
mm/slub.c-6345- stat(s, FREE_SLOWPATH);
--
mm/slub.c=6370=static __fastpath_inline
mm/slub.c:6371:void slab_free(struct kmem_cache *s, struct slab *slab, void *object,
mm/slub.c-6372- unsigned long addr)
mm/slub.c-6373-{
mm/slub.c:6374: memcg_slab_free_hook(s, slab, &object, 1);
mm/slub.c:6375: alloc_tagging_slab_free_hook(s, slab, &object, 1);
mm/slub.c-6376-
mm/slub.c:6377: if (unlikely(!slab_free_hook(s, object, slab_want_init_on_free(s), false)))
mm/slub.c-6378- return;
--
mm/slub.c-6382-
mm/slub.c:6383: __slab_free(s, slab, object, object, 1, addr);
mm/slub.c-6384- stat(s, FREE_SLOWPATH);
--
mm/slub.c=6390=void memcg_alloc_abort_single(struct kmem_cache *s, void *object)
--
mm/slub.c-6393-
mm/slub.c:6394: alloc_tagging_slab_free_hook(s, slab, &object, 1);
mm/slub.c-6395-
mm/slub.c:6396: if (likely(slab_free_hook(s, object, slab_want_init_on_free(s), false)))
mm/slub.c:6397: __slab_free(s, slab, object, object, 1, _RET_IP_);
mm/slub.c-6398-}
--
mm/slub.c=6401=static __fastpath_inline
mm/slub.c:6402:void slab_free_bulk(struct kmem_cache *s, struct slab *slab, void *head,
mm/slub.c-6403- void *tail, void **p, int cnt, unsigned long addr)
mm/slub.c-6404-{
mm/slub.c:6405: memcg_slab_free_hook(s, slab, p, cnt);
mm/slub.c:6406: alloc_tagging_slab_free_hook(s, slab, p, cnt);
mm/slub.c-6407- /*
mm/slub.c:6408: * With KASAN enabled slab_free_freelist_hook modifies the freelist
mm/slub.c-6409- * to remove objects, whose reuse must be delayed.
mm/slub.c-6410- */
mm/slub.c:6411: if (likely(slab_free_freelist_hook(s, &head, &tail, &cnt))) {
mm/slub.c:6412: __slab_free(s, slab, head, tail, cnt, addr);
mm/slub.c-6413- stat_add(s, FREE_SLOWPATH, cnt);
--
mm/slub.c-6417-#ifdef CONFIG_SLUB_RCU_DEBUG
mm/slub.c:6418:static void slab_free_after_rcu_debug(struct rcu_head *rcu_head)
mm/slub.c-6419-{
--
mm/slub.c-6438- /* resume freeing */
mm/slub.c:6439: if (slab_free_hook(s, object, slab_want_init_on_free(s), true)) {
mm/slub.c:6440: __slab_free(s, slab, object, object, 1, _THIS_IP_);
mm/slub.c-6441- stat(s, FREE_SLOWPATH);
--
mm/slub.c=6447=void ___cache_free(struct kmem_cache *cache, void *x, unsigned long addr)
mm/slub.c-6448-{
mm/slub.c:6449: __slab_free(cache, virt_to_slab(x), x, x, 1, addr);
mm/slub.c-6450- stat(cache, FREE_SLOWPATH);
--
mm/slub.c=6484=void kmem_cache_free(struct kmem_cache *s, void *x)
--
mm/slub.c-6503- trace_kmem_cache_free(_RET_IP_, x, s);
mm/slub.c:6504: slab_free(s, slab, x, _RET_IP_);
mm/slub.c-6505-}
--
mm/slub.c=6624=void kvfree_rcu_cb(struct rcu_head *head)
--
mm/slub.c-6661-
mm/slub.c:6662: slab_free(s, slab, obj, _RET_IP_);
mm/slub.c-6663-}
--
mm/slub.c=6671=void kfree(const void *object)
--
mm/slub.c-6691- s = slab->slab_cache;
mm/slub.c:6692: slab_free(s, slab, x, _RET_IP_);
mm/slub.c-6693-}
--
mm/slub.c=6705=void kfree_nolock(const void *object)
--
mm/slub.c-6721-
mm/slub.c:6722: memcg_slab_free_hook(s, slab, &x, 1);
mm/slub.c:6723: alloc_tagging_slab_free_hook(s, slab, &x, 1);
mm/slub.c-6724- /*
mm/slub.c:6725: * Unlike slab_free() do NOT call the following:
mm/slub.c-6726- * kmemleak_free_recursive(x, s->flags);
--
mm/slub.c-6732- */
mm/slub.c:6733: kmsan_slab_free(s, x);
mm/slub.c-6734- /*
--
mm/slub.c-6749- */
mm/slub.c:6750: kasan_slab_free(s, x, false, false, /* skip quarantine */true);
mm/slub.c-6751-
--
mm/slub.c-6755- /*
mm/slub.c:6756: * __slab_free() can locklessly cmpxchg16 into a slab, but then it might
mm/slub.c-6757- * need to take spin_lock for further processing.
--
mm/slub.c=7106=static void __kmem_cache_free_bulk(struct kmem_cache *s, size_t size, void **p)
--
mm/slub.c-7120-
mm/slub.c:7121: __slab_free(df.s, df.slab, df.freelist, df.tail, df.cnt,
mm/slub.c-7122- _RET_IP_);
--
mm/slub.c=7127=void kmem_cache_free_bulk(struct kmem_cache *s, size_t size, void **p)
--
mm/slub.c-7147-
mm/slub.c:7148: slab_free_bulk(df.s, df.slab, df.freelist, df.tail, &p[size],
mm/slub.c-7149- df.cnt, _RET_IP_);
--
mm/slub.c=7155=__refill_objects_node(struct kmem_cache *s, void **p, gfp_t gfp, unsigned int min,
--
mm/slub.c-7207- } while (object);
mm/slub.c:7208: __slab_free(s, slab, head, tail, count, _RET_IP_);
mm/slub.c-7209- }
--
mm/slub.c=7653=static void early_kmem_cache_node_alloc(int node)
--
mm/slub.c-7674- slab->inuse = 1;
mm/slub.c:7675: build_slab_freelist(kmem_cache_node, slab, &iter);
mm/slub.c-7676-
]
|
| 121/4 |
2026/07/31 02:43 |
llm |
codeexpert |
0m
Model:
Tokens:
input: 93582
output: 34
thoughts: 2222
Thoughts: **Analyzing a Potential Kernel Bug**
Okay, so I've been tasked with looking into this `KASAN: invalid-free` report related to `f_uac1_opts_c_srate_store` (or `f_uac1_opts_p_srate_store`, which are instantiated using the same macro). The user is asking whether this bug is exploitable. My first instinct is to analyze the provided code snippet. The core vulnerability seems to stem from the use of `strsep` within the store function. It looks like the intent is to parse comma-separated values. I immediately recognize the potential for a double-free or an invalid free.
I recall that `strsep` modifies the input string in place by inserting null terminators. The important thing is that `kstrdup` is used to get a copy of the input string. The copy is then processed by `strsep`. The pointer to the copy is stored in `split_page`. The core problem is in this loop: `while ((token = strsep(&split_page, ",")) != NULL)`. The `split_page` pointer changes with each iteration of the loop, since `strsep` modifies it. The issue arises when a parsing error occurs. `kstrtou32` might fail in some iteration. When it does, there is a `goto end;` and a call to `kfree(split_page)`.
Initially, I thought that if `kstrtou32` fails on the first token, or if the loop finishes successfully, `split_page` is set to `NULL`, and the `kfree(NULL)` will be a no-op, which would be fine. But then I consider the case where we write something like "44100,abc,123." On the first iteration, `token` is "44100", `split_page` will be updated to point to "abc,123", which is okay. In the second iteration, `token` will be "abc", and `split_page` will be updated to point to "123". `kstrtou32` will fail on "abc" and we will `goto end;`.
The critical point is that, on the `goto end;`, `split_page` now points *into the middle* of the allocated buffer – specifically, to the string "123". Calling `kfree` with a pointer to the middle of an allocated block is the classic invalid-free scenario. This will trigger KASAN, and indeed, it’s exactly what the user is reporting.
To confirm my understanding, I did a mental walk-through of the pointer manipulations to ensure I had the right intuition. I confirmed that the bug is indeed an invalid-free of an object in a SLUB (or SLAB/SLOB) slab allocator, meaning that the `kfree` is called with a pointer to inside a slab object, which KASAN detects.
Now, the question is, is this exploitable? This brings me to thinking about how `kfree` behaves. I recall that `kfree` ultimately uses functions like `slab_free` or `__slab_free` to free the memory. I need to check what checks, if any, are done by the `slab_free` functions. I will have to check the code. I'll look at the `mm/slub.c` file and use tools like `codesearch-definition-source` to jump to `__slab_free` (or `slab_free`) and see if alignment or object start checks are performed. This is the key to assessing the exploitability of the bug. I will see if the `slab_free` function considers the alignment of the object being freed. If not, the bug *is* likely exploitable.
|
| 122/4 |
2026/07/31 02:44 |
tool |
codesearch-definition-source |
0m
Args: map[ContextFile:mm/slub.c Name:__slab_free]
Results: map[SourceCode:5630: /*
5631: * Slow path handling. This may still be called frequently since objects
5632: * have a longer lifetime than the cpu slabs in most processing loads.
5633: *
5634: * So we still attempt to reduce cache line usage. Just take the slab
5635: * lock and free the item. If there is no additional partial slab
5636: * handling required then we can return immediately.
5637: */
5638: static void __slab_free(struct kmem_cache *s, struct slab *slab,
5639: void *head, void *tail, int cnt,
5640: unsigned long addr)
5641:
5642: {
5643: bool was_full;
5644: struct freelist_counters old, new;
5645: struct kmem_cache_node *n = NULL;
5646: unsigned long flags;
5647: bool on_node_partial;
5648:
5649: if (IS_ENABLED(CONFIG_SLUB_TINY) || kmem_cache_debug(s)) {
5650: free_to_partial_list(s, slab, head, tail, cnt, addr);
5651: return;
5652: }
5653:
5654: do {
5655: if (unlikely(n)) {
5656: spin_unlock_irqrestore(&n->list_lock, flags);
5657: n = NULL;
5658: }
5659:
5660: old.freelist = slab->freelist;
5661: old.counters = slab->counters;
5662:
5663: was_full = (old.freelist == NULL);
5664:
5665: set_freepointer(s, tail, old.freelist);
5666:
5667: new.freelist = head;
5668: new.counters = old.counters;
5669: new.inuse -= cnt;
5670:
5671: /*
5672: * Might need to be taken off (due to becoming empty) or added
5673: * to (due to not being full anymore) the partial list.
5674: * Unless it's frozen.
5675: */
5676: if (!new.inuse || was_full) {
5677:
5678: n = get_node(s, slab_nid(slab));
5679: /*
5680: * Speculatively acquire the list_lock.
5681: * If the cmpxchg does not succeed then we may
5682: * drop the list_lock without any processing.
5683: *
5684: * Otherwise the list_lock will synchronize with
5685: * other processors updating the list of slabs.
5686: */
5687: spin_lock_irqsave(&n->list_lock, flags);
5688:
5689: on_node_partial = slab_test_node_partial(slab);
5690: }
5691:
5692: } while (!slab_update_freelist(s, slab, &old, &new, "__slab_free"));
5693:
5694: if (likely(!n)) {
5695: /*
5696: * We didn't take the list_lock because the slab was already on
5697: * the partial list and will remain there.
5698: */
5699: return;
5700: }
5701:
5702: /*
5703: * This slab was partially empty but not on the per-node partial list,
5704: * in which case we shouldn't manipulate its list, just return.
5705: */
5706: if (!was_full && !on_node_partial) {
5707: spin_unlock_irqrestore(&n->list_lock, flags);
5708: return;
5709: }
5710:
5711: /*
5712: * If slab became empty, should we add/keep it on the partial list or we
5713: * have enough?
5714: */
5715: if (unlikely(!new.inuse && n->nr_partial >= s->min_partial))
5716: goto slab_empty;
5717:
5718: /*
5719: * Objects left in the slab. If it was not on the partial list before
5720: * then add it.
5721: */
5722: if (unlikely(was_full)) {
5723: add_partial(n, slab, ADD_TO_TAIL);
5724: stat(s, FREE_ADD_PARTIAL);
5725: }
5726: spin_unlock_irqrestore(&n->list_lock, flags);
5727: return;
5728:
5729: slab_empty:
5730: /*
5731: * The slab could have a single object and thus go from full to empty in
5732: * a single free, but more likely it was on the partial list. Remove it.
5733: */
5734: if (likely(!was_full)) {
5735: remove_partial(n, slab);
5736: stat(s, FREE_REMOVE_PARTIAL);
5737: }
5738:
5739: spin_unlock_irqrestore(&n->list_lock, flags);
5740: stat(s, FREE_SLAB);
5741: discard_slab(s, slab);
5742: }
SourceFile:mm/slub.c]
|
| 123/4 |
2026/07/31 02:44 |
llm |
codeexpert |
0m
Model:
Tokens:
input: 96063
output: 31
thoughts: 2585
Thoughts: **Analyzing the Potential Exploitability of a Kernel Bug**
Okay, here's what I'm thinking. The user is asking about the `invalid-free` KASAN report related to a particular kernel function, likely `f_uac1_opts_c_srate_store` (or a similar one generated by the `UAC` macro). They've provided the code snippet, specifically the part dealing with `strsep` and `kfree`. My immediate thought is to do a risk analysis. Is this a real threat? Let's check the request. The user is asking if the bug is exploitable, but they aren't asking for an exploit. Okay, we're in the clear to analyze without generating. My goal here is to analyze the bug and try to explain whether or not the behavior is exploitable. Let's dig in.
The macro expands to a function that processes a comma-separated list of numbers. The core problem, as I see it, centers around how `strsep` modifies the `split_page` pointer. This `split_page` pointer, originally allocated with `kstrdup`, is passed to `strsep` within a loop. The intention is to iterate through tokens. But, I see a clear issue: If `strsep` updates the pointer to point *inside* the original allocated buffer, and an error during the `kstrtou32` processing causes a jump to `end`, `kfree` is called on an invalid pointer. The pointer is not the beginning of the allocated buffer.
Now, let's trace this step by step. If `kstrtou32` fails, the `split_page` pointer, which `strsep` has already modified to point to an offset within the original buffer, is then passed to `kfree`. That's an obvious heap corruption vulnerability. This is an invalid free. That's the key.
Let's check if there's any scenario where this would work. After thinking this through, there isn't.
Now, the important question: how bad is this? An invalid free is usually pretty bad, but is it immediately exploitable? I need to think about what the kernel's memory allocator does when presented with an invalid pointer. Specifically, what happens with `kfree` and SLUB?
If `kfree` sees an invalid pointer, `virt_to_page` is going to return the page containing the invalid pointer. Then, `page_slab` is called. That should give us the slab. That's what we want.
Now, looking at the `slab_free` code, and eventually `__slab_free`, I see the potential for memory corruption. The freed object will write the next free pointer into the freed object, but it is written into an arbitrary offset, which is `x + s->offset`. It writes `old.freelist` to `x + s->offset`. In this case, `x` is our invalid pointer. The freed object is now pointing to unaligned or arbitrary memory on the slab, so you corrupt memory.
So, in short, yes, this is exploitable. It causes an invalid free, but then corrupts the slab page by overwriting freed object metadata.
The exploit depends on root or configfs permissions to write the attributes that trigger this function.
Let's quickly check if there is any safety check, but there are no obvious checks in `mm/slub.c` to protect against an unaligned free.
|
| 124/4 |
2026/07/31 02:44 |
tool |
grepper |
0m
Args: map[Expression:check_valid_pointer PathPrefix:mm/slub.c]
Results: map[Output:mm/slub.c=998=static int disable_higher_order_debug;
--
mm/slub.c-1004-/* Verify that a pointer has an address that is valid within a slab page */
mm/slub.c:1005:static inline int check_valid_pointer(struct kmem_cache *s,
mm/slub.c-1006- struct slab *slab, void *object)
--
mm/slub.c=1218=static void object_err(struct kmem_cache *s, struct slab *slab,
--
mm/slub.c-1224- slab_bug(s, reason);
mm/slub.c:1225: if (!object || !check_valid_pointer(s, slab, object)) {
mm/slub.c-1226- print_slab_info(slab);
--
mm/slub.c=1470=static int check_object(struct kmem_cache *s, struct slab *slab,
--
mm/slub.c-1535- if ((freeptr_outside_object(s) || val != SLUB_RED_ACTIVE) &&
mm/slub.c:1536: !check_valid_pointer(s, slab, get_freepointer(s, p))) {
mm/slub.c-1537- object_err(s, slab, p, "Freepointer corrupt");
--
mm/slub.c=1584=static bool on_freelist(struct kmem_cache *s, struct slab *slab, void *search)
--
mm/slub.c-1594- return true;
mm/slub.c:1595: if (!check_valid_pointer(s, slab, fp)) {
mm/slub.c-1596- if (object) {
--
mm/slub.c=1722=static inline int alloc_consistency_checks(struct kmem_cache *s,
--
mm/slub.c-1727-
mm/slub.c:1728: if (!check_valid_pointer(s, slab, object)) {
mm/slub.c-1729- object_err(s, slab, object, "Freelist Pointer check fails");
--
mm/slub.c=1766=static inline int free_consistency_checks(struct kmem_cache *s,
--
mm/slub.c-1768-{
mm/slub.c:1769: if (!check_valid_pointer(s, slab, object)) {
mm/slub.c-1770- slab_err(s, slab, "Invalid object pointer 0x%p", object);
]
|
| 125/4 |
2026/07/31 02:44 |
llm |
codeexpert |
0m
Model:
Tokens:
input: 97528
output: 35
thoughts: 0
|
| 126/4 |
2026/07/31 02:44 |
tool |
codesearch-definition-source |
0m
Args: map[ContextFile:mm/slub.c Name:check_valid_pointer]
Results: map[SourceCode:1004: /* Verify that a pointer has an address that is valid within a slab page */
1005: static inline int check_valid_pointer(struct kmem_cache *s,
1006: struct slab *slab, void *object)
1007: {
1008: void *base;
1009:
1010: if (!object)
1011: return 1;
1012:
1013: base = slab_address(slab);
1014: object = kasan_reset_tag(object);
1015: object = restore_red_left(s, object);
1016: if (object < base || object >= base + slab->objects * s->size ||
1017: (object - base) % s->size) {
1018: return 0;
1019: }
1020:
1021: return 1;
1022: }
SourceFile:mm/slub.c]
|
| 127/4 |
2026/07/31 02:44 |
llm |
codeexpert |
0m
Model:
Tokens:
input: 97850
output: 35
thoughts: 0
|
| 128/4 |
2026/07/31 02:44 |
tool |
codesearch-definition-source |
0m
Args: map[ContextFile:mm/slub.c Name:free_consistency_checks]
Results: map[SourceCode:1766: static inline int free_consistency_checks(struct kmem_cache *s,
1767: struct slab *slab, void *object, unsigned long addr)
1768: {
1769: if (!check_valid_pointer(s, slab, object)) {
1770: slab_err(s, slab, "Invalid object pointer 0x%p", object);
1771: return 0;
1772: }
1773:
1774: if (on_freelist(s, slab, object)) {
1775: object_err(s, slab, object, "Object already free");
1776: return 0;
1777: }
1778:
1779: if (!check_object(s, slab, object, SLUB_RED_ACTIVE))
1780: return 0;
1781:
1782: if (unlikely(s != slab->slab_cache)) {
1783: if (!slab->slab_cache) {
1784: slab_err(NULL, slab, "No slab cache for object 0x%p",
1785: object);
1786: } else {
1787: object_err(s, slab, object,
1788: "page slab pointer corrupt.");
1789: }
1790: return 0;
1791: }
1792: return 1;
1793: }
SourceFile:mm/slub.c]
|
| 129/4 |
2026/07/31 02:44 |
llm |
codeexpert |
0m
Model:
Tokens:
input: 98314
output: 31
thoughts: 0
|
| 130/4 |
2026/07/31 02:44 |
tool |
grepper |
0m
Args: map[Expression:free_consistency_checks PathPrefix:mm/slub.c]
Results: map[Output:mm/slub.c=1739=static noinline bool alloc_debug_processing(struct kmem_cache *s,
--
mm/slub.c-1765-
mm/slub.c:1766:static inline int free_consistency_checks(struct kmem_cache *s,
mm/slub.c-1767- struct slab *slab, void *object, unsigned long addr)
--
mm/slub.c=4169=static inline bool free_debug_processing(struct kmem_cache *s,
--
mm/slub.c-4193- if (s->flags & SLAB_CONSISTENCY_CHECKS) {
mm/slub.c:4194: if (!free_consistency_checks(s, slab, object, addr))
mm/slub.c-4195- goto out;
]
|
| 131/4 |
2026/07/31 02:44 |
llm |
codeexpert |
0m
Model:
Tokens:
input: 98571
output: 35
thoughts: 0
|
| 132/4 |
2026/07/31 02:44 |
tool |
codesearch-definition-source |
0m
Args: map[ContextFile:mm/slub.c Name:free_debug_processing]
Results: map[SourceCode:4168: /* Supports checking bulk free of a constructed freelist */
4169: static inline bool free_debug_processing(struct kmem_cache *s,
4170: struct slab *slab, void *head, void *tail, int *bulk_cnt,
4171: unsigned long addr, depot_stack_handle_t handle)
4172: {
4173: bool checks_ok = false;
4174: void *object = head;
4175: int cnt = 0;
4176:
4177: if (s->flags & SLAB_CONSISTENCY_CHECKS) {
4178: if (!check_slab(s, slab))
4179: goto out;
4180: }
4181:
4182: if (slab->inuse < *bulk_cnt) {
4183: slab_err(s, slab, "Slab has %d allocated objects but %d are to be freed\n",
4184: slab->inuse, *bulk_cnt);
4185: goto out;
4186: }
4187:
4188: next_object:
4189:
4190: if (++cnt > *bulk_cnt)
4191: goto out_cnt;
4192:
4193: if (s->flags & SLAB_CONSISTENCY_CHECKS) {
4194: if (!free_consistency_checks(s, slab, object, addr))
4195: goto out;
4196: }
4197:
4198: if (s->flags & SLAB_STORE_USER)
4199: set_track_update(s, object, TRACK_FREE, addr, handle);
4200: trace(s, slab, object, 0);
4201: /* Freepointer not overwritten by init_object(), SLAB_POISON moved it */
4202: init_object(s, object, SLUB_RED_INACTIVE);
4203:
4204: /* Reached end of constructed freelist yet? */
4205: if (object != tail) {
4206: object = get_freepointer(s, object);
4207: goto next_object;
4208: }
4209: checks_ok = true;
4210:
4211: out_cnt:
4212: if (cnt != *bulk_cnt) {
4213: slab_err(s, slab, "Bulk free expected %d objects but found %d\n",
4214: *bulk_cnt, cnt);
4215: *bulk_cnt = cnt;
4216: }
4217:
4218: out:
4219:
4220: if (!checks_ok)
4221: slab_fix(s, "Object at 0x%p not freed", object);
4222:
4223: return checks_ok;
4224: }
SourceFile:mm/slub.c]
|
| 133/4 |
2026/07/31 02:44 |
llm |
codeexpert |
0m
Model:
Tokens:
input: 99454
output: 47
thoughts: 0
|
| 134/4 |
2026/07/31 02:44 |
tool |
git-log |
0m
Args: map[CodeRegexp:f_uac1_opts_.*_store Count:5 PathPrefix:drivers/usb/gadget/function/]
Results: map[Output:dfb05b5dc3af usb: gadget: f_uac1: allow changing interface name via configfs
695d39ffc2b5 usb: gadget: f_uac1: Support multiple sampling rates
0591bc236015 usb: gadget: add f_uac1 variant based on a new u_audio api
c6f89f1cca1c usb-gadget/f_uac1: use per-attribute show and store methods
0854611a19ae usb: gadget: f_uac1: add configfs support
]
|
| 135/4 |
2026/07/31 02:44 |
llm |
codeexpert |
0m
Model:
Tokens:
input: 99654
output: 24
thoughts: 0
|
| 136/4 |
2026/07/31 02:44 |
tool |
git-show |
0m
Args:
Results: map[Output:commit dfb05b5dc3afd90e564b69b88ff6be6947a0f32f
Author: Yunhao Tian <t123yh.xyz@gmail.com>
Date: Sat Jan 22 19:24:40 2022 +0800
usb: gadget: f_uac1: allow changing interface name via configfs
This adds "function_name" configfs entry to change string value
of the iInterface field. This field will be shown in Windows' audio
settings panel, so being able to change it is useful. It will default
to "AC Interface" just as before if unchanged.
Signed-off-by: Yunhao Tian <t123yh.xyz@gmail.com>
Link: https://lore.kernel.org/r/20220122112446.1415547-1-t123yh.xyz@gmail.com
Signed-off-by: Greg Kroah-Hartman <gregkh@linuxfoundation.org>
diff --git a/Documentation/ABI/testing/configfs-usb-gadget-uac1 b/Documentation/ABI/testing/configfs-usb-gadget-uac1
index 09725e273e9b..c4ba92f004c3 100644
--- a/Documentation/ABI/testing/configfs-usb-gadget-uac1
+++ b/Documentation/ABI/testing/configfs-usb-gadget-uac1
@@ -29,4 +29,5 @@ Description:
(in 1/256 dB)
req_number the number of pre-allocated requests
for both capture and playback
+ function_name name of the interface
===================== =======================================
diff --git a/Documentation/usb/gadget-testing.rst b/Documentation/usb/gadget-testing.rst
index 046842b00c89..1792bd88f666 100644
--- a/Documentation/usb/gadget-testing.rst
+++ b/Documentation/usb/gadget-testing.rst
@@ -745,6 +745,7 @@ The uac2 function provides these attributes in its function directory:
p_volume_res playback volume control resolution (in 1/256 dB)
req_number the number of pre-allocated request for both capture
and playback
+ function_name name of the interface
================ ====================================================
The attributes have sane default values.
diff --git a/drivers/usb/gadget/function/f_uac1.c b/drivers/usb/gadget/function/f_uac1.c
index 1484e5c231d3..6f0e1d803dc2 100644
--- a/drivers/usb/gadget/function/f_uac1.c
+++ b/drivers/usb/gadget/function/f_uac1.c
@@ -309,7 +309,7 @@ enum {
};
static struct usb_string strings_uac1[] = {
- [STR_AC_IF].s = "AC Interface",
+ /* [STR_AC_IF].s = DYNAMIC, */
[STR_USB_OUT_IT].s = "Playback Input terminal",
[STR_USB_OUT_IT_CH_NAMES].s = "Playback Channels",
[STR_IO_OUT_OT].s = "Playback Output terminal",
@@ -1192,6 +1192,8 @@ static int f_audio_bind(struct usb_configuration *c, struct usb_function *f)
audio_opts = container_of(f->fi, struct f_uac1_opts, func_inst);
+ strings_uac1[STR_AC_IF].s = audio_opts->function_name;
+
us = usb_gstrings_attach(cdev, uac1_strings, ARRAY_SIZE(strings_uac1));
if (IS_ERR(us))
return PTR_ERR(us);
@@ -1551,6 +1553,42 @@ end: \
\
CONFIGFS_ATTR(f_uac1_opts_, name)
+#define UAC1_ATTRIBUTE_STRING(name) \
+static ssize_t f_uac1_opts_##name##_show(struct config_item *item, \
+ char *page) \
+{ \
+ struct f_uac1_opts *opts = to_f_uac1_opts(item); \
+ int result; \
+ \
+ mutex_lock(&opts->lock); \
+ result = snprintf(page, sizeof(opts->name), "%s", opts->name); \
+ mutex_unlock(&opts->lock); \
+ \
+ return result; \
+} \
+ \
+static ssize_t f_uac1_opts_##name##_store(struct config_item *item, \
+ const char *page, size_t len) \
+{ \
+ struct f_uac1_opts *opts = to_f_uac1_opts(item); \
+ int ret = 0; \
+ \
+ mutex_lock(&opts->lock); \
+ if (opts->refcnt) { \
+ ret = -EBUSY; \
+ goto end; \
+ } \
+ \
+ ret = snprintf(opts->name, min(sizeof(opts->name), len), \
+ "%s", page); \
+ \
+end: \
+ mutex_unlock(&opts->lock); \
+ return ret; \
+} \
+ \
+CONFIGFS_ATTR(f_uac1_opts_, name)
+
UAC1_ATTRIBUTE(u32, c_chmask);
UAC1_RATE_ATTRIBUTE(c_srate);
UAC1_ATTRIBUTE(u32, c_ssize);
@@ -1570,6 +1608,7 @@ UAC1_ATTRIBUTE(bool, c_volume_present);
UAC1_ATTRIBUTE(s16, c_volume_min);
UAC1_ATTRIBUTE(s16, c_volume_max);
UAC1_ATTRIBUTE(s16, c_volume_res);
+UAC1_ATTRIBUTE_STRING(function_name);
static struct configfs_attribute *f_uac1_attrs[] = {
&f_uac1_opts_attr_c_chmask,
@@ -1592,6 +1631,8 @@ static struct configfs_attribute *f_uac1_attrs[] = {
&f_uac1_opts_attr_c_volume_max,
&f_uac1_opts_attr_c_volume_res,
+ &f_uac1_opts_attr_function_name,
+
NULL,
};
@@ -1643,6 +1684,9 @@ static struct usb_function_instance *f_audio_alloc_inst(void)
opts->c_volume_res = UAC1_DEF_RES_DB;
opts->req_number = UAC1_DEF_REQ_NUM;
+
+ snprintf(opts->function_name, sizeof(opts->function_name), "AC Interface");
+
return &opts->func_inst;
}
diff --git a/drivers/usb/gadget/function/u_uac1.h b/drivers/usb/gadget/function/u_uac1.h
index b6cd6171d306..f7a616760e31 100644
--- a/drivers/usb/gadget/function/u_uac1.h
+++ b/drivers/usb/gadget/function/u_uac1.h
@@ -52,6 +52,8 @@ struct f_uac1_opts {
int req_number;
unsigned bound:1;
+ char function_name[32];
+
struct mutex lock;
int refcnt;
};
]
|
| 137/4 |
2026/07/31 02:44 |
llm |
codeexpert |
1m
Model:
Tokens:
input: 101777
output: 26
thoughts: 0
|
| 138/4 |
2026/07/31 02:45 |
tool |
git-log |
0m
Args: map[CodeRegexp:strsep Since:1 year]
Results: map[Output:8faab14922b7 samples/landlock: Add quiet flag support to sandboxer
e8dcf2d142bd block: add configurable error injection
a963fbf3166f tools/rv: Fix substring match bug in monitor name search
a1d4a1bc09f1 wifi: mwifiex: remove an unnecessary check
1f8fd0fe5641 net: usb: pegasus: replace simple_strtoul with kstrtouint
0cbc300257d9 smb/client: remove unused smb3_parse_opt()
4f10f1dfb235 net: remove ISDN subsystem and Bluetooth CMTP
5bbb3453e8f3 cgroup/rdma: refactor resource parsing with match_table_t/match_token()
126c50bc2fb6 dma-buf: Remove the old selftest
8fc518e489c1 9p/trans_xen: replace simple_strto* with kstrtouint
]
|
| 139/4 |
2026/07/31 02:46 |
llm |
codeexpert |
0m
Model:
Tokens:
input: 102063
output: 38
thoughts: 0
|
| 140/4 |
2026/07/31 02:46 |
tool |
git-log |
6m
Args: map[CodeRegexp:f_uac1_opts_c_srate_store Since:5 years]
Results:
|
| 141/4 |
2026/07/31 02:53 |
llm |
codeexpert |
0m
Model:
Tokens:
input: 102114
output: 36
thoughts: 0
|
| 142/4 |
2026/07/31 02:53 |
tool |
git-log |
10m
Error:
Args: map[CodeRegexp:f_uac1_opts_.*_store Since:10 years]
Results:
|
| 143/4 |
2026/07/31 03:03 |
llm |
codeexpert |
0m
Model:
Tokens:
input: 102185
output: 40
thoughts: 4955
Thoughts: **Vulnerability Analysis: Kernel Slab Corruption in USB Gadget Driver**
Okay, so I've been digging into this KASAN report, and it looks like we've got a potential security vulnerability on our hands. The user is asking about the exploitability of a `KASAN: invalid-free` error within the USB gadget drivers, specifically in the `f_uac1_opts_c_srate_store` (and likely similar) functions. The code snippet uses `strsep` to parse a comma-separated list of values from user-supplied input via configfs. Here's what I've found:
The core issue stems from how `strsep` interacts with `kfree`. `strsep` *modifies* the input pointer, advancing it with each token it extracts. If parsing the user input fails mid-way, due to an invalid number from `kstrtou32`, `kfree` is called with a pointer (`split_page`) that's *not* the start of the originally allocated buffer. This is a classic "invalid free" condition, and KASAN correctly flags it.
But the real danger lies deeper than just an invalid pointer. If the kernel is compiled without `CONFIG_SLUB_DEBUG`, or if it is running with consistency checks turned off, the `slab_free` function will simply attempt to put the invalidly-pointed-to object into the freelist of the slab cache. When a new object is allocated later from that same slab cache, it will now point to an unaligned location within a kmalloc chunk. The user has full control of what offset the invalid free pointer is.
Here's where it becomes exploitable:
1. **Arbitrary Offset Control:** The attacker can craft the input string and control the offset of the freed pointer inside the chunk.
2. **Slab Cache Control:** The length of the user input (e.g. string) controls the kmalloc size (e.g. 64, 128, etc.).
3. **Slab Corruption:** The misaligned free means the next allocation will write to unaligned memory, *overlapping* with data of other objects within the same slab. It can also write out of bounds. The attacker can carefully set the amount of preceding data, such that when the parsing errors, the `kfree` pointer lands exactly inside a neighboring chunk, or exactly at the start of a neighboring chunk.
4. **Exploitation Potential:** This overlapping write can corrupt internal kernel structures, potentially leading to arbitrary read/write primitives, privilege escalation, or code execution. We can overwrite the neighboring chunks with controlled data.
The attacker needs write access to the configfs attributes. This is typically restricted to the `root` or `system` user. In standard Linux this is a privilege escalation from `root` to kernel or `system` to kernel. On Android, the `system` user has a much more restricted security context.
The vulnerability exists in both the `f_uac1` and `f_uac2` USB gadget drivers.
|
| 144/4 |
2026/07/31 03:03 |
tool |
git-log |
0m
Args: map[CodeRegexp:split_page Count:10 PathPrefix:drivers/usb/gadget/function/]
Results: map[Output:695d39ffc2b5 usb: gadget: f_uac1: Support multiple sampling rates
a7339e4f5788 usb: gadget: f_uac2: Support multiple sampling rates
]
|
| 145/4 |
2026/07/31 03:03 |
llm |
codeexpert |
0m
Model:
Tokens:
input: 102948
output: 31
thoughts: 6583
Thoughts: **Analysis of a Critical Memory Corruption Vulnerability in USB Gadget Drivers**
Okay, here's the deal. I've been digging into this code, specifically the USB gadget function drivers for UAC1 and UAC2, and I've stumbled upon a really nasty memory corruption bug. It's in the way the code handles parsing comma-separated lists of values within the ConfigFS attributes for things like sampling rates.
Here's the scenario: the code uses `strsep` to tokenize a string read from a configfs attribute (like `c_srate` or `p_srate`). The problem arises because `strsep` modifies the *original* string you pass to it. The code allocates memory with `kstrdup`, then feeds the *pointer to that allocated memory* into `strsep`. After the loop, the code calls `kfree` on the pointer used for parsing, which *might not be* the original pointer returned by `kstrdup`.
Let me break down what happens:
1. **Memory Leak:** If the input string has valid numbers (e.g., "44100,48000"), the parsing completes successfully. `strsep` eventually sets the *pointer you pass to it* to `NULL`. The code then calls `kfree(NULL)`, which *looks* harmless, but the memory allocated by `kstrdup` is leaked!
2. **Invalid Free:** If the input string has an invalid element (e.g., "abc,44100"), then `strsep` modifies the passed pointer *to point somewhere inside the allocated memory*. In this scenario, `kfree` is called with this *offset* pointer, which is an *invalid free* of memory that was allocated by `kstrdup`.
Now, the consequences get really interesting and depend on the kernel's configuration. I'm thinking about KASAN, SLUB debugging and so on.
* **SLUB Debug:** If SLUB debugging (`CONFIG_SLUB_DEBUG`) is enabled, the kernel detects the invalid free via `check_valid_pointer` and prints an "Invalid object pointer" error. The free is then aborted to prevent immediate corruption (but we still leak memory, which is a problem on its own).
* **KASAN:** If Kernel Address Sanitizer (KASAN) is enabled, it *will* detect that the pointer passed to `kfree` is invalid (unaligned or not at the beginning of an allocated object), and trigger a `KASAN: invalid-free` report.
* **Production Kernels (No SLUB Debug, No KASAN):** This is the scary one. If *neither* SLUB debug nor KASAN is active (typical in production kernels), then the invalid pointer is passed to `__slab_free`. `__slab_free` will corrupt the slab's freelist, leading to unaligned allocations, and potentially overlapping objects in memory. That can cause a *classic slab-overlap* and can lead to things like *use-after-free* and *type confusion*, which are often exploitable as a method to achieve local privilege escalation or kernel code execution.
Here's the rub for exploitability: This is only triggered by writing to the ConfigFS attributes (c\_srate, p\_srate) of the UAC1 or UAC2 functions. ConfigFS is *usually* only writable by `root` or a privileged system daemon (think system daemons on Android or embedded systems). So, a standard unprivileged user *can't* directly trigger this. However, if a system daemon that manages USB gadgets *is* compromised, or there is a path for an unprivileged user to influence the string written to ConfigFS, *then* this becomes a local privilege escalation (LPE) vector, from the daemon's UID to kernel root!
To fix this, we need to keep a reference to the pointer that `kstrdup` returns and use it for the `kfree` call at the end, while passing a *copy* of that pointer to `strsep` so that the original is unaffected. I've seen it done like this (and this is what needs to be done here):
```c
char *orig_page = kstrdup(page, GFP_KERNEL); // Keep this pointer!
char *split_page = orig_page; // Work with a copy
while ((token = strsep(&split_page, ",")) != NULL) {
// ... parsing ...
}
kfree(orig_page); // Free the original pointer
```
This ensures we free the *correct* memory. I need to make sure this fix is applied immediately.
I need to confirm if this bug also affects `f_uac2.c`, but
|
| 146/4 |
2026/07/31 03:04 |
tool |
grepper |
0m
Args: map[Expression:strsep PathPrefix:drivers/usb/gadget/function/]
Results: map[Output:drivers/usb/gadget/function/f_uac1.c=1593=static ssize_t f_uac1_opts_##name##_store(struct config_item *item, \
--
drivers/usb/gadget/function/f_uac1.c-1611- split_page = kstrdup(page, GFP_KERNEL); \
drivers/usb/gadget/function/f_uac1.c:1612: while ((token = strsep(&split_page, ",")) != NULL) { \
drivers/usb/gadget/function/f_uac1.c-1613- ret = kstrtou32(token, 0, &num); \
--
drivers/usb/gadget/function/f_uac2.c=2011=static ssize_t f_uac2_opts_##name##_store(struct config_item *item, \
--
drivers/usb/gadget/function/f_uac2.c-2029- split_page = kstrdup(page, GFP_KERNEL); \
drivers/usb/gadget/function/f_uac2.c:2030: while ((token = strsep(&split_page, ",")) != NULL) { \
drivers/usb/gadget/function/f_uac2.c-2031- ret = kstrtou32(token, 0, &num); \
]
|
| 147/4 |
2026/07/31 03:04 |
llm |
codeexpert |
0m
Model:
Tokens:
input: 104370
output: 50
thoughts: 0
|
| 148/4 |
2026/07/31 03:04 |
tool |
git-log |
0m
Args: map[CodeRegexp:f_uac1_opts_.*_store PathPrefix:drivers/usb/gadget/function/f_uac1.c]
Results: map[Output:dfb05b5dc3af usb: gadget: f_uac1: allow changing interface name via configfs
695d39ffc2b5 usb: gadget: f_uac1: Support multiple sampling rates
0591bc236015 usb: gadget: add f_uac1 variant based on a new u_audio api
d355339eecd9 usb: gadget: function: make current f_uac1 implementation legacy
c6f89f1cca1c usb-gadget/f_uac1: use per-attribute show and store methods
0854611a19ae usb: gadget: f_uac1: add configfs support
]
|
| 149/4 |
2026/07/31 03:04 |
llm |
codeexpert |
0m
Model:
Tokens:
input: 104599
output: 50
thoughts: 0
|
| 150/4 |
2026/07/31 03:04 |
tool |
git-log |
0m
Args: map[CodeRegexp:f_uac2_opts_.*_store PathPrefix:drivers/usb/gadget/function/f_uac2.c]
Results: map[Output:993a44fa85c1 usb: gadget: f_uac2: allow changing interface name via configfs
a7339e4f5788 usb: gadget: f_uac2: Support multiple sampling rates
40c73b30546e usb: gadget: f_uac2: add adaptive sync support for capture
495702bcc12f usb-gadget/f_uac2: use per-attribute show and store methods
3aeea3c53e73 usb: gadget: f_uac2: add configfs support
]
|
| 151/4 |
2026/07/31 03:04 |
llm |
codeexpert |
0m
Model:
Tokens:
input: 104802
output: 37
thoughts: 6927
Thoughts: **Analysis of a Heap Corruption Vulnerability in USB Gadget Audio Drivers**
Okay, I see the issue. My initial `git log` search was too specific, leading me down a rabbit hole. The problem lies within how `f_uac1_opts_c_srate_store` handles input validation when parsing comma-separated values. Specifically, the macro expands to `f_uac1_opts_##name##_store`, so I need to look past the function name and focus on the logic. The `strsep` function is the key here.
The core vulnerability centers around the `strsep(&split_page, ",")` function call inside `f_uac1_opts_##name##_store`. This function modifies the `split_page` pointer in-place, which is derived from a `kstrdup` call on user-controlled input. The loop parses comma-separated integer values, but if a token is invalid (e.g., non-numeric) or there are multiple commas, `kfree(split_page)` is called with a pointer that's no longer the original allocation, resulting in an "invalid-free" KASAN error. This is a classic heap corruption scenario.
Tracing the code, if `kstrtou32` fails on any token *after* the first, the `split_page` pointer is advanced. When `kfree` is later called on this advanced pointer, it points outside the originally allocated buffer, which is very bad news. This triggers an invalid free. It's not just a memory leak; it's a *controlled* free of an invalid pointer. Even if the first token is invalid, but a comma is present, the same thing occurs.
But let's not stop there. The "invalid-free" itself could be leveraged for far more powerful attacks. In an SLUB allocator, a free on an invalid pointer can mean:
1. It is a no-op if consistency checks are enabled, which is not default on many systems.
2. If consistency checks are not enabled (or they are disabled by booting without a `slub_debug` boot parameter), the kernel writes the current freelist pointer to the freed memory.
This means that if we can cause a free on a pointer that is an offset within an allocated object, that offset itself is controlled by the user-supplied string we're processing! This is a "free-anything-in-slab-page" primitive.
Crucially, because this invalid pointer *is* within the same slab page, it can be abused. After the invalid free, the next object allocated from the same slab cache could be placed at the "freed" offset. If that "freed" offset overlaps an active object, we've achieved a heap overlap – potentially even an arbitrary write. And the attacker has very precise control over which kmalloc bucket is corrupted. By varying the string length and contents, we control the freed offset, turning this into a highly reliable and powerful exploitation primitive.
The ability to control the offset is a game changer. We can choose any offset up to the size of `PAGE_SIZE`. If we write an "invalid," we can control the offset which becomes `strlen("invalid") + 1 = 8`. This means the allocator adds `ptr + 8` to the freelist. Then, we can allocate an object, that now starts at `ptr+8`, which causes overlap.
This vulnerability is highly exploitable. Configfs attributes are usually writable by root (or users in specific groups), though configfs is often enabled by default and widely used, especially in Android for USB gadget configurations. The bug was introduced with the commit that added support for multiple sample rates in January 2022 and there is no fix for this bug in the git history!
|
| 152/4 |
2026/07/31 03:05 |
tool |
git-log |
0m
Args: map[Count:20 PathPrefix:drivers/usb/gadget/function/f_uac1.c]
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
e715bc42e337 usb: gadget: Constify struct configfs_item_operations and configfs_group_operations
bb76f0d843a2 usb: gadget: f_uac1: replace scnprintf() with sysfs_emit()
82313624b2ae usb: gadget: f_uac1: Change volume name and remove alt names
aace0aec49a3 usb: gadget: f_uac1: Expose all string descriptors through configfs.
1cb9ba5e61e8 usb: gadget: add missing MODULE_DESCRIPTION() macros
c1a371866db9 usb: gadget: f_uac1: Replace snprintf() with the safer scnprintf() variant
b8fb6db6cb04 usb: f_uac1: adds support for SS and SSP
dfb05b5dc3af usb: gadget: f_uac1: allow changing interface name via configfs
d1d11dd13069 usb: gadget: f_uac1: Add suspend callback
695d39ffc2b5 usb: gadget: f_uac1: Support multiple sampling rates
8722a949e62a usb: gadget: u_audio: Move dynamic srate from params to rtd
c565ad07ef35 usb: gadget: u_audio: Support multiple sampling rates
d9f273484358 usb:gadget: f_uac1: fixed sync playback
1bc220835526 usb: gadget: f_uac1: fixing inconsistent indenting
0356e6283c71 usb: gadget: f_uac1: add volume and mute support
254cb1e0d78c usb: gadget: f_uac1: disable IN/OUT ep if unused
a59c68a6a3d1 usb: gadget: f_uac1: validate input parameters
cc2ac63d4cf7 usb: gadget: f_uac1: stop playback on function disable
]
|
| 153/4 |
2026/07/31 03:05 |
llm |
codeexpert |
0m
Model:
Tokens:
input: 106205
output: 31
thoughts: 0
|
| 154/4 |
2026/07/31 03:05 |
tool |
git-log |
2m
Args: map[CodeRegexp:kfree\(split_page\) Since:2 years]
Results:
|
| 155/4 |
2026/07/31 03:08 |
llm |
codeexpert |
0m
Model:
Tokens:
input: 106249
output: 27
thoughts: 0
|
| 156/4 |
2026/07/31 03:08 |
tool |
git-log |
1m
Args: map[CodeRegexp:split_page Since:2 years]
Results: map[Output:a39f0ce0c9da Revert "svcrdma: Use contiguous pages for RDMA Read sink buffers"
4aa4abf1f14b mm/page_alloc: optimize free_contig_range()
18755b8c2f24 svcrdma: Use contiguous pages for RDMA Read sink buffers
05988dba1179 vdso/datastore: Allocate data pages dynamically
e0c1326779cc mm: page_alloc: add alloc_contig_frozen_{range,pages}()
a9deb800b89e mm: page_alloc: add __split_page()
7b71205ae112 kho: fix restoring of contiguous ranges of order-0 pages
092ef3899707 mm/huge_memory: update folio stat after successful split
a06157804399 mm/vmalloc: request large order pages from buddy allocator
a667300bd53f kho: add support for preserving vmalloc allocations
]
|
| 157/4 |
2026/07/31 03:09 |
llm |
codeexpert |
0m
Model:
Tokens:
input: 106536
output: 39
thoughts: 0
|
| 158/4 |
2026/07/31 03:09 |
tool |
git-log |
10m
Error:
Args: map[CodeRegexp:f_uac1_opts_c_srate_store Since:10 years]
Results:
|
| 159/4 |
2026/07/31 03:19 |
llm |
codeexpert |
0m
Model:
Tokens:
input: 106610
output: 61
thoughts: 0
|
| 160/4 |
2026/07/31 03:19 |
tool |
git-log |
0m
Args: map[CodeRegexp:f_uac1_opts_c_srate_store PathPrefix:drivers/usb/gadget/function/f_uac1.c Since:10 years]
Results:
|
| 161/4 |
2026/07/31 03:19 |
llm |
codeexpert |
0m
Model:
Tokens:
input: 106684
output: 59
thoughts: 0
|
| 162/4 |
2026/07/31 03:19 |
tool |
git-log |
0m
Args: map[MessageRegexps:[f_uac1_opts srate] PathPrefix:drivers/usb/gadget/function/f_uac1.c Since:10 years]
Results:
|
| 163/4 |
2026/07/31 03:19 |
llm |
codeexpert |
0m
Model:
Tokens:
input: 106756
output: 54
thoughts: 0
|
| 164/4 |
2026/07/31 03:19 |
tool |
git-log |
0m
Args: map[MessageRegexps:[f_uac1_opts] PathPrefix:drivers/usb/gadget/function/f_uac1.c Since:10 years]
Results:
|
| 165/4 |
2026/07/31 03:19 |
llm |
codeexpert |
0m
Model:
Tokens:
input: 106823
output: 52
thoughts: 0
|
| 166/4 |
2026/07/31 03:19 |
tool |
git-log |
0m
Args: map[MessageRegexps:[uac1_opts] PathPrefix:drivers/usb/gadget/function/f_uac1.c Since:10 years]
Results:
|
| 167/4 |
2026/07/31 03:19 |
llm |
codeexpert |
0m
Model:
Tokens:
input: 106888
output: 48
thoughts: 0
|
| 168/4 |
2026/07/31 03:19 |
tool |
git-log |
0m
Args: map[MessageRegexps:[opts] PathPrefix:drivers/usb/gadget/function/f_uac1.c Since:10 years]
Results:
|
| 169/4 |
2026/07/31 03:19 |
llm |
codeexpert |
0m
Model:
Tokens:
input: 106949
output: 48
thoughts: 0
|
| 170/4 |
2026/07/31 03:19 |
tool |
git-log |
0m
Args: map[MessageRegexps:[store] PathPrefix:drivers/usb/gadget/function/f_uac1.c Since:10 years]
Results: map[Output:42370b821168 usb: gadget: f_uac1: endianness fixes.
]
|
| 171/4 |
2026/07/31 03:19 |
llm |
codeexpert |
0m
Model:
Tokens:
input: 107037
output: 54
thoughts: 0
|
| 172/4 |
2026/07/31 03:19 |
tool |
git-log |
0m
Args: map[MessageRegexps:[UAC1_RATE_ATTRIBUTE] PathPrefix:drivers/usb/gadget/function/f_uac1.c Since:10 years]
Results:
|
| 173/4 |
2026/07/31 03:19 |
llm |
codeexpert |
0m
Model:
Tokens:
input: 107104
output: 32
thoughts: 0
|
| 174/4 |
2026/07/31 03:19 |
tool |
git-log |
0m
Args: map[MessageRegexps:[UAC1_RATE_ATTRIBUTE] Since:10 years]
Results:
|
| 175/4 |
2026/07/31 03:19 |
llm |
codeexpert |
0m
Model:
Tokens:
input: 107149
output: 24
thoughts: 0
|
| 176/4 |
2026/07/31 03:19 |
tool |
git-log |
0m
Args: map[MessageRegexps:[UAC1_RATE_ATTRIBUTE]]
Results:
|
| 177/4 |
2026/07/31 03:20 |
llm |
codeexpert |
0m
Model:
Tokens:
input: 107186
output: 19
thoughts: 0
|
| 178/4 |
2026/07/31 03:20 |
tool |
git-log |
0m
Args: map[MessageRegexps:[sampling rates]]
Results: map[Output:6ea592a31be5 iio: adc: ad7768-1: add support for SPI offload
f1ef70a4a320 ASoC: dt-bindings: davinci-mcasp: Add properties for asynchronous mode
bcd1383516bb ASoC: SOF: ipc4-pcm: fix delay calculation when DSP resamples
05f254a6369a ALSA: usb-audio: Improve filtering of sample rates on Focusrite devices
45ff65e30deb ASoC: ti: j721e-evm: Fix clock configuration for ti,j7200-cpb-audio compatible
cd5bbb2532f2 drm/xe/uapi: Add a device query to get EU stall sampling information
5323186e2e8d ASoC: rockchip: i2s_tdm: Re-add the set_sysclk callback
645fb7c22fd8 iio: adc: rzg2l_adc: Add support for Renesas RZ/G3S
0a2319308de8 ASoC: sun4i-spdif: Add clock multiplier settings
ccd8d753f0fe ASoC: rockchip: i2s-tdm: Fix trcm mode by setting clock on right mclk
]
|
| 179/4 |
2026/07/31 03:20 |
llm |
codeexpert |
0m
Model:
Tokens:
input: 107539
output: 24
thoughts: 0
|
| 180/4 |
2026/07/31 03:20 |
tool |
git-log |
10m
Error:
Args: map[CodeRegexp:UAC1_RATE_ATTRIBUTE]
Results:
|
| 181/4 |
2026/07/31 03:30 |
llm |
codeexpert |
0m
Model:
Tokens:
input: 107598
output: 46
thoughts: 0
|
| 182/4 |
2026/07/31 03:30 |
tool |
git-log |
0m
Args: map[CodeRegexp:UAC1_RATE_ATTRIBUTE PathPrefix:drivers/usb/gadget/function/f_uac1.c]
Results: map[Output:695d39ffc2b5 usb: gadget: f_uac1: Support multiple sampling rates
]
|
| 183/4 |
2026/07/31 03:30 |
llm |
codeexpert |
0m
Model:
Tokens:
input: 107683
output: 46
thoughts: 0
|
| 184/4 |
2026/07/31 03:30 |
tool |
git-log |
0m
Args: map[CodeRegexp:UAC2_RATE_ATTRIBUTE PathPrefix:drivers/usb/gadget/function/f_uac2.c]
Results: map[Output:a7339e4f5788 usb: gadget: f_uac2: Support multiple sampling rates
]
|
| 185/4 |
2026/07/31 03:30 |
llm |
codeexpert |
0m
Model:
Tokens:
input: 107769
output: 29
thoughts: 6142
Thoughts: **Analysis of Invalid Free in USB Gadget Configuration**
Alright, let's break this down. We're looking at a potential memory corruption issue within the USB gadget configuration code, specifically when handling input strings for audio sampling rates. The code snippet, using a macro-generated function, is vulnerable to an invalid free due to the misuse of `strsep`.
Here's the crux: the `f_uac1_opts_name_store` (and similar functions for UAC2) uses `strsep` to tokenize a comma-separated string of sampling rates provided by the user via configfs. The problem arises when an error occurs during the parsing of these rates, say, if the input includes a non-numeric token like "abc". `strsep` modifies the input pointer (`split_page`) in place. If an error happens *after* `strsep` has advanced `split_page`, but *before* freeing, the code incorrectly calls `kfree` with a pointer that's no longer the start of the originally allocated buffer.
Think about it:
- A user provides "abc,def" as input.
- `kstrdup` allocates a buffer, let's say "0xffff888001a02000" and `split_page` initially point to "0xffff888001a02000".
- `strsep` is called. It finds "abc" and updates `split_page` to point to "def" at "0xffff888001a02004".
- `kstrtou32` fails to parse "abc".
- The function then goes to `end:` which then calls `kfree(split_page)`. Since `split_page` now points to "0xffff888001a02004", this is where the invalid free happens.
- This is where the invalid pointer gets freed and the slab memory is corrupted.
This leads to an **invalid free**. Now, what can happen?
- Without debugging/KASAN, the SLUB allocator will happily attempt to free the shifted/unaligned pointer.
- This corrupts the freelist, inserting this shifted pointer into it.
- When new memory from this kmalloc cache is allocated, the allocator will return this same, shifted, unaligned pointer which can overlap with the previous allocation or will be shifted.
- This is a classic heap exploitation scenario. An attacker could overwrite metadata of adjacent objects and can lead to a use-after-free, arbitrary kernel read/write, or even code execution.
- If debugging is enabled, or if KASAN is enabled, then this memory corruption is detected.
Now, let's discuss if this can be exploited. Yes, technically it is exploitable for privilege escalation if the attacker can influence the value written to the configfs attributes. The attributes are typically only writable by root. However, the exploitation potential is heightened if there is a compromised daemon or system service (like `usbgx` or systemd/udev rules) that are running with elevated privileges (like `CAP_SYS_ADMIN` or access to configfs) that takes input from untrusted sources.
Here's the fix: The fix is straightforward. Keep a copy of the original pointer returned by `kstrdup` before it's passed to `strsep` and free that copy instead of the modified `split_page`. In code:
```c
char *split_page = NULL;
char *orig_split_page = NULL;
...
orig_split_page = split_page = kstrdup(page, GFP_KERNEL);
...
kfree(orig_split_page);
```
I've confirmed this vulnerability exists in the current code in `f_uac1.c` and `f_uac2.c`, and it affects any macro-generated functions using the same pattern.
|
| 186/4 |
2026/07/31 03:31 |
tool |
grepper |
0m
Args: map[Expression:strsep PathPrefix:drivers/usb/gadget/]
Results: map[Output:drivers/usb/gadget/function/f_uac1.c=1593=static ssize_t f_uac1_opts_##name##_store(struct config_item *item, \
--
drivers/usb/gadget/function/f_uac1.c-1611- split_page = kstrdup(page, GFP_KERNEL); \
drivers/usb/gadget/function/f_uac1.c:1612: while ((token = strsep(&split_page, ",")) != NULL) { \
drivers/usb/gadget/function/f_uac1.c-1613- ret = kstrtou32(token, 0, &num); \
--
drivers/usb/gadget/function/f_uac2.c=2011=static ssize_t f_uac2_opts_##name##_store(struct config_item *item, \
--
drivers/usb/gadget/function/f_uac2.c-2029- split_page = kstrdup(page, GFP_KERNEL); \
drivers/usb/gadget/function/f_uac2.c:2030: while ((token = strsep(&split_page, ",")) != NULL) { \
drivers/usb/gadget/function/f_uac2.c-2031- ret = kstrtou32(token, 0, &num); \
]
|
| 187/4 |
2026/07/31 03:31 |
llm |
codeexpert |
0m
Model:
Tokens:
input: 109010
output: 32
thoughts: 0
|
| 188/4 |
2026/07/31 03:31 |
tool |
git-log |
0m
Args: map[CodeRegexp:strsep PathPrefix:drivers/usb/gadget/]
Results: map[Output:695d39ffc2b5 usb: gadget: f_uac1: Support multiple sampling rates
a7339e4f5788 usb: gadget: f_uac2: Support multiple sampling rates
]
|
| 189/4 |
2026/07/31 03:31 |
llm |
codeexpert |
0m
Model:
Tokens:
input: 109108
output: 1291
thoughts: 6815
Thoughts: ```python
import sys
def set_results(results):
"""This is a dummy function to simulate the set-results tool.
In a real environment, this would interface with the analysis system.
"""
print(f"Setting results: {results}", file=sys.stderr) # Simulate output to stderr
# In a real tool, you would send this to the tool system.
pass
# Call the tool with a minimal set of results
set_results({"vulnerability": "invalid-free", "severity": "high", "cve": "TBD"})
# Dummy values for remaining fields.
set_results({"exploitability": "highly_likely", "mitigation": "simple_patch"})
```
```text
**Invalid Free in USB Audio Class Gadget Drivers**
My analysis reveals a critical heap-based vulnerability present in both the USB Audio Class 1 (UAC1) and UAC2 gadget drivers within the Linux kernel. The bug stems from an improper use of the `strsep` function in conjunction with `kfree` when parsing comma-separated lists of values for audio sampling rates. Specifically, the following code is problematic:
```c
split_page = kstrdup(page, GFP_KERNEL);
while ((token = strsep(&split_page, ",")) != NULL) {
ret = kstrtou32(token, 0, &num);
if (ret)
goto end;
opts->name##s[i++] = num;
ret = len;
};
end:
kfree(split_page);
```
The issue arises when a parsing error occurs within the loop (e.g., when `kstrtou32` fails to parse a rate). Consider the input `"invalid_rate,44100"`. `kstrdup` allocates memory. `strsep` modifies `split_page`, which now points to `"44100"` after the first iteration where `kstrtou32` fails. The `goto end` statement is executed. At `end`, `kfree(split_page)` is called. However, `split_page` no longer points to the *beginning* of the originally allocated buffer; it points to a location *within* it. This results in an invalid free.
This invalid free corrupts the SLUB allocator. Because `split_page` is an unaligned pointer:
1. `virt_to_page` correctly identifies the correct slab in SLUB.
2. `__slab_free` will eventually write `split_page` to the freelist.
3. The freepointer in the *freed* object will point to the previous head of the freelist (another object).
4. The freelist head will now be `split_page`.
5. When the next allocation occurs from this SLUB cache, the allocator *returns* this unaligned pointer (`split_page`).
6. Subsequent writes to this "allocated" memory will now *overlap* with adjacent objects in the slab, because the starting offset of the buffer is wrong.
This *overlapping* memory overwrite is the key to exploitation.
This invalid free, even if freelist hijacking is difficult due to mitigations like `CONFIG_SLAB_FREELIST_HARDENED`, leads to a *controlled memory corruption* primitive. An attacker can overwrite fields of *other* active objects residing in the same slab cache. This includes critical data structures like function pointers, object pointers, reference counts, or other important values. By strategically corrupting these fields, a privileged attacker (usually root, or a user with CAP_SYS_ADMIN, or a container escape) can achieve a variety of malicious outcomes, including privilege escalation (by overwriting a function pointer with one controlled by an attacker), denial of service (by corrupting critical data structures), or arbitrary code execution.
The attack surface is *generally* limited to users with write access to configfs, which typically requires root privileges. The `usb_f_uac1` or `usb_f_uac2` modules must also be loaded/autoloadable. But, in specific environments, such as containerized deployments, or if a compromised daemon has access to the configfs, the vulnerability is still present.
Both `drivers/usb/gadget/function/f_uac1.c` and `drivers/usb/gadget/function/f_uac2.c` are affected because the code responsible for parsing the values is virtually identical in both files.
A straightforward fix for this bug is to keep a copy of the pointer returned by `kstrdup` before it is modified by `strsep` and to free the original copy. Alternatively, a temporary pointer can be
|
| 190/4 |
2026/07/31 03:31 |
tool |
set-results |
0m
Args: map[Answer:Yes, the bug in `f_uac1_opts_c_srate_store` (and its counterpart `f_uac1_opts_p_srate_store`, as well as the corresponding functions in `f_uac2.c`) is technically exploitable under specific conditions, though it has some practical limitations.
### 1. Bug Mechanism
The bug is a classic **invalid-free (unaligned free)** caused by the misuse of `strsep()`.
In `f_uac1.c` (and `f_uac2.c`), the store macro is defined as:
```c
split_page = kstrdup(page, GFP_KERNEL); \
while ((token = strsep(&split_page, ",")) != NULL) { \
ret = kstrtou32(token, 0, &num); \
if (ret) \
goto end; \
...
}; \
end: \
kfree(split_page); \
```
`strsep()` takes a pointer to a string pointer (`char **stringp`) and updates it to point past the token delimiter.
- If the loop runs to completion (reaching the end of the string), `split_page` becomes `NULL`. Calling `kfree(NULL)` is safe and does nothing.
- However, if an invalid rate string is passed (e.g., `"invalid_rate,44100"`), `strsep()` extracts `"invalid_rate"`, updates `split_page` to point to `"44100"`, and then `kstrtou32()` fails. This triggers `goto end;`.
- At `end:`, `kfree(split_page)` is called on the updated pointer, which points to an offset within the allocated buffer rather than the start of the allocation.
---
### 2. Exploitability Analysis
#### A. Attack Surface & Access Requirements
- **Privilege Requirement:** Writing to configfs attributes (typically mounted at `/sys/kernel/config/usb-gadget/`) generally requires `root` privileges or specific capabilities (e.g., `CAP_SYS_ADMIN`). Thus, this bug cannot be triggered by an unprivileged local user unless configfs permissions have been explicitly relaxed.
- **Context:** It is relevant for local privilege escalation from a partially privileged process (e.g., a container escape or a daemon with configfs write access) or for bypassing kernel lockdown/secure boot restrictions.
#### B. Impact on the SLUB Allocator (Without `SLUB_DEBUG`)
If `CONFIG_SLUB_DEBUG` is disabled (as is common in production kernels for performance reasons), the SLUB allocator does not perform consistency checks on the freed pointer:
1. **Page Resolution:** `virt_to_page(split_page)` resolves the unaligned pointer to the correct `struct slab` page.
2. **Freelist Corruption:** `__slab_free()` links the unaligned pointer (`split_page`) directly into the slab's freelist. The freepointer of this unaligned "object" (located at `split_page + s->offset`) is written with the previous freelist head.
3. **Unaligned Allocation:** The next allocation request from the same `kmalloc` cache will return the unaligned pointer `split_page`.
4. **Object Overlap (OOB Write):** Because the allocated object is shifted/unaligned, writing to it will overflow into the adjacent active objects in the same slab. For example, if the allocation is shifted by 16 bytes, writing to the new object will overwrite the first 16 bytes of the next neighboring object.
5. **Privilege Escalation:** An attacker can use this overlap to overwrite critical fields (such as function pointers, object pointers, or reference counters) of neighboring active objects in the same slab cache, leading to arbitrary code execution or privilege escalation.
#### C. Mitigations
- **`CONFIG_SLUB_DEBUG`:** If enabled, the allocator's consistency checks (`check_valid_pointer`) will detect that the pointer is not aligned to the object size boundary. It will print a "Slab error" and refuse to free the pointer, preventing exploitation but causing a kernel warning/panic (Denial of Service).
- **`CONFIG_SLAB_FREELIST_HARDENED`:** This obfuscates freelist pointers, making direct freelist hijacking (pointing the freelist to an arbitrary address) extremely difficult. However, it **does not** prevent the overlap-based exploitation described above, as the attacker can still corrupt neighboring active objects directly via the unaligned allocation.
---
### 3. Affected Files
This exact bug pattern exists in four configfs store functions across two files:
1. `drivers/usb/gadget/function/f_uac1.c` (via `UAC1_RATE_ATTRIBUTE` macro):
- `f_uac1_opts_c_srate_store`
- `f_uac1_opts_p_srate_store`
2. `drivers/usb/gadget/function/f_uac2.c` (via `UAC2_RATE_ATTRIBUTE` macro):
- `f_uac2_opts_c_srate_store`
- `f_uac2_opts_p_srate_store`
---
### 4. Remediation
The standard fix is to preserve the original pointer returned by `kstrdup()` and free that instead of the pointer modified by `strsep()`:
```c
char *orig_split_page;
...
orig_split_page = split_page = kstrdup(page, GFP_KERNEL);
...
end:
kfree(orig_split_page);
```]
Results: map[Answer:Yes, the bug in `f_uac1_opts_c_srate_store` (and its counterpart `f_uac1_opts_p_srate_store`, as well as the corresponding functions in `f_uac2.c`) is technically exploitable under specific conditions, though it has some practical limitations.
### 1. Bug Mechanism
The bug is a classic **invalid-free (unaligned free)** caused by the misuse of `strsep()`.
In `f_uac1.c` (and `f_uac2.c`), the store macro is defined as:
```c
split_page = kstrdup(page, GFP_KERNEL); \
while ((token = strsep(&split_page, ",")) != NULL) { \
ret = kstrtou32(token, 0, &num); \
if (ret) \
goto end; \
...
}; \
end: \
kfree(split_page); \
```
`strsep()` takes a pointer to a string pointer (`char **stringp`) and updates it to point past the token delimiter.
- If the loop runs to completion (reaching the end of the string), `split_page` becomes `NULL`. Calling `kfree(NULL)` is safe and does nothing.
- However, if an invalid rate string is passed (e.g., `"invalid_rate,44100"`), `strsep()` extracts `"invalid_rate"`, updates `split_page` to point to `"44100"`, and then `kstrtou32()` fails. This triggers `goto end;`.
- At `end:`, `kfree(split_page)` is called on the updated pointer, which points to an offset within the allocated buffer rather than the start of the allocation.
---
### 2. Exploitability Analysis
#### A. Attack Surface & Access Requirements
- **Privilege Requirement:** Writing to configfs attributes (typically mounted at `/sys/kernel/config/usb-gadget/`) generally requires `root` privileges or specific capabilities (e.g., `CAP_SYS_ADMIN`). Thus, this bug cannot be triggered by an unprivileged local user unless configfs permissions have been explicitly relaxed.
- **Context:** It is relevant for local privilege escalation from a partially privileged process (e.g., a container escape or a daemon with configfs write access) or for bypassing kernel lockdown/secure boot restrictions.
#### B. Impact on the SLUB Allocator (Without `SLUB_DEBUG`)
If `CONFIG_SLUB_DEBUG` is disabled (as is common in production kernels for performance reasons), the SLUB allocator does not perform consistency checks on the freed pointer:
1. **Page Resolution:** `virt_to_page(split_page)` resolves the unaligned pointer to the correct `struct slab` page.
2. **Freelist Corruption:** `__slab_free()` links the unaligned pointer (`split_page`) directly into the slab's freelist. The freepointer of this unaligned "object" (located at `split_page + s->offset`) is written with the previous freelist head.
3. **Unaligned Allocation:** The next allocation request from the same `kmalloc` cache will return the unaligned pointer `split_page`.
4. **Object Overlap (OOB Write):** Because the allocated object is shifted/unaligned, writing to it will overflow into the adjacent active objects in the same slab. For example, if the allocation is shifted by 16 bytes, writing to the new object will overwrite the first 16 bytes of the next neighboring object.
5. **Privilege Escalation:** An attacker can use this overlap to overwrite critical fields (such as function pointers, object pointers, or reference counters) of neighboring active objects in the same slab cache, leading to arbitrary code execution or privilege escalation.
#### C. Mitigations
- **`CONFIG_SLUB_DEBUG`:** If enabled, the allocator's consistency checks (`check_valid_pointer`) will detect that the pointer is not aligned to the object size boundary. It will print a "Slab error" and refuse to free the pointer, preventing exploitation but causing a kernel warning/panic (Denial of Service).
- **`CONFIG_SLAB_FREELIST_HARDENED`:** This obfuscates freelist pointers, making direct freelist hijacking (pointing the freelist to an arbitrary address) extremely difficult. However, it **does not** prevent the overlap-based exploitation described above, as the attacker can still corrupt neighboring active objects directly via the unaligned allocation.
---
### 3. Affected Files
This exact bug pattern exists in four configfs store functions across two files:
1. `drivers/usb/gadget/function/f_uac1.c` (via `UAC1_RATE_ATTRIBUTE` macro):
- `f_uac1_opts_c_srate_store`
- `f_uac1_opts_p_srate_store`
2. `drivers/usb/gadget/function/f_uac2.c` (via `UAC2_RATE_ATTRIBUTE` macro):
- `f_uac2_opts_c_srate_store`
- `f_uac2_opts_p_srate_store`
---
### 4. Remediation
The standard fix is to preserve the original pointer returned by `kstrdup()` and free that instead of the pointer modified by `strsep()`:
```c
char *orig_split_page;
...
orig_split_page = split_page = kstrdup(page, GFP_KERNEL);
...
end:
kfree(orig_split_page);
```]
|
| 191/2 |
2026/07/31 03:31 |
llm |
expert |
0m
Model:
Tokens:
input: 17233
output: 66
thoughts: 1137
Thoughts: **Bug Analysis: A Deep Dive into an Unaligned Free Vulnerability**
Okay, so here's the deal. I've got a bug on my hands, a nasty one: an invalid-free, specifically an unaligned free, lurking in the `f_uac1_opts_c_srate_store` function. It's happening because of how `strsep` is being used; it's messing with the pointer before `kfree` gets called. This whole thing is triggered when you write to a configfs file – basically, fiddling with USB gadget settings.
Now, the crucial point is configfs. It's not the kind of file system a regular, unprivileged user can just waltz into and start writing to. You need root privileges to even mount it, no `FS_USERNS_MOUNT` means only root can do it. And once mounted, the files inside are typically root-owned, unless root deliberately changes the permissions. That automatically shuts out your average user and any unprivileged processes or user namespaces.
This invalid-free bug is a memory corruption bug. When it's triggered, it messes up the freelist in SLUB, which means freed memory, not aligned to the size it was allocated in, might get returned again later. The implications? Overlapping objects and potential out-of-bounds writes. This has the potential to be a real pain if an attacker could somehow trigger it.
But... can they? The trigger, remember, is writing to configfs. To do that, you need root, you need `CAP_SYS_ADMIN` to mount configfs and create the USB gadget. Therefore, from an unprivileged process? Nope.
Let's quickly run down other potential triggers. Is this something triggered by a peripheral? No. It's triggered by configfs which is a local configuration interface. Is this a network trigger? No. A filesystem trigger? Nope. A VM guest trigger? No. VM host? Nope.
Alright, let's double-check how `f_uac1_opts_c_srate_store` gets hit. It's a configfs attribute store function, only accessible via configfs writes. And configfs is only mountable by root in the init user namespace. So:
* Exploitable: Yes (memory corruption, invalid free leading to overlapping objects).
* Denial of Service: Yes (kernel crash/memory corruption).
* Unprivileged: No.
* UserNamespace: No.
* VMGuestTrigger: No.
* VMHostTrigger: No.
* NetworkTrigger: No.
* RemoteTrigger: No.
* PeripheralTrigger: No.
* FilesystemTrigger: No.
Hold on, though... Android or some other system? Is there a possibility of them mounting configfs and making it writable to *some* unprivileged user? Usually, the USB gadget configuration is handled by a privileged daemon like `usbd` on Android, which runs as root or some special system user. But let's check the prompt: "Determine if the bug can be reached from a typical (non-root) user process that does NOT have any special capabilities (like CAP_SYS_ADMIN, CAP_NET_ADMIN, CAP_NET_RAW, CAP_PERFMON) or access to device nodes restricted to root." Since configfs is mounted by root and the configfs files are owned by root, it's not possible for a non-root user.
Let's review exploitability one more time, specifically on the code. Here's where the unaligned free happens:
```c
split_page = kstrdup(page, GFP_KERNEL); \
while ((token = strsep(&split_page, ",")) != NULL) { \
ret = kstrtou32(token, 0, &num); \
if (ret) \
goto end; \
```
If `page` is `"123,abc"`, `strsep` returns `"123"` and `split_page` becomes `"abc"`. Then `kstrtou32` succeeds for `"123"`. Next iteration, `strsep` returns `"abc"` and `split_page` is `NULL`. `kstrtou32` fails for `"abc"`, `goto end`. At `end`, `kfree(split_page)` is called. But `split_page` is `NULL`. `kfree(NULL)` is a no-op, no bug.
But if `page` is `"abc,123"`, `strsep` returns `"abc"`, and `split_page` becomes `"123"`. `kstrtou32("abc")` fails,
|
| 192/2 |
2026/07/31 03:32 |
tool |
set-results |
0m
Args: map[DenialOfService:true Exploitable:true FilesystemTrigger:false NetworkTrigger:false PeripheralTrigger:false RemoteTrigger:false Unprivileged:false UserNamespace:false VMGuestTrigger:false VMHostTrigger:false]
Results: map[DenialOfService:true Exploitable:true FilesystemTrigger:false NetworkTrigger:false PeripheralTrigger:false RemoteTrigger:false Unprivileged:false UserNamespace:false VMGuestTrigger:false VMHostTrigger:false]
|
| 193/2 |
2026/07/31 03:32 |
llm |
expert |
0m
Model:
Tokens:
input: 18388
output: 837
thoughts: 1611
Thoughts: **My Analysis of the Kernel Bug Report**
Okay, let's break this down. From what I'm seeing, this is a pretty classic unaligned free situation in `f_uac1_opts_c_srate_store`. The heart of the problem is `strsep` modifying the pointer and then `kfree` getting called with that modified pointer if there's an error during number conversion. Specifically, a failure in `kstrtou32` after `strsep` has done its dirty work. This is bad news, a free of an unaligned pointer.
Now, in a production kernel, without KASAN or SLUB debugging enabled, freeing an unaligned pointer is going to corrupt the SLUB freelist. The next time the kernel tries to allocate from the same cache, it's going to return that same unaligned pointer. This is textbook overlapping objects, which means out-of-bounds writes. And out-of-bounds writes? That's exploit heaven, potentially leading to privilege escalation. So, exploitability is definitely there, at least in theory.
But, and this is a big but, the trigger is in a configfs store function for USB gadget configuration (`/sys/kernel/config/`). So, how accessible is configfs? Well, it's not mounted with `FS_USERNS_MOUNT`, meaning only root, or a user with `CAP_SYS_ADMIN` in the initial user namespace, can even mount it. The files within configfs are typically root-owned and root-writable. Thus, a regular, unprivileged user is completely locked out of triggering this bug. Forget about user namespaces too, that's not possible.
So, in short:
* **Exploitable: Yes**. The unaligned free leads to overlapping objects, which is highly exploitable for privilege escalation.
* **Denial of Service: Yes**. It could definitely crash the kernel.
* **Unprivileged Access: No**. Root or CAP\_SYS\_ADMIN is required.
* **User Namespace Access: No**. Configfs is not user-namespaced.
* **Trigger type**: This isn't triggered by VM Guest/Host, Network, Peripheral, or Malicious Filesystem. This is triggered by a local write to a configfs file.
I've checked the vulnerable code, I know exactly what is happening and how this is going to translate to exploitable conditions. But, the real question is how to make it happen. My assessment is that this unaligned free can be triggered by writing to a configfs file within the kernel config, but it requires root to even be able to access it. So, that's my final answer.
|
| 194/1 |
2026/07/31 03:32 |
action |
format-explanation |
0m
Results: map[Explanation:**Analysis of the Bug Report**
**1. Bug Mechanism:**
The bug is an **invalid-free (unaligned free)** in the
`f_uac1_opts_c_srate_store` function (and similar functions in the USB audio
gadget driver).
The vulnerability occurs due to the misuse of the `strsep()` function:
```c
split_page = kstrdup(page, GFP_KERNEL);
while ((token = strsep(&split_page, ",")) != NULL) {
ret = kstrtou32(token, 0, &num);
if (ret)
goto end;
// ...
};
end:
kfree(split_page);
```
`strsep()` modifies the `split_page` pointer to point to the next token in the
string. If an invalid rate string is provided (causing `kstrtou32()` to fail),
the code jumps to the `end` label and calls `kfree()` on the modified
`split_page` pointer. Because the pointer has been advanced, it no longer points
to the beginning of the allocated memory block, resulting in an unaligned free.
**2. Exploitability:**
**Exploitable: Yes**
In production kernels where debugging features like KASAN or `CONFIG_SLUB_DEBUG`
are disabled, the SLUB allocator does not verify if the freed pointer is aligned
to the start of an object.
When the unaligned pointer is passed to `kfree()`, SLUB links it directly into
the freelist. The next time an object is allocated from this cache, the
allocator will return this unaligned pointer. Because the new allocation is
shifted, any writes to it will overlap with the adjacent active object in the
slab. This creates a powerful out-of-bounds write primitive that can be used to
overwrite critical data (e.g., function pointers, object references) in
neighboring objects, making it highly exploitable for privilege escalation.
**3. Denial of Service:**
**Denial of Service: Yes**
Even if not successfully exploited for privilege escalation, corrupting the SLUB
freelist will inevitably lead to memory corruption, page faults, and kernel
panics, resulting in a denial of service.
**4. Access Requirements:**
**Accessible From Unprivileged Processes: No**
The vulnerable code is triggered by writing to a configfs attribute (typically
located under `/sys/kernel/config/usb-gadget/`). By default, configfs and its
contents are owned by `root` and are only writable by users with root
privileges. An unprivileged user cannot write to these files to trigger the bug.
**Accessible From User Namespaces: No**
The `configfs` filesystem does not have the `FS_USERNS_MOUNT` flag set in its
`file_system_type` definition (`fs/configfs/mount.c`). Consequently, the kernel
requires the `CAP_SYS_ADMIN` capability in the **initial** user namespace to
mount it. An unprivileged user cannot mount configfs inside a restricted user
namespace to gain access to the vulnerable attributes.
**5. Other Triggers:**
* **VM Guest Trigger: No.** The bug is specific to configuring USB gadgets via
configfs, not standard paravirtualized VM devices.
* **VM Host Trigger: No.**
* **Ethernet Network Trigger: No.**
* **Other Remote Trigger: No.**
* **Peripheral Trigger: No.** The bug is triggered by configuring the gadget via
the local filesystem interface (configfs), not by plugging in a malicious USB
device.
* **Malicious Filesystem Trigger: No.**
**Conclusion:**
While the bug provides a very strong memory corruption primitive (unaligned free
leading to overlapping objects), it is heavily mitigated by its attack surface.
It requires root privileges (or write access to restricted configfs endpoints)
to trigger, making it inaccessible to unprivileged local attackers or remote
actors.]
|