| 0/0 |
2026/07/30 00:14 |
flow |
assessment-security |
11m
Results: map[DenialOfService:true Explanation:**Root Cause Analysis**
The bug is a slab-use-after-free (UAF) in the USB gadget UVC (USB Video Class)
function, specifically in `uvcg_extension_drop()` located in
`drivers/usb/gadget/function/uvc_configfs.c`.
When an extension configfs item is dropped (e.g., via `rmdir` or when `mkdir`
fails and cleans up), `uvcg_extension_drop()` is invoked. The function is
implemented as follows:
```c
static void uvcg_extension_drop(struct config_group *group, struct config_item
*item)
{
struct uvcg_extension *xu = container_of(item, struct uvcg_extension,
item);
// ...
config_item_put(item);
list_del(&xu->list);
kfree(xu->desc.baSourceID);
kfree(xu->desc.bmControls);
// ...
}
```
Calling `config_item_put(item)` decrements the reference count of the item. If
it reaches zero, it triggers the release callback (`uvcg_extension_release()`),
which immediately calls `kfree(xu)`.
However, the function continues to execute and calls `list_del(&xu->list)`, as
well as freeing other members of `xu`. This results in a use-after-free read and
write on the `xu` object.
**Exploitability**
If an attacker can win the extremely tight race condition (literally two C
statements without a context switch) by having an interrupt or preemption occur
exactly after `kfree(xu)` and reallocating the object, they could control the
`next` and `prev` pointers before `list_del()` is called. This would lead to a
classic list unlinking arbitrary write primitive (`next->prev = prev; prev->next
= next;`), which is highly exploitable.
If the race is not won, `list_del()` writes `LIST_POISON1` and `LIST_POISON2`
into the freed object, which can cause a kernel crash (Denial of Service) if the
memory is later reallocated and used uninitialized, or if debug tools like
KASAN/DEBUG_LIST are enabled.
**Access and Trigger**
Despite being technically exploitable, this bug is **not accessible to
unprivileged users**.
The vulnerable code is triggered exclusively via `configfs` operations (creating
or removing directories under `/sys/kernel/config/usb_gadget/`).
- `configfs` is typically mounted by root and its directories are owned by root
with restricted permissions.
- `configfs` lacks the `FS_USERNS_MOUNT` flag in its file system type
definition, meaning it **cannot be mounted inside a user namespace** by an
unprivileged user.
- The bug is triggered by local administrative configuration of the USB gadget,
not by plugging in a malicious USB device (peripheral) or by processing remote
network traffic.
Therefore, an attacker must already possess `CAP_SYS_ADMIN` in the initial
namespace (i.e., root privileges) to reach this code, making the practical
security impact negligible. Exploitable:true FilesystemTrigger:false NetworkTrigger:false PeripheralTrigger:false RemoteTrigger:false Unprivileged:false UserNamespace:false VMGuestTrigger:false VMHostTrigger:false]
|
| 1/1 |
2026/07/30 00:14 |
action |
syz-repro-to-c-repro |
0m
Results:
|
| 2/1 |
2026/07/30 00:14 |
action |
kernel-checkouter |
0m
Results: map[KernelSrc:/app/workdir/cache/src/59323400ba46e708d687f6274371ea997808e9ce]
|
| 3/1 |
2026/07/30 00:14 |
action |
kernel-builder |
0m
Results: map[KernelObj:/app/workdir/cache/build/80d7d1b2fd9bb699a440984dbfe9e1661576f4a6]
|
| 4/1 |
2026/07/30 00:14 |
action |
codesearch-prepare |
0m
Results: map[Index:codesearch-index]
|
| 5/1 |
2026/07/30 00:14 |
agent |
expert |
11m
Model:
Results: map[DenialOfService:true Exploitable:true FilesystemTrigger:false NetworkTrigger:false PeripheralTrigger:false RemoteTrigger:false Unprivileged:false UserNamespace:false VMGuestTrigger:false VMHostTrigger:false]
Instruction: You are an experienced Linux kernel security engineer. Your task is to analyze given kernel bug report
and determine its security impact based on the following dimensions.
Use the provided tools to examine the source code, check for capability checks (e.g., capable(), ns_capable()),
and understand the nature of the bug. Analyze the given kernel build and configuration.
You can check the kernel config by grepping ".config" file; you can check kernel cmdline by grepping
".config" file for "CONFIG_CMDLINE=". Assume sysctl parameters have default values.
But analyze for the corresponding production build w/o debugging tools enabled (like KASAN, KMSAN, UBSAN).
Try different strategies when analyzing the bug:
- think of ways in which the vulnerable code is unreachable
- or the other way around: try to come up with different ideas of how an unprivileged user can reach the bug
If still unsure err on the side of the bug being non-exploitable/not-accessible.
In the final reply, provide a reasoning for your assessment.
Analysis dimensions:
* Exploitable:
Determine if the bug can result in memory corruption, elevated privileges, or an information leak.
Memory safety issues are almost always exploitable (KASAN or UBSAN reports for use-after-free, out-of-bounds;
refcounting issues, corrupted lists, etc). When kernel is crashing on a completely wild pointer access
(e.g. user-space address, or non-canonical address, but not on NULL or address corresponding to KASAN shadow
for NULL address), including both data accesses and control transfers, that also usually implies possibility
of exploitation. Such reports usually say "unable to handle kernel paging request".
Uses of uninitialized values detected by KMSAN may be exploitable b/c attacker frequently can affect uninit
values with spraying techniques. However, for these exploitability depends on how exactly the uninit value
is used in the code, and what it affects.
Information leaks are exploitable on their own and should be classified as such. A bug that copies kernel
memory contents to userspace (e.g. an out-of-bounds read whose result is returned to the caller, or
uninitialized stack/heap bytes written to a user buffer) is exploitable: it can reveal kernel pointer
values and defeat KASLR, expose sensitive data such as cryptographic keys or other processes' memory, and
serves as a necessary building block in most modern kernel privilege-escalation exploit chains. Do not classify
an information leak as non-exploitable solely because it does not directly cause a memory write or control-flow
hijack; the leak itself is the exploit primitive.
Think of what happens after the bug is triggered. Some bugs cause kernel panic and halt execution,
they are harder to exploit. For example, BUG reports halts the kernel. However, WARNING reports don't halt
execution in production builds. Debug bug detection tools (like KASAN, KMSAN, KCSAN, UBSAN) are also not enabled
in production builds, so attacker can freely exploit these bugs w/o being detected by these tools.
If you see an integer overflow, think how the overflowed value used later (if it's used as allocation size,
or an array index). If you see an out-of-bounds read, think if it's followed by an out-of-bounds write as well.
Some KCSAN data-races may be exploitable by skilled attackers as well. Think what data structures got corrupted
as the result of data races and how. However, note that kernel has lots of "benign" data races that don't lead
to any runtime misbehavior at all.
* Denial Of Service:
Determine if the bug can result in denial-of-service. Most bugs can, since they cause system crash,
hangs, deadlocks, or resource leaks. This is mostly applicable to WARNING bugs that won't cause system crash
in production. For these think what will be consequences of the violation of the kernel assumptions flagged
by the WARNING. In some cases the unexpected condition is also properly handled by the normal control flow
(e.g. with "if (WARN_ON(...))"), these won't cause denial-of-service. If the condition is not handled,
then it may or may not cause denial-of-service.
* Accessible From Unprivileged Processes:
Determine if the bug can be reached from a typical (non-root) user process that does NOT have any special capabilities
(like CAP_SYS_ADMIN, CAP_NET_ADMIN, CAP_NET_RAW, CAP_PERFMON) or access to device nodes restricted to root.
Assume that unprivileged_bpf_disabled=1, that is eBPF loading is not accessible. However, cBPF (classical BPF)
is still accessible to non-root processes.
Assume that user namespaces are not accessible, that is, the process cannot get the mentioned capabilities even
within a new user namespace (checked by ns_capable() function in the kernel sources).
* Accessible From User Namespaces:
Determine if the bug can be reached within a user-namespace where the process has all capabilities
(including CAP_SYS_ADMIN, CAP_NET_ADMIN, CAP_NET_RAW, CAP_PERFMON). Such capabilities are checked with ns_capable()
function in the kernel sources.
* VM Guest Trigger:
Determine if the bug can be triggered from the context of a typical KVM guest (e.g., set up by a QEMU VMM).
Consider accesses to standard Linux host paravirtualized features (virtio-blk, virtio-net, etc.),
and handling of VM exits in the KVM code.
* VM Host Trigger in The Confidential Computing Context:
Determine if the bug can be triggered in a confidential computing guest kernel from the context of a KVM host.
Consider access to standard Linux guest paravirtualized features (virtio-blk, virtio-net, etc.).
* Ethernet Network Trigger:
Determine if the bug can be triggered by processing ingress network Ethernet traffic, either directly (network stack)
or via drivers exposed to network data.
* Other Remote Trigger:
Determine if the bug can be triggered by processing remote traffic other than Ethernet (Wifi, Bluetooth, NFC, etc).
* Peripheral Trigger:
Determine if the bug can be triggered via an untrusted peripheral device that can be physically plugged
into a system, such as a USB device or a niche hardware driver handling external hardware inputs.
This is particularly important for mobile and desktop environments where users can plug in unknown devices.
* Malicious Filesystem Trigger:
Determine if the bug can be triggered by the kernel mounting and parsing a malicious filesystem image.
This is highly critical for Desktop and Mobile environments where external media or downloaded images
might be auto-mounted.
Don't make assumptions about the kernel source code (it may be different from what you assume it is).
Extensively use the provided code access tools (codesearch-*, git-*, grepper, etc)
to examine the actual source code, and confirm any assumptions.
Prefer calling several tools at the same time to save round-trips.
Use set-results tool to provide results of the analysis.
It must be called exactly once before the final reply.
Ignore results of this tool.
Prompt:
The kernel bug report is:
BUG: KASAN: slab-use-after-free in __list_del_entry_valid_or_report+0x18a/0x1d0 lib/list_debug.c:49
Read of size 8 at addr ffff888034983358 by task syz.2.16/5919
CPU: 3 UID: 0 PID: 5919 Comm: syz.2.16 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 lib/dump_stack.c:94 [inline]
dump_stack_lvl+0x100/0x190 lib/dump_stack.c:120
print_address_description mm/kasan/report.c:378 [inline]
print_report+0x13d/0x4b0 mm/kasan/report.c:482
kasan_report+0xdf/0x1c0 mm/kasan/report.c:595
__list_del_entry_valid_or_report+0x18a/0x1d0 lib/list_debug.c:49
__list_del_entry_valid include/linux/list.h:132 [inline]
__list_del_entry include/linux/list.h:246 [inline]
list_del include/linux/list.h:260 [inline]
uvcg_extension_drop+0x88/0x290 drivers/usb/gadget/function/uvc_configfs.c:1260
client_drop_item+0xa6/0x100 fs/configfs/dir.c:970
configfs_mkdir+0x712/0xb70 fs/configfs/dir.c:1443
vfs_mkdir+0x361/0x850 fs/namei.c:5276
filename_mkdirat+0x48b/0x5e0 fs/namei.c:5309
__do_sys_mkdir fs/namei.c:5336 [inline]
__se_sys_mkdir fs/namei.c:5333 [inline]
__x64_sys_mkdir+0x6b/0x90 fs/namei.c:5333
do_syscall_x64 arch/x86/entry/syscall_64.c:63 [inline]
do_syscall_64+0x115/0x870 arch/x86/entry/syscall_64.c:94
entry_SYSCALL_64_after_hwframe+0x77/0x7f
RIP: 0033:0x7fe1d5d9de99
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:00007fe1d6c7c028 EFLAGS: 00000246 ORIG_RAX: 0000000000000053
RAX: ffffffffffffffda RBX: 00007fe1d6025fa0 RCX: 00007fe1d5d9de99
RDX: 0000000000000000 RSI: 00000000000001ff RDI: 0000200000000500
RBP: 00007fe1d5e33eaf R08: 0000000000000000 R09: 0000000000000000
R10: 0000000000000000 R11: 0000000000000246 R12: 0000000000000000
R13: 00007fe1d6026038 R14: 00007fe1d6025fa0 R15: 00007ffe8ba27028
</TASK>
Allocated by task 5919:
kasan_save_stack+0x30/0x50 mm/kasan/common.c:57
kasan_save_track+0x14/0x30 mm/kasan/common.c:78
poison_kmalloc_redzone mm/kasan/common.c:398 [inline]
__kasan_kmalloc+0xaa/0xb0 mm/kasan/common.c:415
kasan_kmalloc include/linux/kasan.h:263 [inline]
__kmalloc_cache_noprof+0x2e5/0x6c0 mm/slub.c:5489
_kmalloc_noprof include/linux/slab.h:988 [inline]
_kzalloc_noprof include/linux/slab.h:1309 [inline]
uvcg_extension_make+0x98/0x4e0 drivers/usb/gadget/function/uvc_configfs.c:1276
configfs_mkdir+0x7f6/0xb70 fs/configfs/dir.c:1369
vfs_mkdir+0x361/0x850 fs/namei.c:5276
filename_mkdirat+0x48b/0x5e0 fs/namei.c:5309
__do_sys_mkdir fs/namei.c:5336 [inline]
__se_sys_mkdir fs/namei.c:5333 [inline]
__x64_sys_mkdir+0x6b/0x90 fs/namei.c:5333
do_syscall_x64 arch/x86/entry/syscall_64.c:63 [inline]
do_syscall_64+0x115/0x870 arch/x86/entry/syscall_64.c:94
entry_SYSCALL_64_after_hwframe+0x77/0x7f
Freed by task 5919:
kasan_save_stack+0x30/0x50 mm/kasan/common.c:57
kasan_save_track+0x14/0x30 mm/kasan/common.c:78
kasan_save_free_info+0x3b/0x70 mm/kasan/generic.c:584
poison_slab_object mm/kasan/common.c:253 [inline]
__kasan_slab_free+0x5f/0x80 mm/kasan/common.c:285
kasan_slab_free include/linux/kasan.h:235 [inline]
slab_free_hook mm/slub.c:2677 [inline]
slab_free mm/slub.c:6377 [inline]
kfree+0x22b/0x6c0 mm/slub.c:6692
config_item_cleanup+0x146/0x240 fs/configfs/item.c:128
config_item_release fs/configfs/item.c:137 [inline]
kref_put include/linux/kref.h:65 [inline]
config_item_put fs/configfs/item.c:149 [inline]
config_item_put+0x93/0xb0 fs/configfs/item.c:146
uvcg_extension_drop+0x80/0x290 drivers/usb/gadget/function/uvc_configfs.c:1259
client_drop_item+0xa6/0x100 fs/configfs/dir.c:970
configfs_mkdir+0x712/0xb70 fs/configfs/dir.c:1443
vfs_mkdir+0x361/0x850 fs/namei.c:5276
filename_mkdirat+0x48b/0x5e0 fs/namei.c:5309
__do_sys_mkdir fs/namei.c:5336 [inline]
__se_sys_mkdir fs/namei.c:5333 [inline]
__x64_sys_mkdir+0x6b/0x90 fs/namei.c:5333
do_syscall_x64 arch/x86/entry/syscall_64.c:63 [inline]
do_syscall_64+0x115/0x870 arch/x86/entry/syscall_64.c:94
entry_SYSCALL_64_after_hwframe+0x77/0x7f
The buggy address belongs to the object at ffff888034983300
which belongs to the cache kmalloc-192 of size 192
The buggy address is located 88 bytes inside of
freed 192-byte region [ffff888034983300, ffff8880349833c0)
The buggy address belongs to the physical page:
page: refcount:0 mapcount:0 mapping:0000000000000000 index:0x0 pfn:0x34983
flags: 0xfff00000000000(node=0|zone=1|lastcpupid=0x7ff)
page_type: f5(slab)
raw: 00fff00000000000 ffff88801bc423c0 dead000000000100 dead000000000122
raw: 0000000000000000 0000000800100010 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 0xd2cc0(GFP_KERNEL|__GFP_NOWARN|__GFP_NORETRY|__GFP_COMP|__GFP_NOMEMALLOC), pid 1, tgid 1 (swapper/0), ts 16007878600, free_ts 15817405881
set_page_owner include/linux/page_owner.h:32 [inline]
post_alloc_hook+0xfd/0x120 mm/page_alloc.c:1859
prep_new_page mm/page_alloc.c:1867 [inline]
get_page_from_freelist+0xf48/0x3530 mm/page_alloc.c:3946
__alloc_frozen_pages_noprof+0x299/0x2dc0 mm/page_alloc.c:5304
alloc_slab_page mm/slub.c:3266 [inline]
allocate_slab mm/slub.c:3380 [inline]
new_slab+0xa2/0x640 mm/slub.c:3426
refill_objects+0xe3/0x410 mm/slub.c:7310
refill_sheaf mm/slub.c:2804 [inline]
__pcs_replace_empty_main+0x376/0x680 mm/slub.c:4675
alloc_from_pcs mm/slub.c:4773 [inline]
slab_alloc_node mm/slub.c:4905 [inline]
__kmalloc_cache_noprof+0x479/0x6c0 mm/slub.c:5485
_kmalloc_noprof include/linux/slab.h:988 [inline]
_kzalloc_noprof include/linux/slab.h:1309 [inline]
call_usermodehelper_setup+0xaf/0x360 kernel/umh.c:362
kobject_uevent_env+0x17c1/0x18b0 lib/kobject_uevent.c:631
driver_register+0x29c/0x360 drivers/base/driver.c:182
__hid_register_driver+0x146/0x1f0 drivers/hid/hid-core.c:3142
do_one_initcall+0x11d/0x700 init/main.c:1347
do_initcall_level init/main.c:1409 [inline]
do_initcalls init/main.c:1425 [inline]
do_basic_setup init/main.c:1445 [inline]
kernel_init_freeable+0x6ea/0x7b0 init/main.c:1658
kernel_init+0x1f/0x1e0 init/main.c:1548
ret_from_fork+0x72b/0xd50 arch/x86/kernel/process.c:158
ret_from_fork_asm+0x1a/0x30 arch/x86/entry/entry_64.S:245
page last free pid 1 tgid 1 stack trace:
reset_page_owner include/linux/page_owner.h:25 [inline]
__free_pages_prepare mm/page_alloc.c:1406 [inline]
free_pages_prepare+0x586/0xd80 mm/page_alloc.c:1451
__free_contig_range_common+0x14f/0x250 mm/page_alloc.c:6897
__free_contig_range mm/page_alloc.c:6942 [inline]
free_pages_bulk+0x12a/0x200 mm/page_alloc.c:5257
vm_area_free_pages+0xad/0x2b0 mm/vmalloc.c:3439
vfree mm/vmalloc.c:3488 [inline]
vfree+0x107/0x750 mm/vmalloc.c:3462
tpg_free+0x12e/0x490 drivers/media/common/v4l2-tpg/v4l2-tpg-core.c:197
vivid_dev_release+0xd0/0x160 drivers/media/test-drivers/vivid/vivid-core.c:865
v4l2_device_release drivers/media/v4l2-core/v4l2-device.c:51 [inline]
kref_put include/linux/kref.h:65 [inline]
v4l2_device_put+0xa3/0xd0 drivers/media/v4l2-core/v4l2-device.c:56
vivid_create_instance drivers/media/test-drivers/vivid/vivid-core.c:2070 [inline]
vivid_probe.cold+0xf88/0x921e drivers/media/test-drivers/vivid/vivid-core.c:2095
platform_probe+0x106/0x1d0 drivers/base/platform.c:1439
call_driver_probe drivers/base/dd.c:628 [inline]
really_probe+0x241/0xa60 drivers/base/dd.c:706
__driver_probe_device+0x20e/0x450 drivers/base/dd.c:868
driver_probe_device+0x4a/0x140 drivers/base/dd.c:898
__driver_attach+0x21f/0x5b0 drivers/base/dd.c:1292
bus_for_each_dev+0x13e/0x1d0 drivers/base/bus.c:383
bus_add_driver+0x305/0x5b0 drivers/base/bus.c:763
Memory state around the buggy address:
ffff888034983200: 00 00 00 00 00 00 00 00 00 00 00 00 00 00 00 00
ffff888034983280: 00 00 00 00 fc fc fc fc fc fc fc fc fc fc fc fc
>ffff888034983300: fa fb fb fb fb fb fb fb fb fb fb fb fb fb fb fb
^
ffff888034983380: fb fb fb fb fb fb fb fb fc fc fc fc fc fc fc fc
ffff888034983400: fa fb fb fb fb fb fb fb fb fb fb fb fb fb fb fb
==================================================================
Reply: **Root Cause Analysis**
The bug is a slab-use-after-free (UAF) in the USB gadget UVC (USB Video Class) function, specifically in `uvcg_extension_drop()` located in `drivers/usb/gadget/function/uvc_configfs.c`.
When an extension configfs item is dropped (e.g., via `rmdir` or when `mkdir` fails and cleans up), `uvcg_extension_drop()` is invoked. The function is implemented as follows:
```c
static void uvcg_extension_drop(struct config_group *group, struct config_item *item)
{
struct uvcg_extension *xu = container_of(item, struct uvcg_extension, item);
// ...
config_item_put(item);
list_del(&xu->list);
kfree(xu->desc.baSourceID);
kfree(xu->desc.bmControls);
// ...
}
```
Calling `config_item_put(item)` decrements the reference count of the item. If it reaches zero, it triggers the release callback (`uvcg_extension_release()`), which immediately calls `kfree(xu)`.
However, the function continues to execute and calls `list_del(&xu->list)`, as well as freeing other members of `xu`. This results in a use-after-free read and write on the `xu` object.
**Exploitability**
If an attacker can win the extremely tight race condition (literally two C statements without a context switch) by having an interrupt or preemption occur exactly after `kfree(xu)` and reallocating the object, they could control the `next` and `prev` pointers before `list_del()` is called. This would lead to a classic list unlinking arbitrary write primitive (`next->prev = prev; prev->next = next;`), which is highly exploitable.
If the race is not won, `list_del()` writes `LIST_POISON1` and `LIST_POISON2` into the freed object, which can cause a kernel crash (Denial of Service) if the memory is later reallocated and used uninitialized, or if debug tools like KASAN/DEBUG_LIST are enabled.
**Access and Trigger**
Despite being technically exploitable, this bug is **not accessible to unprivileged users**.
The vulnerable code is triggered exclusively via `configfs` operations (creating or removing directories under `/sys/kernel/config/usb_gadget/`).
- `configfs` is typically mounted by root and its directories are owned by root with restricted permissions.
- `configfs` lacks the `FS_USERNS_MOUNT` flag in its file system type definition, meaning it **cannot be mounted inside a user namespace** by an unprivileged user.
- The bug is triggered by local administrative configuration of the USB gadget, not by plugging in a malicious USB device (peripheral) or by processing remote network traffic.
Therefore, an attacker must already possess `CAP_SYS_ADMIN` in the initial namespace (i.e., root privileges) to reach this code, making the practical security impact negligible.
|
| 6/2 |
2026/07/30 00:14 |
llm |
expert |
0m
Model:
Tokens:
input: 10240
output: 45
thoughts: 635
Thoughts: **Analyzing a KASAN Slab-Use-After-Free**
Okay, here we go. We've got a KASAN slab-use-after-free report. The crash is happening in `__list_del_entry_valid_or_report`, which is called from `uvcg_extension_drop`. That immediately screams "potential double-free or use-after-free" situation, right? The object was allocated in `uvcg_extension_make` and freed in `uvcg_extension_drop`. The important part is determining *when* in `uvcg_extension_drop` it's being accessed, relative to its deallocation.
Let's look at the stack traces. The crash shows `__list_del_entry_valid_or_report` deeper in the call chain. I see `list_del` being called, which is standard. But this means something is dereferencing after the `list_del` call.
Now, the "freed by" stack trace is revealing. We have `kfree` at the bottom there. Diving deeper, I see `config_item_cleanup`, `config_item_release`, and finally, `config_item_put` on line 1259 of `uvcg_extension_drop`. This is where it's being freed. Then, on line 1260 we have the crash in `__list_del_entry`. So, `uvcg_extension_drop` is calling `config_item_put` to free the object and then, on the very next line, it's attempting to use the now-freed object with `list_del`. The critical issue is the order of operations, and the exact lines in `uvc_configfs.c` are where the problem is. I need to confirm this assumption and then look at the code and determine why it does that.
|
| 7/2 |
2026/07/30 00:14 |
tool |
read-file |
0m
Args: map[File:drivers/usb/gadget/function/uvc_configfs.c FirstLine:1250 LineCount:30]
Results: map[Contents:1250: struct uvcg_extension *xu = container_of(item, struct uvcg_extension, item);
1251: struct config_item *opts_item;
1252: struct f_uvc_opts *opts;
1253:
1254: opts_item = group->cg_item.ci_parent->ci_parent;
1255: opts = to_f_uvc_opts(opts_item);
1256:
1257: mutex_lock(&opts->lock);
1258:
1259: config_item_put(item);
1260: list_del(&xu->list);
1261: kfree(xu->desc.baSourceID);
1262: kfree(xu->desc.bmControls);
1263:
1264: mutex_unlock(&opts->lock);
1265: }
1266:
1267: static struct config_item *uvcg_extension_make(struct config_group *group, const char *name)
1268: {
1269: struct config_item *opts_item;
1270: struct uvcg_extension *xu;
1271: struct f_uvc_opts *opts;
1272:
1273: opts_item = group->cg_item.ci_parent->ci_parent;
1274: opts = to_f_uvc_opts(opts_item);
1275:
1276: xu = kzalloc_obj(*xu);
1277: if (!xu)
1278: return ERR_PTR(-ENOMEM);
1279:
]
|
| 8/2 |
2026/07/30 00:14 |
llm |
expert |
0m
Model:
Tokens:
input: 11787
output: 44
thoughts: 43
Thoughts: Okay, here's my interpretation of that thought process, assuming I'm an expert in this area:
**Digging into a Potential Memory Management Issue**
Right, *that's* a potential gotcha. `config_item_put(item)`β¦ that definitely deallocates `item`, doesn't it? The question is, how is `item` actually *used*? Specifically, is `item` directly embedded within `xu` or is `xu` itself the `item`? I need to clarify that relationship immediately, because if `xu` is somehow *using* memory that's part of `item`, we've got a classic double-free or use-after-free situation brewing. Time to dive into the definition of `struct uvcg_extension` and nail down how those two variables are linked. I can't be sure, but I have a strong feeling that this is where the bug lives.
|
| 9/2 |
2026/07/30 00:14 |
tool |
codesearch-definition-source |
0m
Args: map[ContextFile:drivers/usb/gadget/function/uvc_configfs.c Name:uvcg_extension]
Results: map[SourceCode: 181: struct uvcg_extension {
182: struct config_item item;
183: struct list_head list;
184: u8 string_descriptor_index;
185: struct uvcg_extension_unit_descriptor desc;
186: };
SourceFile:drivers/usb/gadget/function/uvc_configfs.h]
|
| 10/2 |
2026/07/30 00:14 |
llm |
expert |
0m
Model:
Tokens:
input: 11515
output: 59
thoughts: 203
Thoughts: **Analyzing a Potential Use-After-Free Vulnerability**
Okay, so I'm looking at this bug report and it seems like we have a pretty clear use-after-free situation in `uvcg_extension_drop` at `drivers/usb/gadget/function/uvc_configfs.c:1260`. The code snippet provided immediately flags a red flag. We're talking about this sequence:
```c
config_item_put(item);
list_del(&xu->list);
```
My initial thought is that `config_item_put(item)` likely frees `xu`. Remember, `xu` embeds `item`, and `config_item_put` will eventually call `kref_put`, which in turn leads to `config_item_release`. This is the point where `kfree(xu)` is probably happening. So, we release the memory associated with `xu`. Then, right after that, we try to access `xu->list` with `list_del(&xu->list)`. That's textbook use-after-free. The system should crash.
But wait a minute, I better make sure I have all the details. I know that `config_item_put` calls `config_item_cleanup`, right? I need to verify that `config_item_cleanup` isn't freeing `item` directly, potentially making my initial assessment incorrect. I will have to follow the chain of `config_item_put`, `config_item_cleanup`, and then confirm how `uvcg_extension_release` handles the associated structures. The devil is in the details, so I'm going to dig into the source code of both functions to confirm my initial hypothesis and eliminate any other potential issues.
|
| 11/2 |
2026/07/30 00:14 |
tool |
codesearch-find-references |
0m
Args: map[ContextFile:drivers/usb/gadget/function/uvc_configfs.c IncludeSnippetLines:0 Name:uvcg_extension_drop SourceTreePrefix:]
Results: map[References:[map[ReferenceKind:takes-address-of ReferencingEntityKind:global_variable ReferencingEntityName:uvcg_extensions_grp_ops SourceFile:drivers/usb/gadget/function/uvc_configfs.c SourceLine:1303]] TruncatedOutput:false]
|
| 12/2 |
2026/07/30 00:14 |
llm |
expert |
0m
Model:
Tokens:
input: 12039
output: 45
thoughts: 0
|
| 13/2 |
2026/07/30 00:14 |
tool |
read-file |
0m
Args: map[File:drivers/usb/gadget/function/uvc_configfs.c FirstLine:1290 LineCount:20]
Results: map[Contents:1290:
1291: xu->desc.bUnitID = ++opts->last_unit_id;
1292:
1293: config_item_init_type_name(&xu->item, name, &uvcg_extension_type);
1294: list_add_tail(&xu->list, &opts->extension_units);
1295:
1296: mutex_unlock(&opts->lock);
1297:
1298: return &xu->item;
1299: }
1300:
1301: static const struct configfs_group_operations uvcg_extensions_grp_ops = {
1302: .make_item = uvcg_extension_make,
1303: .drop_item = uvcg_extension_drop,
1304: };
1305:
1306: static const struct uvcg_config_group_type uvcg_extensions_grp_type = {
1307: .type = {
1308: .ct_item_ops = &uvcg_config_item_ops,
1309: .ct_group_ops = &uvcg_extensions_grp_ops,
]
|
| 14/2 |
2026/07/30 00:14 |
llm |
expert |
0m
Model:
Tokens:
input: 13303
output: 59
thoughts: 12
Thoughts: Okay, let's dive into this. Here's my take on those thoughts, as if they're my own:
**Analyzing `uvcg_extension_type`**
Alright, let's get down to brass tacks. The phrase `uvcg_extension_type` has caught my attention. It's concise, so I need to unpack it quickly. My initial thought is to examine how the name itself is constructed and to then define its implications. I'm immediately running through the potential domains this might apply to, it could be any application dealing with data or file types, maybe it's related to some kind of image or video format, could have something to do with medical imaging, perhaps it's a proprietary internal data structure? The prefix "uvcg" has me thinking about an acronym, or some kind of convention. I need to figure out what that stands for β is it a specific project, an organization, or a common framework? Then I need to understand its relationship to "extension\_type". It hints at a defined category or structure. Are we talking about a standard type definition, a custom-defined enum, or something more complex? I need to think about how these values are handled: stored, validated, and used in any functions. I will assess if this requires its own definition file. I want to figure out what type of programming language or framework this is associated with. Are there any existing implementations or references that I can immediately pull up to understand what that data type looks like? My goal is to determine the scope and meaning of `uvcg_extension_type` and its implications for the task at hand.
|
| 15/2 |
2026/07/30 00:14 |
tool |
codesearch-find-references |
0m
Args: map[ContextFile:drivers/usb/gadget/function/uvc_configfs.c IncludeSnippetLines:0 Name:uvcg_extension_type SourceTreePrefix:]
Results: map[References:<nil> TruncatedOutput:false]
|
| 16/2 |
2026/07/30 00:14 |
llm |
expert |
0m
Model:
Tokens:
input: 13732
output: 42
thoughts: 31
Thoughts: Okay, here's my take on those thoughts, framed as my own:
**Connecting the Dots in the UVC Landscape**
Hold on a second... let me retrace my steps. I've been digging through the UVC driver code, and I'm trying to pin down the source of `uvcg_extension_type`. I need to figure out how that's being set. Ah, *that's* it! The definition for `uvcg_extension_type`... that key piece of the puzzle... it's residing within `drivers/usb/gadget/function/uvc_configfs.c`. Right there, in the very heart of the ConfigFS implementation for UVC gadgets. That's precisely where I need to focus my attention. It all makes sense now. I can move forward knowing where to look!
|
| 17/2 |
2026/07/30 00:14 |
tool |
grepper |
0m
Args: map[Expression:uvcg_extension_type PathPrefix:drivers/usb/gadget/function/uvc_configfs.c]
Results: map[Output: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-1241-
drivers/usb/gadget/function/uvc_configfs.c:1242:static const struct config_item_type uvcg_extension_type = {
drivers/usb/gadget/function/uvc_configfs.c-1243- .ct_item_ops = &uvcg_extension_item_ops,
--
drivers/usb/gadget/function/uvc_configfs.c=1267=static struct config_item *uvcg_extension_make(struct config_group *group, const char *name)
--
drivers/usb/gadget/function/uvc_configfs.c-1292-
drivers/usb/gadget/function/uvc_configfs.c:1293: config_item_init_type_name(&xu->item, name, &uvcg_extension_type);
drivers/usb/gadget/function/uvc_configfs.c-1294- list_add_tail(&xu->list, &opts->extension_units);
]
|
| 18/2 |
2026/07/30 00:14 |
llm |
expert |
0m
Model:
Tokens:
input: 14310
output: 45
thoughts: 0
|
| 19/2 |
2026/07/30 00:14 |
tool |
read-file |
0m
Args: map[File:drivers/usb/gadget/function/uvc_configfs.c FirstLine:1230 LineCount:20]
Results: map[Contents:1230:
1231: mutex_unlock(&opts->lock);
1232:
1233: mutex_unlock(su_mutex);
1234: }
1235:
1236: static const struct configfs_item_operations uvcg_extension_item_ops = {
1237: .release = uvcg_extension_release,
1238: .allow_link = uvcg_extension_allow_link,
1239: .drop_link = uvcg_extension_drop_link,
1240: };
1241:
1242: static const struct config_item_type uvcg_extension_type = {
1243: .ct_item_ops = &uvcg_extension_item_ops,
1244: .ct_attrs = uvcg_extension_attrs,
1245: .ct_owner = THIS_MODULE,
1246: };
1247:
1248: static void uvcg_extension_drop(struct config_group *group, struct config_item *item)
1249: {
]
|
| 20/2 |
2026/07/30 00:14 |
llm |
expert |
0m
Model:
Tokens:
input: 14675
output: 45
thoughts: 0
|
| 21/2 |
2026/07/30 00:14 |
tool |
read-file |
0m
Args: map[File:drivers/usb/gadget/function/uvc_configfs.c FirstLine:1210 LineCount:20]
Results: map[Contents:1210: mutex_unlock(su_mutex);
1211:
1212: return ret;
1213: }
1214:
1215: static void uvcg_extension_drop_link(struct config_item *src, struct config_item *tgt)
1216: {
1217: struct mutex *su_mutex = &src->ci_group->cg_subsys->su_mutex;
1218: struct uvcg_extension *xu = to_uvcg_extension(src);
1219: struct config_item *opts_item;
1220: struct f_uvc_opts *opts;
1221:
1222: mutex_lock(su_mutex); /* for navigating configfs hierarchy */
1223:
1224: opts_item = src->ci_parent->ci_parent->ci_parent;
1225: opts = to_f_uvc_opts(opts_item);
1226:
1227: mutex_lock(&opts->lock);
1228:
1229: xu->string_descriptor_index = 0;
]
|
| 22/2 |
2026/07/30 00:14 |
llm |
expert |
0m
Model:
Tokens:
input: 15039
output: 42
thoughts: 14
Thoughts: Here's my thought process, as if I were considering this myself:
**Tracing Down a Variable's Definition**
Okay, so I'm seeing `uvcg_extension_release` here, but my first thought is, "Wait, where *is* this thing defined?". It's a key variable, and I can't just assume I remember where it's declared and initialized. I need to quickly track down its origin. Is it a global, something passed in from a configuration file, a constant within this module, or maybe pulled in from a library I'm using? Understanding its scope and potential values is critical to interpreting the current code. I need to know the context before I can confidently proceed. Let me start digging...
|
| 23/2 |
2026/07/30 00:14 |
tool |
grepper |
0m
Args: map[Expression:uvcg_extension_release PathPrefix:drivers/usb/gadget/function/uvc_configfs.c]
Results: map[Output:drivers/usb/gadget/function/uvc_configfs.c=1166=static struct configfs_attribute *uvcg_extension_attrs[] = {
--
drivers/usb/gadget/function/uvc_configfs.c-1178-
drivers/usb/gadget/function/uvc_configfs.c:1179:static void uvcg_extension_release(struct config_item *item)
drivers/usb/gadget/function/uvc_configfs.c-1180-{
--
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-1238- .allow_link = uvcg_extension_allow_link,
]
|
| 24/2 |
2026/07/30 00:14 |
llm |
expert |
0m
Model:
Tokens:
input: 15511
output: 45
thoughts: 0
|
| 25/2 |
2026/07/30 00:15 |
tool |
read-file |
0m
Args: map[File:drivers/usb/gadget/function/uvc_configfs.c FirstLine:1175 LineCount:20]
Results: map[Contents:1175: &uvcg_extension_attr_i_extension,
1176: NULL,
1177: };
1178:
1179: static void uvcg_extension_release(struct config_item *item)
1180: {
1181: struct uvcg_extension *xu = container_of(item, struct uvcg_extension, item);
1182:
1183: kfree(xu);
1184: }
1185:
1186: static int uvcg_extension_allow_link(struct config_item *src, struct config_item *tgt)
1187: {
1188: struct mutex *su_mutex = &src->ci_group->cg_subsys->su_mutex;
1189: struct uvcg_extension *xu = to_uvcg_extension(src);
1190: struct config_item *gadget_item;
1191: struct gadget_string *string;
1192: struct config_item *strings;
1193: int ret = 0;
1194:
]
|
| 26/2 |
2026/07/30 00:15 |
llm |
expert |
0m
Model:
Tokens:
input: 14932
output: 37
thoughts: 220
Thoughts: **Debugging a Kernel Use-After-Free**
Okay, so I've got a use-after-free, specifically in `__list_del_entry_valid_or_report`, which is being called from `uvcg_extension_drop`. That's not ideal, and the trace confirms it: `uvcg_extension_drop` at line 1260 in `uvc_configfs.c`, then `client_drop_item`, and further up, calls to `configfs_mkdir` and down to `vfs_mkdir`. Wait a second⦠`uvcg_extension_drop` is being called *during* a `configfs_mkdir` operation? That's⦠unexpected. My gut says there's a problem here.
Hang on⦠is `configfs_mkdir` calling `client_drop_item` as part of its *error handling*? That makes more sense, but I need to confirm the flow. I need to dive into the source for `configfs_mkdir` to understand exactly what's happening. Let's see what that function's doing.
|
| 27/2 |
2026/07/30 00:15 |
tool |
read-file |
0m
Args: map[File:fs/configfs/dir.c FirstLine:1430 LineCount:20]
Results: map[Contents:1430: configfs_dir_set_ready(dentry->d_fsdata);
1431: spin_unlock(&configfs_dirent_lock);
1432:
1433: out_unlink:
1434: if (ret) {
1435: /* Tear down everything we built up */
1436: mutex_lock(&subsys->su_mutex);
1437:
1438: client_disconnect_notify(parent_item, item);
1439: if (group)
1440: unlink_group(group);
1441: else
1442: unlink_obj(item);
1443: client_drop_item(parent_item, item);
1444:
1445: mutex_unlock(&subsys->su_mutex);
1446:
1447: if (module_got)
1448: module_put(new_item_owner);
1449: }
]
|
| 28/2 |
2026/07/30 00:15 |
llm |
expert |
0m
Model:
Tokens:
input: 16644
output: 44
thoughts: 897
Thoughts: **Analyzing a Potential Use-After-Free Vulnerability**
Okay, so `configfs_mkdir` failed, and now we're in the teardown phase. Specifically, `client_drop_item` is calling `uvcg_extension_drop`. My immediate concern is this: it looks like `config_item_put(item)` is freeing the `struct uvcg_extension` *before* `list_del(&xu->list)` is called. That's a textbook use-after-free, accessing freed memory. I know that `config_item_put` will call `uvcg_extension_release`, which in turn calls `kfree(xu)`. The fix is obvious: move the `config_item_put` call to the end, or at least after the access to `xu->list`.
But, is it exploitable? Let's break it down. `list_del` accesses `xu->list`, which is now freed. I see it: `list_del` writes to the `next` and `prev` pointers of the `list_head` structure within our freed `xu` object. The vulnerability is an arbitrary write primitive since an attacker could control these pointers if they had the ability to control the contents of the freed memory.
However, the real question is timing. `config_item_put` calls `kref_put`, which calls the relevant release function, `uvcg_extension_release` which in turn calls `kfree(xu)`, and then immediately `list_del` is called. Is there a window for an attacker to reallocate and overwrite the freed object? `kfree` itself doesn't normally block, but it can be preempted. We have `PREEMPT(full)` enabled! If preemption occurs *between* the `kfree` and `list_del`, another thread could swoop in, allocate the freed memory, and overwrite the `xu` object's data. This is our opportunity for exploitation. Even without preemption `kfree` might take some time to do its work.
Even without preemption, an interrupt could trigger, so it's a very tight race. I need to keep in mind, however, that KASAN will poison the object which might mitigate exploitation. In a non-KASAN build, it's just added to the SLUB freelist, and the first few bytes (or middle, depending on the SLUB config) will be overwritten by the freelist pointer. The `xu->list` is located at the `sizeof(struct config_item)` offset in the `struct uvcg_extension`, so I need to check the layout of `struct uvcg_extension` to be sure.
|
| 29/2 |
2026/07/30 00:15 |
tool |
codesearch-struct-layout |
0m
Args: map[ContextFile:drivers/usb/gadget/function/uvc_configfs.c Name:uvcg_extension]
Results: map[Fields:[map[Name:item OffsetBits:0 SizeBits:640] map[Name:list OffsetBits:640 SizeBits:128] map[Name:string_descriptor_index OffsetBits:768 SizeBits:8] map[Name:desc OffsetBits:776 SizeBits:320]]]
|
| 30/2 |
2026/07/30 00:15 |
llm |
expert |
0m
Model:
Tokens:
input: 18261
output: 37
thoughts: 499
Thoughts: **Analyzing a Potential Use-After-Free Vulnerability**
Okay, let's break this down. The `item` is 80 bytes, or 640 bits. The `list` pointer, `xu->list`, is at offset 80. Now, I know the SLUB freelist pointer is usually at offset 0 or somewhere in the middle. Offset 80 is unlikely to be the freelist pointer unless it's deliberately configured that way. My initial thought was that `xu->list` would retain its old `next` and `prev` pointers after the `kfree`, and thus the `list_del` function would simply remove the item from the list again.
But then I remembered that `xu->list` was added to `opts->extension_units` in `uvcg_extension_make`. This means those pointers are actually valid, pointing within the linked list. So, `list_del` will perform the standard unlink: `next->prev = prev; prev->next = next;` and successfully remove the object.
The problem, as I now realize, isn't about the *removal* itself. It's about a *use-after-free*. If the object is freed by `kfree` and *then* reallocated by another thread *before* `list_del` can run, then the `list_del` would be writing to memory that is no longer valid. This would lead to memory corruption.
Could this really happen? Yes. Preemption is a strong possibility. It's also possible that `kfree` itself triggers another allocation (though less likely). And in a multi-core system, another CPU could grab the object. Since the object is in `kmalloc-192`, itβs a general-purpose cache, so another CPU could indeed allocate from the same SLUB cache. In reality, `kfree` puts the object on the CPU's local freelist. So, another thread on the same CPU (if preempted) or an interrupt handler could allocate it.
If an attacker can trigger this, they could likely exploit it through a race condition. The vulnerability hinges on getting the re-allocation done *before* `list_del` happens.
But wait, how does this bug even get triggered? I have to look at when `configfs_mkdir` fails AFTER `client_make_item` succeeds. I need to understand why `configfs_mkdir` would fail after `client_make_item` has already succeeded. I have to trace how this leads to the use-after-free.
|
| 31/2 |
2026/07/30 00:15 |
tool |
read-file |
0m
Args: map[File:fs/configfs/dir.c FirstLine:1360 LineCount:80]
Results: map[Contents:1360: group = type->ct_group_ops->make_group(to_config_group(parent_item), name);
1361: if (!group)
1362: group = ERR_PTR(-ENOMEM);
1363: if (!IS_ERR(group)) {
1364: link_group(to_config_group(parent_item), group);
1365: item = &group->cg_item;
1366: } else
1367: ret = PTR_ERR(group);
1368: } else {
1369: item = type->ct_group_ops->make_item(to_config_group(parent_item), name);
1370: if (!item)
1371: item = ERR_PTR(-ENOMEM);
1372: if (!IS_ERR(item))
1373: link_obj(parent_item, item);
1374: else
1375: ret = PTR_ERR(item);
1376: }
1377: mutex_unlock(&subsys->su_mutex);
1378:
1379: if (ret) {
1380: /*
1381: * If ret != 0, then link_obj() was never called.
1382: * There are no extra references to clean up.
1383: */
1384: goto out_subsys_put;
1385: }
1386:
1387: /*
1388: * link_obj() has been called (via link_group() for groups).
1389: * From here on out, errors must clean that up.
1390: */
1391:
1392: type = item->ci_type;
1393: if (!type) {
1394: ret = -EINVAL;
1395: goto out_unlink;
1396: }
1397:
1398: new_item_owner = type->ct_owner;
1399: if (!try_module_get(new_item_owner)) {
1400: ret = -EINVAL;
1401: goto out_unlink;
1402: }
1403:
1404: /*
1405: * I hate doing it this way, but if there is
1406: * an error, module_put() probably should
1407: * happen after any cleanup.
1408: */
1409: module_got = 1;
1410:
1411: /*
1412: * Make racing rmdir() fail if it did not tag parent with
1413: * CONFIGFS_USET_DROPPING
1414: * Note: if CONFIGFS_USET_DROPPING is already set, attach_group() will
1415: * fail and let rmdir() terminate correctly
1416: */
1417: spin_lock(&configfs_dirent_lock);
1418: /* This will make configfs_detach_prep() fail */
1419: sd->s_type |= CONFIGFS_USET_IN_MKDIR;
1420: spin_unlock(&configfs_dirent_lock);
1421:
1422: if (group)
1423: ret = configfs_attach_group(item, dentry, frag);
1424: else
1425: ret = configfs_attach_item(item, dentry, frag);
1426:
1427: spin_lock(&configfs_dirent_lock);
1428: sd->s_type &= ~CONFIGFS_USET_IN_MKDIR;
1429: if (!ret)
1430: configfs_dir_set_ready(dentry->d_fsdata);
1431: spin_unlock(&configfs_dirent_lock);
1432:
1433: out_unlink:
1434: if (ret) {
1435: /* Tear down everything we built up */
1436: mutex_lock(&subsys->su_mutex);
1437:
1438: client_disconnect_notify(parent_item, item);
1439: if (group)
]
|
| 32/2 |
2026/07/30 00:15 |
llm |
expert |
0m
Model:
Tokens:
input: 20516
output: 36
thoughts: 46
Thoughts: Okay, here's my understanding, formatted as requested:
**Debugging a Potential `configfs` Failure**
Alright, I'm digging into this `configfs` issue. It seems pretty straightforward on the surface β `configfs_mkdir` might fail, and from what I can tell, that failure hinges on the success of `configfs_attach_item`. Seems like a cascading failure, the classic "one thing goes wrong, everything falls apart" scenario.
So, the key question is: what's making `configfs_attach_item` trip up? What are the conditions that could lead to that function returning an error? Time to drill down into the guts of *that* function and see what's really going on under the hood. I need to understand its failure modes and what the potential root causes are. Gotta get to the bottom of this.
|
| 33/2 |
2026/07/30 00:15 |
tool |
read-file |
0m
Args: map[File:fs/configfs/dir.c FirstLine:800 LineCount:40]
Results: map[Contents: 800: * cg_children
801: */
802: config_item_get(item);
803: }
804:
805: static void unlink_group(struct config_group *group)
806: {
807: struct config_group *new_group;
808:
809: list_for_each_entry(new_group, &group->default_groups, group_entry)
810: unlink_group(new_group);
811:
812: group->cg_subsys = NULL;
813: unlink_obj(&group->cg_item);
814: }
815:
816: static void link_group(struct config_group *parent_group, struct config_group *group)
817: {
818: struct config_group *new_group;
819: struct configfs_subsystem *subsys = NULL; /* gcc is a turd */
820:
821: link_obj(&parent_group->cg_item, &group->cg_item);
822:
823: if (parent_group->cg_subsys)
824: subsys = parent_group->cg_subsys;
825: else if (configfs_is_root(&parent_group->cg_item))
826: subsys = to_configfs_subsystem(group);
827: else
828: BUG();
829: group->cg_subsys = subsys;
830:
831: list_for_each_entry(new_group, &group->default_groups, group_entry)
832: link_group(group, new_group);
833: }
834:
835: /* Caller holds the mutex of the item's inode */
836: static void configfs_detach_item(struct dentry *dentry)
837: {
838: detach_attrs(dentry);
839: configfs_remove_dir(dentry);
]
|
| 34/2 |
2026/07/30 00:15 |
llm |
expert |
0m
Model:
Tokens:
input: 21378
output: 34
thoughts: 27
Thoughts: Here's my attempt at summarizing the thought process, tailored for an expert audience:
**Debugging a Kernel Module Attachment Point**
Okay, so I'm digging into this `configfs` issue. Let me see... I thought that `configfs_attach_item` was around line 840 in this file, but that feels a bit off. Nah, let's not rely on memory here, let's use the source. I'm going to do a quick search within the code. Let's find this function and make sure that this is the attachment point I think it is. I'm going to jump in there and get a more solid footing.
|
| 35/2 |
2026/07/30 00:15 |
tool |
grepper |
0m
Args: map[Expression:configfs_attach_item PathPrefix:fs/configfs/dir.c]
Results: map[Output:fs/configfs/dir.c=836=static void configfs_detach_item(struct dentry *dentry)
--
fs/configfs/dir.c-842-/*
fs/configfs/dir.c:843: * The goal is that configfs_attach_item() (and
fs/configfs/dir.c-844- * configfs_attach_group()) can be called from either the VFS or this
--
fs/configfs/dir.c-856- */
fs/configfs/dir.c:857:static int configfs_attach_item(struct config_item *item,
fs/configfs/dir.c-858- struct dentry *dentry,
--
fs/configfs/dir.c=891=static int configfs_attach_group(struct config_item *item,
--
fs/configfs/dir.c-897-
fs/configfs/dir.c:898: ret = configfs_attach_item(item, dentry, frag);
fs/configfs/dir.c-899- if (!ret) {
--
fs/configfs/dir.c=1289=static struct dentry *configfs_mkdir(struct mnt_idmap *idmap, struct inode *dir,
--
fs/configfs/dir.c-1424- else
fs/configfs/dir.c:1425: ret = configfs_attach_item(item, dentry, frag);
fs/configfs/dir.c-1426-
]
|
| 36/2 |
2026/07/30 00:15 |
llm |
expert |
0m
Model:
Tokens:
input: 21976
output: 36
thoughts: 0
|
| 37/2 |
2026/07/30 00:15 |
tool |
read-file |
0m
Args: map[File:fs/configfs/dir.c FirstLine:850 LineCount:40]
Results: map[Contents: 850: * handle cleaning up the dcache bits.
851: *
852: * configfs_detach_group() and configfs_detach_item() behave similarly on
853: * the way out. They assume that the proper semaphores are held, they
854: * clean up the configfs items, and they expect their callers will
855: * handle the dcache bits.
856: */
857: static int configfs_attach_item(struct config_item *item,
858: struct dentry *dentry,
859: struct configfs_fragment *frag)
860: {
861: int ret;
862:
863: ret = configfs_create_dir(item, dentry, frag);
864: if (!ret) {
865: ret = populate_attrs(item);
866: if (ret) {
867: /*
868: * We are going to remove an inode and its dentry but
869: * the VFS may already have hit and used them. Thus,
870: * we must lock them as rmdir() would.
871: */
872: inode_lock(d_inode(dentry));
873: configfs_detach_item(dentry);
874: d_inode(dentry)->i_flags |= S_DEAD;
875: dont_mount(dentry);
876: inode_unlock(d_inode(dentry));
877: d_delete(dentry);
878: }
879: }
880:
881: return ret;
882: }
883:
884: /* Caller holds the mutex of the group's inode */
885: static void configfs_detach_group(struct dentry *dentry)
886: {
887: detach_groups(dentry);
888: configfs_detach_item(dentry);
889: }
]
|
| 38/2 |
2026/07/30 00:15 |
llm |
expert |
0m
Model:
Tokens:
input: 22598
output: 36
thoughts: 246
Thoughts: **My Analysis of a Potential Security Flaw in configfs USB Gadget Configuration**
Okay, so I'm tracing through this code, and it looks like `configfs_attach_item` could bail out if `populate_attrs` fails. `populate_attrs`, as I understand it, is where the attribute files are created. If that fails, let's say due to a memory issue (like an ENOMEM), `configfs_attach_item` returns an error, which in turn leads `configfs_mkdir` down the path to `out_unlink` and a call to `client_drop_item`.
The real question here, and what I need to dig into, is whether this failure path, and ultimately `uvcg_extension_drop` β which I believe is part of the USB Gadget UVC (USB Video Class) function configuration through configfs β is accessible from an unprivileged process. The bug seems to reside within that `uvcg_extension_drop` function.
I know that Configfs is usually mounted at `/sys/kernel/config`. Now, normally, USB gadget configuration is a privileged operation, typically done by root or a process with `CAP_SYS_ADMIN`. The crucial point is, can a non-root user actually access `/sys/kernel/config/usb_gadget`?
Typically, `/sys/kernel/config` is owned by root and has permissions of something like `rwxr-xr-x` or even more restrictive, like `rwx------`. But hang on⦠I need to consider that `configfs` directories are created with permissions determined by the driver itself, or possibly a default of `0755`. This changes things. I should really check how `configfs_create_dir` works to get the complete picture. This could be a vulnerability if an unprivileged user can trigger this failure path.
|
| 39/2 |
2026/07/30 00:15 |
tool |
read-file |
0m
Args: map[File:fs/configfs/dir.c FirstLine:200 LineCount:20]
Results: map[Contents: 200: sd->s_type = type;
201: configfs_init_dirent_depth(sd);
202: spin_lock(&configfs_dirent_lock);
203: if (parent_sd->s_type & CONFIGFS_USET_DROPPING) {
204: spin_unlock(&configfs_dirent_lock);
205: kmem_cache_free(configfs_dir_cachep, sd);
206: return ERR_PTR(-ENOENT);
207: }
208: sd->s_frag = get_fragment(frag);
209:
210: /*
211: * configfs_lookup scans only for unpinned items. s_children is
212: * partitioned so that configfs_lookup can bail out early.
213: * CONFIGFS_PINNED and CONFIGFS_NOT_PINNED are not symmetrical. readdir
214: * cursors still need to be inserted at the front of the list.
215: */
216: if (sd->s_type & CONFIGFS_PINNED)
217: list_add_tail(&sd->s_sibling, &parent_sd->s_children);
218: else
219: list_add(&sd->s_sibling, &parent_sd->s_children);
]
|
| 40/2 |
2026/07/30 00:15 |
llm |
expert |
0m
Model:
Tokens:
input: 23653
output: 32
thoughts: 48
Thoughts: **Investigating Unprivileged User Mounts of ConfigFS**
Okay, so I'm thinking about `configfs` and how it's usually managed. My understanding is that, by default, it's mounted and accessed by root. But the question is: can an unprivileged user actually mount `configfs` within a user namespace? That's the crux of what I need to figure out.
The next logical step is to dive into the kernel and see if `configfs` has the `FS_USERNS_MOUNT` capability. That flag, if present, would be the key indicator that it supports mounting in a user namespace. Let's get down to brass tacks and check the source code.
|
| 41/2 |
2026/07/30 00:15 |
tool |
grepper |
0m
Error:
Args: map[Expression:FS_USERNS_MOUNT PathPrefix:fs/configfs/]
Results:
|
| 42/2 |
2026/07/30 00:15 |
llm |
expert |
0m
Model:
Tokens:
input: 23898
output: 59
thoughts: 110
Thoughts: **Investigating `configfs` Mounting and Permissions**
Okay, so I'm thinking through this `configfs` situation, and it seems clear that it doesn't support `FS_USERNS_MOUNT`. That means a standard unprivileged user is completely locked out of mounting `configfs` even within their own user namespace, which is expected. That's not too surprising, but it eliminates a possible workaround right away.
The real question is, if root *has* already mounted it, what kind of access do unprivileged users get? Can they create directories or modify things within an existing `configfs` mount? Looking at `/sys/kernel/config/usb_gadget` as a typical example, the standard permissions I see are `drwxr-xr-x root root`. That indicates that only the root user owns this directory and can create new subdirectories within it, which makes perfect sense for system integrity.
I really need to confirm this practically, and can test that pretty easily. I can create a user account and then attempt to modify the `configfs` mount from within that account. This seems like a trivial test, but I really need to get my hands dirty and verify it before drawing any conclusions.
|
| 43/2 |
2026/07/30 00:16 |
tool |
codeexpert |
4m
Args: map[Question:Can an unprivileged user create directories in configfs, specifically under /sys/kernel/config/usb_gadget? Are there any scenarios where an unprivileged user or a user namespace can trigger mkdir in configfs?]
Results: map[Answer:By default, an unprivileged user cannot create directories in configfs, including under `/sys/kernel/config/usb_gadget`. However, they can do so if a privileged user explicitly delegates permissions (via `chmod` or `chown`) to them. Additionally, if such delegation is in place, a user namespace mapping that delegated UID/GID can also trigger `mkdir` using namespace capabilities like `CAP_DAC_OVERRIDE`. Without such explicit delegation, unprivileged users and unprivileged user namespaces have no way to trigger `mkdir` in configfs.]
|
| 44/3 |
2026/07/30 00:16 |
agent |
codeexpert |
4m
Model:
Results: map[Answer:By default, an unprivileged user cannot create directories in configfs, including under `/sys/kernel/config/usb_gadget`. However, they can do so if a privileged user explicitly delegates permissions (via `chmod` or `chown`) to them. Additionally, if such delegation is in place, a user namespace mapping that delegated UID/GID can also trigger `mkdir` using namespace capabilities like `CAP_DAC_OVERRIDE`. Without such explicit delegation, unprivileged users and unprivileged user namespaces have no way to trigger `mkdir` in configfs.]
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 an unprivileged user create directories in configfs, specifically under /sys/kernel/config/usb_gadget? Are there any scenarios where an unprivileged user or a user namespace can trigger mkdir in configfs?
|
| 45/4 |
2026/07/30 00:16 |
llm |
codeexpert |
0m
Model:
Tokens:
input: 3906
output: 22
thoughts: 118
Thoughts: **Investigating Configfs Permissions for Unprivileged Users**
Okay, so here's what's running through my head. The user wants to know about directory creation within `configfs`, specifically under `/sys/kernel/config/usb_gadget`, and whether an unprivileged user can do it. Essentially, I'm trying to figure out if there are any scenarios where a standard user, or perhaps even a user within a namespace, could somehow trigger an `mkdir` operation within that specific part of `configfs`. Itβs a valid question, considering the security implications.
My next step is to dive into the kernel source code. I need to understand how `configfs` handles `mkdir` operations and, importantly, what permissions are involved. Time to get my hands dirty. I'm thinking of using `grepper` or perhaps `codesearch-find-references` to efficiently hunt down the `configfs` `mkdir` implementation or any code related to inode operations within `configfs`. That should point me in the right direction to understand the access control mechanisms in play.
|
| 46/4 |
2026/07/30 00:16 |
tool |
grepper |
0m
Args: map[Expression:configfs_dir_inode_operations]
Results: map[Output:fs/configfs/configfs_internal.h=87=extern const struct file_operations configfs_bin_file_operations;
fs/configfs/configfs_internal.h:88:extern const struct inode_operations configfs_dir_inode_operations;
fs/configfs/configfs_internal.h-89-extern const struct inode_operations configfs_root_inode_operations;
--
fs/configfs/dir.c=293=static int configfs_create_dir(struct config_item *item, struct dentry *dentry,
--
fs/configfs/dir.c-314-
fs/configfs/dir.c:315: inode->i_op = &configfs_dir_inode_operations;
fs/configfs/dir.c-316- inode->i_fop = &configfs_dir_operations;
--
fs/configfs/dir.c=1469=static int configfs_rmdir(struct inode *dir, struct dentry *dentry)
--
fs/configfs/dir.c-1578-
fs/configfs/dir.c:1579:const struct inode_operations configfs_dir_inode_operations = {
fs/configfs/dir.c-1580- .mkdir = configfs_mkdir,
]
|
| 47/4 |
2026/07/30 00:16 |
llm |
codeexpert |
0m
Model:
Tokens:
input: 4602
output: 36
thoughts: 54
Thoughts: Here's my take on the thought process:
**Diving into the ConfigFS Directory Creation**
Alright, let's get down to brass tacks. I need to understand how directory creation works within the ConfigFS filesystem. My starting point is `fs/configfs/dir.c`. I'm particularly interested in the `configfs_mkdir` function. I suspect that's where the directory creation logic resides, along with understanding how the underlying inode operations are managed, so I'll also be looking at `configfs_dir_inode_operations` in the vicinity.
To zero in on `configfs_mkdir` quickly, let's leverage `codesearch-definition-source`. That should quickly pull up the definition, and the source where it's defined. That'll let me see the function signature, its internal workings, and hopefully illuminate its role within the broader directory structure. From there I can start tracing call paths and understand the whole picture.
|
| 48/4 |
2026/07/30 00:16 |
tool |
codesearch-definition-source |
0m
Args: map[ContextFile:fs/configfs/dir.c Name:configfs_mkdir]
Results: map[SourceCode:1289: static struct dentry *configfs_mkdir(struct mnt_idmap *idmap, struct inode *dir,
1290: struct dentry *dentry, umode_t mode)
1291: {
1292: int ret = 0;
1293: int module_got = 0;
1294: struct config_group *group = NULL;
1295: struct config_item *item = NULL;
1296: struct config_item *parent_item;
1297: struct configfs_subsystem *subsys;
1298: struct configfs_dirent *sd;
1299: const struct config_item_type *type;
1300: struct module *subsys_owner = NULL, *new_item_owner = NULL;
1301: struct configfs_fragment *frag;
1302: struct name_snapshot n;
1303: const char *name;
1304:
1305: take_dentry_name_snapshot(&n, dentry);
1306: name = n.name.name;
1307:
1308: sd = dentry->d_parent->d_fsdata;
1309:
1310: /*
1311: * Fake invisibility if dir belongs to a group/default groups hierarchy
1312: * being attached
1313: */
1314: if (!configfs_dirent_is_ready(sd)) {
1315: ret = -ENOENT;
1316: goto out;
1317: }
1318:
1319: if (!(sd->s_type & CONFIGFS_USET_DIR)) {
1320: ret = -EPERM;
1321: goto out;
1322: }
1323:
1324: frag = new_fragment();
1325: if (!frag) {
1326: ret = -ENOMEM;
1327: goto out;
1328: }
1329:
1330: /* Get a working ref for the duration of this function */
1331: parent_item = configfs_get_config_item(dentry->d_parent);
1332: type = parent_item->ci_type;
1333: subsys = to_config_group(parent_item)->cg_subsys;
1334: BUG_ON(!subsys);
1335:
1336: if (!type || !type->ct_group_ops ||
1337: (!type->ct_group_ops->make_group &&
1338: !type->ct_group_ops->make_item)) {
1339: ret = -EPERM; /* Lack-of-mkdir returns -EPERM */
1340: goto out_put;
1341: }
1342:
1343: /*
1344: * The subsystem may belong to a different module than the item
1345: * being created. We don't want to safely pin the new item but
1346: * fail to pin the subsystem it sits under.
1347: */
1348: if (!subsys->su_group.cg_item.ci_type) {
1349: ret = -EINVAL;
1350: goto out_put;
1351: }
1352: subsys_owner = subsys->su_group.cg_item.ci_type->ct_owner;
1353: if (!try_module_get(subsys_owner)) {
1354: ret = -EINVAL;
1355: goto out_put;
1356: }
1357:
1358: mutex_lock(&subsys->su_mutex);
1359: if (type->ct_group_ops->make_group) {
1360: group = type->ct_group_ops->make_group(to_config_group(parent_item), name);
1361: if (!group)
1362: group = ERR_PTR(-ENOMEM);
1363: if (!IS_ERR(group)) {
1364: link_group(to_config_group(parent_item), group);
1365: item = &group->cg_item;
1366: } else
1367: ret = PTR_ERR(group);
1368: } else {
1369: item = type->ct_group_ops->make_item(to_config_group(parent_item), name);
1370: if (!item)
1371: item = ERR_PTR(-ENOMEM);
1372: if (!IS_ERR(item))
1373: link_obj(parent_item, item);
1374: else
1375: ret = PTR_ERR(item);
1376: }
1377: mutex_unlock(&subsys->su_mutex);
1378:
1379: if (ret) {
1380: /*
1381: * If ret != 0, then link_obj() was never called.
1382: * There are no extra references to clean up.
1383: */
1384: goto out_subsys_put;
1385: }
1386:
1387: /*
1388: * link_obj() has been called (via link_group() for groups).
1389: * From here on out, errors must clean that up.
1390: */
1391:
1392: type = item->ci_type;
1393: if (!type) {
1394: ret = -EINVAL;
1395: goto out_unlink;
1396: }
1397:
1398: new_item_owner = type->ct_owner;
1399: if (!try_module_get(new_item_owner)) {
1400: ret = -EINVAL;
1401: goto out_unlink;
1402: }
1403:
1404: /*
1405: * I hate doing it this way, but if there is
1406: * an error, module_put() probably should
1407: * happen after any cleanup.
1408: */
1409: module_got = 1;
1410:
1411: /*
1412: * Make racing rmdir() fail if it did not tag parent with
1413: * CONFIGFS_USET_DROPPING
1414: * Note: if CONFIGFS_USET_DROPPING is already set, attach_group() will
1415: * fail and let rmdir() terminate correctly
1416: */
1417: spin_lock(&configfs_dirent_lock);
1418: /* This will make configfs_detach_prep() fail */
1419: sd->s_type |= CONFIGFS_USET_IN_MKDIR;
1420: spin_unlock(&configfs_dirent_lock);
1421:
1422: if (group)
1423: ret = configfs_attach_group(item, dentry, frag);
1424: else
1425: ret = configfs_attach_item(item, dentry, frag);
1426:
1427: spin_lock(&configfs_dirent_lock);
1428: sd->s_type &= ~CONFIGFS_USET_IN_MKDIR;
1429: if (!ret)
1430: configfs_dir_set_ready(dentry->d_fsdata);
1431: spin_unlock(&configfs_dirent_lock);
1432:
1433: out_unlink:
1434: if (ret) {
1435: /* Tear down everything we built up */
1436: mutex_lock(&subsys->su_mutex);
1437:
1438: client_disconnect_notify(parent_item, item);
1439: if (group)
1440: unlink_group(group);
1441: else
1442: unlink_obj(item);
1443: client_drop_item(parent_item, item);
1444:
1445: mutex_unlock(&subsys->su_mutex);
1446:
1447: if (module_got)
1448: module_put(new_item_owner);
1449: }
1450:
1451: out_subsys_put:
1452: if (ret)
1453: module_put(subsys_owner);
1454:
1455: out_put:
1456: /*
1457: * link_obj()/link_group() took a reference from child->parent,
1458: * so the parent is safely pinned. We can drop our working
1459: * reference.
1460: */
1461: config_item_put(parent_item);
1462: put_fragment(frag);
1463:
1464: out:
1465: release_dentry_name_snapshot(&n);
1466: return ERR_PTR(ret);
1467: }
SourceFile:fs/configfs/dir.c]
|
| 49/4 |
2026/07/30 00:16 |
llm |
codeexpert |
0m
Model:
Tokens:
input: 7487
output: 20
thoughts: 88
Thoughts: **Understanding Configfs `mkdir` Permissions**
Okay, so I'm digging into how permissions are handled when a user attempts a `mkdir` operation within configfs. My initial thought is to trace the execution path. Naturally, `mkdir` in the VFS layer, specifically `vfs_mkdir`, is the entry point for permission checks. I need to understand how these permissions are actually evaluated in the context of configfs.
A key question arises: how is configfs configured in the first place? And can it be mounted within a user namespace? That's essential to understand the isolation and security implications. I'll need to explore the mounting process of configfs to paint a complete picture.
Therefore, I'll need to find the definition of `configfs_fs_type` and where `register_filesystem` is called for configfs to get the mount information. That should give me the entry point and the relevant configuration metadata.
|
| 50/4 |
2026/07/30 00:16 |
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");
]
|
| 51/4 |
2026/07/30 00:16 |
llm |
codeexpert |
0m
Model:
Tokens:
input: 8196
output: 36
thoughts: 37
Thoughts: Okay, let's dive into this. Here's my thought process on the matter:
**Checking `configfs` Mount Behavior**
Right, so I need to understand how `configfs` handles user namespaces during mount operations. The obvious place to start is the core mount implementation. Let's get the source code for `fs/configfs/mount.c` and take a look. Specifically, I'm interested in checking whether the `FS_USERNS_MOUNT` flag is set on the `configfs_fs_type` structure. That flag, if present, will tell me if the filesystem is designed to interact properly with user namespaces during the mount process. I'll need to confirm if this flag is set. That should give me the answer I need.
|
| 52/4 |
2026/07/30 00:16 |
tool |
read-file |
0m
Args: map[File:fs/configfs/mount.c FirstLine:100 LineCount:30]
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:
]
|
| 53/4 |
2026/07/30 00:16 |
llm |
codeexpert |
0m
Model:
Tokens:
input: 8890
output: 20
thoughts: 207
Thoughts: **Configfs and User Namespaces: A Permission Puzzle**
Okay, so I'm taking a look at this `configfs_fs_type` thing, and it's hitting me right away that it *doesn't* have that `FS_USERNS_MOUNT` flag. Now, for those of us who work with user namespaces, we know this is a big deal! If you want a filesystem mountable from within an unprivileged user namespace, that flag is non-negotiable. I need to confirm this... yes, a filesystem *must* have `FS_USERNS_MOUNT` in its `fs_flags` to be mountable in a user namespace. Since `configfs_fs_type` lacks it, we're saying an unprivileged user *cannot* mount configfs.
But wait, hold on. This got me thinking. What if configfs is already mounted? Typically, it lives at `/sys/kernel/config`. How are the permissions set on it, actually? Let me check the permissions on `/sys/kernel/config` and `/sys/kernel/config/usb_gadget`... I need to see how the kernel sets permissions on those directories and files. Let's dig into `fs/configfs/dir.c` and `fs/configfs/inode.c`, specifically. I'm going to search for where the code sets `i_mode` or anything dealing with permissions in those files. I'm sure I'll find something illuminating in there.
|
| 54/4 |
2026/07/30 00:16 |
tool |
grepper |
0m
Error:
Args: map[Expression:configfs_init_inode]
Results:
|
| 55/4 |
2026/07/30 00:16 |
llm |
codeexpert |
0m
Model:
Tokens:
input: 9453
output: 28
thoughts: 23
Thoughts: Okay, here's my thought process laid out, as if I were thinking it:
**Digging into configfs Implementation**
Alright, I need to understand how these configfs inodes are created. I'm going to start by looking for the core inode creation function. Let's start with a focused search. My initial instinct is to look for something named along the lines of `configfs_new_inode` or something very similar, because that's what seems logical, right? Given the naming conventions in the kernel, that's where I'd start. And, of course, I'll be looking specifically within the `fs/configfs/` directory, since that's where the relevant code would be. This should be a good starting point for tracing inode creation within the configfs framework. Let's get to work.
|
| 56/4 |
2026/07/30 00:16 |
tool |
grepper |
0m
Args: map[Expression:new_inode PathPrefix:fs/configfs/]
Results: map[Output:fs/configfs/configfs_internal.h=66=extern int configfs_is_root(struct config_item *item);
fs/configfs/configfs_internal.h-67-
fs/configfs/configfs_internal.h:68:extern struct inode * configfs_new_inode(umode_t mode, struct configfs_dirent *, struct super_block *);
fs/configfs/configfs_internal.h-69-extern struct inode *configfs_create(struct dentry *, umode_t mode);
--
fs/configfs/inode.c=94=static inline void set_inode_attr(struct inode * inode, struct iattr * iattr)
--
fs/configfs/inode.c-103-
fs/configfs/inode.c:104:struct inode *configfs_new_inode(umode_t mode, struct configfs_dirent *sd,
fs/configfs/inode.c-105- struct super_block *s)
fs/configfs/inode.c-106-{
fs/configfs/inode.c:107: struct inode * inode = new_inode(s);
fs/configfs/inode.c-108- if (inode) {
--
fs/configfs/inode.c=156=struct inode *configfs_create(struct dentry *dentry, umode_t mode)
--
fs/configfs/inode.c-167- sd = dentry->d_fsdata;
fs/configfs/inode.c:168: inode = configfs_new_inode(mode, sd, dentry->d_sb);
fs/configfs/inode.c-169- if (!inode)
--
fs/configfs/mount.c=63=static int configfs_fill_super(struct super_block *sb, struct fs_context *fc)
--
fs/configfs/mount.c-73-
fs/configfs/mount.c:74: inode = configfs_new_inode(S_IFDIR | S_IRWXU | S_IRUGO | S_IXUGO,
fs/configfs/mount.c-75- &configfs_root, sb);
]
|
| 57/4 |
2026/07/30 00:16 |
llm |
codeexpert |
0m
Model:
Tokens:
input: 10234
output: 38
thoughts: 30
Thoughts: Okay, let's dive into this. Here's how I'd approach understanding the ownership and permission settings within `configfs_new_inode` in `fs/configfs/inode.c`:
**Dissecting Ownership and Permissions in `configfs_new_inode`**
Alright, my immediate task is to get a handle on how `configfs_new_inode` handles setting the ownership and permission attributes for inodes within the ConfigFS framework. Knowing this is crucial for understanding how ConfigFS interacts with the wider file system security context and how it might handle things like access controls. I need to refresh my memory on the likely spots, given this function's name. I need to quickly recall how this function might interact with `i_uid_write`, `i_gid_write`, the permission bits, and whether it has interactions with `security` modules in the kernel. Let me start by opening that file. I expect to be looking at calls to things like `inode_init_owner`, `inode_set_mode`, or potentially, custom ConfigFS functions related to security. Understanding the defaults, how these are initialized, and if there are any specific ConfigFS-level policies influencing them, is key. I'm going to look for any logic that deviates from the standard VFS behavior, as ConfigFS is a specialized virtual file system. The interplay between ConfigFS-specific code and the generic file system infrastructure is what's really interesting here, and where the subtle aspects are.
|
| 58/4 |
2026/07/30 00:16 |
tool |
codesearch-definition-source |
0m
Args: map[ContextFile:fs/configfs/inode.c Name:configfs_new_inode]
Results: map[SourceCode: 104: struct inode *configfs_new_inode(umode_t mode, struct configfs_dirent *sd,
105: struct super_block *s)
106: {
107: struct inode * inode = new_inode(s);
108: if (inode) {
109: inode->i_ino = get_next_ino();
110: inode->i_mapping->a_ops = &ram_aops;
111: inode->i_op = &configfs_inode_operations;
112:
113: if (sd->s_iattr) {
114: /* sysfs_dirent has non-default attributes
115: * get them for the new inode from persistent copy
116: * in sysfs_dirent
117: */
118: set_inode_attr(inode, sd->s_iattr);
119: } else
120: set_default_inode_attr(inode, mode);
121: }
122: return inode;
123: }
SourceFile:fs/configfs/inode.c]
|
| 59/4 |
2026/07/30 00:16 |
llm |
codeexpert |
0m
Model:
Tokens:
input: 10949
output: 39
thoughts: 42
Thoughts: Here's my summary, as the thought process unfolds:
**Examining Inode Attributes in ConfigFS**
Okay, time to dig into the details. I need to understand how default inode attributes are handled within ConfigFS. My first step is to focus on the `set_default_inode_attr` function. I think this function would be the place to start understanding how inodes are being initialized with their defaults within ConfigFS. Now, let's fire up `codesearch-definition-source` to get directly to the source code, or if that's not accessible, I'll just open the `fs/configfs/inode.c` file itself and read it. Let's see what secrets it holds. I expect that I'll find the logic for setting permissions, ownership, and other initial properties there. From there, I'll be in a better position to trace its usages and understand the overall inode creation flow in this specific file system.
|
| 60/4 |
2026/07/30 00:16 |
tool |
codesearch-definition-source |
0m
Args: map[ContextFile:fs/configfs/inode.c Name:set_default_inode_attr]
Results: map[SourceCode: 88: static inline void set_default_inode_attr(struct inode * inode, umode_t mode)
89: {
90: inode->i_mode = mode;
91: simple_inode_init_ts(inode);
92: }
SourceFile:fs/configfs/inode.c]
|
| 61/4 |
2026/07/30 00:16 |
llm |
codeexpert |
0m
Model:
Tokens:
input: 11334
output: 35
thoughts: 31
Thoughts: Here's my take on the thought process:
**Diving into ConfigFS Ownership Configuration**
Okay, hold on a second. I need to figure out precisely where the user and group IDs for the ownership of ConfigFS inodes are being set. Time to go digging through the kernel source code. My gut feeling is that it should be in the file handling inode metadata, which I believe would be `fs/configfs/inode.c`. Let's see... I'll jump right to around line 88. That seems like a reasonable place to start, based on the function names in the include headers. Let's see what's happening there.
|
| 62/4 |
2026/07/30 00:16 |
tool |
read-file |
0m
Args: map[File:fs/configfs/inode.c FirstLine:60 LineCount:40]
Results: map[Contents: 60: }
61: /* attributes were changed atleast once in past */
62:
63: error = simple_setattr(idmap, dentry, iattr);
64: if (error)
65: return error;
66:
67: if (ia_valid & ATTR_UID)
68: sd_iattr->ia_uid = iattr->ia_uid;
69: if (ia_valid & ATTR_GID)
70: sd_iattr->ia_gid = iattr->ia_gid;
71: if (ia_valid & ATTR_ATIME)
72: sd_iattr->ia_atime = iattr->ia_atime;
73: if (ia_valid & ATTR_MTIME)
74: sd_iattr->ia_mtime = iattr->ia_mtime;
75: if (ia_valid & ATTR_CTIME)
76: sd_iattr->ia_ctime = iattr->ia_ctime;
77: if (ia_valid & ATTR_MODE) {
78: umode_t mode = iattr->ia_mode;
79:
80: if (!in_group_p(inode->i_gid) && !capable(CAP_FSETID))
81: mode &= ~S_ISGID;
82: sd_iattr->ia_mode = sd->s_mode = mode;
83: }
84:
85: return error;
86: }
87:
88: static inline void set_default_inode_attr(struct inode * inode, umode_t mode)
89: {
90: inode->i_mode = mode;
91: simple_inode_init_ts(inode);
92: }
93:
94: static inline void set_inode_attr(struct inode * inode, struct iattr * iattr)
95: {
96: inode->i_mode = iattr->ia_mode;
97: inode->i_uid = iattr->ia_uid;
98: inode->i_gid = iattr->ia_gid;
99: inode_set_atime_to_ts(inode, iattr->ia_atime);
]
|
| 63/4 |
2026/07/30 00:16 |
llm |
codeexpert |
0m
Model:
Tokens:
input: 12169
output: 28
thoughts: 100
Thoughts: **Investigating Inode Ownership in ConfigFS**
Okay, so I'm digging into how user and group IDs are managed within the ConfigFS filesystem, and I've hit a snag. I'm wondering where the `inode->i_uid` and `inode->i_gid` fields are being initialized, specifically in the case where `sd->s_iattr` (which I assume refers to super-block inode attributes) is `NULL`. That immediately raises a flag.
My first instinct is to check the inode creation functions β I'm thinking either `new_inode` or perhaps `configfs_new_inode` would be the place. The obvious question is: are `i_uid` and `i_gid` set within those functions, or are they being handled elsewhere in the lifecycle?
To nail this down quickly, I'm going to search directly for references to `i_uid` within `fs/configfs/inode.c`. That should give me a clear picture of how these critical ownership attributes are being set in the context of ConfigFS inodes.
|
| 64/4 |
2026/07/30 00:16 |
tool |
grepper |
0m
Args: map[Expression:i_uid PathPrefix:fs/configfs/]
Results: map[Output:fs/configfs/inode.c=94=static inline void set_inode_attr(struct inode * inode, struct iattr * iattr)
--
fs/configfs/inode.c-96- inode->i_mode = iattr->ia_mode;
fs/configfs/inode.c:97: inode->i_uid = iattr->ia_uid;
fs/configfs/inode.c-98- inode->i_gid = iattr->ia_gid;
]
|
| 65/4 |
2026/07/30 00:16 |
llm |
codeexpert |
0m
Model:
Tokens:
input: 12654
output: 32
thoughts: 124
Thoughts: **Analyzing Inode Attribute Initialization**
Okay, hold on a second. I'm following the logic here, and something feels off. If `sd->s_iattr` is `NULL`, then `set_default_inode_attr` gets called. But as far as I can see, that *only* sets the `inode->i_mode` and the timestamps. However, I seem to remember that `new_inode` *also* initializes the `inode->i_uid` and `inode->i_gid` to `GLOBAL_ROOT_UID` and `GLOBAL_ROOT_GID`. Where is that coming from? I need to track this down to be sure.
Right, time to dive into the code. I need to double-check this initialization in either `new_inode` or possibly even `alloc_inode`, both in `fs/inode.c`. Let me try a `codesearch-definition-source` query for `new_inode`. That should quickly pinpoint the exact source code definition and show me what's actually going on. I need to be sure the uid/gid are set in the right place!
|
| 66/4 |
2026/07/30 00:16 |
tool |
codesearch-definition-source |
0m
Args: map[ContextFile:fs/inode.c Name:new_inode]
Results: map[SourceCode:1158: /**
1159: * new_inode - obtain an inode
1160: * @sb: superblock
1161: *
1162: * Allocates a new inode for given superblock. The default gfp_mask
1163: * for allocations related to inode->i_mapping is GFP_HIGHUSER_MOVABLE.
1164: * If HIGHMEM pages are unsuitable or it is known that pages allocated
1165: * for the page cache are not reclaimable or migratable,
1166: * mapping_set_gfp_mask() must be called with suitable flags on the
1167: * newly created inode's mapping
1168: *
1169: */
1170: struct inode *new_inode(struct super_block *sb)
1171: {
1172: struct inode *inode;
1173:
1174: inode = alloc_inode(sb);
1175: if (inode)
1176: inode_sb_list_add(inode);
1177: return inode;
1178: }
SourceFile:fs/inode.c]
|
| 67/4 |
2026/07/30 00:16 |
llm |
codeexpert |
0m
Model:
Tokens:
input: 13387
output: 34
thoughts: 35
Thoughts: Here's my analysis of the provided text, rendered in the first person, assuming an expert user:
**Investigating Inode Initialization**
Alright, let's dig into this. I need to understand where the `i_uid` and `i_gid` fields of an inode are being initialized. Given my understanding of the kernel's file system architecture, the most likely candidates are `alloc_inode` or perhaps `inode_init_always`. These functions are central to the creation and setup of inode structures. I'll need to examine the source code for each of these functions, paying close attention to the variable assignments and initializations within. Knowing the precise point of initialization for these UID/GID attributes is crucial for any deeper analysis I need to do regarding security, permissions, or how the kernel handles file ownership. Time to get my hands dirty.
|
| 68/4 |
2026/07/30 00:16 |
tool |
codesearch-definition-source |
0m
Args: map[ContextFile:fs/inode.c Name:inode_init_always]
Results: map[SourceCode:2943: static inline int inode_init_always(struct super_block *sb, struct inode *inode)
2944: {
2945: return inode_init_always_gfp(sb, inode, GFP_NOFS);
2946: }
SourceFile:include/linux/fs.h]
|
| 69/4 |
2026/07/30 00:16 |
llm |
codeexpert |
0m
Model:
Tokens:
input: 13734
output: 30
thoughts: 30
Thoughts: Okay, here's my take on those thoughts, framed as an expert's internal monologue:
**Navigating the Inode Landscape**
Right, time to dive into the inode initialization. I need to refresh my memory on how they're set up, specifically the "always" allocation flags. I'm thinking I'll start by checking out `fs/inode.c`. That's the obvious place to find the `inode_init_always_gfp` or even the plain `inode_init_always` function. Let's see what the current implementations look like and how the allocation is being managed. It's usually a good starting point for understanding how new inodes are created, and if the "always" allocation is actually always happening, or conditionally.
|
| 70/4 |
2026/07/30 00:16 |
tool |
grepper |
0m
Args: map[Expression:inode_init_always PathPrefix:fs/inode.c]
Results: map[Output:fs/inode.c=208=static int no_open(struct inode *inode, struct file *file)
--
fs/inode.c-213-/**
fs/inode.c:214: * inode_init_always_gfp - perform inode structure initialisation
fs/inode.c-215- * @sb: superblock inode belongs to
--
fs/inode.c-222- */
fs/inode.c:223:int inode_init_always_gfp(struct super_block *sb, struct inode *inode, gfp_t gfp)
fs/inode.c-224-{
--
fs/inode.c-304-}
fs/inode.c:305:EXPORT_SYMBOL(inode_init_always_gfp);
fs/inode.c-306-
--
fs/inode.c=332=struct inode *alloc_inode(struct super_block *sb)
--
fs/inode.c-344-
fs/inode.c:345: if (unlikely(inode_init_always(sb, inode))) {
fs/inode.c-346- if (ops->destroy_inode) {
]
|
| 71/4 |
2026/07/30 00:16 |
llm |
codeexpert |
0m
Model:
Tokens:
input: 14256
output: 33
thoughts: 18
Thoughts: Here's my attempt at summarizing the thought process, tailored for an expert audience:
**Diving into `fs/inode.c`**
Okay, time to get my hands dirty. I'm going to jump into the `fs/inode.c` file and zero in on around line 223. That section likely deals with a core inode operation, given the naming convention. I'm guessing it probably involves some fundamental inode manipulation, perhaps related to allocation, deallocation, or even lifecycle management. Knowing the kernel's code, it could be anything, from a simple field initialization to a complex synchronization routine related to some concurrent access. I'm hoping it's not some obscure corner case, but something more fundamental, that will offer a good starting point for my investigation. Let's see what interesting tidbits we can dig up from there.
|
| 72/4 |
2026/07/30 00:17 |
tool |
read-file |
0m
Args: map[File:fs/inode.c FirstLine:223 LineCount:40]
Results: map[Contents: 223: int inode_init_always_gfp(struct super_block *sb, struct inode *inode, gfp_t gfp)
224: {
225: static const struct inode_operations empty_iops;
226: static const struct file_operations no_open_fops = {.open = no_open};
227: struct address_space *const mapping = &inode->i_data;
228:
229: inode->i_sb = sb;
230: inode->i_blkbits = sb->s_blocksize_bits;
231: inode->i_flags = 0;
232: inode_state_assign_raw(inode, 0);
233: atomic64_set(&inode->i_sequence, 0);
234: atomic_set(&inode->i_count, 1);
235: inode->i_op = &empty_iops;
236: inode->i_fop = &no_open_fops;
237: inode->i_ino = 0;
238: inode->__i_nlink = 1;
239: inode->i_opflags = 0;
240: if (sb->s_xattr)
241: inode->i_opflags |= IOP_XATTR;
242: if (sb->s_type->fs_flags & FS_MGTIME)
243: inode->i_opflags |= IOP_MGTIME;
244: i_uid_write(inode, 0);
245: i_gid_write(inode, 0);
246: atomic_set(&inode->i_writecount, 0);
247: inode->i_size = 0;
248: inode->i_write_hint = WRITE_LIFE_NOT_SET;
249: inode->i_blocks = 0;
250: inode->i_bytes = 0;
251: inode->i_generation = 0;
252: inode->i_pipe = NULL;
253: inode->i_cdev = NULL;
254: inode->i_link = NULL;
255: inode->i_dir_seq = 0;
256: inode->i_rdev = 0;
257: inode->dirtied_when = 0;
258:
259: #ifdef CONFIG_CGROUP_WRITEBACK
260: inode->i_wb_frn_winner = 0;
261: inode->i_wb_frn_avg_time = 0;
262: inode->i_wb_frn_history = 0;
]
|
| 73/4 |
2026/07/30 00:17 |
llm |
codeexpert |
0m
Model:
Tokens:
input: 15234
output: 21
thoughts: 312
Thoughts: **Analyzing Configfs Permissions and Ownership**
Okay, so I'm looking at this `configfs` code, specifically the allocation of inodes. The line `i_uid_write(inode, 0);` and `i_gid_write(inode, 0);` immediately flags that new inodes are defaulted to root ownership β UID and GID of 0. That makes sense, but the question that pops into my head, given my experience, is whether an unprivileged user can *actually* interact with this thing. Can they write to or create anything within `configfs`? It's a key question when dealing with any filesystem that's designed for configuration.
Right, time to check the permissions of those configfs directories themselves. What mode gets applied when a directory is created within the file system? I'll need to dig into the directory creation code. Ah, here it is in `fs/configfs/mount.c`. And there it is: `inode = configfs_new_inode(S_IFDIR | S_IRWXU | S_IRUGO | S_IXUGO, ...`.
Let's break that down. `S_IRWXU | S_IRUGO | S_IXUGO` translates to 0755, which is quite common. So the root directory of `configfs` is owned by root with read, write, and execute permissions for the owner (root), and read and execute permissions for group and others. *That* confirms that only root can write to it β precisely what I suspected. It makes sense from a security perspective.
So, the root is locked down, but what about other directories beneath it? Are they similarly restricted? Let's take a look at a specific instance, `/sys/kernel/config/usb_gadget`, as an example. I should quickly be able to find how that specific mount point or directory is registered by searching for `usb_gadget` within the `configfs` sources or potentially the `drivers/usb/gadget` directory. I'm going to track down how that portion of the virtual file system is put together.
|
| 74/4 |
2026/07/30 00:17 |
tool |
grepper |
0m
Args: map[Expression:configfs_register_subsystem]
Results: map[Output:Documentation/filesystems/configfs.rst=345=tells configfs to make the subsystem appear in the file tree::
--
Documentation/filesystems/configfs.rst-351-
Documentation/filesystems/configfs.rst:352: int configfs_register_subsystem(struct configfs_subsystem *subsys);
Documentation/filesystems/configfs.rst-353- void configfs_unregister_subsystem(struct configfs_subsystem *subsys);
--
Documentation/filesystems/configfs.rst=357=this group is usually defined statically. Before calling
Documentation/filesystems/configfs.rst:358:configfs_register_subsystem(), the subsystem must have initialized the
Documentation/filesystems/configfs.rst-359-group via the usual group _init() functions, and it must also have
--
drivers/acpi/acpi_configfs.c=255=static int __init acpi_configfs_init(void)
--
drivers/acpi/acpi_configfs.c-261-
drivers/acpi/acpi_configfs.c:262: ret = configfs_register_subsystem(&acpi_configfs);
drivers/acpi/acpi_configfs.c-263- if (ret)
--
drivers/block/null_blk/main.c=2125=static int __init null_init(void)
--
drivers/block/null_blk/main.c-2164-
drivers/block/null_blk/main.c:2165: ret = configfs_register_subsystem(&nullb_subsys);
drivers/block/null_blk/main.c-2166- if (ret)
--
drivers/gpio/gpio-aggregator.c=1695=static int __init gpio_aggregator_init(void)
--
drivers/gpio/gpio-aggregator.c-1700- mutex_init(&gpio_aggregator_subsys.su_mutex);
drivers/gpio/gpio-aggregator.c:1701: ret = configfs_register_subsystem(&gpio_aggregator_subsys);
drivers/gpio/gpio-aggregator.c-1702- if (ret) {
--
drivers/gpio/gpio-sim.c=1608=static int __init gpio_sim_init(void)
--
drivers/gpio/gpio-sim.c-1619- mutex_init(&gpio_sim_config_subsys.su_mutex);
drivers/gpio/gpio-sim.c:1620: ret = configfs_register_subsystem(&gpio_sim_config_subsys);
drivers/gpio/gpio-sim.c-1621- if (ret) {
--
drivers/gpio/gpio-virtuser.c=1761=static int __init gpio_virtuser_init(void)
--
drivers/gpio/gpio-virtuser.c-1772- mutex_init(&gpio_virtuser_config_subsys.su_mutex);
drivers/gpio/gpio-virtuser.c:1773: ret = configfs_register_subsystem(&gpio_virtuser_config_subsys);
drivers/gpio/gpio-virtuser.c-1774- if (ret) {
--
drivers/gpu/drm/vkms/vkms_configfs.c=824=int vkms_configfs_register(void)
--
drivers/gpu/drm/vkms/vkms_configfs.c-831- config_group_init(&vkms_subsys.su_group);
drivers/gpu/drm/vkms/vkms_configfs.c:832: ret = configfs_register_subsystem(&vkms_subsys);
drivers/gpu/drm/vkms/vkms_configfs.c-833-
--
drivers/gpu/drm/xe/xe_configfs.c=1334=int __init xe_configfs_init(void)
--
drivers/gpu/drm/xe/xe_configfs.c-1339- mutex_init(&xe_configfs.su_mutex);
drivers/gpu/drm/xe/xe_configfs.c:1340: ret = configfs_register_subsystem(&xe_configfs);
drivers/gpu/drm/xe/xe_configfs.c-1341- if (ret) {
--
drivers/hwtracing/coresight/coresight-syscfg-configfs.c=451=int cscfg_configfs_init(struct cscfg_manager *cscfg_mgr)
--
drivers/hwtracing/coresight/coresight-syscfg-configfs.c-476-
drivers/hwtracing/coresight/coresight-syscfg-configfs.c:477: return configfs_register_subsystem(subsys);
drivers/hwtracing/coresight/coresight-syscfg-configfs.c-478-}
--
drivers/hwtracing/stm/policy.c=560=int __init stp_configfs_init(void)
--
drivers/hwtracing/stm/policy.c-563- mutex_init(&stp_policy_subsys.su_mutex);
drivers/hwtracing/stm/policy.c:564: return configfs_register_subsystem(&stp_policy_subsys);
drivers/hwtracing/stm/policy.c-565-}
--
drivers/iio/industrialio-configfs.c=32=static int __init iio_configfs_init(void)
--
drivers/iio/industrialio-configfs.c-35-
drivers/iio/industrialio-configfs.c:36: return configfs_register_subsystem(&iio_configfs_subsys);
drivers/iio/industrialio-configfs.c-37-}
--
drivers/infiniband/core/cma_configfs.c=349=int __init cma_configfs_init(void)
--
drivers/infiniband/core/cma_configfs.c-354- mutex_init(&cma_subsys.su_mutex);
drivers/infiniband/core/cma_configfs.c:355: ret = configfs_register_subsystem(&cma_subsys);
drivers/infiniband/core/cma_configfs.c-356- if (ret)
--
drivers/most/configfs.c=649=int most_register_configfs_subsys(struct most_component *c)
--
drivers/most/configfs.c-654- most_cdev.mod = c->mod;
drivers/most/configfs.c:655: ret = configfs_register_subsystem(&most_cdev.subsys);
drivers/most/configfs.c-656- } else if (!strcmp(c->name, "net")) {
drivers/most/configfs.c-657- most_net.mod = c->mod;
drivers/most/configfs.c:658: ret = configfs_register_subsystem(&most_net.subsys);
drivers/most/configfs.c-659- } else if (!strcmp(c->name, "video")) {
drivers/most/configfs.c-660- most_video.mod = c->mod;
drivers/most/configfs.c:661: ret = configfs_register_subsystem(&most_video.subsys);
drivers/most/configfs.c-662- } else if (!strcmp(c->name, "sound")) {
drivers/most/configfs.c-663- most_sound_subsys.mod = c->mod;
drivers/most/configfs.c:664: ret = configfs_register_subsystem(&most_sound_subsys.subsys);
drivers/most/configfs.c-665- } else {
--
drivers/net/netconsole.c=205=static int __init dynamic_netconsole_init(void)
--
drivers/net/netconsole.c-208- mutex_init(&netconsole_subsys.su_mutex);
drivers/net/netconsole.c:209: return configfs_register_subsystem(&netconsole_subsys);
drivers/net/netconsole.c-210-}
--
drivers/nvme/target/configfs.c=2363=int __init nvmet_init_configfs(void)
--
drivers/nvme/target/configfs.c-2384-
drivers/nvme/target/configfs.c:2385: ret = configfs_register_subsystem(&nvmet_configfs_subsystem);
drivers/nvme/target/configfs.c-2386- if (ret) {
drivers/nvme/target/configfs.c:2387: pr_err("configfs_register_subsystem: %d\n", ret);
drivers/nvme/target/configfs.c-2388- return ret;
--
drivers/pci/endpoint/pci-ep-cfs.c=722=static int __init pci_ep_cfs_init(void)
--
drivers/pci/endpoint/pci-ep-cfs.c-728-
drivers/pci/endpoint/pci-ep-cfs.c:729: ret = configfs_register_subsystem(&pci_ep_cfs_subsys);
drivers/pci/endpoint/pci-ep-cfs.c-730- if (ret) {
--
drivers/target/target_core_configfs.c=3744=static int __init target_core_init_configfs(void)
--
drivers/target/target_core_configfs.c-3799- */
drivers/target/target_core_configfs.c:3800: ret = configfs_register_subsystem(subsys);
drivers/target/target_core_configfs.c-3801- if (ret < 0) {
--
drivers/thunderbolt/configfs.c=51=int tb_configfs_init(void)
--
drivers/thunderbolt/configfs.c-54- mutex_init(&tb_configfs.su_mutex);
drivers/thunderbolt/configfs.c:55: return configfs_register_subsystem(&tb_configfs);
drivers/thunderbolt/configfs.c-56-}
--
drivers/usb/gadget/configfs.c=2093=static int __init gadget_cfs_init(void)
--
drivers/usb/gadget/configfs.c-2098-
drivers/usb/gadget/configfs.c:2099: ret = configfs_register_subsystem(&gadget_subsys);
drivers/usb/gadget/configfs.c-2100- return ret;
--
drivers/virt/coco/guest/report.c=508=static int __init tsm_report_init(void)
--
drivers/virt/coco/guest/report.c-514- config_group_init(root);
drivers/virt/coco/guest/report.c:515: rc = configfs_register_subsystem(&tsm_configfs);
drivers/virt/coco/guest/report.c-516- if (rc)
--
fs/configfs/dir.c=992=static int configfs_dump(struct configfs_dirent *sd, int level)
--
fs/configfs/dir.c-1034- * subsystem isn't registered, or when configfs is loading or unloading.
fs/configfs/dir.c:1035: * Just like configfs_register_subsystem(). So we take the same
fs/configfs/dir.c-1036- * precautions. We pin the filesystem. We lock configfs_dirent_lock.
--
fs/configfs/dir.c=1870=EXPORT_SYMBOL(configfs_unregister_default_group);
fs/configfs/dir.c-1871-
fs/configfs/dir.c:1872:int configfs_register_subsystem(struct configfs_subsystem *subsys)
fs/configfs/dir.c-1873-{
--
fs/configfs/dir.c=1934=void configfs_unregister_subsystem(struct configfs_subsystem *subsys)
--
fs/configfs/dir.c-1978-
fs/configfs/dir.c:1979:EXPORT_SYMBOL(configfs_register_subsystem);
fs/configfs/dir.c-1980-EXPORT_SYMBOL(configfs_unregister_subsystem);
--
fs/dlm/config.c=639=int __init dlm_config_init(void)
--
fs/dlm/config.c-642- mutex_init(&clusters_root.subsys.su_mutex);
fs/dlm/config.c:643: return configfs_register_subsystem(&clusters_root.subsys);
fs/dlm/config.c-644-}
--
fs/ocfs2/cluster/nodemanager.c=826=static int __init init_o2nm(void)
--
fs/ocfs2/cluster/nodemanager.c-841- mutex_init(&o2nm_cluster_group.cs_subsys.su_mutex);
fs/ocfs2/cluster/nodemanager.c:842: ret = configfs_register_subsystem(&o2nm_cluster_group.cs_subsys);
fs/ocfs2/cluster/nodemanager.c-843- if (ret) {
--
include/linux/configfs.h=233=static inline struct configfs_subsystem *to_configfs_subsystem(struct config_group *group)
--
include/linux/configfs.h-239-
include/linux/configfs.h:240:int configfs_register_subsystem(struct configfs_subsystem *subsys);
include/linux/configfs.h-241-void configfs_unregister_subsystem(struct configfs_subsystem *subsys);
--
kernel/crash_dump_dm_crypt.c=451=static int __init configfs_dmcrypt_keys_init(void)
--
kernel/crash_dump_dm_crypt.c-461- mutex_init(&config_keys_subsys.su_mutex);
kernel/crash_dump_dm_crypt.c:462: ret = configfs_register_subsystem(&config_keys_subsys);
kernel/crash_dump_dm_crypt.c-463- if (ret) {
--
rust/kernel/configfs.rs=147= pub fn new(
--
rust/kernel/configfs.rs-179- // SAFETY: We initialized `this.subsystem` according to C API contract above.
rust/kernel/configfs.rs:180: unsafe { bindings::configfs_register_subsystem(this.subsystem.get()) },
rust/kernel/configfs.rs-181- )
--
samples/configfs/configfs_sample.c=330=static int __init configfs_example_init(void)
--
samples/configfs/configfs_sample.c-339- mutex_init(&subsys->su_mutex);
samples/configfs/configfs_sample.c:340: ret = configfs_register_subsystem(subsys);
samples/configfs/configfs_sample.c-341- if (ret) {
]
|
| 75/4 |
2026/07/30 00:17 |
llm |
codeexpert |
0m
Model:
Tokens:
input: 19902
output: 40
thoughts: 53
Thoughts: Okay, let's dive into this.
**Investigating the Gadget Subsystem Definition**
My immediate thought is to examine the source code for the gadget configuration framework. Specifically, I need to see how `gadget_subsys` is defined and registered. My go-to location for this kind of core USB gadget functionality is usually `drivers/usb/gadget/configfs.c`. It's a prime suspect for handling the system-level configuration, given the context.
To get the exact definition and where it's all wired up, I'll leverage my `codesearch-definition-source` tool. It's the fastest way to trace the symbol and see the full picture of how `gadget_subsys` is initialized and attached to the kernel. This will give me the contextual information I need.
|
| 76/4 |
2026/07/30 00:17 |
tool |
codesearch-definition-source |
0m
Args: map[ContextFile:drivers/usb/gadget/configfs.c Name:gadget_subsys]
Results: map[SourceCode:2073: static struct configfs_subsystem gadget_subsys = {
2074: .su_group = {
2075: .cg_item = {
2076: .ci_namebuf = "usb_gadget",
2077: .ci_type = &gadgets_type,
2078: },
2079: },
2080: .su_mutex = __MUTEX_INITIALIZER(gadget_subsys.su_mutex),
2081: };
SourceFile:drivers/usb/gadget/configfs.c]
|
| 77/4 |
2026/07/30 00:17 |
llm |
codeexpert |
0m
Model:
Tokens:
input: 20339
output: 39
thoughts: 25
Thoughts: Okay, here's my summary, taking on the persona you've requested:
**Diving into the `configfs.c` USB Gadget Configuration**
Alright, let's get into the nitty-gritty. I need to take a look at how this gadget stuff is put together, specifically how it's configured. My immediate target is `gadgets_type` β I'm betting that's the key structure or variable we're using in `drivers/usb/gadget/configfs.c` for handling the different types of USB gadgets. I need to understand how these gadgets are classified, and how that classification impacts their configuration within the configfs framework. I'm hoping I can find the central point of gadget definition, to get a handle on the available types, the supported options, and how the underlying USB core interacts with the configuration system for each of them. Let's see what gems we can find in there!
|
| 78/4 |
2026/07/30 00:17 |
tool |
codesearch-definition-source |
0m
Args: map[ContextFile:drivers/usb/gadget/configfs.c Name:gadgets_type]
Results: map[SourceCode:2068: static const struct config_item_type gadgets_type = {
2069: .ct_group_ops = &gadgets_ops,
2070: .ct_owner = THIS_MODULE,
2071: };
SourceFile:drivers/usb/gadget/configfs.c]
|
| 79/4 |
2026/07/30 00:17 |
llm |
codeexpert |
0m
Model:
Tokens:
input: 20703
output: 39
thoughts: 25
Thoughts: Here's a summary of my thought process, as if I were analyzing that code:
**Diving into the USB Gadget Configuration**
Okay, time to dissect `gadgets_ops` in `drivers/usb/gadget/configfs.c`. I need to get a clear picture of how this section handles the configuration of USB gadgets within the ConfigFS framework. Knowing this is crucial for understanding how we dynamically manage USB device functionality. I'm focusing on those operational functions β the *ops* β to understand the core interactions with the system. I anticipate this will involve understanding how we define and implement different USB functions and their related configurations via ConfigFS. Let's see how they hook into the gadget controller and present the available options to the user. I'll be looking for how interfaces are created, how endpoints are managed, and how we handle various setup requests.
|
| 80/4 |
2026/07/30 00:17 |
tool |
codesearch-definition-source |
0m
Args: map[ContextFile:drivers/usb/gadget/configfs.c Name:gadgets_ops]
Results: map[SourceCode:2063: static const struct configfs_group_operations gadgets_ops = {
2064: .make_group = &gadgets_make,
2065: .drop_item = &gadgets_drop,
2066: };
SourceFile:drivers/usb/gadget/configfs.c]
|
| 81/4 |
2026/07/30 00:17 |
llm |
codeexpert |
0m
Model:
Tokens:
input: 21050
output: 39
thoughts: 25
Thoughts: Here's a summary of my thought process, as if I were analyzing that code:
**Diving into the USB Gadget Configuration**
Okay, time to dissect `gadgets_make` in `drivers/usb/gadget/configfs.c`. This function is likely the linchpin for how USB gadgets are configured and instantiated within the ConfigFS framework. I'm guessing it's responsible for the creation or setup of a USB gadget based on the configuration provided through the filesystem. My initial thought is it probably handles a lot of the low-level device controller stuff. I need to understand how it interacts with the underlying USB controller hardware and which ConfigFS attributes it reads. I'll need to pay close attention to the data structures involved, like the gadget descriptor and any associated USB configuration objects. It's probably the core of the instantiation process for the USB gadget - connecting hardware to the higher-level ConfigFS configuration. Let's see how the drivers are set up.
|
| 82/4 |
2026/07/30 00:17 |
tool |
codesearch-definition-source |
0m
Args: map[ContextFile:drivers/usb/gadget/configfs.c Name:gadgets_make]
Results: map[SourceCode:1988: static struct config_group *gadgets_make(
1989: struct config_group *group,
1990: const char *name)
1991: {
1992: struct gadget_info *gi;
1993:
1994: gi = kzalloc_obj(*gi);
1995: if (!gi)
1996: return ERR_PTR(-ENOMEM);
1997:
1998: config_group_init_type_name(&gi->group, name, &gadget_root_type);
1999:
2000: config_group_init_type_name(&gi->functions_group, "functions",
2001: &functions_type);
2002: configfs_add_default_group(&gi->functions_group, &gi->group);
2003:
2004: config_group_init_type_name(&gi->configs_group, "configs",
2005: &config_desc_type);
2006: configfs_add_default_group(&gi->configs_group, &gi->group);
2007:
2008: config_group_init_type_name(&gi->strings_group, "strings",
2009: &gadget_language_strings_type);
2010: configfs_add_default_group(&gi->strings_group, &gi->group);
2011:
2012: config_group_init_type_name(&gi->os_desc_group, "os_desc",
2013: &os_desc_type);
2014: configfs_add_default_group(&gi->os_desc_group, &gi->group);
2015:
2016: config_group_init_type_name(&gi->webusb_group, "webusb",
2017: &webusb_type);
2018: configfs_add_default_group(&gi->webusb_group, &gi->group);
2019:
2020: gi->composite.bind = configfs_do_nothing;
2021: gi->composite.unbind = configfs_do_nothing;
2022: gi->composite.suspend = NULL;
2023: gi->composite.resume = NULL;
2024: gi->composite.max_speed = USB_SPEED_SUPER_PLUS;
2025:
2026: spin_lock_init(&gi->spinlock);
2027: mutex_init(&gi->lock);
2028: INIT_LIST_HEAD(&gi->string_list);
2029: INIT_LIST_HEAD(&gi->available_func);
2030:
2031: composite_init_dev(&gi->cdev);
2032: gi->cdev.desc.bLength = USB_DT_DEVICE_SIZE;
2033: gi->cdev.desc.bDescriptorType = USB_DT_DEVICE;
2034: gi->cdev.desc.bcdDevice = cpu_to_le16(get_default_bcdDevice());
2035:
2036: gi->composite.gadget_driver = configfs_driver_template;
2037:
2038: gi->composite.gadget_driver.driver.name = kasprintf(GFP_KERNEL,
2039: "configfs-gadget.%s", name);
2040: if (!gi->composite.gadget_driver.driver.name)
2041: goto err;
2042:
2043: gi->composite.gadget_driver.function = kstrdup(name, GFP_KERNEL);
2044: gi->composite.name = gi->composite.gadget_driver.function;
2045:
2046: if (!gi->composite.gadget_driver.function)
2047: goto out_free_driver_name;
2048:
2049: return &gi->group;
2050:
2051: out_free_driver_name:
2052: kfree(gi->composite.gadget_driver.driver.name);
2053: err:
2054: kfree(gi);
2055: return ERR_PTR(-ENOMEM);
2056: }
SourceFile:drivers/usb/gadget/configfs.c]
|
| 83/4 |
2026/07/30 00:17 |
llm |
codeexpert |
0m
Model:
Tokens:
input: 22496
output: 34
thoughts: 107
Thoughts: **Analyzing Potential Security Vulnerabilities in the USB Gadget Configuration**
Alright, let's break this down. My initial concern is the `gadgets_make` function β it's triggered by the creation of a directory under `/sys/kernel/config/usb_gadget`. This screams potential vulnerability to me. My immediate instinct is to dissect the permission handling within `gadgets_make` itself, and by extension, the broader ConfigFS infrastructure.
But before diving deep into `gadgets_make`, I need to establish a baseline. How does the Virtual Filesystem (VFS) handle permission checks for a `mkdir` operation *in general*? I need to understand the standard flow first. Let's trace it down. `mkdir` in VFS is ultimately handled by `vfs_mkdir`. Right, so I need to check `fs/namei.c` to understand the standard permission flow. From there, I can get a clear picture and better understand if ConfigFS introduces any deviations or oversights.
|
| 84/4 |
2026/07/30 00:17 |
tool |
codesearch-definition-source |
0m
Args: map[ContextFile:fs/namei.c Name:vfs_mkdir]
Results: map[SourceCode:5225: /**
5226: * vfs_mkdir - create directory returning correct dentry if possible
5227: * @idmap: idmap of the mount the inode was found from
5228: * @dir: inode of the parent directory
5229: * @dentry: dentry of the child directory
5230: * @mode: mode of the child directory
5231: * @delegated_inode: returns parent inode, if the inode is delegated.
5232: *
5233: * Create a directory.
5234: *
5235: * If the inode has been found through an idmapped mount the idmap of
5236: * the vfsmount must be passed through @idmap. This function will then take
5237: * care to map the inode according to @idmap before checking permissions.
5238: * On non-idmapped mounts or if permission checking is to be performed on the
5239: * raw inode simply pass @nop_mnt_idmap.
5240: *
5241: * In the event that the filesystem does not use the *@dentry but leaves it
5242: * negative or unhashes it and possibly splices a different one returning it,
5243: * the original dentry is dput() and the alternate is returned.
5244: *
5245: * In case of an error the dentry is dput() and an ERR_PTR() is returned.
5246: */
5247: struct dentry *vfs_mkdir(struct mnt_idmap *idmap, struct inode *dir,
5248: struct dentry *dentry, umode_t mode,
5249: struct delegated_inode *delegated_inode)
5250: {
5251: int error;
5252: unsigned max_links = dir->i_sb->s_max_links;
5253: struct dentry *de;
5254:
5255: error = may_create_dentry(idmap, dir, dentry);
5256: if (error)
5257: goto err;
5258:
5259: error = -EPERM;
5260: if (!dir->i_op->mkdir)
5261: goto err;
5262:
5263: mode = vfs_prepare_mode(idmap, dir, mode, S_IRWXUGO | S_ISVTX, 0);
5264: error = security_inode_mkdir(dir, dentry, mode);
5265: if (error)
5266: goto err;
5267:
5268: error = -EMLINK;
5269: if (max_links && dir->i_nlink >= max_links)
5270: goto err;
5271:
5272: error = try_break_deleg(dir, LEASE_BREAK_DIR_CREATE, delegated_inode);
5273: if (error)
5274: goto err;
5275:
5276: de = dir->i_op->mkdir(idmap, dir, dentry, mode);
5277: error = PTR_ERR(de);
5278: if (IS_ERR(de))
5279: goto err;
5280: if (de) {
5281: dput(dentry);
5282: dentry = de;
5283: }
5284: fsnotify_mkdir(dir, dentry);
5285: return dentry;
5286:
5287: err:
5288: end_creating(dentry);
5289: return ERR_PTR(error);
5290: }
SourceFile:fs/namei.c]
|
| 85/4 |
2026/07/30 00:17 |
llm |
codeexpert |
0m
Model:
Tokens:
input: 23897
output: 36
thoughts: 22
Thoughts: Here's my take on those thoughts, tailored for an expert audience:
**Diving into `may_create_dentry`**
Okay, so the code's pointing me towards `may_create_dentry` in `fs/namei.c`. Right, I remember that function. It's the gatekeeper, isn't it? The one that determines if a new dentry can actually be born. Let's see what I can recall... I suspect it's going to be a multi-faceted check. We're talking security, permissions, and possibly quota considerations all rolled into one. I'm mentally parsing the likely arguments - probably the parent directory's inode, the new dentry's name, the calling process's credentials, and a few flags indicating the nature of the request.
I'd expect it's going to check the parent directory's permissions first, usually involving an `inode_permission` call or a similar check with flags specifying `MAY_CREATE`. We're going to need to ensure that the user has the necessary rights in the *parent* to create *any* entry there. Next, there might be checks related to the directory's size limitations or quotas. Finally, the system might need to verify the filename itself. The kernel has to ensure it's not a reserved filename, and that it conforms to any file system-specific naming rules. I imagine we'll also see some checks related to the security module, SELinux, AppArmor, or something similar, likely calling functions to see if the security context of the operation is permitted. Ultimately, itβs all about maintaining file system integrity, adhering to established security policies, and ensuring that no unauthorized operations occur. It's a critical function; a lot depends on it being robust and correct.
|
| 86/4 |
2026/07/30 00:17 |
tool |
codesearch-definition-source |
0m
Args: map[ContextFile:fs/namei.c Name:may_create_dentry]
Results: map[SourceCode:3722: /* Check whether we can create an object with dentry child in directory
3723: * dir.
3724: * 1. We can't do it if child already exists (open has special treatment for
3725: * this case, but since we are inlined it's OK)
3726: * 2. We can't do it if dir is read-only (done in permission())
3727: * 3. We can't do it if the fs can't represent the fsuid or fsgid.
3728: * 4. We should have write and exec permissions on dir
3729: * 5. We can't do it if dir is immutable (done in permission())
3730: */
3731: int may_create_dentry(struct mnt_idmap *idmap,
3732: struct inode *dir, struct dentry *child)
3733: {
3734: audit_inode_child(dir, child, AUDIT_TYPE_CHILD_CREATE);
3735: if (child->d_inode)
3736: return -EEXIST;
3737: if (IS_DEADDIR(dir))
3738: return -ENOENT;
3739: if (!fsuidgid_has_mapping(dir->i_sb, idmap))
3740: return -EOVERFLOW;
3741:
3742: return inode_permission(idmap, dir, MAY_WRITE | MAY_EXEC);
3743: }
SourceFile:fs/namei.c]
|
| 87/4 |
2026/07/30 00:17 |
llm |
codeexpert |
0m
Model:
Tokens:
input: 24776
output: 33
thoughts: 50
Thoughts: Okay, here's my interpretation of that thought process, tailored for an expert audience:
**Diving Deep into Dentry Creation Permissions**
Right, so `may_create_dentry`... it's the gatekeeper, isn't it? My mind immediately jumps to that call to `inode_permission`. I know that function is the crux of access control for file operations in Linux. Specifically, we're checking for `MAY_WRITE | MAY_EXEC` against the directory inode. That makes perfect sense; creating a new dentry within a directory requires both write permission to modify the directory's content and execute permission which is often needed to traverse the parent directory.
Now, where does that permission check actually happen? No time to waste; let's drill down into `fs/namei.c` and take a good, hard look at the `inode_permission` function itself. I want to see how it's actually implemented. I'm especially interested in how it interacts with the ID map and how things might be handled in more complex scenarios like network filesystems or permission caching. Is it using ACLs? Capabilities? What kind of checks are done? Are there any potential gotchas or optimizations I should be aware of? This is where the rubber meets the road.
|
| 88/4 |
2026/07/30 00:18 |
tool |
codesearch-definition-source |
0m
Args: map[ContextFile:fs/namei.c Name:inode_permission]
Results: map[SourceCode: 616: /**
617: * inode_permission - Check for access rights to a given inode
618: * @idmap: idmap of the mount the inode was found from
619: * @inode: Inode to check permission on
620: * @mask: Right to check for (%MAY_READ, %MAY_WRITE, %MAY_EXEC)
621: *
622: * Check for read/write/execute permissions on an inode. We use fs[ug]id for
623: * this, letting us set arbitrary permissions for filesystem access without
624: * changing the "normal" UIDs which are used for other things.
625: *
626: * When checking for MAY_APPEND, MAY_WRITE must also be set in @mask.
627: */
628: int inode_permission(struct mnt_idmap *idmap,
629: struct inode *inode, int mask)
630: {
631: int retval;
632:
633: retval = sb_permission(inode->i_sb, inode, mask);
634: if (unlikely(retval))
635: return retval;
636:
637: if (mask & MAY_WRITE) {
638: /*
639: * Nobody gets write access to an immutable file.
640: */
641: if (unlikely(IS_IMMUTABLE(inode)))
642: return -EPERM;
643:
644: /*
645: * Updating mtime will likely cause i_uid and i_gid to be
646: * written back improperly if their true value is unknown
647: * to the vfs.
648: */
649: if (unlikely(HAS_UNMAPPED_ID(idmap, inode)))
650: return -EACCES;
651: }
652:
653: retval = do_inode_permission(idmap, inode, mask);
654: if (unlikely(retval))
655: return retval;
656:
657: retval = devcgroup_inode_permission(inode, mask);
658: if (unlikely(retval))
659: return retval;
660:
661: return security_inode_permission(inode, mask);
662: }
SourceFile:fs/namei.c]
|
| 89/4 |
2026/07/30 00:18 |
llm |
codeexpert |
0m
Model:
Tokens:
input: 25835
output: 35
thoughts: 21
Thoughts: Here's a summary of my thought process, as requested:
**Diving into `do_inode_permission`**
Okay, let's get down to brass tacks. I need to understand how the kernel handles inode permissions. My initial instinct is to go straight to the source, so I'm heading over to `fs/namei.c`. I know this is where the name lookup and permission checks often intersect, so `do_inode_permission` is a likely candidate for the core permission logic. I expect to find the meat and potatoes of how the kernel determines if a process has the right to access a given inode. I'm hoping to understand the interplay between user/group/other permissions, access control lists, capabilities, and any potential security module hooks. Let's see what we've got. I'm expecting this to be a crucial piece in the bigger puzzle.
|
| 90/4 |
2026/07/30 00:18 |
tool |
codesearch-definition-source |
0m
Args: map[ContextFile:fs/namei.c Name:do_inode_permission]
Results: map[SourceCode: 567: /**
568: * do_inode_permission - UNIX permission checking
569: * @idmap: idmap of the mount the inode was found from
570: * @inode: inode to check permissions on
571: * @mask: right to check for (%MAY_READ, %MAY_WRITE, %MAY_EXEC ...)
572: *
573: * We _really_ want to just do "generic_permission()" without
574: * even looking at the inode->i_op values. So we keep a cache
575: * flag in inode->i_opflags, that says "this has not special
576: * permission function, use the fast case".
577: */
578: static inline int do_inode_permission(struct mnt_idmap *idmap,
579: struct inode *inode, int mask)
580: {
581: if (unlikely(!(inode->i_opflags & IOP_FASTPERM))) {
582: if (likely(inode->i_op->permission))
583: return inode->i_op->permission(idmap, inode, mask);
584:
585: /* This gets set once for the inode lifetime */
586: spin_lock(&inode->i_lock);
587: inode->i_opflags |= IOP_FASTPERM;
588: spin_unlock(&inode->i_lock);
589: }
590: return generic_permission(idmap, inode, mask);
591: }
SourceFile:fs/namei.c]
|
| 91/4 |
2026/07/30 00:18 |
llm |
codeexpert |
0m
Model:
Tokens:
input: 26537
output: 31
thoughts: 35
Thoughts: Okay, here's my take on those thoughts, tailored for an expert audience:
**Investigating configfs Permissions**
Right, so I'm wondering about how `configfs` handles permissions. Does it use some custom `permission` operation, or does it lean on the standard VFS mechanisms? My immediate instinct is to dig into the inode operations. Let's see... I'll jump right into `fs/configfs/inode.c`, specifically `configfs_inode_operations`. That should be the best place to start looking for the core permission logic. I expect to find either a custom implementation or clues about how it integrates with the generic VFS permission checks. Time to see what's actually going on in there!
|
| 92/4 |
2026/07/30 00:18 |
tool |
grepper |
0m
Args: map[Expression:configfs_inode_operations PathPrefix:fs/configfs/]
Results: map[Output:fs/configfs/inode.c=28=static struct lock_class_key default_group_class[MAX_LOCK_DEPTH];
--
fs/configfs/inode.c-30-
fs/configfs/inode.c:31:static const struct inode_operations configfs_inode_operations ={
fs/configfs/inode.c-32- .setattr = configfs_setattr,
--
fs/configfs/inode.c=104=struct inode *configfs_new_inode(umode_t mode, struct configfs_dirent *sd,
--
fs/configfs/inode.c-110- inode->i_mapping->a_ops = &ram_aops;
fs/configfs/inode.c:111: inode->i_op = &configfs_inode_operations;
fs/configfs/inode.c-112-
]
|
| 93/4 |
2026/07/30 00:18 |
llm |
codeexpert |
0m
Model:
Tokens:
input: 26984
output: 35
thoughts: 23
Thoughts: Here's a first-person summary, tailored for an expert audience, of the thought process you described:
**Diving into configfs' Inode Operations**
Alright, let's get down to brass tacks. I'm going to take a look at `configfs_inode_operations`. I need to understand how the filesystem handles inode operations within the `configfs` framework. Time to dive into the core implementation in `fs/configfs/inode.c`. I'm expecting to see the standard inode operation hooks defined here β how the filesystem interacts with the VFS layer for tasks like creating, deleting, and managing files and directories *within* the configfs structure. This is where the magic happens, connecting the kernel's internal representation of this virtual filesystem to the actual configuration data. I'm particularly interested in how they've handled security, permission checks, and the lifecycle management of these dynamically created and removed "files."
|
| 94/4 |
2026/07/30 00:18 |
tool |
read-file |
0m
Args: map[File:fs/configfs/inode.c FirstLine:25 LineCount:15]
Results: map[Contents: 25: #include "configfs_internal.h"
26:
27: #ifdef CONFIG_LOCKDEP
28: static struct lock_class_key default_group_class[MAX_LOCK_DEPTH];
29: #endif
30:
31: static const struct inode_operations configfs_inode_operations ={
32: .setattr = configfs_setattr,
33: };
34:
35: int configfs_setattr(struct mnt_idmap *idmap, struct dentry *dentry,
36: struct iattr *iattr)
37: {
38: struct inode * inode = d_inode(dentry);
39: struct configfs_dirent * sd = dentry->d_fsdata;
]
|
| 95/4 |
2026/07/30 00:18 |
llm |
codeexpert |
0m
Model:
Tokens:
input: 27466
output: 33
thoughts: 533
Thoughts: **Analyzing ConfigFS Permissions**
Okay, so I'm looking at how permissions are handled within `configfs`. It seems neither `configfs_inode_operations` nor `configfs_dir_inode_operations` implement a custom `.permission` callback. That means the standard `generic_permission` function is being used for access control. Right, let's see what that entails...
`generic_permission` enforces the usual UNIX-style permissions: it compares the user's `fsuid` and `fsgid` against the inode's `i_uid` and `i_gid`, respectively, and considers supplementary groups. If those checks fail, it falls back to "other" permissions. Crucially, it also checks for `CAP_DAC_OVERRIDE` or `CAP_DAC_READ_SEARCH` within the user's user namespace.
Now, the real question: what are the default ownership and permissions for something like `/sys/kernel/config/usb_gadget`? I remember from before that all configfs inodes are initialized with `i_uid = 0` and `i_gid = 0` (unless modified by `chown`/`chmod`). So, by default, it's owned by root:root. And given it's a directory, the default mode is `0755`.
This means a typical unprivileged user (with a non-zero `fsuid` and no initial namespace `CAP_DAC_OVERRIDE`) will fall into the "other" category. For a directory with `0755` permissions, "other" has read and execute (`r-x`) access. To create a directory, `MAY_WRITE` is required, and "other" doesn't have write permissions. So, an unprivileged user *can't* create a directory under `/sys/kernel/config/usb_gadget`!
But what about user namespaces? If a user is root inside a user namespace and has `CAP_DAC_OVERRIDE` within that namespace, can they create a directory in configfs? This is where that capability check in `generic_permission` comes into play. I'll need to double-check the implementation of `generic_permission` in `fs/namei.c` to be certain.
|
| 96/4 |
2026/07/30 00:18 |
tool |
codesearch-definition-source |
0m
Args: map[ContextFile:fs/namei.c Name:generic_permission]
Results: map[SourceCode: 499: /**
500: * generic_permission - check for access rights on a Posix-like filesystem
501: * @idmap: idmap of the mount the inode was found from
502: * @inode: inode to check access rights for
503: * @mask: right to check for (%MAY_READ, %MAY_WRITE, %MAY_EXEC,
504: * %MAY_NOT_BLOCK ...)
505: *
506: * Used to check for read/write/execute permissions on a file.
507: * We use "fsuid" for this, letting us set arbitrary permissions
508: * for filesystem access without changing the "normal" uids which
509: * are used for other things.
510: *
511: * generic_permission is rcu-walk aware. It returns -ECHILD in case an rcu-walk
512: * request cannot be satisfied (eg. requires blocking or too much complexity).
513: * It would then be called again in ref-walk mode.
514: *
515: * If the inode has been found through an idmapped mount the idmap of
516: * the vfsmount must be passed through @idmap. This function will then take
517: * care to map the inode according to @idmap before checking permissions.
518: * On non-idmapped mounts or if permission checking is to be performed on the
519: * raw inode simply pass @nop_mnt_idmap.
520: */
521: int generic_permission(struct mnt_idmap *idmap, struct inode *inode,
522: int mask)
523: {
524: int ret;
525:
526: /*
527: * Do the basic permission checks.
528: */
529: ret = acl_permission_check(idmap, inode, mask);
530: if (ret != -EACCES)
531: return ret;
532:
533: if (S_ISDIR(inode->i_mode)) {
534: /* DACs are overridable for directories */
535: if (!(mask & MAY_WRITE))
536: if (capable_wrt_inode_uidgid(idmap, inode,
537: CAP_DAC_READ_SEARCH))
538: return 0;
539: if (capable_wrt_inode_uidgid(idmap, inode,
540: CAP_DAC_OVERRIDE))
541: return 0;
542: return -EACCES;
543: }
544:
545: /*
546: * Searching includes executable on directories, else just read.
547: */
548: mask &= MAY_READ | MAY_WRITE | MAY_EXEC;
549: if (mask == MAY_READ)
550: if (capable_wrt_inode_uidgid(idmap, inode,
551: CAP_DAC_READ_SEARCH))
552: return 0;
553: /*
554: * Read/write DACs are always overridable.
555: * Executable DACs are overridable when there is
556: * at least one exec bit set.
557: */
558: if (!(mask & MAY_EXEC) || (inode->i_mode & S_IXUGO))
559: if (capable_wrt_inode_uidgid(idmap, inode,
560: CAP_DAC_OVERRIDE))
561: return 0;
562:
563: return -EACCES;
564: }
SourceFile:fs/namei.c]
|
| 97/4 |
2026/07/30 00:18 |
llm |
codeexpert |
0m
Model:
Tokens:
input: 29597
output: 39
thoughts: 62
Thoughts: **Investigating CAP_DAC_OVERRIDE and capable_wrt_inode_uidgid**
Right, so `capable_wrt_inode_uidgid` is the function that's being called when checking for `CAP_DAC_OVERRIDE`. Interesting! I'm familiar with capability checks, but I don't recall this specific function off the top of my head. Time to delve into its implementation. Let's see... I'll just use my go-to, `codesearch-definition-source`, to quickly find its definition and source. That should give me a good starting point for understanding how `CAP_DAC_OVERRIDE` is evaluated in this context. I'm keen to understand the relationship between the capability check and the inode UID/GID, as the function name suggests a relevant link.
|
| 98/4 |
2026/07/30 00:18 |
tool |
codesearch-definition-source |
0m
Args: map[ContextFile:fs/namei.c Name:capable_wrt_inode_uidgid]
Results: map[SourceCode: 463: /**
464: * capable_wrt_inode_uidgid - Check nsown_capable and uid and gid mapped
465: * @idmap: idmap of the mount @inode was found from
466: * @inode: The inode in question
467: * @cap: The capability in question
468: *
469: * Return true if the current task has the given capability targeted at
470: * its own user namespace and that the given inode's uid and gid are
471: * mapped into the current user namespace.
472: */
473: bool capable_wrt_inode_uidgid(struct mnt_idmap *idmap,
474: const struct inode *inode, int cap)
475: {
476: struct user_namespace *ns = current_user_ns();
477:
478: return ns_capable(ns, cap) &&
479: privileged_wrt_inode_uidgid(ns, idmap, inode);
480: }
SourceFile:kernel/capability.c]
|
| 99/4 |
2026/07/30 00:18 |
llm |
codeexpert |
0m
Model:
Tokens:
input: 30193
output: 38
thoughts: 24
Thoughts: Here's my take on those thoughts, tailored for an expert audience:
**Diving into `privileged_wrt_inode_uidgid`**
Okay, so I'm thinking about `privileged_wrt_inode_uidgid` in `kernel/capability.c`. Time to refresh my memory on the specifics of how the kernel handles these privilege checks. I know this function is critical for how the kernel decides whether a process has sufficient permissions to bypass file system access controls based on UID/GID. I want to quickly evaluate the implementation details. I need to understand how it interacts with capabilities, how it handles things like file flags, and any potential security implications, especially in the context of recent vulnerabilities or hardening efforts. Specifically Iβll be paying close attention to any possible race conditions, use of uninitialized data, or integer overflows. I also need to consider its impact on different file systems and whether any corner cases exist in its implementation.
|
| 100/4 |
2026/07/30 00:18 |
tool |
codesearch-definition-source |
0m
Args: map[ContextFile:kernel/capability.c Name:privileged_wrt_inode_uidgid]
Results: map[SourceCode: 447: /**
448: * privileged_wrt_inode_uidgid - Do capabilities in the namespace work over the inode?
449: * @ns: The user namespace in question
450: * @idmap: idmap of the mount @inode was found from
451: * @inode: The inode in question
452: *
453: * Return true if the inode uid and gid are within the namespace.
454: */
455: bool privileged_wrt_inode_uidgid(struct user_namespace *ns,
456: struct mnt_idmap *idmap,
457: const struct inode *inode)
458: {
459: return vfsuid_has_mapping(ns, i_uid_into_vfsuid(idmap, inode)) &&
460: vfsgid_has_mapping(ns, i_gid_into_vfsgid(idmap, inode));
461: }
SourceFile:kernel/capability.c]
|
| 101/4 |
2026/07/30 00:18 |
llm |
codeexpert |
0m
Model:
Tokens:
input: 30743
output: 37
thoughts: 630
Thoughts: **Analyzing User Namespace Security with ConfigFS**
Okay, here's the breakdown. I was looking at `privileged_wrt_inode_uidgid` and it checks if an inode's UID and GID are mapped into the current user namespace. That's the key.
Now, if an unprivileged user creates a user namespace, can they *really* map UID 0 (root) into that new namespace? Normally, no, right? They'd need to be root in the *parent* namespace or have a `/etc/subuid` setup which is extremely rare (and bad security practice).
Even *if* they somehow managed to map UID 0 in, they'd still need control over UID 0 in the parent namespace to make that mapping happen in the first place. An unprivileged user is limited to mapping their *own* UID from the parent (say, 1000) into the child namespace (as UID 0 *inside* that namespace, which gives them root inside, just not outside).
But the inode's UID we're looking at is `GLOBAL_ROOT_UID`, which is 0. So, if the user namespace only maps UID 1000 from the parent to UID 0 in the child, then `GLOBAL_ROOT_UID` (0) is *not* mapped. Therefore, `vfsuid_has_mapping` will return false! And, as a result, `privileged_wrt_inode_uidgid` is going to return `false`, which then causes `capable_wrt_inode_uidgid` to also return `false`.
So, the conclusion is: root inside an unprivileged user namespace *doesn't* get `CAP_DAC_OVERRIDE` over configfs inodes (which are owned by `GLOBAL_ROOT_UID`/`GLOBAL_ROOT_GID`). This is how it should be; it's a standard security property of the kernel, capabilities within a user namespace don't grant power over resources (files, directories) owned by UIDs/GIDs that aren't actually mapped into the namespace.
Okay, is there another angle to consider? Could permissions of something like `/sys/kernel/config/usb_gadget` be changed? An unprivileged user can't change permissions with `chmod` or `chown` because they don't have write access or ownership.
But what if a privileged process (root in the initial namespace) *explicitly* changes the owner to the unprivileged user? Can that work? We have to check if `chown` and `chmod` are even supported on configfs. Looks like `configfs_setattr` is defined, so, we can look at that to see if changing UID/GID/mode is allowed.
|
| 102/4 |
2026/07/30 00:18 |
tool |
codesearch-definition-source |
0m
Args: map[ContextFile:fs/configfs/inode.c Name:configfs_setattr]
Results: map[SourceCode: 35: int configfs_setattr(struct mnt_idmap *idmap, struct dentry *dentry,
36: struct iattr *iattr)
37: {
38: struct inode * inode = d_inode(dentry);
39: struct configfs_dirent * sd = dentry->d_fsdata;
40: struct iattr * sd_iattr;
41: unsigned int ia_valid = iattr->ia_valid;
42: int error;
43:
44: if (!sd)
45: return -EINVAL;
46:
47: sd_iattr = sd->s_iattr;
48: if (!sd_iattr) {
49: /* setting attributes for the first time, allocate now */
50: sd_iattr = kzalloc_obj(struct iattr);
51: if (!sd_iattr)
52: return -ENOMEM;
53: /* assign default attributes */
54: sd_iattr->ia_mode = sd->s_mode;
55: sd_iattr->ia_uid = GLOBAL_ROOT_UID;
56: sd_iattr->ia_gid = GLOBAL_ROOT_GID;
57: sd_iattr->ia_atime = sd_iattr->ia_mtime =
58: sd_iattr->ia_ctime = current_time(inode);
59: sd->s_iattr = sd_iattr;
60: }
61: /* attributes were changed atleast once in past */
62:
63: error = simple_setattr(idmap, dentry, iattr);
64: if (error)
65: return error;
66:
67: if (ia_valid & ATTR_UID)
68: sd_iattr->ia_uid = iattr->ia_uid;
69: if (ia_valid & ATTR_GID)
70: sd_iattr->ia_gid = iattr->ia_gid;
71: if (ia_valid & ATTR_ATIME)
72: sd_iattr->ia_atime = iattr->ia_atime;
73: if (ia_valid & ATTR_MTIME)
74: sd_iattr->ia_mtime = iattr->ia_mtime;
75: if (ia_valid & ATTR_CTIME)
76: sd_iattr->ia_ctime = iattr->ia_ctime;
77: if (ia_valid & ATTR_MODE) {
78: umode_t mode = iattr->ia_mode;
79:
80: if (!in_group_p(inode->i_gid) && !capable(CAP_FSETID))
81: mode &= ~S_ISGID;
82: sd_iattr->ia_mode = sd->s_mode = mode;
83: }
84:
85: return error;
86: }
SourceFile:fs/configfs/inode.c]
|
| 103/4 |
2026/07/30 00:18 |
llm |
codeexpert |
0m
Model:
Tokens:
input: 32847
output: 38
thoughts: 733
Thoughts: **Analyzing ConfigFS and User Namespaces for Potential Vulnerabilities**
Okay, so `configfs_setattr` *does* allow UID, GID, and mode changes. That's the starting point. My thinking is, if a privileged process, like a system daemon or root in the initial namespace, deliberately adjusts the permissions of `/sys/kernel/config/usb_gadget` β or any subdirectory under it, for that matter β allowing an unprivileged user access, then that unprivileged user *could* indeed `mkdir` inside it.
The next thing I'm wondering is, how common is this really? Are we likely to see systems where USB gadget managers, like `gt` or custom scripts, manipulate permissions on `/sys/kernel/config/usb_gadget` to give a specific user or group (say, a `usb` group) the ability to configure USB gadgets? It seems plausible. If that's the case, then an unprivileged user *in* that group, or who owns that directory, could definitely trigger the `mkdir` vulnerability in configfs.
Now, let's broaden the scope. What about user namespaces? This is where it gets interesting. Can someone create a user namespace and trigger `mkdir` within that context?
Here's the key scenario I'm mapping out: If `/sys/kernel/config/usb_gadget` is owned by UID 1000 in the initial namespace, and the unprivileged user maps UID 1000 to UID 0 (or some other UID) inside their newly created user namespace, a few things fall into place: 1) the inode's UID remains 1000, 2) the user namespace performs a UID mapping. 3) this means `privileged_wrt_inode_uidgid` will evaluate to `true` (important!). 4) Consequently, a process *inside* that user namespace with `CAP_DAC_OVERRIDE` β which root naturally possesses β should be able to write to `/sys/kernel/config/usb_gadget` and thus be able to `mkdir`.
Wait, before jumping to conclusions, let's step through this with meticulous care. If we have the scenario where `/sys/kernel/config/usb_gadget` has an ownership of UID 1000 in the initial namespace. Let's say, an unprivileged user (UID 1000) creates a user namespace where UID 1000 is mapped to UID 0. Within this user namespace, the user is root (UID 0), and has all capabilities, including `CAP_DAC_OVERRIDE`. Now, they are trying to do `mkdir` in `/sys/kernel/config/usb_gadget`.
The question is, how does `may_create_dentry` function, and what gets checked?
```c
int may_create_dentry(struct mnt_idmap *idmap,
struct inode *dir, struct dentry *child)
{
audit_inode_child(dir, child, AUDIT_TYPE_CHILD_CREATE);
if (child->d_inode)
return -EEXIST;
if (IS_DEADDIR(dir))
return -ENOENT;
if (!fsuidgid_has_mapping(dir->i_sb, idmap))
return -EOVERFLOW;
return inode_permission(idmap, dir, MAY_WRITE | MAY_EXEC);
}
```
Ah! The critical check is `fsuidgid_has_mapping(dir->i_sb, idmap)`. This is key. Let's see what that function does. It is looking at the relationship between the filesystem's superblock and the idmap.
|
| 104/4 |
2026/07/30 00:18 |
tool |
codesearch-definition-source |
0m
Args: map[ContextFile:fs/namei.c Name:fsuidgid_has_mapping]
Results: map[SourceCode:1566: /**
1567: * fsuidgid_has_mapping() - check whether caller's fsuid/fsgid is mapped
1568: * @sb: the superblock we want a mapping in
1569: * @idmap: idmap of the relevant mount
1570: *
1571: * Check whether the caller's fsuid and fsgid have a valid mapping in the
1572: * s_user_ns of the superblock @sb. If the caller is on an idmapped mount map
1573: * the caller's fsuid and fsgid according to the @idmap first.
1574: *
1575: * Return: true if fsuid and fsgid is mapped, false if not.
1576: */
1577: static inline bool fsuidgid_has_mapping(struct super_block *sb,
1578: struct mnt_idmap *idmap)
1579: {
1580: struct user_namespace *fs_userns = sb->s_user_ns;
1581: kuid_t kuid;
1582: kgid_t kgid;
1583:
1584: kuid = mapped_fsuid(idmap, fs_userns);
1585: if (!uid_valid(kuid))
1586: return false;
1587: kgid = mapped_fsgid(idmap, fs_userns);
1588: if (!gid_valid(kgid))
1589: return false;
1590: return kuid_has_mapping(fs_userns, kuid) &&
1591: kgid_has_mapping(fs_userns, kgid);
1592: }
SourceFile:include/linux/fs.h]
|
| 105/4 |
2026/07/30 00:18 |
llm |
codeexpert |
0m
Model:
Tokens:
input: 34939
output: 35
thoughts: 410
Thoughts: **Analyzing Permissions in Configfs**
Okay, let's break down this `fsuidgid_has_mapping` function call. First, we're looking at `fs_userns` which is just `sb->s_user_ns`. Now, what is `sb->s_user_ns` for configfs? Right, since configfs is mounted in the initial user namespace, that resolves to `init_user_ns`.
Next, we see `kuid = mapped_fsuid(idmap, fs_userns)`. Because configfs isn't an idmapped mount, `idmap` is effectively `nop_mnt_idmap`. Therefore, `mapped_fsuid` should just return the caller's `fsuid` in the initial user namespace, which, by definition, is `current_fsuid()`. Hold on... `current_fsuid()` returns a `kuid_t`, and that's *always* in the initial user namespace.
Wait, is `current_fsuid()` even *valid* in `init_user_ns`? Yes, any valid `kuid_t` *must* have a mapping in `init_user_ns`. Thus `kuid_has_mapping(init_user_ns, kuid)` will always be true for any valid `kuid`. So, *that* means `fsuidgid_has_mapping` will simply return `true`. Excellent.
Now, let's move on to `inode_permission(idmap, dir, MAY_WRITE | MAY_EXEC)`. This function is going to call `do_inode_permission(idmap, dir, mask)`, and that, in turn, calls `generic_permission(idmap, dir, mask)`. Within `generic_permission`, there's this `acl_permission_check(idmap, inode, mask)` call. I need to understand what this `acl_permission_check` is actually doing. Let me fire up the code search and find the source code for that function.
|
| 106/4 |
2026/07/30 00:19 |
tool |
codesearch-definition-source |
0m
Args: map[ContextFile:fs/namei.c Name:acl_permission_check]
Results: map[SourceCode: 422: /**
423: * acl_permission_check - perform basic UNIX permission checking
424: * @idmap: idmap of the mount the inode was found from
425: * @inode: inode to check permissions on
426: * @mask: right to check for (%MAY_READ, %MAY_WRITE, %MAY_EXEC ...)
427: *
428: * This function performs the basic UNIX permission checking. Since this
429: * function may retrieve POSIX acls it needs to know whether it is called from a
430: * blocking or non-blocking context and thus cares about the MAY_NOT_BLOCK bit.
431: *
432: * If the inode has been found through an idmapped mount the idmap of
433: * the vfsmount must be passed through @idmap. This function will then take
434: * care to map the inode according to @idmap before checking permissions.
435: * On non-idmapped mounts or if permission checking is to be performed on the
436: * raw inode simply pass @nop_mnt_idmap.
437: */
438: static int acl_permission_check(struct mnt_idmap *idmap,
439: struct inode *inode, int mask)
440: {
441: unsigned int mode = inode->i_mode;
442: vfsuid_t vfsuid;
443:
444: /*
445: * Common cheap case: everybody has the requested
446: * rights, and there are no ACLs to check. No need
447: * to do any owner/group checks in that case.
448: *
449: * - 'mask&7' is the requested permission bit set
450: * - multiplying by 0111 spreads them out to all of ugo
451: * - '& ~mode' looks for missing inode permission bits
452: * - the '!' is for "no missing permissions"
453: *
454: * After that, we just need to check that there are no
455: * ACL's on the inode - do the 'IS_POSIXACL()' check last
456: * because it will dereference the ->i_sb pointer and we
457: * want to avoid that if at all possible.
458: */
459: if (!((mask & 7) * 0111 & ~mode)) {
460: if (no_acl_inode(inode))
461: return 0;
462: if (!IS_POSIXACL(inode))
463: return 0;
464: }
465:
466: /* Are we the owner? If so, ACL's don't matter */
467: vfsuid = i_uid_into_vfsuid(idmap, inode);
468: if (likely(vfsuid_eq_kuid(vfsuid, current_fsuid()))) {
469: mask &= 7;
470: mode >>= 6;
471: return (mask & ~mode) ? -EACCES : 0;
472: }
473:
474: /* Do we have ACL's? */
475: if (IS_POSIXACL(inode) && (mode & S_IRWXG)) {
476: int error = check_acl(idmap, inode, mask);
477: if (error != -EAGAIN)
478: return error;
479: }
480:
481: /* Only RWX matters for group/other mode bits */
482: mask &= 7;
483:
484: /*
485: * Are the group permissions different from
486: * the other permissions in the bits we care
487: * about? Need to check group ownership if so.
488: */
489: if (mask & (mode ^ (mode >> 3))) {
490: vfsgid_t vfsgid = i_gid_into_vfsgid(idmap, inode);
491: if (vfsgid_in_group_p(vfsgid))
492: mode >>= 3;
493: }
494:
495: /* Bits in 'mode' clear that we require? */
496: return (mask & ~mode) ? -EACCES : 0;
497: }
SourceFile:fs/namei.c]
|
| 107/4 |
2026/07/30 00:19 |
llm |
codeexpert |
0m
Model:
Tokens:
input: 37107
output: 21
thoughts: 200
Thoughts: **Tracing `acl_permission_check` for an Unprivileged User**
Okay, so I'm diving into this `acl_permission_check` function. The scenario is pretty straightforward: an unprivileged user, UID 1000, owns the `/sys/kernel/config/usb_gadget` file, meaning the `inode->i_uid` is 1000. Let's see how permissions are evaluated.
First, we hit `vfsuid` which, in this context, translates to `i_uid_into_vfsuid(idmap, inode)`. Now, the crucial piece is that `idmap` is the `nop_mnt_idmap`. So, unsurprisingly, `vfsuid` ends up being 1000. Makes sense, so far so good.
Next, we look at `current_fsuid()`, and it also returns 1000. This tracks the user's effective file system UID. Wait a second, this is where it gets interesting... I need to clarify something. Is `current_fsuid()` anchored to the initial namespace or to the current namespace? That could have big implications for how permissions are assessed. I need to make sure I'm crystal clear on this! Let's check `current_fsuid()`'s implementation. I'll need to use `codesearch-definition-source` or `grepper` to get to the bottom of this.
|
| 108/4 |
2026/07/30 00:19 |
tool |
grepper |
0m
Args: map[Expression:#define current_fsuid]
Results: map[Output:include/linux/cred.h=291=DEFINE_FREE(put_cred, struct cred *, if (!IS_ERR_OR_NULL(_T)) put_cred(_T))
--
include/linux/cred.h-387-#define current_sgid() (current_cred_xxx(sgid))
include/linux/cred.h:388:#define current_fsuid() (current_cred_xxx(fsuid))
include/linux/cred.h-389-#define current_fsgid() (current_cred_xxx(fsgid))
--
include/linux/cred.h=414=do { \
--
include/linux/cred.h-420-
include/linux/cred.h:421:#define current_fsuid_fsgid(_fsuid, _fsgid) \
include/linux/cred.h-422-do { \
]
|
| 109/4 |
2026/07/30 00:19 |
llm |
codeexpert |
0m
Model:
Tokens:
input: 37880
output: 32
thoughts: 20
Thoughts: Okay, here's my take on those thoughts, tailored for an expert audience:
**Diving into Credential Structure**
Alright, before I go any further down this rabbit hole, I need to get a clear picture of the `struct cred` layout. Specifically, I want to make *damn* sure I understand the precise type of `fsuid` within that structure. A basic understanding of the layout isn't enough; I need to know the fundamental datatype. Is it an integer type like `uid_t`, or something more involved? I'll need to check the kernel headers β probably the relevant architecture-specific ones too, just to be thorough. Variations exist, and I don't want to make assumptions that could lead to subtle, hard-to-debug bugs later on, especially if security is involved. This initial type check is critical; it informs everything that follows.
|
| 110/4 |
2026/07/30 00:19 |
tool |
codesearch-struct-layout |
0m
Args: map[ContextFile:include/linux/cred.h Name:cred]
Results: map[Fields:[map[Name:usage OffsetBits:0 SizeBits:64] map[Name:uid OffsetBits:64 SizeBits:32] map[Name:gid OffsetBits:96 SizeBits:32] map[Name:suid OffsetBits:128 SizeBits:32] map[Name:sgid OffsetBits:160 SizeBits:32] map[Name:euid OffsetBits:192 SizeBits:32] map[Name:egid OffsetBits:224 SizeBits:32] map[Name:fsuid OffsetBits:256 SizeBits:32] map[Name:fsgid OffsetBits:288 SizeBits:32] map[Name:securebits OffsetBits:320 SizeBits:32] map[Name:cap_inheritable OffsetBits:384 SizeBits:64] map[Name:cap_permitted OffsetBits:448 SizeBits:64] map[Name:cap_effective OffsetBits:512 SizeBits:64] map[Name:cap_bset OffsetBits:576 SizeBits:64] map[Name:cap_ambient OffsetBits:640 SizeBits:64] map[Name:jit_keyring OffsetBits:704 SizeBits:8] map[Name:session_keyring OffsetBits:768 SizeBits:64] map[Name:process_keyring OffsetBits:832 SizeBits:64] map[Name:thread_keyring OffsetBits:896 SizeBits:64] map[Name:request_key_auth OffsetBits:960 SizeBits:64] map[Name:security OffsetBits:1024 SizeBits:64] map[Name:user OffsetBits:1088 SizeBits:64] map[Name:user_ns OffsetBits:1152 SizeBits:64] map[Name:ucounts OffsetBits:1216 SizeBits:64] map[Name:group_info OffsetBits:1280 SizeBits:64] map[Name: OffsetBits:1344 SizeBits:128]]]
|
| 111/4 |
2026/07/30 00:19 |
llm |
codeexpert |
0m
Model:
Tokens:
input: 38669
output: 28
thoughts: 765
Thoughts: **Analyzing ConfigFS `mkdir` Vulnerabilities**
Okay, let's break this down systematically. So, `fsuid` is a `kuid_t`, a 32-bit kernel-internal UID. That's the UID in the initial user namespace, got it. `current_fsuid()` gives me the process's `kuid_t`, which is the UID in that initial namespace. Now, the key is the user namespace context. If my process is *inside* a user namespace, and UID 1000 in the initial namespace maps to UID 0 within *my* namespace, the `fsuid` stored in `current->cred->fsuid` is *still* `kuid_t` representing 1000! So `current_fsuid()` returns 1000. Therefore, `vfsuid_eq_kuid(vfsuid, current_fsuid())` *will* be `true` if the filesystem's UID matches that 1000. So the process, even though it *looks* like root inside its namespace, is *still* recognized as the owner of the directory (if it's owned by 1000 in the initial namespace), and the process can then write to it.
Even if I'm *not* the owner, if I've got `CAP_DAC_OVERRIDE` inside *my* user namespace, and the directory is owned by UID 1000 (which is mapped to a different UID within my user namespace), then `capable_wrt_inode_uidgid(idmap, inode, CAP_DAC_OVERRIDE)` returns `true` because of two things: first, `ns_capable(ns, CAP_DAC_OVERRIDE)` is true, since I'm root within my namespace. Second, `privileged_wrt_inode_uidgid(ns, idmap, inode)` is true since the inode's UID (1000 in the initial namespace) is mapped. So, yes, if `/sys/kernel/config/usb_gadget` is owned by UID 1000, a non-privileged user (UID 1000) or a user namespace created by that user *can* write to it and trigger `mkdir`. Good.
But wait, are there other scenarios? What about broader permissions? What if `/sys/kernel/config/usb_gadget` is owned by root (0:0) *but* has permissions 0777? Then "other" has write and execute permissions. Any unprivileged user, even in the initial namespace, can then write to it and perform `mkdir`! Okay, that's another important case.
Now, the real question: are there other avenues? Could other kernel subsystems or drivers somehow expose an interface (char device, sysfs file, netlink socket, or ioctl) that, when called by an unprivileged user, triggers a `mkdir` call on the kernel side? Is there an unexpected entry point? That's the core of the problem: "Are there any scenarios where an unprivileged user or a user namespace can trigger mkdir in configfs?" I need to look for code patterns; I'm going to search for `configfs_register_subsystem`, `config_group_init`, `configfs_mkdir`, `vfs_mkdir`, `kern_path`, or similar functions throughout the kernel source. The code will tell me. Then, I need to consider what kernel threads or workqueues might call `mkdir`. I'll search for references to `vfs_mkdir`, and that should point me in the right direction.
|
| 112/4 |
2026/07/30 00:19 |
tool |
codesearch-find-references |
0m
Args: map[IncludeSnippetLines:1 Name:vfs_mkdir]
Results: map[References:[map[ReferenceKind:calls ReferencingEntityKind:function ReferencingEntityName:cachefiles_get_directory SourceFile:fs/cachefiles/namei.c SourceLine:132 SourceSnippet: 131: if (ret == 0) {
132: subdir = vfs_mkdir(&nop_mnt_idmap, d_inode(dir), subdir, 0700, NULL);
133: if (IS_ERR(subdir))
] map[ReferenceKind:calls ReferencingEntityKind:function ReferencingEntityName:dev_mkdir SourceFile:drivers/base/devtmpfs.c SourceLine:182 SourceSnippet: 181:
182: dentry = vfs_mkdir(&nop_mnt_idmap, d_inode(path.dentry), dentry, mode, NULL);
183: if (!IS_ERR(dentry))
] map[ReferenceKind:calls ReferencingEntityKind:function ReferencingEntityName:ecryptfs_mkdir SourceFile:fs/ecryptfs/inode.c SourceLine:519 SourceSnippet: 518: lower_dir = lower_dir_dentry->d_inode;
519: lower_dentry = vfs_mkdir(&nop_mnt_idmap, lower_dir,
520: lower_dentry, mode, NULL);
] map[ReferenceKind:calls ReferencingEntityKind:function ReferencingEntityName:filename_mkdirat SourceFile:fs/namei.c SourceLine:5309 SourceSnippet:5308: if (!error) {
5309: dentry = vfs_mkdir(mnt_idmap(path.mnt), path.dentry->d_inode,
5310: dentry, mode, &delegated_inode);
] map[ReferenceKind:calls ReferencingEntityKind:function ReferencingEntityName:ksmbd_vfs_mkdir SourceFile:fs/smb/server/vfs.c SourceLine:203 SourceSnippet: 202: d = dentry;
203: dentry = vfs_mkdir(idmap, d_inode(path.dentry), dentry, mode, NULL);
204: if (IS_ERR(dentry))
] map[ReferenceKind:calls ReferencingEntityKind:function ReferencingEntityName:nfsd_create_locked SourceFile:fs/nfsd/vfs.c SourceLine:1823 SourceSnippet:1822: case S_IFDIR:
1823: dchild = vfs_mkdir(&nop_mnt_idmap, dirp, dchild, iap->ia_mode, NULL);
1824: if (IS_ERR(dchild)) {
] map[ReferenceKind:calls ReferencingEntityKind:function ReferencingEntityName:ovl_do_mkdir SourceFile:fs/overlayfs/overlayfs.h SourceLine:257 SourceSnippet: 256:
257: ret = vfs_mkdir(ovl_upper_mnt_idmap(ofs), dir, dentry, mode, NULL);
258: pr_debug("mkdir(%pd2, 0%o) = %i\n", dentry, mode, PTR_ERR_OR_ZERO(ret));
] map[ReferenceKind:calls ReferencingEntityKind:function ReferencingEntityName:xrep_orphanage_create SourceFile:fs/xfs/scrub/orphanage.c SourceLine:168 SourceSnippet: 167: if (d_really_is_negative(orphanage_dentry)) {
168: orphanage_dentry = vfs_mkdir(&nop_mnt_idmap, root_inode,
169: orphanage_dentry, 0750, NULL);
] map[ReferenceKind:takes-address-of ReferencingEntityKind:global_variable ReferencingEntityName:__UNIQUE_ID_addressable_vfs_mkdir_1345 SourceFile:fs/namei.c SourceLine:5291 SourceSnippet:5291: EXPORT_SYMBOL(vfs_mkdir);
] map[ReferenceKind:takes-address-of ReferencingEntityKind:global_variable ReferencingEntityName:__gendwarfksyms_ptr_vfs_mkdir SourceFile:fs/namei.c SourceLine:5291 SourceSnippet:5291: EXPORT_SYMBOL(vfs_mkdir);
] map[ReferenceKind:takes-address-of ReferencingEntityKind:global_variable ReferencingEntityName:__gendwarfksyms_ptr_vfs_mkdir SourceFile:fs/namei.c SourceLine:5291 SourceSnippet:5291: EXPORT_SYMBOL(vfs_mkdir);
]] TruncatedOutput:false]
|
| 113/4 |
2026/07/30 00:19 |
llm |
codeexpert |
0m
Model:
Tokens:
input: 41544
output: 30
thoughts: 936
Thoughts: **My Configfs Directory Creation Analysis**
Okay, so I'm diving into how directories get created within configfs, specifically thinking about the attack surface for an unprivileged user. Let's see... looking at where `vfs_mkdir` gets called, it's pretty clear that direct calls *to* `vfs_mkdir` from kernel code on configfs are limited. It's mostly just the standard `mkdir` system call path (`filename_mkdirat`), a few specialized cases, and some module-related registrations.
My initial thought is that since `filename_mkdirat` handles `mkdir` and `mkdirat`, a user-space process *must* be initiating any `mkdir` calls within configfs, right? No other magical kernel code seems to be directly calling that function *on configfs*.
But I have to consider other methods, right? Is there any other way to create a directory within configfs beyond the `mkdir` syscall? I need to think this through. Hmm, configfs directories are explicitly created via that syscall, usually called by userspace.
Okay, what about the kernel? Could some kernel-space mechanism be dynamically calling `configfs_register_subsystem` or `config_group_init`? I'm thinking, `configfs_register_subsystem` is what brings in a top-level directory like `/sys/kernel/config/usb_gadget`. Could *that* be triggered by something an unprivileged user does?
And then I remember β that call happens during module initialization. Specifically, when modules like `libcomposite`, `usb_f_fs`, or similar get loaded. So, the question is: can an unprivileged user trigger *that* module loading?
Normally, you need `CAP_SYS_MODULE` for module loading, right? But what about automatic module loading, via something like `request_module`? Could a socket, or some other interface, automatically trigger that? Yes! I can see that happening, for example, a specific socket family, network protocol, or device access triggering `request_module` and loading a module. If the module then calls `configfs_register_subsystem` in its `init` function, a new top-level directory in configfs gets created!
But hold on, does that count as an unprivileged user *creating* the directory? Technically, the *kernel* is creating the top-level directory (e.g., `/sys/kernel/config/usb_gadget`) automatically when the module is loaded. And even that directory will be owned by root, with permissions 0755! So, the user can't then create subdirectories unless they have the required permissions.
Okay, okay, I need to double-check this to make sure I'm not missing anything. I need to make sure I haven't missed any other edge cases. Let's do a quick search for "usb_gadget" and "mkdir" or "configfs" in the kernel docs or security advisories. Let's look for anything that is related to unprivileged user directory creation in configfs. I'm thinking maybe `usbfs` or `gadgetfs` or `functionfs` could be involved.
And *wait*β¦`functionfs`β¦ does that use configfs? Ah yes, I remember that `FunctionFS` *does* use configfs! It lets user space implement USB functions, and it's configured via configfs under `/sys/kernel/config/usb_gadget/g1/functions/ffs.usb0`. But to get *there*, you first need to create the gadget directory in `/sys/kernel/config/usb_gadget/g1` using `mkdir`. And, as we established earlier, *that* requires write permission to `/sys/kernel/config/usb_gadget`!
Right. I'll need to do a broader search with `grepper` across the documentation and source code to be sure. I'll search for `configfs` along with "user namespace" or "unprivileged" to find anything I've missed.
|
| 114/4 |
2026/07/30 00:19 |
tool |
grepper |
0m
Error:
Args: map[Expression:user namespace|unprivileged PathPrefix:fs/configfs/]
Results:
|
| 115/4 |
2026/07/30 00:19 |
llm |
codeexpert |
0m
Model:
Tokens:
input: 43395
output: 24
thoughts: 27
Thoughts: Okay, here's my take on those thoughts, framed as an expert's internal monologue:
**Investigating ConfigFS Permissions and User Namespaces**
Alright, let's dive into this `configfs` situation. I need to refresh my memory on how it handles permissions and, crucially, its interaction with user namespaces. My gut feeling is that there might be some nuanced behavior there that requires a deep dive. I'll start by searching within the `Documentation/` directory of the kernel source. Specifically, I'll use a `git grep` or similar tool to look for mentions of `configfs` in conjunction with keywords like "permissions," "security," "namespace," or even just "user." This is the first logical step. My experience tells me this is often the best place to start. It's usually the most well-maintained documentation, and hopefully, I'll find some hints on how these features interact or any known caveats that I should be aware of. Let's see what the documentation uncovers.
|
| 116/4 |
2026/07/30 00:19 |
tool |
grepper |
0m
Args: map[Expression:configfs PathPrefix:Documentation/]
Results: map[Output:
Full output is too long, showing 500 out of 1014 lines.
Use more precise expression if possible.
Documentation/ABI/testing/configfs-most=4=Description: Interface is used to configure and connect device channels
--
Documentation/ABI/testing/configfs-most-6-
Documentation/ABI/testing/configfs-most:7: Attributes are visible only when configfs is mounted. To mount
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-6-
Documentation/ABI/testing/configfs-rdma_cm:7: Attributes are visible only when configfs is mounted. To mount
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/ABI/testing/configfs-thunderbolt_stream=34=Description:
Documentation/ABI/testing/configfs-thunderbolt_stream-35- This matches the X in /dev/tbstreamX and allows userspace
Documentation/ABI/testing/configfs-thunderbolt_stream:36: to map the configfs directory to the corresponding character
Documentation/ABI/testing/configfs-thunderbolt_stream-37- device.
--
Documentation/ABI/testing/configfs-tsm-report=14=Description:
--
Documentation/ABI/testing/configfs-tsm-report-20- This interface fails reads and sets errno to EFBIG when the
Documentation/ABI/testing/configfs-tsm-report:21: report generated by @provider exceeds the configfs-tsm-report
Documentation/ABI/testing/configfs-tsm-report-22- internal maximums. Contact the platform provider for the
--
Documentation/ABI/testing/configfs-usb-gadget-tcm=4=Description:
Documentation/ABI/testing/configfs-usb-gadget-tcm-5- There are no attributes because all the configuration
Documentation/ABI/testing/configfs-usb-gadget-tcm:6: is performed in the "target" subsystem of configfs.
--
Documentation/ABI/testing/configfs-usb-gadget-uvc=437=KernelVersion: 4.19
Documentation/ABI/testing/configfs-usb-gadget-uvc:438:Description: UVC configfs function instance name
--
Documentation/PCI/endpoint/function/binding/pci-ntb.rst=4=PCI NTB Endpoint Function
--
Documentation/PCI/endpoint/function/binding/pci-ntb.rst-6-
Documentation/PCI/endpoint/function/binding/pci-ntb.rst:7:1) Create a subdirectory to pci_epf_ntb directory in configfs.
Documentation/PCI/endpoint/function/binding/pci-ntb.rst-8-
--
Documentation/PCI/endpoint/pci-endpoint-cfs.rst=4=Configuring PCI Endpoint Using CONFIGFS
--
Documentation/PCI/endpoint/pci-endpoint-cfs.rst-8-
Documentation/PCI/endpoint/pci-endpoint-cfs.rst:9:The PCI Endpoint Core exposes configfs entry (pci_ep) to configure the
Documentation/PCI/endpoint/pci-endpoint-cfs.rst-10-PCI endpoint function and to bind the endpoint function
--
Documentation/PCI/endpoint/pci-endpoint-cfs.rst=12=configure the PCI Endpoint Function refer to [1]).
Documentation/PCI/endpoint/pci-endpoint-cfs.rst-13-
Documentation/PCI/endpoint/pci-endpoint-cfs.rst:14:Mounting configfs
Documentation/PCI/endpoint/pci-endpoint-cfs.rst-15-=================
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-endpoint-cfs.rst=22=Directory Structure
--
Documentation/PCI/endpoint/pci-endpoint-cfs.rst-24-
Documentation/PCI/endpoint/pci-endpoint-cfs.rst:25:The pci_ep configfs has two directories at its root: controllers and
Documentation/PCI/endpoint/pci-endpoint-cfs.rst-26-functions. Every EPC device present in the system will have an entry in
--
Documentation/PCI/endpoint/pci-endpoint.rst=5=This document is a guide to use the PCI Endpoint Framework in order to create
Documentation/PCI/endpoint/pci-endpoint.rst:6:endpoint controller driver, endpoint function driver, and using configfs
Documentation/PCI/endpoint/pci-endpoint.rst-7-interface to bind the function driver to the controller driver.
--
Documentation/PCI/endpoint/pci-endpoint.rst=27=The PCI Endpoint Core layer comprises 3 components: the Endpoint Controller
Documentation/PCI/endpoint/pci-endpoint.rst:28:library, the Endpoint Function library, and the configfs layer to bind the
Documentation/PCI/endpoint/pci-endpoint.rst-29-endpoint function with the endpoint controller.
--
Documentation/PCI/endpoint/pci-endpoint.rst=219=by the PCI endpoint function driver.
--
Documentation/PCI/endpoint/pci-endpoint.rst-226- device and EPF device
Documentation/PCI/endpoint/pci-endpoint.rst:227: * add_cfs: optional ops to create function specific configfs
Documentation/PCI/endpoint/pci-endpoint.rst-228- attributes
--
Documentation/PCI/endpoint/pci-ntb-howto.rst=49=Creating pci-epf-ntb Device
--
Documentation/PCI/endpoint/pci-ntb-howto.rst-51-
Documentation/PCI/endpoint/pci-ntb-howto.rst:52:PCI endpoint function device can be created using the configfs. To create
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-ntb-howto.rst=81=Configuring pci-epf-ntb Device
--
Documentation/PCI/endpoint/pci-ntb-howto.rst-83-
Documentation/PCI/endpoint/pci-ntb-howto.rst:84:The user can configure the pci-epf-ntb device using its configfs entry. In order
Documentation/PCI/endpoint/pci-ntb-howto.rst-85-to change the vendorid and the deviceid, the following
--
Documentation/PCI/endpoint/pci-test-howto.rst=44=Creating pci-epf-test Device
--
Documentation/PCI/endpoint/pci-test-howto.rst-46-
Documentation/PCI/endpoint/pci-test-howto.rst:47:PCI endpoint function device can be created using the configfs. To create
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-test-howto.rst=75=Configuring pci-epf-test Device
--
Documentation/PCI/endpoint/pci-test-howto.rst-77-
Documentation/PCI/endpoint/pci-test-howto.rst:78:The user can configure the pci-epf-test device using configfs entry. In order
Documentation/PCI/endpoint/pci-test-howto.rst-79-to change the vendorid and the number of MSI interrupts used by the function
--
Documentation/PCI/endpoint/pci-test-howto.rst=103=Note: Some endpoint controllers might have fixed-size BARs or reserved BARs;
Documentation/PCI/endpoint/pci-test-howto.rst:104:for such controllers, the corresponding BAR size in configfs will be ignored.
Documentation/PCI/endpoint/pci-test-howto.rst-105-
--
Documentation/PCI/endpoint/pci-test-howto.rst=110=In order for the endpoint function device to be useful, it has to be bound to
Documentation/PCI/endpoint/pci-test-howto.rst:111:a PCI endpoint controller driver. Use the configfs to bind the function
Documentation/PCI/endpoint/pci-test-howto.rst-112-device to one of the controller driver present in the system::
--
Documentation/PCI/endpoint/pci-vntb-howto.rst=45=Creating pci-epf-vntb Device
--
Documentation/PCI/endpoint/pci-vntb-howto.rst-47-
Documentation/PCI/endpoint/pci-vntb-howto.rst:48:PCI endpoint function device can be created using the configfs. To create
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/PCI/endpoint/pci-vntb-howto.rst=77=Configuring pci-epf-vntb Device
--
Documentation/PCI/endpoint/pci-vntb-howto.rst-79-
Documentation/PCI/endpoint/pci-vntb-howto.rst:80:The user can configure the pci-epf-vntb device using its configfs entry. In order
Documentation/PCI/endpoint/pci-vntb-howto.rst-81-to change the vendorid and the deviceid, the following
--
Documentation/admin-guide/acpi/ssdt-overlays.rst=127=variable with the content from a given file::
--
Documentation/admin-guide/acpi/ssdt-overlays.rst-167-
Documentation/admin-guide/acpi/ssdt-overlays.rst:168:Loading ACPI SSDTs from configfs
Documentation/admin-guide/acpi/ssdt-overlays.rst-169-================================
Documentation/admin-guide/acpi/ssdt-overlays.rst-170-
Documentation/admin-guide/acpi/ssdt-overlays.rst:171:This option allows loading of user defined SSDTs from user space via the configfs
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/admin-guide/gpio/gpio-aggregator.rst=72=Aggregating GPIOs using Configfs
--
Documentation/admin-guide/gpio/gpio-aggregator.rst-76-
Documentation/admin-guide/gpio/gpio-aggregator.rst:77: This is the root directory of the gpio-aggregator configfs tree.
Documentation/admin-guide/gpio/gpio-aggregator.rst-78-
--
Documentation/admin-guide/gpio/gpio-aggregator.rst-82- name to ``<example-name>`` (e.g. ``agg0``), except names starting with
Documentation/admin-guide/gpio/gpio-aggregator.rst:83: ``_sysfs`` prefix, which are reserved for auto-generated configfs
Documentation/admin-guide/gpio/gpio-aggregator.rst-84- entries corresponding to devices created via Sysfs.
--
Documentation/admin-guide/gpio/gpio-aggregator.rst=144=case when you create it using sysfs ``new_device`` interface.
--
Documentation/admin-guide/gpio/gpio-aggregator.rst-147-
Documentation/admin-guide/gpio/gpio-aggregator.rst:148: For aggregators created via Sysfs, the configfs entries are
Documentation/admin-guide/gpio/gpio-aggregator.rst-149- auto-generated and appear as ``/config/gpio-aggregator/_sysfs.<N>/``. You
--
Documentation/admin-guide/gpio/gpio-sim.rst=3=Configfs GPIO Simulator
--
Documentation/admin-guide/gpio/gpio-sim.rst-5-
Documentation/admin-guide/gpio/gpio-sim.rst:6:The configfs GPIO Simulator (gpio-sim) provides a way to create simulated GPIO
Documentation/admin-guide/gpio/gpio-sim.rst-7-chips for testing purposes. The lines exposed by these chips can be accessed
--
Documentation/admin-guide/gpio/gpio-sim.rst=11=Creating simulated chips
--
Documentation/admin-guide/gpio/gpio-sim.rst-13-
Documentation/admin-guide/gpio/gpio-sim.rst:14:The gpio-sim module registers a configfs subsystem called ``'gpio-sim'``. For
Documentation/admin-guide/gpio/gpio-sim.rst:15:details of the configfs filesystem, please refer to the configfs documentation.
Documentation/admin-guide/gpio/gpio-sim.rst-16-
Documentation/admin-guide/gpio/gpio-sim.rst:17:The user can create a hierarchy of configfs groups and items as well as modify
Documentation/admin-guide/gpio/gpio-sim.rst-18-values of exposed attributes. Once the chip is instantiated, this hierarchy
Documentation/admin-guide/gpio/gpio-sim.rst=19=will be translated to appropriate device properties. The general structure is:
--
Documentation/admin-guide/gpio/gpio-sim.rst-22-
Documentation/admin-guide/gpio/gpio-sim.rst:23:This is the top directory of the gpio-sim configfs tree.
Documentation/admin-guide/gpio/gpio-sim.rst-24-
--
Documentation/admin-guide/gpio/gpio-virtuser.rst=7=that request GPIOs and then control their behavior over debugfs. Virtual
Documentation/admin-guide/gpio/gpio-virtuser.rst:8:consumer devices can be instantiated from device-tree or over configfs.
Documentation/admin-guide/gpio/gpio-virtuser.rst-9-
--
Documentation/admin-guide/gpio/gpio-virtuser.rst=14=Creating GPIO consumers
--
Documentation/admin-guide/gpio/gpio-virtuser.rst-16-
Documentation/admin-guide/gpio/gpio-virtuser.rst:17:The gpio-consumer module registers a configfs subsystem called
Documentation/admin-guide/gpio/gpio-virtuser.rst:18:``'gpio-virtuser'``. For details of the configfs filesystem, please refer to
Documentation/admin-guide/gpio/gpio-virtuser.rst:19:the configfs documentation.
Documentation/admin-guide/gpio/gpio-virtuser.rst-20-
Documentation/admin-guide/gpio/gpio-virtuser.rst:21:The user can create a hierarchy of configfs groups and items as well as modify
Documentation/admin-guide/gpio/gpio-virtuser.rst-22-values of exposed attributes. Once the consumer is instantiated, this hierarchy
Documentation/admin-guide/gpio/gpio-virtuser.rst=23=will be translated to appropriate device properties. The general structure is:
--
Documentation/admin-guide/gpio/gpio-virtuser.rst-26-
Documentation/admin-guide/gpio/gpio-virtuser.rst:27:This is the top directory of the gpio-consumer configfs tree.
Documentation/admin-guide/gpio/gpio-virtuser.rst-28-
--
Documentation/admin-guide/gpio/gpio-virtuser.rst=46=Creating GPIO lookup tables
--
Documentation/admin-guide/gpio/gpio-virtuser.rst-48-
Documentation/admin-guide/gpio/gpio-virtuser.rst:49:Users can create a number of configfs groups under the device group:
Documentation/admin-guide/gpio/gpio-virtuser.rst-50-
--
Documentation/admin-guide/gpio/gpio-virtuser.rst=72=This is a group describing a single GPIO in the ``con_id-gpios`` property.
Documentation/admin-guide/gpio/gpio-virtuser.rst-73-
Documentation/admin-guide/gpio/gpio-virtuser.rst:74:For virtual consumers created using configfs we use machine lookup tables so
Documentation/admin-guide/gpio/gpio-virtuser.rst-75-this group can be considered as a mapping between the filesystem and the fields
--
Documentation/admin-guide/mm/shrinker_debugfs.rst=17=Usage:
--
Documentation/admin-guide/mm/shrinker_debugfs.rst-35- sb-cgroup2-30 sb-proc-39 sb-tmpfs-27 xfs-buf:vda1-37
Documentation/admin-guide/mm/shrinker_debugfs.rst:36: sb-configfs-23 sb-proc-41 sb-tmpfs-29 xfs-inodegc:vda1-38
Documentation/admin-guide/mm/shrinker_debugfs.rst-37- sb-dax-11 sb-proc-45 sb-tmpfs-35
--
Documentation/driver-api/ioctl.rst=235=problem. Alternatives include:
--
Documentation/driver-api/ioctl.rst-249-
Documentation/driver-api/ioctl.rst:250:* configfs can be used for more complex configuration than sysfs
Documentation/driver-api/ioctl.rst-251-
--
Documentation/driver-api/usb/dwc3.rst=58=These features have all been exercised with many of the **in-tree**
Documentation/driver-api/usb/dwc3.rst:59:gadget drivers. We have verified both *ConfigFS* [#configfs]_ and
Documentation/driver-api/usb/dwc3.rst-60-legacy gadget drivers.
--
Documentation/driver-api/usb/dwc3.rst=684=Structures, Methods and Definitions
--
Documentation/driver-api/usb/dwc3.rst-706-.. [#debugfs] The Debug File System
Documentation/driver-api/usb/dwc3.rst:707:.. [#configfs] The Config File System
Documentation/driver-api/usb/dwc3.rst-708-.. [#cbw] Command Block Wrapper
--
Documentation/filesystems/9p.rst=64=where <device> is the tag associated by the usb gadget transport.
Documentation/filesystems/9p.rst:65:It is defined by the configfs instance name.
Documentation/filesystems/9p.rst-66-
--
Documentation/filesystems/configfs.rst=9=Copyright (c) 2005 Oracle Corporation,
--
Documentation/filesystems/configfs.rst-12-
Documentation/filesystems/configfs.rst:13:What is configfs?
Documentation/filesystems/configfs.rst-14-=================
Documentation/filesystems/configfs.rst-15-
Documentation/filesystems/configfs.rst:16:configfs is a ram-based filesystem that provides the converse of
Documentation/filesystems/configfs.rst-17-sysfs's functionality. Where sysfs is a filesystem-based view of
Documentation/filesystems/configfs.rst:18:kernel objects, configfs is a filesystem-based manager of kernel
Documentation/filesystems/configfs.rst-19-objects, or config_items.
--
Documentation/filesystems/configfs.rst=27=representation, and sysfs is merely a window on all this.
Documentation/filesystems/configfs.rst-28-
Documentation/filesystems/configfs.rst:29:A configfs config_item is created via an explicit userspace operation:
Documentation/filesystems/configfs.rst-30-mkdir(2). It is destroyed via rmdir(2). The attributes appear at
--
Documentation/filesystems/configfs.rst=35=kernel modules backing the items must respond to this.
Documentation/filesystems/configfs.rst-36-
Documentation/filesystems/configfs.rst:37:Both sysfs and configfs can and should exist together on the same
Documentation/filesystems/configfs.rst-38-system. One is not a replacement for the other.
Documentation/filesystems/configfs.rst-39-
Documentation/filesystems/configfs.rst:40:Using configfs
Documentation/filesystems/configfs.rst-41-==============
Documentation/filesystems/configfs.rst-42-
Documentation/filesystems/configfs.rst:43:configfs can be compiled as a module or into the kernel. You can access
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/filesystems/configfs.rst:48:The configfs tree will be empty unless client modules are also loaded.
Documentation/filesystems/configfs.rst:49:These are modules that register their item types with configfs as
Documentation/filesystems/configfs.rst-50-subsystems. Once a client subsystem is loaded, it will appear as a
Documentation/filesystems/configfs.rst=51=subdirectory (or more than one) under /config. Like sysfs, the
Documentation/filesystems/configfs.rst:52:configfs tree is always there, whether mounted on /config or not.
Documentation/filesystems/configfs.rst-53-
--
Documentation/filesystems/configfs.rst=57=values. Don't mix more than one attribute in one attribute file.
Documentation/filesystems/configfs.rst-58-
Documentation/filesystems/configfs.rst:59:There are two types of configfs attributes:
Documentation/filesystems/configfs.rst-60-
--
Documentation/filesystems/configfs.rst-64- Configfs expects write(2) to store the entire buffer at once. When writing to
Documentation/filesystems/configfs.rst:65: normal configfs attributes, userspace processes should first read the entire
Documentation/filesystems/configfs.rst-66- file, modify the portions they wish to change, and then write the entire
--
Documentation/filesystems/configfs.rst=86=Imagine there's a Network Block Device (NBD) driver that allows you to
Documentation/filesystems/configfs.rst:87:access remote block devices. Call it FakeNBD. FakeNBD uses configfs
Documentation/filesystems/configfs.rst-88-for its configuration. Obviously, there will be a nice program that
Documentation/filesystems/configfs.rst=89=sysadmins use to configure FakeNBD, but somehow that program has to tell
Documentation/filesystems/configfs.rst:90:the driver about it. Here's where configfs comes in.
Documentation/filesystems/configfs.rst-91-
Documentation/filesystems/configfs.rst:92:When the FakeNBD driver is loaded, it registers itself with configfs.
Documentation/filesystems/configfs.rst-93-readdir(3) sees this just fine::
--
Documentation/filesystems/configfs.rst=116=shell no less.
Documentation/filesystems/configfs.rst-117-
Documentation/filesystems/configfs.rst:118:Coding With configfs
Documentation/filesystems/configfs.rst-119-====================
Documentation/filesystems/configfs.rst-120-
Documentation/filesystems/configfs.rst:121:Every object in configfs is a config_item. A config_item reflects an
Documentation/filesystems/configfs.rst-122-object in the subsystem. It has attributes that match values on that
Documentation/filesystems/configfs.rst:123:object. configfs handles the filesystem representation of that object
Documentation/filesystems/configfs.rst-124-and its attributes, allowing the subsystem to ignore all but the
--
Documentation/filesystems/configfs.rst=128=collection of items that share the same attributes and operations.
Documentation/filesystems/configfs.rst:129:Items are created by mkdir(2) and removed by rmdir(2), but configfs
Documentation/filesystems/configfs.rst-130-handles that. The group has a set of operations to perform these tasks
--
Documentation/filesystems/configfs.rst=132=A subsystem is the top level of a client module. During initialization,
Documentation/filesystems/configfs.rst:133:the client module registers the subsystem with configfs, the subsystem
Documentation/filesystems/configfs.rst:134:appears as a directory at the top of the configfs filesystem. A
Documentation/filesystems/configfs.rst-135-subsystem is also a config_group, and can do everything a config_group
--
Documentation/filesystems/configfs.rst=163=config_item portion of that structure is how the object interacts with
Documentation/filesystems/configfs.rst:164:configfs.
Documentation/filesystems/configfs.rst-165-
--
Documentation/filesystems/configfs.rst=173=config_item_put().
Documentation/filesystems/configfs.rst-174-
Documentation/filesystems/configfs.rst:175:By itself, a config_item cannot do much more than appear in configfs.
Documentation/filesystems/configfs.rst-176-Usually a subsystem wants the item to display and/or store attributes,
--
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;
Documentation/filesystems/configfs.rst:196: struct configfs_attribute **ct_attrs;
Documentation/filesystems/configfs.rst:197: struct configfs_bin_attribute **ct_bin_attrs;
Documentation/filesystems/configfs.rst-198- };
--
Documentation/filesystems/configfs.rst=204=reaches zero.
Documentation/filesystems/configfs.rst-205-
Documentation/filesystems/configfs.rst:206:struct configfs_attribute
Documentation/filesystems/configfs.rst-207-=========================
--
Documentation/filesystems/configfs.rst-210-
Documentation/filesystems/configfs.rst:211: struct configfs_attribute {
Documentation/filesystems/configfs.rst-212- char *ca_name;
--
Documentation/filesystems/configfs.rst=219=When a config_item wants an attribute to appear as a file in the item's
Documentation/filesystems/configfs.rst:220:configfs directory, it must define a configfs_attribute describing it.
Documentation/filesystems/configfs.rst-221-It then adds the attribute to the NULL-terminated array
Documentation/filesystems/configfs.rst:222:config_item_type->ct_attrs. When the item appears in configfs, the
Documentation/filesystems/configfs.rst:223:attribute file will appear with the configfs_attribute->ca_name
Documentation/filesystems/configfs.rst:224:filename. configfs_attribute->ca_mode specifies the file permissions.
Documentation/filesystems/configfs.rst-225-
--
Documentation/filesystems/configfs.rst=229=called whenever userspace asks for a write(2) on the attribute.
Documentation/filesystems/configfs.rst-230-
Documentation/filesystems/configfs.rst:231:struct configfs_bin_attribute
Documentation/filesystems/configfs.rst-232-=============================
--
Documentation/filesystems/configfs.rst-235-
Documentation/filesystems/configfs.rst:236: struct configfs_bin_attribute {
Documentation/filesystems/configfs.rst:237: struct configfs_attribute cb_attr;
Documentation/filesystems/configfs.rst-238- void *cb_private;
--
Documentation/filesystems/configfs.rst=242=The binary attribute is used when the one needs to use binary blob to
Documentation/filesystems/configfs.rst:243:appear as the contents of a file in the item's configfs directory.
Documentation/filesystems/configfs.rst-244-To do so add the binary attribute to the NULL-terminated array
Documentation/filesystems/configfs.rst:245:config_item_type->ct_bin_attrs, and the item appears in configfs, the
Documentation/filesystems/configfs.rst:246:attribute file will appear with the configfs_bin_attribute->cb_attr.ca_name
Documentation/filesystems/configfs.rst:247:filename. configfs_bin_attribute->cb_attr.ca_mode specifies the file
Documentation/filesystems/configfs.rst-248-permissions.
--
Documentation/filesystems/configfs.rst=265=child item::
--
Documentation/filesystems/configfs.rst-269- struct list_head cg_children;
Documentation/filesystems/configfs.rst:270: struct configfs_subsystem *cg_subsys;
Documentation/filesystems/configfs.rst-271- struct list_head default_groups;
--
Documentation/filesystems/configfs.rst=285=config_item_type::
Documentation/filesystems/configfs.rst-286-
Documentation/filesystems/configfs.rst:287: struct configfs_group_operations {
Documentation/filesystems/configfs.rst-288- struct config_item *(*make_item)(struct config_group *group,
--
Documentation/filesystems/configfs.rst=301=config_item (or more likely, its container structure), initializes it,
Documentation/filesystems/configfs.rst:302:and returns it to configfs. Configfs will then populate the filesystem
Documentation/filesystems/configfs.rst-303-tree to reflect the new item.
--
Documentation/filesystems/configfs.rst=313=upon item allocation. If a subsystem has no work to do, it may omit
Documentation/filesystems/configfs.rst:314:the ct_group_ops->drop_item() method, and configfs will call
Documentation/filesystems/configfs.rst-315-config_item_put() on the item on behalf of the subsystem.
--
Documentation/filesystems/configfs.rst=317=Important:
Documentation/filesystems/configfs.rst-318- drop_item() is void, and as such cannot fail. When rmdir(2)
Documentation/filesystems/configfs.rst:319: is called, configfs WILL remove the item from the filesystem tree
Documentation/filesystems/configfs.rst-320- (assuming that it has no children to keep it busy). The subsystem is
--
Documentation/filesystems/configfs.rst-323- for the item to actually disappear from the subsystem's usage. But it
Documentation/filesystems/configfs.rst:324: is gone from configfs.
Documentation/filesystems/configfs.rst-325-
--
Documentation/filesystems/configfs.rst=330=ct_group_ops->disconnect_notify() method. The method is called after
Documentation/filesystems/configfs.rst:331:configfs has removed the item from the filesystem view but before the
Documentation/filesystems/configfs.rst-332-item is removed from its parent group. Like drop_item(),
--
Documentation/filesystems/configfs.rst=336=A config_group cannot be removed while it still has child items. This
Documentation/filesystems/configfs.rst:337:is implemented in the configfs rmdir(2) code. ->drop_item() will not be
Documentation/filesystems/configfs.rst-338-called, as the item has not been dropped. rmdir(2) will fail, as the
Documentation/filesystems/configfs.rst=339=directory is not empty.
Documentation/filesystems/configfs.rst-340-
Documentation/filesystems/configfs.rst:341:struct configfs_subsystem
Documentation/filesystems/configfs.rst-342-=========================
--
Documentation/filesystems/configfs.rst=344=A subsystem must register itself, usually at module_init time. This
Documentation/filesystems/configfs.rst:345:tells configfs to make the subsystem appear in the file tree::
Documentation/filesystems/configfs.rst-346-
Documentation/filesystems/configfs.rst:347: struct configfs_subsystem {
Documentation/filesystems/configfs.rst-348- struct config_group su_group;
--
Documentation/filesystems/configfs.rst-351-
Documentation/filesystems/configfs.rst:352: int configfs_register_subsystem(struct configfs_subsystem *subsys);
Documentation/filesystems/configfs.rst:353: void configfs_unregister_subsystem(struct configfs_subsystem *subsys);
Documentation/filesystems/configfs.rst-354-
--
Documentation/filesystems/configfs.rst=357=this group is usually defined statically. Before calling
Documentation/filesystems/configfs.rst:358:configfs_register_subsystem(), the subsystem must have initialized the
Documentation/filesystems/configfs.rst-359-group via the usual group _init() functions, and it must also have
--
Documentation/filesystems/configfs.rst=362=When the register call returns, the subsystem is live, and it
Documentation/filesystems/configfs.rst:363:will be visible via configfs. At that point, mkdir(2) can be called and
Documentation/filesystems/configfs.rst-364-the subsystem must be ready for it.
--
Documentation/filesystems/configfs.rst=370=subsystem/group and the simple_child item in
Documentation/filesystems/configfs.rst:371:samples/configfs/configfs_sample.c. It shows a trivial object displaying
Documentation/filesystems/configfs.rst-372-and storing an attribute, and a simple group creating and destroying
--
Documentation/filesystems/configfs.rst=375=Hierarchy Navigation and the Subsystem Mutex
--
Documentation/filesystems/configfs.rst-377-
Documentation/filesystems/configfs.rst:378:There is an extra bonus that configfs provides. The config_groups and
Documentation/filesystems/configfs.rst-379-config_items are arranged in a hierarchy due to the fact that they
--
Documentation/filesystems/configfs.rst=385=A subsystem can navigate the cg_children list and the ci_parent pointer
Documentation/filesystems/configfs.rst:386:to see the tree created by the subsystem. This can race with configfs'
Documentation/filesystems/configfs.rst:387:management of the hierarchy, so configfs uses the subsystem mutex to
Documentation/filesystems/configfs.rst-388-protect modifications. Whenever a subsystem wants to navigate the
--
Documentation/filesystems/configfs.rst=395=yet been unlinked. This means that an item's ci_parent pointer will
Documentation/filesystems/configfs.rst:396:never be NULL while the item is in configfs, and that an item will only
Documentation/filesystems/configfs.rst-397-be in its parent's cg_children list for the same duration. This allows
--
Documentation/filesystems/configfs.rst=401=Item Aggregation Via symlink(2)
--
Documentation/filesystems/configfs.rst-403-
Documentation/filesystems/configfs.rst:404:configfs provides a simple group via the group->item parent/child
Documentation/filesystems/configfs.rst-405-relationship. Often, however, a larger environment requires aggregation
--
Documentation/filesystems/configfs.rst=411=symlink(2) may be called with the config_item as the source of the link.
Documentation/filesystems/configfs.rst:412:These links are only allowed between configfs config_items. Any
Documentation/filesystems/configfs.rst:413:symlink(2) attempt outside the configfs filesystem will be denied.
Documentation/filesystems/configfs.rst-414-
--
Documentation/filesystems/configfs.rst=427=can it be removed while an item links to it. Dangling symlinks are not
Documentation/filesystems/configfs.rst:428:allowed in configfs.
Documentation/filesystems/configfs.rst-429-
--
Documentation/filesystems/configfs.rst=437=Rather than have a group where some items behave differently than
Documentation/filesystems/configfs.rst:438:others, configfs provides a method whereby one or many subgroups are
Documentation/filesystems/configfs.rst-439-automatically created inside the parent at its creation. Thus,
--
Documentation/filesystems/configfs.rst=447=other child groups can be created on the parent group directly.
Documentation/filesystems/configfs.rst-448-
Documentation/filesystems/configfs.rst:449:A configfs subsystem specifies default groups by adding them using the
Documentation/filesystems/configfs.rst:450:configfs_add_default_group() function to the parent config_group
Documentation/filesystems/configfs.rst:451:structure. Each added group is populated in the configfs tree at the same
Documentation/filesystems/configfs.rst-452-time as the parent group. Similarly, they are removed at the same time
--
Documentation/filesystems/configfs.rst=461=Dependent Subsystems
--
Documentation/filesystems/configfs.rst-463-
Documentation/filesystems/configfs.rst:464:Sometimes other drivers depend on particular configfs items. For
Documentation/filesystems/configfs.rst-465-example, ocfs2 mounts depend on a heartbeat region item. If that
--
Documentation/filesystems/configfs.rst=467=readonly. Not happy.
Documentation/filesystems/configfs.rst-468-
Documentation/filesystems/configfs.rst:469:configfs provides two additional API calls: configfs_depend_item() and
Documentation/filesystems/configfs.rst:470:configfs_undepend_item(). A client driver can call
Documentation/filesystems/configfs.rst:471:configfs_depend_item() on an existing item to tell configfs that it is
Documentation/filesystems/configfs.rst:472:depended on. configfs will then return -EBUSY from rmdir(2) for that
Documentation/filesystems/configfs.rst-473-item. When the item is no longer depended on, the client driver calls
Documentation/filesystems/configfs.rst:474:configfs_undepend_item() on it.
Documentation/filesystems/configfs.rst-475-
Documentation/filesystems/configfs.rst:476:These API cannot be called underneath any configfs callbacks, as
Documentation/filesystems/configfs.rst-477-they will conflict. They can block and allocate. A client driver
--
Documentation/filesystems/configfs.rst=483=heartbeat code. Inside the heartbeat code, the region item is looked
Documentation/filesystems/configfs.rst:484:up. Here, the heartbeat code calls configfs_depend_item(). If it
Documentation/filesystems/configfs.rst-485-succeeds, then heartbeat knows the region is safe to give to ocfs2.
--
Documentation/filesystems/index.rst=66=Documentation for filesystem implementations.
--
Documentation/filesystems/index.rst-81- coda
]
|
| 117/4 |
2026/07/30 00:19 |
llm |
codeexpert |
0m
Model:
Tokens:
input: 54255
output: 131
thoughts: 1730
Thoughts: **My Analysis of Configfs Permissions**
Okay, here's my understanding of how directory creation works within `configfs`, specifically in relation to `/sys/kernel/config/usb_gadget`.
First, it's pretty straightforward. By default, `configfs` is mounted with the root user and group owning everything (UID 0, GID 0). The default permissions for directories, including `/sys/kernel/config/usb_gadget` and its subdirectories, are `0755`. This means that only the root user has write permission, so only they can create new directories, essentially preventing unprivileged users from directly creating USB gadgets or modifying their configuration under `/sys/kernel/config/usb_gadget`.
Now, things get interesting when we consider user namespaces. Since the `configfs_fs_type` struct *doesn't* have the `FS_USERNS_MOUNT` flag, an unprivileged user cannot mount their own private instance of `configfs` within a user namespace. That's a key restriction.
However, the interactions with user namespaces *are* relevant with regard to permissions. If a directory under `/sys/kernel/config/usb_gadget` is owned by `root:root` in the initial namespace, then a root user inside *an unprivileged* user namespace *cannot* create directories. The reason is that UID 0 isn't mapped into the unprivileged namespace, so `CAP_DAC_OVERRIDE` capability (even if it's held by root inside the namespace) doesn't apply to the inode.
The other scenario is that a system administrator, or some privileged process, *explicitly* delegates ownership or permissions of `/sys/kernel/config/usb_gadget` to an unprivileged user (e.g., using `chown` or `chmod`). Then, inside a user namespace where this unprivileged UID is mapped to a UID in the namespace, the process *with* `CAP_DAC_OVERRIDE` can successfully create directories. Essentially, if a directory *is* owned by a user or group that is mapped into the namespace, the `CAP_DAC_OVERRIDE` will allow modification.
Beyond the user namespace stuff, the only other scenario that allows directory creation boils down to explicit permission delegation. A privileged process or user (root, or a process running with appropriate capabilities, such as a USB gadget management daemon) could use `chown` or `chmod` to grant write permissions to a specific user or group on `/sys/kernel/config/usb_gadget` or its subdirectories. This would allow an unprivileged user to make changes.
I've also confirmed that there aren't any sneaky backdoors here. A quick check of the kernel code reveals that all directory creation through `configfs` goes through the standard VFS `mkdir` calls. There aren't any custom permission checks or hidden interfaces, and no special case is created for unprivileged users. I've also verified that the lack of the `FS_USERNS_MOUNT` flag in `configfs_fs_type` prevents mounting configfs within a namespace. The default ownership and permission are handled within the `new_inode` functions. This means standard UNIX permission checks are used, and there is no special permission check handler.
In conclusion, it's a solid, consistent implementation: an unprivileged user *cannot* create directories in `/sys/kernel/config/usb_gadget` by default. They can only create them if permissions are delegated or if they are in a user namespace where a mapped UID is appropriately mapped into that namespace.
|
| 118/4 |
2026/07/30 00:20 |
tool |
set-results |
0m
Args: map[Answer:By default, an unprivileged user cannot create directories in configfs, including under `/sys/kernel/config/usb_gadget`. However, they can do so if a privileged user explicitly delegates permissions (via `chmod` or `chown`) to them. Additionally, if such delegation is in place, a user namespace mapping that delegated UID/GID can also trigger `mkdir` using namespace capabilities like `CAP_DAC_OVERRIDE`. Without such explicit delegation, unprivileged users and unprivileged user namespaces have no way to trigger `mkdir` in configfs.]
Results: map[Answer:By default, an unprivileged user cannot create directories in configfs, including under `/sys/kernel/config/usb_gadget`. However, they can do so if a privileged user explicitly delegates permissions (via `chmod` or `chown`) to them. Additionally, if such delegation is in place, a user namespace mapping that delegated UID/GID can also trigger `mkdir` using namespace capabilities like `CAP_DAC_OVERRIDE`. Without such explicit delegation, unprivileged users and unprivileged user namespaces have no way to trigger `mkdir` in configfs.]
|
| 119/2 |
2026/07/30 00:20 |
llm |
expert |
0m
Model:
Tokens:
input: 24451
output: 35
thoughts: 106
Thoughts: **Analyzing the Kernel Vulnerability**
Alright, let's break this down. It's clear this isn't immediately accessible to just anyone, not by default through unprivileged processes or user namespaces. The key question, though, is whether it's *exploitable* despite that. The core of the problem appears to be a classic use-after-free, specifically within `list_del` and on an object that's already been freed. It's that familiar pattern: `kfree` is called, and *bam*, `list_del` immediately follows without any intervening operations to prevent the race. This is the exact kind of setup that can lead to all sorts of havoc.
However, the devil is in the details, and I need to consider SLUB's memory management, and `kfree`. The worry is that `kfree`'s internal workings in SLUB *might* re-enable interrupts or preemption. If that's the case, we're talking about a classic race condition and far greater exploit potential. I need to confirm this. Time to dig into `mm/slub.c` and check how `kfree` handles this specific scenario.
|
| 120/2 |
2026/07/30 00:20 |
tool |
read-file |
0m
Args: map[File:mm/slub.c FirstLine:6680 LineCount:20]
Results: map[Contents: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,
]
|
| 121/2 |
2026/07/30 00:20 |
llm |
expert |
0m
Model:
Tokens:
input: 22011
output: 263
thoughts: 0
|
| 122/2 |
2026/07/30 00:20 |
tool |
codeexpert |
1m
Args: map[Question:In the provided bug report, we see a slab-use-after-free in __list_del_entry_valid_or_report. The allocation trace shows uvcg_extension_make allocating a uvcg_extension object. The free trace shows uvcg_extension_drop freeing the same object. The crash happens in uvcg_extension_drop when it calls list_del(&xu->list). Wait, if uvcg_extension_drop is called twice on the same object, the second time it will try to access the freed object. Let's look at the free trace:
config_item_cleanup -> config_item_release -> kref_put -> config_item_put -> uvcg_extension_drop.
Wait, uvcg_extension_drop calls config_item_put(item). Is it possible that config_item_put(item) inside uvcg_extension_drop triggers the release of the item, which then frees the item (via uvcg_extension_release), and then uvcg_extension_drop continues to access xu->list?
Let's check uvcg_extension_drop.]
Results: map[Answer:### Root Cause Analysis
In `drivers/usb/gadget/function/uvc_configfs.c`, the function `uvcg_extension_drop()` is responsible for dropping an extension unit configfs item. The implementation of `uvcg_extension_drop()` is as follows:
```c
static void uvcg_extension_drop(struct config_group *group, struct config_item *item)
{
struct uvcg_extension *xu = container_of(item, struct uvcg_extension, item);
struct config_item *opts_item;
struct f_uvc_opts *opts;
opts_item = group->cg_item.ci_parent->ci_parent;
opts = to_f_uvc_opts(opts_item);
mutex_lock(&opts->lock);
config_item_put(item);
list_del(&xu->list);
kfree(xu->desc.baSourceID);
kfree(xu->desc.bmControls);
mutex_unlock(&opts->lock);
}
```
When `config_item_put(item)` is called, it decrements the reference count of the configfs item. If this is the last reference to the item, it triggers the release callback `uvcg_extension_release()`:
```c
static void uvcg_extension_release(struct config_item *item)
{
struct uvcg_extension *xu = container_of(item, struct uvcg_extension, item);
kfree(xu);
}
```
This immediately frees the `uvcg_extension` object `xu`.
However, `uvcg_extension_drop()` continues to execute and accesses fields of the freed `xu` object:
1. `list_del(&xu->list)` accesses `xu->list`.
2. `kfree(xu->desc.baSourceID)` accesses `xu->desc.baSourceID`.
3. `kfree(xu->desc.bmControls)` accesses `xu->desc.bmControls`.
This results in a slab-use-after-free (UAF) bug in `__list_del_entry_valid_or_report` and other subsequent memory accesses.
---
### Solution
To resolve this issue, the call to `config_item_put(item)` must be deferred to the very end of `uvcg_extension_drop()`, after all fields of the `xu` structure have been accessed and freed, and after the mutex `opts->lock` has been unlocked. This is consistent with other drop callbacks in the same file (e.g., `uvcg_frame_drop()`).
The corrected `uvcg_extension_drop()` function should look like this:
```c
static void uvcg_extension_drop(struct config_group *group, struct config_item *item)
{
struct uvcg_extension *xu = container_of(item, struct uvcg_extension, item);
struct config_item *opts_item;
struct f_uvc_opts *opts;
opts_item = group->cg_item.ci_parent->ci_parent;
opts = to_f_uvc_opts(opts_item);
mutex_lock(&opts->lock);
list_del(&xu->list);
kfree(xu->desc.baSourceID);
kfree(xu->desc.bmControls);
mutex_unlock(&opts->lock);
config_item_put(item);
}
```]
|
| 123/3 |
2026/07/30 00:20 |
agent |
codeexpert |
1m
Model:
Results: map[Answer:### Root Cause Analysis
In `drivers/usb/gadget/function/uvc_configfs.c`, the function `uvcg_extension_drop()` is responsible for dropping an extension unit configfs item. The implementation of `uvcg_extension_drop()` is as follows:
```c
static void uvcg_extension_drop(struct config_group *group, struct config_item *item)
{
struct uvcg_extension *xu = container_of(item, struct uvcg_extension, item);
struct config_item *opts_item;
struct f_uvc_opts *opts;
opts_item = group->cg_item.ci_parent->ci_parent;
opts = to_f_uvc_opts(opts_item);
mutex_lock(&opts->lock);
config_item_put(item);
list_del(&xu->list);
kfree(xu->desc.baSourceID);
kfree(xu->desc.bmControls);
mutex_unlock(&opts->lock);
}
```
When `config_item_put(item)` is called, it decrements the reference count of the configfs item. If this is the last reference to the item, it triggers the release callback `uvcg_extension_release()`:
```c
static void uvcg_extension_release(struct config_item *item)
{
struct uvcg_extension *xu = container_of(item, struct uvcg_extension, item);
kfree(xu);
}
```
This immediately frees the `uvcg_extension` object `xu`.
However, `uvcg_extension_drop()` continues to execute and accesses fields of the freed `xu` object:
1. `list_del(&xu->list)` accesses `xu->list`.
2. `kfree(xu->desc.baSourceID)` accesses `xu->desc.baSourceID`.
3. `kfree(xu->desc.bmControls)` accesses `xu->desc.bmControls`.
This results in a slab-use-after-free (UAF) bug in `__list_del_entry_valid_or_report` and other subsequent memory accesses.
---
### Solution
To resolve this issue, the call to `config_item_put(item)` must be deferred to the very end of `uvcg_extension_drop()`, after all fields of the `xu` structure have been accessed and freed, and after the mutex `opts->lock` has been unlocked. This is consistent with other drop callbacks in the same file (e.g., `uvcg_frame_drop()`).
The corrected `uvcg_extension_drop()` function should look like this:
```c
static void uvcg_extension_drop(struct config_group *group, struct config_item *item)
{
struct uvcg_extension *xu = container_of(item, struct uvcg_extension, item);
struct config_item *opts_item;
struct f_uvc_opts *opts;
opts_item = group->cg_item.ci_parent->ci_parent;
opts = to_f_uvc_opts(opts_item);
mutex_lock(&opts->lock);
list_del(&xu->list);
kfree(xu->desc.baSourceID);
kfree(xu->desc.bmControls);
mutex_unlock(&opts->lock);
config_item_put(item);
}
```]
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: In the provided bug report, we see a slab-use-after-free in __list_del_entry_valid_or_report. The allocation trace shows uvcg_extension_make allocating a uvcg_extension object. The free trace shows uvcg_extension_drop freeing the same object. The crash happens in uvcg_extension_drop when it calls list_del(&xu->list). Wait, if uvcg_extension_drop is called twice on the same object, the second time it will try to access the freed object. Let's look at the free trace:
config_item_cleanup -> config_item_release -> kref_put -> config_item_put -> uvcg_extension_drop.
Wait, uvcg_extension_drop calls config_item_put(item). Is it possible that config_item_put(item) inside uvcg_extension_drop triggers the release of the item, which then frees the item (via uvcg_extension_release), and then uvcg_extension_drop continues to access xu->list?
Let's check uvcg_extension_drop.
|
| 124/4 |
2026/07/30 00:20 |
llm |
codeexpert |
0m
Model:
Tokens:
input: 4110
output: 48
thoughts: 120
Thoughts: **Investigating a Potential Slab-Use-After-Free Vulnerability**
Okay, so I've got this hunch about a possible use-after-free in `__list_del_entry_valid_or_report` tied to `uvcg_extension_make` and `uvcg_extension_drop`. That's a red flag, and I need to dig deeper. My immediate thought is to start mapping the code. I'll need to understand how these functions interact. First, I want to pinpoint exactly *where* `uvcg_extension_drop` and `uvcg_extension_make` are defined within the codebase. For speed and accuracy, I'll leverage my usual tools. I'll reach for either `grepper` or `codesearch-definition-source` to get those definitions. Let's start with `codesearch-definition-source` for `uvcg_extension_drop`. That should give me a solid starting point for tracing the execution flow.
|
| 125/4 |
2026/07/30 00:20 |
tool |
codesearch-definition-source |
0m
Args: map[Name:uvcg_extension_drop]
Results: map[SourceCode:1248: static void uvcg_extension_drop(struct config_group *group, struct config_item *item)
1249: {
1250: struct uvcg_extension *xu = container_of(item, struct uvcg_extension, item);
1251: struct config_item *opts_item;
1252: struct f_uvc_opts *opts;
1253:
1254: opts_item = group->cg_item.ci_parent->ci_parent;
1255: opts = to_f_uvc_opts(opts_item);
1256:
1257: mutex_lock(&opts->lock);
1258:
1259: config_item_put(item);
1260: list_del(&xu->list);
1261: kfree(xu->desc.baSourceID);
1262: kfree(xu->desc.bmControls);
1263:
1264: mutex_unlock(&opts->lock);
1265: }
SourceFile:drivers/usb/gadget/function/uvc_configfs.c]
|
| 126/4 |
2026/07/30 00:20 |
tool |
codesearch-definition-source |
0m
Args: map[Name:uvcg_extension_make]
Results: map[SourceCode:1267: static struct config_item *uvcg_extension_make(struct config_group *group, const char *name)
1268: {
1269: struct config_item *opts_item;
1270: struct uvcg_extension *xu;
1271: struct f_uvc_opts *opts;
1272:
1273: opts_item = group->cg_item.ci_parent->ci_parent;
1274: opts = to_f_uvc_opts(opts_item);
1275:
1276: xu = kzalloc_obj(*xu);
1277: if (!xu)
1278: return ERR_PTR(-ENOMEM);
1279:
1280: xu->desc.bLength = UVC_DT_EXTENSION_UNIT_SIZE(0, 0);
1281: xu->desc.bDescriptorType = USB_DT_CS_INTERFACE;
1282: xu->desc.bDescriptorSubType = UVC_VC_EXTENSION_UNIT;
1283: xu->desc.bNumControls = 0;
1284: xu->desc.bNrInPins = 0;
1285: xu->desc.baSourceID = NULL;
1286: xu->desc.bControlSize = 0;
1287: xu->desc.bmControls = NULL;
1288:
1289: mutex_lock(&opts->lock);
1290:
1291: xu->desc.bUnitID = ++opts->last_unit_id;
1292:
1293: config_item_init_type_name(&xu->item, name, &uvcg_extension_type);
1294: list_add_tail(&xu->list, &opts->extension_units);
1295:
1296: mutex_unlock(&opts->lock);
1297:
1298: return &xu->item;
1299: }
SourceFile:drivers/usb/gadget/function/uvc_configfs.c]
|
| 127/4 |
2026/07/30 00:20 |
llm |
codeexpert |
0m
Model:
Tokens:
input: 5374
output: 46
thoughts: 39
Thoughts: Okay, here's my take on those thoughts, tailored for an expert audience:
**Analyzing `uvcg_extension_type` and Its Release**
Alright, let's dive into the specifics of `uvcg_extension_type`. My immediate focus is on understanding its release function β how it manages its resources, memory, or whatever else it holds. That's the critical piece for avoiding leaks or dangling pointers, especially in a system like this where we're probably dealing with complex object lifetimes. I'm going to start with a `codesearch-definition-source` query. This is a quick and dirty way to get the definition of `uvcg_extension_type` which, ideally, will show me its structure, any related data, and where that release function is declared, if it exists. From there, I can begin tracing the release function and its implementation.
|
| 128/4 |
2026/07/30 00:20 |
tool |
codesearch-definition-source |
0m
Args: map[ContextFile:drivers/usb/gadget/function/uvc_configfs.c Name:uvcg_extension_type]
Results: map[SourceCode:1242: static const struct config_item_type uvcg_extension_type = {
1243: .ct_item_ops = &uvcg_extension_item_ops,
1244: .ct_attrs = uvcg_extension_attrs,
1245: .ct_owner = THIS_MODULE,
1246: };
SourceFile:drivers/usb/gadget/function/uvc_configfs.c]
|
| 129/4 |
2026/07/30 00:20 |
llm |
codeexpert |
0m
Model:
Tokens:
input: 5777
output: 48
thoughts: 14
Thoughts: Here's my thought process, summarized as you requested:
**Finding the `uvcg_extension_item_ops`**
Okay, so I need to track down `uvcg_extension_item_ops`. This implies I'm working with something related to UV coordinates, extensions, and items, probably in a graphics or rendering context. Given my expertise, I'm likely dealing with a lower-level library or framework. The "ops" suffix suggests this is likely a structure containing function pointers or a dispatch table for operations related to these items. I need to understand what this structure *does*. So, first step: Let's actually locate the definition. Where is it declared? Searching the codebase is the obvious first move, looking for a header file or source file where the variable/structure is defined. I'll need to know what files or libraries I have access to. Is there some form of auto-complete or code indexing I can use? I'll quickly look to see what I can find with those tools. Once I've found it, I can dig into the declaration and see exactly what it is. Then, I'll identify how it's used and where itβs defined. If I'm lucky, there'll be comments, but if I'm not, reverse engineering will be necessary.
|
| 130/4 |
2026/07/30 00:20 |
tool |
codesearch-definition-source |
0m
Args: map[ContextFile:drivers/usb/gadget/function/uvc_configfs.c Name:uvcg_extension_item_ops]
Results: map[SourceCode:1236: static const struct configfs_item_operations uvcg_extension_item_ops = {
1237: .release = uvcg_extension_release,
1238: .allow_link = uvcg_extension_allow_link,
1239: .drop_link = uvcg_extension_drop_link,
1240: };
SourceFile:drivers/usb/gadget/function/uvc_configfs.c]
|
| 131/4 |
2026/07/30 00:20 |
llm |
codeexpert |
0m
Model:
Tokens:
input: 6255
output: 46
thoughts: 12
Thoughts: Okay, here's my take on those thoughts, tailored for an expert audience:
**Hunting Down `uvcg_extension_release`**
Alright, let's get down to it. My initial thought is to locate the `uvcg_extension_release`. Given my experience, the context is likely buried within some configuration file, build script, or perhaps even directly in the source code of a related system. The specific directory structure, project dependencies, and potential version control systems in use will all guide my search strategy. I'll likely start with a global grep or a `find` command, perhaps leveraging wildcards or regex for a more targeted query if I have any hints about the likely location or naming conventions of the surrounding files. I'll be keeping an eye out for any related variables or constants that might point the way, perhaps `UVCG_EXTENSION`, `RELEASE_VERSION`, or similar. I'll need to remember to check for any environment variables as well, since that's a common place to define such things, and I need to keep in mind the environment where this might be used: is it during a build process, runtime, development, or some other phase? A systematic approach will be key. If it's a newer project, I might check the documentation or the project's setup scripts (like a `Makefile`, `CMakeLists.txt`, or even a configuration management tool like Ansible or Chef). Ultimately, I'll need to quickly eliminate dead ends and focus my efforts on the most promising areas.
|
| 132/4 |
2026/07/30 00:20 |
tool |
codesearch-definition-source |
0m
Args: map[ContextFile:drivers/usb/gadget/function/uvc_configfs.c Name:uvcg_extension_release]
Results: map[SourceCode:1179: static void uvcg_extension_release(struct config_item *item)
1180: {
1181: struct uvcg_extension *xu = container_of(item, struct uvcg_extension, item);
1182:
1183: kfree(xu);
1184: }
SourceFile:drivers/usb/gadget/function/uvc_configfs.c]
|
| 133/4 |
2026/07/30 00:20 |
llm |
codeexpert |
0m
Model:
Tokens:
input: 6760
output: 38
thoughts: 474
Thoughts: **Analyzing a Potential Race Condition in `uvcg_extension_drop`**
Okay, let's take a look at this `uvcg_extension_drop` function. Right away, I'm noticing something that's making me pause. Here it is:
```c
static void uvcg_extension_drop(struct config_group *group, struct config_item *item)
{
struct uvcg_extension *xu = container_of(item, struct uvcg_extension, item);
struct config_item *opts_item;
struct f_uvc_opts *opts;
opts_item = group->cg_item.ci_parent->ci_parent;
opts = to_f_uvc_opts(opts_item);
mutex_lock(&opts->lock);
config_item_put(item);
list_del(&xu->list);
kfree(xu->desc.baSourceID);
kfree(xu->desc.bmControls);
mutex_unlock(&opts->lock);
}
```
My immediate concern is the order of operations here. Specifically, `config_item_put(item)` is called *before* the calls to `list_del(&xu->list)`, `kfree(xu->desc.baSourceID)`, and `kfree(xu->desc.bmControls)`. If `config_item_put` decrements the reference count of `item` and, critically, if the last reference is dropped, it will trigger the release function associated with the item, which I'm assuming is `uvcg_extension_release(item)`. Now, if that release function is what frees the `xu` structure, then we've got a race.
Essentially, we're calling `list_del(&xu->list)` and `kfree()` on a structure that *might* already be freed by the release function, `uvcg_extension_release(item)`. This seems highly problematic! It's likely that accessing `xu` after the release function has run will lead to a use-after-free situation.
The next question I have is, why is `config_item_put(item)` even *called* within this `uvcg_extension_drop` function? Is this redundant? Let me dig a bit into how configfs handles item dropping. When a user deletes a directory in configfs, it triggers the `drop_item` or `drop_group` method of the parent `config_group`. Does configfs hold a reference to the item when calling `drop_item`? I need to understand if the framework itself handles a reference count and release.
I need to understand if this is standard behavior. Let me now search the configfs code itself, or maybe in other `uvc_configfs.c` driver implementations. Perhaps there are other examples of `drop_item` implementations. Time to search for other instances of `_drop` in that file, and around the general drivers directory. Let's see if this pattern is common or if it's a bug that needs fixing.
|
| 134/4 |
2026/07/30 00:20 |
tool |
grepper |
0m
Args: map[Expression:_drop PathPrefix:drivers/usb/gadget/function/uvc_configfs.c]
Results: map[Output:drivers/usb/gadget/function/uvc_configfs.c=1186=static int uvcg_extension_allow_link(struct config_item *src, struct config_item *tgt)
--
drivers/usb/gadget/function/uvc_configfs.c-1214-
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-1216-{
--
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-1238- .allow_link = uvcg_extension_allow_link,
drivers/usb/gadget/function/uvc_configfs.c:1239: .drop_link = uvcg_extension_drop_link,
drivers/usb/gadget/function/uvc_configfs.c-1240-};
--
drivers/usb/gadget/function/uvc_configfs.c=1242=static const struct config_item_type uvcg_extension_type = {
--
drivers/usb/gadget/function/uvc_configfs.c-1247-
drivers/usb/gadget/function/uvc_configfs.c:1248:static void uvcg_extension_drop(struct config_group *group, struct config_item *item)
drivers/usb/gadget/function/uvc_configfs.c-1249-{
--
drivers/usb/gadget/function/uvc_configfs.c=1301=static const struct configfs_group_operations uvcg_extensions_grp_ops = {
drivers/usb/gadget/function/uvc_configfs.c-1302- .make_item = uvcg_extension_make,
drivers/usb/gadget/function/uvc_configfs.c:1303: .drop_item = uvcg_extension_drop,
drivers/usb/gadget/function/uvc_configfs.c-1304-};
--
drivers/usb/gadget/function/uvc_configfs.c=1340=static int uvcg_control_class_allow_link(struct config_item *src,
--
drivers/usb/gadget/function/uvc_configfs.c-1381-
drivers/usb/gadget/function/uvc_configfs.c:1382:static void uvcg_control_class_drop_link(struct config_item *src,
drivers/usb/gadget/function/uvc_configfs.c-1383- struct config_item *target)
--
drivers/usb/gadget/function/uvc_configfs.c=1417=static const struct configfs_item_operations uvcg_control_class_item_ops = {
--
drivers/usb/gadget/function/uvc_configfs.c-1419- .allow_link = uvcg_control_class_allow_link,
drivers/usb/gadget/function/uvc_configfs.c:1420: .drop_link = uvcg_control_class_drop_link,
drivers/usb/gadget/function/uvc_configfs.c-1421-};
--
drivers/usb/gadget/function/uvc_configfs.c=1602=static int uvcg_format_allow_link(struct config_item *src, struct config_item *tgt)
--
drivers/usb/gadget/function/uvc_configfs.c-1644-
drivers/usb/gadget/function/uvc_configfs.c:1645:static void uvcg_format_drop_link(struct config_item *src, struct config_item *tgt)
drivers/usb/gadget/function/uvc_configfs.c-1646-{
--
drivers/usb/gadget/function/uvc_configfs.c=1667=static const struct configfs_item_operations uvcg_format_item_operations = {
--
drivers/usb/gadget/function/uvc_configfs.c-1669- .allow_link = uvcg_format_allow_link,
drivers/usb/gadget/function/uvc_configfs.c:1670: .drop_link = uvcg_format_drop_link,
drivers/usb/gadget/function/uvc_configfs.c-1671-};
--
drivers/usb/gadget/function/uvc_configfs.c=1738=static int uvcg_streaming_header_allow_link(struct config_item *src,
--
drivers/usb/gadget/function/uvc_configfs.c-1804-
drivers/usb/gadget/function/uvc_configfs.c:1805:static void uvcg_streaming_header_drop_link(struct config_item *src,
drivers/usb/gadget/function/uvc_configfs.c-1806- struct config_item *target)
--
drivers/usb/gadget/function/uvc_configfs.c=1843=static const struct configfs_item_operations uvcg_streaming_header_item_ops = {
--
drivers/usb/gadget/function/uvc_configfs.c-1845- .allow_link = uvcg_streaming_header_allow_link,
drivers/usb/gadget/function/uvc_configfs.c:1846: .drop_link = uvcg_streaming_header_drop_link,
drivers/usb/gadget/function/uvc_configfs.c-1847-};
--
drivers/usb/gadget/function/uvc_configfs.c=2157=static struct config_item *uvcg_frame_make(struct config_group *group,
--
drivers/usb/gadget/function/uvc_configfs.c-2219-
drivers/usb/gadget/function/uvc_configfs.c:2220:static void uvcg_frame_drop(struct config_group *group, struct config_item *item)
drivers/usb/gadget/function/uvc_configfs.c-2221-{
--
drivers/usb/gadget/function/uvc_configfs.c=2264=static const struct configfs_group_operations uvcg_uncompressed_group_ops = {
drivers/usb/gadget/function/uvc_configfs.c-2265- .make_item = uvcg_frame_make,
drivers/usb/gadget/function/uvc_configfs.c:2266: .drop_item = uvcg_frame_drop,
drivers/usb/gadget/function/uvc_configfs.c-2267-};
--
drivers/usb/gadget/function/uvc_configfs.c=2528=static const struct configfs_group_operations uvcg_mjpeg_group_ops = {
drivers/usb/gadget/function/uvc_configfs.c-2529- .make_item = uvcg_frame_make,
drivers/usb/gadget/function/uvc_configfs.c:2530: .drop_item = uvcg_frame_drop,
drivers/usb/gadget/function/uvc_configfs.c-2531-};
--
drivers/usb/gadget/function/uvc_configfs.c=2718=static const struct configfs_group_operations uvcg_framebased_group_ops = {
drivers/usb/gadget/function/uvc_configfs.c-2719- .make_item = uvcg_frame_make,
drivers/usb/gadget/function/uvc_configfs.c:2720: .drop_item = uvcg_frame_drop,
drivers/usb/gadget/function/uvc_configfs.c-2721-};
--
drivers/usb/gadget/function/uvc_configfs.c=3420=static int uvcg_streaming_class_allow_link(struct config_item *src,
--
drivers/usb/gadget/function/uvc_configfs.c-3492-
drivers/usb/gadget/function/uvc_configfs.c:3493:static void uvcg_streaming_class_drop_link(struct config_item *src,
drivers/usb/gadget/function/uvc_configfs.c-3494- struct config_item *target)
--
drivers/usb/gadget/function/uvc_configfs.c=3533=static const struct configfs_item_operations uvcg_streaming_class_item_ops = {
--
drivers/usb/gadget/function/uvc_configfs.c-3535- .allow_link = uvcg_streaming_class_allow_link,
drivers/usb/gadget/function/uvc_configfs.c:3536: .drop_link = uvcg_streaming_class_drop_link,
drivers/usb/gadget/function/uvc_configfs.c-3537-};
--
drivers/usb/gadget/function/uvc_configfs.c=3643=static int uvc_func_allow_link(struct config_item *src, struct config_item *tgt)
--
drivers/usb/gadget/function/uvc_configfs.c-3683-
drivers/usb/gadget/function/uvc_configfs.c:3684:static void uvc_func_drop_link(struct config_item *src, struct config_item *tgt)
drivers/usb/gadget/function/uvc_configfs.c-3685-{
--
drivers/usb/gadget/function/uvc_configfs.c=3701=static const struct configfs_item_operations uvc_func_item_ops = {
--
drivers/usb/gadget/function/uvc_configfs.c-3703- .allow_link = uvc_func_allow_link,
drivers/usb/gadget/function/uvc_configfs.c:3704: .drop_link = uvc_func_drop_link,
drivers/usb/gadget/function/uvc_configfs.c-3705-};
]
|
| 135/4 |
2026/07/30 00:20 |
llm |
codeexpert |
0m
Model:
Tokens:
input: 10628
output: 46
thoughts: 20
Thoughts: Okay, let's dive into this `uvcg_frame_drop` implementation. I need to understand exactly how this mechanism works. I'm going to carefully examine the code related to `uvcg_frame_drop` to see the nuts and bolts of it. I'm particularly interested in any subtle aspects of the algorithm, and any assumptions the developers made that might impact its performance or reliability in edge cases. I'll be looking for things like the specific conditions that trigger a frame drop, how the system decides which frames to discard, any buffering or queueing strategies involved, and if it interacts with other parts of the system and, if so, how. I need to form a solid mental model of this.
|
| 136/4 |
2026/07/30 00:20 |
tool |
codesearch-definition-source |
0m
Args: map[ContextFile:drivers/usb/gadget/function/uvc_configfs.c Name:uvcg_frame_drop]
Results: map[SourceCode:2220: static void uvcg_frame_drop(struct config_group *group, struct config_item *item)
2221: {
2222: struct uvcg_format *fmt;
2223: struct f_uvc_opts *opts;
2224: struct config_item *opts_item;
2225: struct uvcg_frame *target_frm = NULL;
2226: struct uvcg_frame_ptr *frame_ptr, *tmp;
2227:
2228: opts_item = group->cg_item.ci_parent->ci_parent->ci_parent;
2229: opts = to_f_uvc_opts(opts_item);
2230:
2231: mutex_lock(&opts->lock);
2232: target_frm = container_of(item, struct uvcg_frame, item);
2233: fmt = to_uvcg_format(&group->cg_item);
2234:
2235: list_for_each_entry_safe(frame_ptr, tmp, &fmt->frames, entry)
2236: if (frame_ptr->frm == target_frm) {
2237: list_del(&frame_ptr->entry);
2238: kfree(frame_ptr);
2239: --fmt->num_frames;
2240: break;
2241: }
2242: mutex_unlock(&opts->lock);
2243:
2244: config_item_put(item);
2245: }
SourceFile:drivers/usb/gadget/function/uvc_configfs.c]
|
| 137/4 |
2026/07/30 00:20 |
llm |
codeexpert |
0m
Model:
Tokens:
input: 11305
output: 28
thoughts: 425
Thoughts: **Analyzing Potential Reference Counting Issues in UVCG Code**
Okay, so I'm looking at this `uvcg_frame_drop` function, and the `config_item_put(item)` call is at the end, after all the item access. Seems normal enough. But then I get to `uvcg_extension_drop`, and *whoa*, `config_item_put(item)` is right at the start. That's... different. It comes *before* the `list_del`, the `kfree` of the buffers.
Why is `uvcg_extension_drop` even calling `config_item_put(item)`? It feels redundant. Let's trace this back. Does `uvcg_extension_make` do anything with the reference count? Nope. It just returns `&xu->item`. Let's look again at that `uvcg_extension_make` function... ah, `config_item_init_type_name` *does* initialize the refcount to 1!
Okay, so `uvcg_extension_make` creates the item and initializes the refcount to 1, then the configfs drops the item it calls `drop_item` which is actually `uvcg_extension_drop`. Now I'm starting to get worried: does configfs, after calling `drop_item` *itself* call `config_item_put` on the item? If it does, then the double `put` is what causes the crash. I'm going to jump into the configfs source code. I need to find out how `drop_item` is called and how the refcounting is actually managed inside configfs itself. Let's search for `drop_item` in `fs/configfs/`.
|
| 138/4 |
2026/07/30 00:20 |
tool |
grepper |
0m
Args: map[Expression:drop_item PathPrefix:fs/configfs/]
Results: map[Output:fs/configfs/dir.c=938=static void client_disconnect_notify(struct config_item *parent_item,
--
fs/configfs/dir.c-956- */
fs/configfs/dir.c:957:static void client_drop_item(struct config_item *parent_item,
fs/configfs/dir.c-958- struct config_item *item)
--
fs/configfs/dir.c-965- /*
fs/configfs/dir.c:966: * If ->drop_item() exists, it is responsible for the
fs/configfs/dir.c-967- * config_item_put().
fs/configfs/dir.c-968- */
fs/configfs/dir.c:969: if (type->ct_group_ops && type->ct_group_ops->drop_item)
fs/configfs/dir.c:970: type->ct_group_ops->drop_item(to_config_group(parent_item),
fs/configfs/dir.c-971- item);
--
fs/configfs/dir.c=1289=static struct dentry *configfs_mkdir(struct mnt_idmap *idmap, struct inode *dir,
--
fs/configfs/dir.c-1442- unlink_obj(item);
fs/configfs/dir.c:1443: client_drop_item(parent_item, item);
fs/configfs/dir.c-1444-
--
fs/configfs/dir.c=1469=static int configfs_rmdir(struct inode *dir, struct dentry *dentry)
--
fs/configfs/dir.c-1566-
fs/configfs/dir.c:1567: client_drop_item(parent_item, item);
fs/configfs/dir.c-1568- mutex_unlock(&subsys->su_mutex);
--
fs/configfs/symlink.c=213=int configfs_unlink(struct inode *dir, struct dentry *dentry)
--
fs/configfs/symlink.c-237- * decrementing target's ->s_links, so that the order of
fs/configfs/symlink.c:238: * drop_link(this, target) and drop_item(target) is preserved.
fs/configfs/symlink.c-239- */
]
|
| 139/4 |
2026/07/30 00:20 |
llm |
codeexpert |
0m
Model:
Tokens:
input: 12774
output: 37
thoughts: 239
Thoughts: **Analyzing `configfs` Item Dropping in Kernel Code**
Okay, so I'm looking at this snippet in `fs/configfs/dir.c`, specifically this comment regarding the `drop_item` operation:
```c
/*
* If ->drop_item() exists, it is responsible for the
* config_item_put().
*/
if (type->ct_group_ops && type->ct_group_ops->drop_item)
type->ct_group_ops->drop_item(to_config_group(parent_item),
item);
```
My initial thought is, "Yep, that comment nails it. The `drop_item` function *is* the one responsible for calling `config_item_put(item)`." But, hold on a second... this triggers a concern. If `drop_item` calls `config_item_put(item)` *before* it, say, accesses `xu->list` or does any field cleanup of `xu`, we've got a potential use-after-free issue, right? Because `config_item_put(item)` might actually free the `xu` struct itself!
Let's not jump to conclusions though. To be absolutely certain, let's pull up the implementation of `client_drop_item` from the same file, to see *exactly* what's going on. I'll use `read-file` or `codesearch-definition-source` to get that code and confirm.
|
| 140/4 |
2026/07/30 00:20 |
tool |
codesearch-definition-source |
0m
Args: map[ContextFile:fs/configfs/dir.c Name:client_drop_item]
Results: map[SourceCode: 951: /*
952: * Drop the initial reference from make_item()/make_group()
953: * This function assumes that reference is held on item
954: * and that item holds a valid reference to the parent. Also, it
955: * assumes the caller has validated ci_type.
956: */
957: static void client_drop_item(struct config_item *parent_item,
958: struct config_item *item)
959: {
960: const struct config_item_type *type;
961:
962: type = parent_item->ci_type;
963: BUG_ON(!type);
964:
965: /*
966: * If ->drop_item() exists, it is responsible for the
967: * config_item_put().
968: */
969: if (type->ct_group_ops && type->ct_group_ops->drop_item)
970: type->ct_group_ops->drop_item(to_config_group(parent_item),
971: item);
972: else
973: config_item_put(item);
974: }
SourceFile:fs/configfs/dir.c]
|
| 141/4 |
2026/07/30 00:20 |
llm |
codeexpert |
0m
Model:
Tokens:
input: 13760
output: 46
thoughts: 397
Thoughts: **My Analysis of a Potential Use-After-Free Vulnerability**
Okay, so I've been digging into this code and I think I've found something interesting, potentially a use-after-free bug. My initial thought is that if `->drop_item()` is defined, then it's responsible for calling `config_item_put(item)`. Let's look at `uvcg_extension_drop`:
```c
static void uvcg_extension_drop(struct config_group *group, struct config_item *item)
{
struct uvcg_extension *xu = container_of(item, struct uvcg_extension, item);
struct config_item *opts_item;
struct f_uvc_opts *opts;
opts_item = group->cg_item.ci_parent->ci_parent;
opts = to_f_uvc_opts(opts_item);
mutex_lock(&opts->lock);
config_item_put(item);
list_del(&xu->list);
kfree(xu->desc.baSourceID);
kfree(xu->desc.bmControls);
mutex_unlock(&opts->lock);
}
```
Now, the crucial bit: `config_item_put(item)` is called *before* the other cleanup operations within the function. My understanding is that `config_item_put(item)` will decrement the reference count of `item`. If this reference count goes to zero, the associated `uvcg_extension_release(item)` will be triggered, and *that* function calls `kfree(xu)`. This frees the `xu` struct.
The problem? Afterward, `list_del(&xu->list)` attempts to access `xu->list`, which is now freed memory! Similarly, the subsequent `kfree(xu->desc.baSourceID)` and `kfree(xu->desc.bmControls)` calls are trying to access `xu->desc`, which is *also* freed! This is a textbook example of a use-after-free vulnerability.
Okay, I need to check something... I want to quickly figure out *when* this bug was introduced. Time to run a quick `git-log` or `git-blame` on the `uvcg_extension_drop` function to trace its history.
|
| 142/4 |
2026/07/30 00:21 |
tool |
git-blame |
0m
Args: map[End:1266 File:drivers/usb/gadget/function/uvc_configfs.c Start:1248]
Results: map[Output:0525210c98402 1248) static void uvcg_extension_drop(struct config_group *group, struct config_item *item)
0525210c98402 1249) {
0525210c98402 1250) struct uvcg_extension *xu = container_of(item, struct uvcg_extension, item);
0525210c98402 1251) struct config_item *opts_item;
0525210c98402 1252) struct f_uvc_opts *opts;
0525210c98402 1253)
0525210c98402 1254) opts_item = group->cg_item.ci_parent->ci_parent;
0525210c98402 1255) opts = to_f_uvc_opts(opts_item);
0525210c98402 1256)
0525210c98402 1257) mutex_lock(&opts->lock);
0525210c98402 1258)
0525210c98402 1259) config_item_put(item);
0525210c98402 1260) list_del(&xu->list);
0525210c98402 1261) kfree(xu->desc.baSourceID);
0525210c98402 1262) kfree(xu->desc.bmControls);
0525210c98402 1263)
0525210c98402 1264) mutex_unlock(&opts->lock);
0525210c98402 1265) }
0525210c98402 1266)
]
|
| 143/4 |
2026/07/30 00:21 |
llm |
codeexpert |
0m
Model:
Tokens:
input: 15306
output: 28
thoughts: 27
Thoughts: Okay, here's my interpretation of that request, formatted as requested:
**Deconstructing Commit 0525210c98402 with `git show`**
Alright, let's dive into this specific commit: `0525210c98402`. I'm going to pull it up using `git show`. That should give me the whole shebang: the commit message, the author and date, and, crucially, the diffs. I need to see exactly *what* changed and how it changed the codebase at that point in time. This is standard operating procedure, of course, but it's the starting point for understanding the impact of this particular change. I'm already mentally running through the areas I expect to be affected based on the commit hash, so hopefully, there aren't too many surprises. Let's see what we find.
|
| 144/4 |
2026/07/30 00:21 |
tool |
git-show |
0m
Args: map[Commit:0525210c98402]
Results: map[Output:commit 0525210c9840229e42c6b68e886c72a75a67cf8e
Author: Daniel Scally <dan.scally@ideasonboard.com>
Date: Mon Feb 6 16:17:54 2023 +0000
usb: gadget: uvc: Allow definition of XUs in configfs
The UVC gadget at present has no support for extension units. Add the
infrastructure to uvc_configfs.c that allows users to create XUs via
configfs. These will be stored in a new child of uvcg_control_grp_type
with the name "extensions".
Reported-by: kernel test robot <lkp@intel.com>
Signed-off-by: Daniel Scally <dan.scally@ideasonboard.com>
Link: https://lore.kernel.org/r/20230206161802.892954-4-dan.scally@ideasonboard.com
Signed-off-by: Greg Kroah-Hartman <gregkh@linuxfoundation.org>
diff --git a/Documentation/ABI/testing/configfs-usb-gadget-uvc b/Documentation/ABI/testing/configfs-usb-gadget-uvc
index c2323f2b069b..80b98a4a4d0f 100644
--- a/Documentation/ABI/testing/configfs-usb-gadget-uvc
+++ b/Documentation/ABI/testing/configfs-usb-gadget-uvc
@@ -113,6 +113,34 @@ Description: Default processing unit descriptors
bUnitID a non-zero id of this unit
=============== ========================================
+What: /config/usb-gadget/gadget/functions/uvc.name/control/extensions
+Date: Nov 2022
+KernelVersion: 6.1
+Description: Extension unit descriptors
+
+What: /config/usb-gadget/gadget/functions/uvc.name/control/extensions/name
+Date: Nov 2022
+KernelVersion: 6.1
+Description: Extension Unit (XU) Descriptor
+
+ bLength, bUnitID and iExtension are read-only. All others are
+ read-write.
+
+ ================= ========================================
+ bLength size of the descriptor in bytes
+ bUnitID non-zero ID of this unit
+ guidExtensionCode Vendor-specific code identifying the XU
+ bNumControls number of controls in this XU
+ bNrInPins number of input pins for this unit
+ baSourceID list of the IDs of the units or terminals
+ to which this XU is connected
+ bControlSize size of the bmControls field in bytes
+ bmControls list of bitmaps detailing which vendor
+ specific controls are supported
+ iExtension index of a string descriptor that describes
+ this extension unit
+ ================= ========================================
+
What: /config/usb-gadget/gadget/functions/uvc.name/control/header
Date: Dec 2014
KernelVersion: 4.0
diff --git a/drivers/usb/gadget/function/f_uvc.c b/drivers/usb/gadget/function/f_uvc.c
index 835e121a806f..443333471b4d 100644
--- a/drivers/usb/gadget/function/f_uvc.c
+++ b/drivers/usb/gadget/function/f_uvc.c
@@ -865,6 +865,13 @@ static struct usb_function_instance *uvc_alloc_inst(void)
od->bSourceID = 2;
od->iTerminal = 0;
+ /*
+ * With the ability to add XUs to the UVC function graph, we need to be
+ * able to allocate unique unit IDs to them. The IDs are 1-based, with
+ * the CT, PU and OT above consuming the first 3.
+ */
+ opts->last_unit_id = 3;
+
/* Prepare fs control class descriptors for configfs-based gadgets */
ctl_cls = opts->uvc_fs_control_cls;
ctl_cls[0] = NULL; /* assigned elsewhere by configfs */
@@ -885,6 +892,8 @@ static struct usb_function_instance *uvc_alloc_inst(void)
opts->ss_control =
(const struct uvc_descriptor_header * const *)ctl_cls;
+ INIT_LIST_HEAD(&opts->extension_units);
+
opts->streaming_interval = 1;
opts->streaming_maxpacket = 1024;
snprintf(opts->function_name, sizeof(opts->function_name), "UVC Camera");
diff --git a/drivers/usb/gadget/function/u_uvc.h b/drivers/usb/gadget/function/u_uvc.h
index 67cf319e9c2d..0345b8fc36ff 100644
--- a/drivers/usb/gadget/function/u_uvc.h
+++ b/drivers/usb/gadget/function/u_uvc.h
@@ -28,6 +28,7 @@ struct f_uvc_opts {
unsigned int control_interface;
unsigned int streaming_interface;
char function_name[32];
+ unsigned int last_unit_id;
bool enable_interrupt_ep;
@@ -65,6 +66,12 @@ struct f_uvc_opts {
struct uvc_descriptor_header *uvc_fs_control_cls[5];
struct uvc_descriptor_header *uvc_ss_control_cls[5];
+ /*
+ * Control descriptors for extension units. There could be any number
+ * of these, including none at all.
+ */
+ struct list_head extension_units;
+
/*
* Streaming descriptors for full-speed, high-speed and super-speed.
* Used by configfs only, must not be touched by legacy gadgets. The
diff --git a/drivers/usb/gadget/function/uvc_configfs.c b/drivers/usb/gadget/function/uvc_configfs.c
index b32ecbdfd88d..c365f323af45 100644
--- a/drivers/usb/gadget/function/uvc_configfs.c
+++ b/drivers/usb/gadget/function/uvc_configfs.c
@@ -662,6 +662,485 @@ static const struct uvcg_config_group_type uvcg_terminal_grp_type = {
},
};
+/* -----------------------------------------------------------------------------
+ * control/extensions
+ */
+
+#define UVCG_EXTENSION_ATTR(cname, aname, ro...) \
+static ssize_t uvcg_extension_##cname##_show(struct config_item *item, \
+ char *page) \
+{ \
+ struct config_group *group = to_config_group(item->ci_parent); \
+ struct mutex *su_mutex = &group->cg_subsys->su_mutex; \
+ struct uvcg_extension *xu = to_uvcg_extension(item); \
+ struct config_item *opts_item; \
+ struct f_uvc_opts *opts; \
+ int ret; \
+ \
+ mutex_lock(su_mutex); \
+ \
+ opts_item = item->ci_parent->ci_parent->ci_parent; \
+ opts = to_f_uvc_opts(opts_item); \
+ \
+ mutex_lock(&opts->lock); \
+ ret = sprintf(page, "%u\n", xu->desc.aname); \
+ mutex_unlock(&opts->lock); \
+ \
+ mutex_unlock(su_mutex); \
+ \
+ return ret; \
+} \
+UVC_ATTR##ro(uvcg_extension_, cname, aname)
+
+UVCG_EXTENSION_ATTR(b_length, bLength, _RO);
+UVCG_EXTENSION_ATTR(b_unit_id, bUnitID, _RO);
+UVCG_EXTENSION_ATTR(i_extension, iExtension, _RO);
+
+static ssize_t uvcg_extension_b_num_controls_store(struct config_item *item,
+ const char *page, size_t len)
+{
+ struct config_group *group = to_config_group(item->ci_parent);
+ struct mutex *su_mutex = &group->cg_subsys->su_mutex;
+ struct uvcg_extension *xu = to_uvcg_extension(item);
+ struct config_item *opts_item;
+ struct f_uvc_opts *opts;
+ int ret;
+ u8 num;
+
+ mutex_lock(su_mutex);
+
+ opts_item = item->ci_parent->ci_parent->ci_parent;
+ opts = to_f_uvc_opts(opts_item);
+
+ ret = kstrtou8(page, 0, &num);
+ if (ret)
+ return ret;
+
+ mutex_lock(&opts->lock);
+ xu->desc.bNumControls = num;
+ mutex_unlock(&opts->lock);
+
+ mutex_unlock(su_mutex);
+
+ return len;
+}
+UVCG_EXTENSION_ATTR(b_num_controls, bNumControls);
+
+/*
+ * In addition to storing bNrInPins, this function needs to realloc the
+ * memory for the baSourceID array and additionally expand bLength.
+ */
+static ssize_t uvcg_extension_b_nr_in_pins_store(struct config_item *item,
+ const char *page, size_t len)
+{
+ struct config_group *group = to_config_group(item->ci_parent);
+ struct mutex *su_mutex = &group->cg_subsys->su_mutex;
+ struct uvcg_extension *xu = to_uvcg_extension(item);
+ struct config_item *opts_item;
+ struct f_uvc_opts *opts;
+ void *tmp_buf;
+ int ret;
+ u8 num;
+
+ mutex_lock(su_mutex);
+
+ opts_item = item->ci_parent->ci_parent->ci_parent;
+ opts = to_f_uvc_opts(opts_item);
+
+ ret = kstrtou8(page, 0, &num);
+ if (ret)
+ return ret;
+
+ mutex_lock(&opts->lock);
+
+ if (num == xu->desc.bNrInPins) {
+ ret = len;
+ goto unlock;
+ }
+
+ tmp_buf = krealloc_array(xu->desc.baSourceID, num, sizeof(u8),
+ GFP_KERNEL | __GFP_ZERO);
+ if (!tmp_buf) {
+ ret = -ENOMEM;
+ goto unlock;
+ }
+
+ xu->desc.baSourceID = tmp_buf;
+ xu->desc.bNrInPins = num;
+ xu->desc.bLength = UVC_DT_EXTENSION_UNIT_SIZE(xu->desc.bNrInPins,
+ xu->desc.bControlSize);
+
+ ret = len;
+
+unlock:
+ mutex_unlock(&opts->lock);
+ mutex_unlock(su_mutex);
+ return ret;
+}
+UVCG_EXTENSION_ATTR(b_nr_in_pins, bNrInPins);
+
+/*
+ * In addition to storing bControlSize, this function needs to realloc the
+ * memory for the bmControls array and additionally expand bLength.
+ */
+static ssize_t uvcg_extension_b_control_size_store(struct config_item *item,
+ const char *page, size_t len)
+{
+ struct config_group *group = to_config_group(item->ci_parent);
+ struct mutex *su_mutex = &group->cg_subsys->su_mutex;
+ struct uvcg_extension *xu = to_uvcg_extension(item);
+ struct config_item *opts_item;
+ struct f_uvc_opts *opts;
+ void *tmp_buf;
+ int ret;
+ u8 num;
+
+ mutex_lock(su_mutex);
+
+ opts_item = item->ci_parent->ci_parent->ci_parent;
+ opts = to_f_uvc_opts(opts_item);
+
+ ret = kstrtou8(page, 0, &num);
+ if (ret)
+ return ret;
+
+ mutex_lock(&opts->lock);
+
+ if (num == xu->desc.bControlSize) {
+ ret = len;
+ goto unlock;
+ }
+
+ tmp_buf = krealloc_array(xu->desc.bmControls, num, sizeof(u8),
+ GFP_KERNEL | __GFP_ZERO);
+ if (!tmp_buf) {
+ ret = -ENOMEM;
+ goto unlock;
+ }
+
+ xu->desc.bmControls = tmp_buf;
+ xu->desc.bControlSize = num;
+ xu->desc.bLength = UVC_DT_EXTENSION_UNIT_SIZE(xu->desc.bNrInPins,
+ xu->desc.bControlSize);
+
+ ret = len;
+
+unlock:
+ mutex_unlock(&opts->lock);
+ mutex_unlock(su_mutex);
+ return ret;
+}
+
+UVCG_EXTENSION_ATTR(b_control_size, bControlSize);
+
+static ssize_t uvcg_extension_guid_extension_code_show(struct config_item *item,
+ char *page)
+{
+ struct config_group *group = to_config_group(item->ci_parent);
+ struct mutex *su_mutex = &group->cg_subsys->su_mutex;
+ struct uvcg_extension *xu = to_uvcg_extension(item);
+ struct config_item *opts_item;
+ struct f_uvc_opts *opts;
+
+ mutex_lock(su_mutex);
+
+ opts_item = item->ci_parent->ci_parent->ci_parent;
+ opts = to_f_uvc_opts(opts_item);
+
+ mutex_lock(&opts->lock);
+ memcpy(page, xu->desc.guidExtensionCode, sizeof(xu->desc.guidExtensionCode));
+ mutex_unlock(&opts->lock);
+
+ mutex_unlock(su_mutex);
+
+ return sizeof(xu->desc.guidExtensionCode);
+}
+
+static ssize_t uvcg_extension_guid_extension_code_store(struct config_item *item,
+ const char *page, size_t len)
+{
+ struct config_group *group = to_config_group(item->ci_parent);
+ struct mutex *su_mutex = &group->cg_subsys->su_mutex;
+ struct uvcg_extension *xu = to_uvcg_extension(item);
+ struct config_item *opts_item;
+ struct f_uvc_opts *opts;
+ int ret;
+
+ mutex_lock(su_mutex);
+
+ opts_item = item->ci_parent->ci_parent->ci_parent;
+ opts = to_f_uvc_opts(opts_item);
+
+ mutex_lock(&opts->lock);
+ memcpy(xu->desc.guidExtensionCode, page,
+ min(sizeof(xu->desc.guidExtensionCode), len));
+ mutex_unlock(&opts->lock);
+
+ mutex_unlock(su_mutex);
+
+ ret = sizeof(xu->desc.guidExtensionCode);
+
+ return ret;
+}
+
+UVC_ATTR(uvcg_extension_, guid_extension_code, guidExtensionCode);
+
+static ssize_t uvcg_extension_ba_source_id_show(struct config_item *item,
+ char *page)
+{
+ struct config_group *group = to_config_group(item->ci_parent);
+ struct mutex *su_mutex = &group->cg_subsys->su_mutex;
+ struct uvcg_extension *xu = to_uvcg_extension(item);
+ struct config_item *opts_item;
+ struct f_uvc_opts *opts;
+ char *pg = page;
+ int ret, i;
+
+ mutex_lock(su_mutex);
+
+ opts_item = item->ci_parent->ci_parent->ci_parent;
+ opts = to_f_uvc_opts(opts_item);
+
+ mutex_lock(&opts->lock);
+ for (ret = 0, i = 0; i < xu->desc.bNrInPins; ++i) {
+ ret += sprintf(pg, "%u\n", xu->desc.baSourceID[i]);
+ pg = page + ret;
+ }
+ mutex_unlock(&opts->lock);
+
+ mutex_unlock(su_mutex);
+
+ return ret;
+}
+
+static ssize_t uvcg_extension_ba_source_id_store(struct config_item *item,
+ const char *page, size_t len)
+{
+ struct config_group *group = to_config_group(item->ci_parent);
+ struct mutex *su_mutex = &group->cg_subsys->su_mutex;
+ struct uvcg_extension *xu = to_uvcg_extension(item);
+ struct config_item *opts_item;
+ struct f_uvc_opts *opts;
+ u8 *source_ids, *iter;
+ int ret, n = 0;
+
+ mutex_lock(su_mutex);
+
+ opts_item = item->ci_parent->ci_parent->ci_parent;
+ opts = to_f_uvc_opts(opts_item);
+
+ mutex_lock(&opts->lock);
+
+ ret = __uvcg_iter_item_entries(page, len, __uvcg_count_item_entries, &n,
+ sizeof(u8));
+ if (ret)
+ goto unlock;
+
+ iter = source_ids = kcalloc(n, sizeof(u8), GFP_KERNEL);
+ if (!source_ids) {
+ ret = -ENOMEM;
+ goto unlock;
+ }
+
+ ret = __uvcg_iter_item_entries(page, len, __uvcg_fill_item_entries, &iter,
+ sizeof(u8));
+ if (ret) {
+ kfree(source_ids);
+ goto unlock;
+ }
+
+ kfree(xu->desc.baSourceID);
+ xu->desc.baSourceID = source_ids;
+ xu->desc.bNrInPins = n;
+ xu->desc.bLength = UVC_DT_EXTENSION_UNIT_SIZE(xu->desc.bNrInPins,
+ xu->desc.bControlSize);
+
+ ret = len;
+
+unlock:
+ mutex_unlock(&opts->lock);
+ mutex_unlock(su_mutex);
+ return ret;
+}
+UVC_ATTR(uvcg_extension_, ba_source_id, baSourceID);
+
+static ssize_t uvcg_extension_bm_controls_show(struct config_item *item,
+ char *page)
+{
+ struct config_group *group = to_config_group(item->ci_parent);
+ struct mutex *su_mutex = &group->cg_subsys->su_mutex;
+ struct uvcg_extension *xu = to_uvcg_extension(item);
+ struct config_item *opts_item;
+ struct f_uvc_opts *opts;
+ char *pg = page;
+ int ret, i;
+
+ mutex_lock(su_mutex);
+
+ opts_item = item->ci_parent->ci_parent->ci_parent;
+ opts = to_f_uvc_opts(opts_item);
+
+ mutex_lock(&opts->lock);
+ for (ret = 0, i = 0; i < xu->desc.bControlSize; ++i) {
+ ret += sprintf(pg, "0x%02x\n", xu->desc.bmControls[i]);
+ pg = page + ret;
+ }
+ mutex_unlock(&opts->lock);
+
+ mutex_unlock(su_mutex);
+
+ return ret;
+}
+
+static ssize_t uvcg_extension_bm_controls_store(struct config_item *item,
+ const char *page, size_t len)
+{
+ struct config_group *group = to_config_group(item->ci_parent);
+ struct mutex *su_mutex = &group->cg_subsys->su_mutex;
+ struct uvcg_extension *xu = to_uvcg_extension(item);
+ struct config_item *opts_item;
+ struct f_uvc_opts *opts;
+ u8 *bm_controls, *iter;
+ int ret, n = 0;
+
+ mutex_lock(su_mutex);
+
+ opts_item = item->ci_parent->ci_parent->ci_parent;
+ opts = to_f_uvc_opts(opts_item);
+
+ mutex_lock(&opts->lock);
+
+ ret = __uvcg_iter_item_entries(page, len, __uvcg_count_item_entries, &n,
+ sizeof(u8));
+ if (ret)
+ goto unlock;
+
+ iter = bm_controls = kcalloc(n, sizeof(u8), GFP_KERNEL);
+ if (!bm_controls) {
+ ret = -ENOMEM;
+ goto unlock;
+ }
+
+ ret = __uvcg_iter_item_entries(page, len, __uvcg_fill_item_entries, &iter,
+ sizeof(u8));
+ if (ret) {
+ kfree(bm_controls);
+ goto unlock;
+ }
+
+ kfree(xu->desc.bmControls);
+ xu->desc.bmControls = bm_controls;
+ xu->desc.bControlSize = n;
+ xu->desc.bLength = UVC_DT_EXTENSION_UNIT_SIZE(xu->desc.bNrInPins,
+ xu->desc.bControlSize);
+
+ ret = len;
+
+unlock:
+ mutex_unlock(&opts->lock);
+ mutex_unlock(su_mutex);
+ return ret;
+}
+
+UVC_ATTR(uvcg_extension_, bm_controls, bmControls);
+
+static struct configfs_attribute *uvcg_extension_attrs[] = {
+ &uvcg_extension_attr_b_length,
+ &uvcg_extension_attr_b_unit_id,
+ &uvcg_extension_attr_b_num_controls,
+ &uvcg_extension_attr_b_nr_in_pins,
+ &uvcg_extension_attr_b_control_size,
+ &uvcg_extension_attr_guid_extension_code,
+ &uvcg_extension_attr_ba_source_id,
+ &uvcg_extension_attr_bm_controls,
+ &uvcg_extension_attr_i_extension,
+ NULL,
+};
+
+static void uvcg_extension_release(struct config_item *item)
+{
+ struct uvcg_extension *xu = container_of(item, struct uvcg_extension, item);
+
+ kfree(xu);
+}
+
+static struct configfs_item_operations uvcg_extension_item_ops = {
+ .release = uvcg_extension_release,
+};
+
+static const struct config_item_type uvcg_extension_type = {
+ .ct_item_ops = &uvcg_extension_item_ops,
+ .ct_attrs = uvcg_extension_attrs,
+ .ct_owner = THIS_MODULE,
+};
+
+static void uvcg_extension_drop(struct config_group *group, struct config_item *item)
+{
+ struct uvcg_extension *xu = container_of(item, struct uvcg_extension, item);
+ struct config_item *opts_item;
+ struct f_uvc_opts *opts;
+
+ opts_item = group->cg_item.ci_parent->ci_parent;
+ opts = to_f_uvc_opts(opts_item);
+
+ mutex_lock(&opts->lock);
+
+ config_item_put(item);
+ list_del(&xu->list);
+ kfree(xu->desc.baSourceID);
+ kfree(xu->desc.bmControls);
+
+ mutex_unlock(&opts->lock);
+}
+
+static struct config_item *uvcg_extension_make(struct config_group *group, const char *name)
+{
+ struct config_item *opts_item;
+ struct uvcg_extension *xu;
+ struct f_uvc_opts *opts;
+
+ opts_item = group->cg_item.ci_parent->ci_parent;
+ opts = to_f_uvc_opts(opts_item);
+
+ xu = kzalloc(sizeof(*xu), GFP_KERNEL);
+ if (!xu)
+ return ERR_PTR(-ENOMEM);
+
+ xu->desc.bLength = UVC_DT_EXTENSION_UNIT_SIZE(0, 0);
+ xu->desc.bDescriptorType = USB_DT_CS_INTERFACE;
+ xu->desc.bDescriptorSubType = UVC_VC_EXTENSION_UNIT;
+ xu->desc.bNumControls = 0;
+ xu->desc.bNrInPins = 0;
+ xu->desc.baSourceID = NULL;
+ xu->desc.bControlSize = 0;
+ xu->desc.bmControls = NULL;
+
+ mutex_lock(&opts->lock);
+
+ xu->desc.bUnitID = ++opts->last_unit_id;
+
+ config_item_init_type_name(&xu->item, name, &uvcg_extension_type);
+ list_add_tail(&xu->list, &opts->extension_units);
+
+ mutex_unlock(&opts->lock);
+
+ return &xu->item;
+}
+
+static struct configfs_group_operations uvcg_extensions_grp_ops = {
+ .make_item = uvcg_extension_make,
+ .drop_item = uvcg_extension_drop,
+};
+
+static const struct uvcg_config_group_type uvcg_extensions_grp_type = {
+ .type = {
+ .ct_item_ops = &uvcg_config_item_ops,
+ .ct_group_ops = &uvcg_extensions_grp_ops,
+ .ct_owner = THIS_MODULE,
+ },
+ .name = "extensions",
+};
+
/* -----------------------------------------------------------------------------
* control/class/{fs|ss}
*/
@@ -909,6 +1388,7 @@ static const struct uvcg_config_group_type uvcg_control_grp_type = {
&uvcg_processing_grp_type,
&uvcg_terminal_grp_type,
&uvcg_control_class_grp_type,
+ &uvcg_extensions_grp_type,
NULL,
},
};
diff --git a/drivers/usb/gadget/function/uvc_configfs.h b/drivers/usb/gadget/function/uvc_configfs.h
index 174ee691302b..5557813bcca9 100644
--- a/drivers/usb/gadget/function/uvc_configfs.h
+++ b/drivers/usb/gadget/function/uvc_configfs.h
@@ -142,6 +142,35 @@ static inline struct uvcg_mjpeg *to_uvcg_mjpeg(struct config_item *item)
return container_of(to_uvcg_format(item), struct uvcg_mjpeg, fmt);
}
+/* -----------------------------------------------------------------------------
+ * control/extensions/<NAME>
+ */
+
+struct uvcg_extension_unit_descriptor {
+ u8 bLength;
+ u8 bDescriptorType;
+ u8 bDescriptorSubType;
+ u8 bUnitID;
+ u8 guidExtensionCode[16];
+ u8 bNumControls;
+ u8 bNrInPins;
+ u8 *baSourceID;
+ u8 bControlSize;
+ u8 *bmControls;
+ u8 iExtension;
+} __packed;
+
+struct uvcg_extension {
+ struct config_item item;
+ struct list_head list;
+ struct uvcg_extension_unit_descriptor desc;
+};
+
+static inline struct uvcg_extension *to_uvcg_extension(struct config_item *item)
+{
+ return container_of(item, struct uvcg_extension, item);
+}
+
int uvcg_attach_configfs(struct f_uvc_opts *opts);
#endif /* UVC_CONFIGFS_H */
]
|
| 145/4 |
2026/07/30 00:21 |
llm |
codeexpert |
0m
Model:
Tokens:
input: 23181
output: 43
thoughts: 1098
Thoughts: **Analyzing a Use-After-Free Vulnerability in UVC ConfigFS**
Okay, this is pretty clear-cut. Let's break down this use-after-free issue step by step. I can see the exact sequence of events that triggers this vulnerability.
First, the user creates an extension unit directory. This calls `uvcg_extension_make`. This function is responsible for allocating an `xu` structure using `kzalloc`. Fine, so far so good. It then initializes the `xu->item` with `config_item_init_type_name` and sets its reference count to 1. Afterward, it adds `xu->list` to the `opts->extension_units` list and returns the item. Makes perfect sense.
Now, the vulnerability comes into play when the user *deletes* the extension unit directory. This triggers `uvcg_extension_drop`, which is passed the `item`, or rather, `&xu->item`. `container_of` is used to get the pointer to the `xu` structure. Then, `opts->lock` is taken.
Here's where things get dicey. `config_item_put(item)` is called. Since the refcount of `item` was 1, this decrements it to 0, which then triggers the release callback of `uvcg_extension_type`, which is `uvcg_extension_release`. This function *immediately* calls `kfree(xu)`. The `xu` struct is now freed!
But `uvcg_extension_drop` *continues* to execute! It calls `list_del(&xu->list)`, accessing freed memory. And then `kfree(xu->desc.baSourceID)` and `kfree(xu->desc.bmControls)` are called, again accessing the freed `xu` memory. This is a classic use-after-free. Finally, the lock is released.
So, the fix is straightforward: we need to defer the call to `config_item_put(item)` until *after* all accesses to the `xu` structure are finished. Specifically, `config_item_put(item)` should be called at the very end of `uvcg_extension_drop`.
Wait, should `config_item_put(item)` be called inside or outside the mutex? Let's check `uvcg_frame_drop`. Ah, in `uvcg_frame_drop`, `config_item_put(item)` is called *after* `mutex_unlock(&opts->lock)`. Does it matter if it is called inside or outside the lock? `config_item_put(item)` will call `uvcg_extension_release`, which calls `kfree(xu)`. It looks like `kfree(xu)` doesn't need to be protected by `opts->lock` because `xu` has already been removed from the `opts->extension_units` list. So, calling `config_item_put(item)` after `mutex_unlock(&opts->lock)` is perfectly fine and a cleaner approach, avoiding holding the lock while freeing memory.
Okay, let me just check for other `drop` functions in `uvc_configfs.c` to see if this pattern is repeated elsewhere. I will search for `_drop` in `drivers/usb/gadget/function/uvc_configfs.c`. I found `uvcg_extension_drop` and `uvcg_frame_drop`. I'll look at `uvcg_uncompressed_group_ops`, `uvcg_mjpeg_group_ops`, `uvcg_framebased_group_ops`, and `uvcg_extensions_grp_ops`. They all use `uvcg_frame_drop`. Let's see if there are any other configfs groups in `uvc_configfs.c` that have a `drop_item` or `drop_group` function. I will search for `drop_item` or `drop_group` in `drivers/usb/gadget/function/uvc_configfs.c`.
Looks like there aren't any other `drop` functions. We should probably implement this fix for the code that calls `uvcg_extension_drop`.
|
| 146/4 |
2026/07/30 00:21 |
tool |
grepper |
0m
Args: map[Expression:drop_item|drop_group PathPrefix:drivers/usb/gadget/function/uvc_configfs.c]
Results: map[Output:drivers/usb/gadget/function/uvc_configfs.c=1301=static const struct configfs_group_operations uvcg_extensions_grp_ops = {
drivers/usb/gadget/function/uvc_configfs.c-1302- .make_item = uvcg_extension_make,
drivers/usb/gadget/function/uvc_configfs.c:1303: .drop_item = uvcg_extension_drop,
drivers/usb/gadget/function/uvc_configfs.c-1304-};
--
drivers/usb/gadget/function/uvc_configfs.c=2264=static const struct configfs_group_operations uvcg_uncompressed_group_ops = {
drivers/usb/gadget/function/uvc_configfs.c-2265- .make_item = uvcg_frame_make,
drivers/usb/gadget/function/uvc_configfs.c:2266: .drop_item = uvcg_frame_drop,
drivers/usb/gadget/function/uvc_configfs.c-2267-};
--
drivers/usb/gadget/function/uvc_configfs.c=2528=static const struct configfs_group_operations uvcg_mjpeg_group_ops = {
drivers/usb/gadget/function/uvc_configfs.c-2529- .make_item = uvcg_frame_make,
drivers/usb/gadget/function/uvc_configfs.c:2530: .drop_item = uvcg_frame_drop,
drivers/usb/gadget/function/uvc_configfs.c-2531-};
--
drivers/usb/gadget/function/uvc_configfs.c=2718=static const struct configfs_group_operations uvcg_framebased_group_ops = {
drivers/usb/gadget/function/uvc_configfs.c-2719- .make_item = uvcg_frame_make,
drivers/usb/gadget/function/uvc_configfs.c:2720: .drop_item = uvcg_frame_drop,
drivers/usb/gadget/function/uvc_configfs.c-2721-};
]
|
| 147/4 |
2026/07/30 00:21 |
llm |
codeexpert |
0m
Model:
Tokens:
input: 25819
output: 32
thoughts: 563
Thoughts: **Identifying and Fixing a Potential Use-After-Free Vulnerability**
Okay, so I've narrowed it down. It seems that `uvcg_extension_drop` and `uvcg_frame_drop` are the only `drop_item` implementations within `uvc_configfs.c`. I've already confirmed that `uvcg_frame_drop` correctly handles `config_item_put` *after* releasing its lock. So, let's take another look at `uvcg_extension_drop`.
```c
static void uvcg_extension_drop(struct config_group *group, struct config_item *item)
{
struct uvcg_extension *xu = container_of(item, struct uvcg_extension, item);
struct config_item *opts_item;
struct f_uvc_opts *opts;
opts_item = group->cg_item.ci_parent->ci_parent;
opts = to_f_uvc_opts(opts_item);
mutex_lock(&opts->lock);
config_item_put(item);
list_del(&xu->list);
kfree(xu->desc.baSourceID);
kfree(xu->desc.bmControls);
mutex_unlock(&opts->lock);
}
```
Aha! There it is. The `config_item_put(item)` is *before* the removal of `xu` from the list and the `kfree` calls. That's a clear use-after-free scenario. This is because the underlying data is accessed after `config_item_put` has been called.
The fix is straightforward. We need to move the `config_item_put(item)` to *after* the `kfree` calls, and after the unlock. If we do this, like this:
```c
static void uvcg_extension_drop(struct config_group *group, struct config_item *item)
{
struct uvcg_extension *xu = container_of(item, struct uvcg_extension, item);
struct config_item *opts_item;
struct f_uvc_opts *opts;
opts_item = group->cg_item.ci_parent->ci_parent;
opts = to_f_uvc_opts(opts_item);
mutex_lock(&opts->lock);
list_del(&xu->list);
kfree(xu->desc.baSourceID);
kfree(xu->desc.bmControls);
mutex_unlock(&opts->lock);
config_item_put(item);
}
```
That should completely eliminate the use-after-free issue. However, before I submit this, let's broaden the search a bit. I should look for similar patterns throughout the kernel. I'll need to run a grep looking for `config_item_put` *within* a function named `_drop` in the `drivers/usb/` directory, and likely other relevant directories, as well. Gotta be thorough.
|
| 148/4 |
2026/07/30 00:21 |
tool |
grepper |
0m
Args: map[Expression:config_item_put PathPrefix:drivers/usb/gadget/]
Results: map[Output:drivers/usb/gadget/configfs.c=651=static void function_drop(
--
drivers/usb/gadget/configfs.c-662- mutex_unlock(&gi->lock);
drivers/usb/gadget/configfs.c:663: config_item_put(item);
drivers/usb/gadget/configfs.c-664-}
--
drivers/usb/gadget/configfs.c=762=static void config_desc_drop(
--
drivers/usb/gadget/configfs.c-765-{
drivers/usb/gadget/configfs.c:766: config_item_put(item);
drivers/usb/gadget/configfs.c-767-}
--
drivers/usb/gadget/configfs.c=957=static void gadget_language_drop(struct config_group *group,
--
drivers/usb/gadget/configfs.c-959-{
drivers/usb/gadget/configfs.c:960: config_item_put(item);
drivers/usb/gadget/configfs.c-961-}
--
drivers/usb/gadget/configfs.c=1443=static void ext_prop_drop(struct config_group *group, struct config_item *item)
--
drivers/usb/gadget/configfs.c-1455- mutex_unlock(desc->opts_mutex);
drivers/usb/gadget/configfs.c:1456: config_item_put(item);
drivers/usb/gadget/configfs.c-1457-}
--
drivers/usb/gadget/configfs.c=2058=static void gadgets_drop(struct config_group *group, struct config_item *item)
drivers/usb/gadget/configfs.c-2059-{
drivers/usb/gadget/configfs.c:2060: config_item_put(item);
drivers/usb/gadget/configfs.c-2061-}
--
drivers/usb/gadget/function/f_mass_storage.c=3349=static void fsg_lun_drop(struct config_group *group, struct config_item *item)
--
drivers/usb/gadget/function/f_mass_storage.c-3369-
drivers/usb/gadget/function/f_mass_storage.c:3370: config_item_put(item);
drivers/usb/gadget/function/f_mass_storage.c-3371-}
--
drivers/usb/gadget/function/f_midi2.c=2398=f_midi2_opts_block_drop(struct config_group *group, struct config_item *item)
--
drivers/usb/gadget/function/f_midi2.c-2404- mutex_unlock(&block_opts->ep->opts->lock);
drivers/usb/gadget/function/f_midi2.c:2405: config_item_put(item);
drivers/usb/gadget/function/f_midi2.c-2406-}
--
drivers/usb/gadget/function/f_midi2.c=2550=f_midi2_opts_ep_drop(struct config_group *group, struct config_item *item)
--
drivers/usb/gadget/function/f_midi2.c-2556- mutex_unlock(&ep_opts->opts->lock);
drivers/usb/gadget/function/f_midi2.c:2557: config_item_put(item);
drivers/usb/gadget/function/f_midi2.c-2558-}
--
drivers/usb/gadget/function/f_uvc.c=1008=static void uvc_free(struct usb_function *f)
--
drivers/usb/gadget/function/f_uvc.c-1013- if (!opts->header)
drivers/usb/gadget/function/f_uvc.c:1014: config_item_put(&uvc->header->item);
drivers/usb/gadget/function/f_uvc.c-1015- --opts->refcnt;
--
drivers/usb/gadget/function/f_uvc.c=1083=static struct usb_function *uvc_alloc(struct usb_function_instance *fi)
--
drivers/usb/gadget/function/f_uvc.c-1131- header = config_group_find_item(to_config_group(streaming), "header");
drivers/usb/gadget/function/f_uvc.c:1132: config_item_put(streaming);
drivers/usb/gadget/function/f_uvc.c-1133- if (!header)
--
drivers/usb/gadget/function/f_uvc.c-1136- h = config_group_find_item(to_config_group(header), "h");
drivers/usb/gadget/function/f_uvc.c:1137: config_item_put(header);
drivers/usb/gadget/function/f_uvc.c-1138- if (!h)
--
drivers/usb/gadget/function/uvc_configfs.c=171=static void uvcg_config_remove_children(struct config_group *group)
--
drivers/usb/gadget/function/uvc_configfs.c-177- uvcg_config_remove_children(child);
drivers/usb/gadget/function/uvc_configfs.c:178: config_item_put(&child->cg_item);
drivers/usb/gadget/function/uvc_configfs.c-179- }
--
drivers/usb/gadget/function/uvc_configfs.c=1186=static int uvcg_extension_allow_link(struct config_item *src, struct config_item *tgt)
--
drivers/usb/gadget/function/uvc_configfs.c-1208-put_strings:
drivers/usb/gadget/function/uvc_configfs.c:1209: config_item_put(strings);
drivers/usb/gadget/function/uvc_configfs.c-1210- mutex_unlock(su_mutex);
--
drivers/usb/gadget/function/uvc_configfs.c=1248=static void uvcg_extension_drop(struct config_group *group, struct config_item *item)
--
drivers/usb/gadget/function/uvc_configfs.c-1258-
drivers/usb/gadget/function/uvc_configfs.c:1259: config_item_put(item);
drivers/usb/gadget/function/uvc_configfs.c-1260- list_del(&xu->list);
--
drivers/usb/gadget/function/uvc_configfs.c=1340=static int uvcg_control_class_allow_link(struct config_item *src,
--
drivers/usb/gadget/function/uvc_configfs.c-1376-out:
drivers/usb/gadget/function/uvc_configfs.c:1377: config_item_put(header);
drivers/usb/gadget/function/uvc_configfs.c-1378- mutex_unlock(su_mutex);
--
drivers/usb/gadget/function/uvc_configfs.c=1382=static void uvcg_control_class_drop_link(struct config_item *src,
--
drivers/usb/gadget/function/uvc_configfs.c-1412-out:
drivers/usb/gadget/function/uvc_configfs.c:1413: config_item_put(header);
drivers/usb/gadget/function/uvc_configfs.c-1414- mutex_unlock(su_mutex);
--
drivers/usb/gadget/function/uvc_configfs.c=1580=uvcg_format_get_default_color_match(struct config_item *streaming)
--
drivers/usb/gadget/function/uvc_configfs.c-1591- "default");
drivers/usb/gadget/function/uvc_configfs.c:1592: config_item_put(color_matching_item);
drivers/usb/gadget/function/uvc_configfs.c-1593- if (!cm_default)
--
drivers/usb/gadget/function/uvc_configfs.c-1596- color_match = to_uvcg_color_matching(to_config_group(cm_default));
drivers/usb/gadget/function/uvc_configfs.c:1597: config_item_put(cm_default);
drivers/usb/gadget/function/uvc_configfs.c-1598-
--
drivers/usb/gadget/function/uvc_configfs.c=1602=static int uvcg_format_allow_link(struct config_item *src, struct config_item *tgt)
--
drivers/usb/gadget/function/uvc_configfs.c-1638-out_put_cm:
drivers/usb/gadget/function/uvc_configfs.c:1639: config_item_put(color_matching);
drivers/usb/gadget/function/uvc_configfs.c-1640- mutex_unlock(su_mutex);
--
drivers/usb/gadget/function/uvc_configfs.c=2220=static void uvcg_frame_drop(struct config_group *group, struct config_item *item)
--
drivers/usb/gadget/function/uvc_configfs.c-2243-
drivers/usb/gadget/function/uvc_configfs.c:2244: config_item_put(item);
drivers/usb/gadget/function/uvc_configfs.c-2245-}
--
drivers/usb/gadget/function/uvc_configfs.c=3420=static int uvcg_streaming_class_allow_link(struct config_item *src,
--
drivers/usb/gadget/function/uvc_configfs.c-3487-out:
drivers/usb/gadget/function/uvc_configfs.c:3488: config_item_put(header);
drivers/usb/gadget/function/uvc_configfs.c-3489- mutex_unlock(su_mutex);
--
drivers/usb/gadget/function/uvc_configfs.c=3493=static void uvcg_streaming_class_drop_link(struct config_item *src,
--
drivers/usb/gadget/function/uvc_configfs.c-3528-out:
drivers/usb/gadget/function/uvc_configfs.c:3529: config_item_put(header);
drivers/usb/gadget/function/uvc_configfs.c-3530- mutex_unlock(su_mutex);
--
drivers/usb/gadget/function/uvc_configfs.c=3643=static int uvc_func_allow_link(struct config_item *src, struct config_item *tgt)
--
drivers/usb/gadget/function/uvc_configfs.c-3677-put_strings:
drivers/usb/gadget/function/uvc_configfs.c:3678: config_item_put(strings);
drivers/usb/gadget/function/uvc_configfs.c-3679- mutex_unlock(su_mutex);
]
|
| 149/4 |
2026/07/30 00:21 |
llm |
codeexpert |
0m
Model:
Tokens:
input: 30041
output: 44
thoughts: 1454
Thoughts: **Deep Dive into a ConfigFS Bug and Its Nuances**
Okay, this code is clear, which is a good starting point. My initial thought is to verify if this pattern exists elsewhere in the `drivers/usb/gadget/` directory. So, let's systematically check other files within that directory. I'm focusing on those that handle `config_item_put` calls in relation to mutexes, looking for potential issues similar to the one identified. I see several instances across different `configfs.c` functions and other gadget function files: `configfs.c`, `f_mass_storage.c`, and `f_midi2.c`.
Aha! It appears that the issue of `config_item_put` being called *before* releasing resources only exists in `uvcg_extension_drop`! That's interesting, so this is a localized concern.
Now, let's meticulously document the bug and devise a correction. But before that, let's do a more careful re-examination of the `uvcg_extension_drop` function. Reading it again highlights a potential resource management error:
```c
static void uvcg_extension_drop(struct config_group *group, struct config_item *item)
{
struct uvcg_extension *xu = container_of(item, struct uvcg_extension, item);
struct config_item *opts_item;
struct f_uvc_opts *opts;
opts_item = group->cg_item.ci_parent->ci_parent;
opts = to_f_uvc_opts(opts_item);
mutex_lock(&opts->lock);
config_item_put(item);
list_del(&xu->list);
kfree(xu->desc.baSourceID);
kfree(xu->desc.bmControls);
mutex_unlock(&opts->lock);
}
```
My concern arises from the fact that `config_item_put` is called *before* freeing `xu->desc.baSourceID` and `xu->desc.bmControls`. This could lead to a use-after-free scenario. Let's trace where these fields are freed.
Looking at `uvcg_extension_release`, I see:
```c
static void uvcg_extension_release(struct config_item *item)
{
struct uvcg_extension *xu = container_of(item, struct uvcg_extension, item);
kfree(xu);
}
```
Wait, `uvcg_extension_release` does *not* free `xu->desc.baSourceID` or `xu->desc.bmControls`. This confirms my suspicion: those are freed in `uvcg_extension_drop`. That looks like the culprit.
These fields are allocated dynamically when the user interacts with the `ba_source_id` or `bm_controls` attributes through `uvcg_extension_ba_source_id_store` and `uvcg_extension_bm_controls_store` functions. That means `uvcg_extension_drop` *should* be the only place where these resources are freed. But, what if there's a problem during `uvcg_extension_make` which prevents it from succeeding or `uvcg_extension_drop` is not invoked?
But if `uvcg_extension_make` is successful, the item exists. Configfs will *always* call `drop_item`, and that function will, in turn, invoke `uvcg_extension_drop`. This is correct by design. What about the scenarios like the removal of the whole configfs subsystem, module unloading, etc.?
Even in cases of clearing or unmounting the configfs subsystem, configfs will traverse and call `drop_item` or `drop_group` for all elements. The same applies during module unloading, since removal of the configfs items must be performed before the module is unloaded, thus making sure `drop_item` will be called for each item. So, the resources are indeed handled in `uvcg_extension_drop`.
Wait. Should the resources be freed before calling `config_item_put`? Yes, because these resources are freed while `opts->lock` is held, and then `config_item_put` will be eventually called and call `uvcg_extension_release` to free `xu`. The current implementation *should* be safe.
I need to confirm the initial states of these fields. Let's look at `uvcg_extension_make` again:
```c
xu->desc.baSourceID = NULL;
xu->desc.bmControls = NULL;
```
Ah, initially, they
|
| 150/4 |
2026/07/30 00:21 |
tool |
git-log |
0m
Error:
Args: map[PathPrefix:drivers/usb/gadget/function/uvc_configfs.c SymbolName:uvcg_extension_drop]
Results:
|
| 151/4 |
2026/07/30 00:21 |
llm |
codeexpert |
0m
Model:
Tokens:
input: 32586
output: 44
thoughts: 29
Thoughts: Okay, here's my summary, tailored as if I, an expert, am processing these thoughts:
**Navigating Configuration Options**
Alright, so I'm looking at how to configure this particular component. Immediately, I'm noting a conflict. It seems that `SymbolName` and `PathPrefix` are mutually exclusive. Using them together just isn't going to work β probably leads to ambiguous or conflicting behavior, depending on the implementation. So, I have to make a choice. Thinking through the requirements here, and considering the overall architecture... Yes, *that's* the cleaner solution. I'll stick with `SymbolName` and couple it with `SourcePath` instead. That combination offers a more direct and less confusing approach, providing the information needed without the overlap and potential for ambiguity. That feels right.
|
| 152/4 |
2026/07/30 00:21 |
tool |
git-log |
0m
Args: map[SourcePath:drivers/usb/gadget/function/uvc_configfs.c SymbolName:uvcg_extension_drop]
Results: map[Output:08ddd71bf020 usb: gadget: uvc: Allow linking XUs to string descriptors
]
|
| 153/4 |
2026/07/30 00:22 |
llm |
codeexpert |
0m
Model:
Tokens:
input: 32868
output: 24
thoughts: 19
Thoughts: Okay, let's dive into this.
**Examining Commit 08ddd71bf020**
Alright, I need to understand the impact of commit `08ddd71bf020`. Time to see what changes were introduced with that specific changeset. I'm going to quickly review the diff. Let's look for significant file modifications, any function renames or alterations, and of course, any introduced bugs. I'll need to assess if this commit introduces any breaking changes, and whether the author's intent aligns with the overall project strategy. I will check for any performance implications, or potential security vulnerabilities introduced with the changes in this commit. Right, let's get to work.
|
| 154/4 |
2026/07/30 00:22 |
tool |
git-show |
0m
Args:
Results: map[Output:commit 08ddd71bf020c3b713d343959df39bf8a48ddd0b
Author: Daniel Scally <dan.scally@ideasonboard.com>
Date: Mon Feb 6 16:17:59 2023 +0000
usb: gadget: uvc: Allow linking XUs to string descriptors
Add .allow_link() and .drop_link() callbacks to allow users to link
an extension unit descriptor to a string descriptor.
Signed-off-by: Daniel Scally <dan.scally@ideasonboard.com>
Link: https://lore.kernel.org/r/20230206161802.892954-9-dan.scally@ideasonboard.com
Signed-off-by: Greg Kroah-Hartman <gregkh@linuxfoundation.org>
diff --git a/drivers/usb/gadget/function/uvc_configfs.c b/drivers/usb/gadget/function/uvc_configfs.c
index c365f323af45..3ac27838514c 100644
--- a/drivers/usb/gadget/function/uvc_configfs.c
+++ b/drivers/usb/gadget/function/uvc_configfs.c
@@ -1064,8 +1064,60 @@ static void uvcg_extension_release(struct config_item *item)
kfree(xu);
}
+static int uvcg_extension_allow_link(struct config_item *src, struct config_item *tgt)
+{
+ struct mutex *su_mutex = &src->ci_group->cg_subsys->su_mutex;
+ struct uvcg_extension *xu = to_uvcg_extension(src);
+ struct config_item *gadget_item;
+ struct gadget_string *string;
+ struct config_item *strings;
+ int ret = 0;
+
+ mutex_lock(su_mutex); /* for navigating configfs hierarchy */
+
+ /* Validate that the target of the link is an entry in strings/<langid> */
+ gadget_item = src->ci_parent->ci_parent->ci_parent->ci_parent->ci_parent;
+ strings = config_group_find_item(to_config_group(gadget_item), "strings");
+ if (!strings || tgt->ci_parent->ci_parent != strings) {
+ ret = -EINVAL;
+ goto put_strings;
+ }
+
+ string = to_gadget_string(tgt);
+ xu->string_descriptor_index = string->usb_string.id;
+
+put_strings:
+ config_item_put(strings);
+ mutex_unlock(su_mutex);
+
+ return ret;
+}
+
+static void uvcg_extension_drop_link(struct config_item *src, struct config_item *tgt)
+{
+ struct mutex *su_mutex = &src->ci_group->cg_subsys->su_mutex;
+ struct uvcg_extension *xu = to_uvcg_extension(src);
+ struct config_item *opts_item;
+ struct f_uvc_opts *opts;
+
+ mutex_lock(su_mutex); /* for navigating configfs hierarchy */
+
+ opts_item = src->ci_parent->ci_parent->ci_parent;
+ opts = to_f_uvc_opts(opts_item);
+
+ mutex_lock(&opts->lock);
+
+ xu->string_descriptor_index = 0;
+
+ mutex_unlock(&opts->lock);
+
+ mutex_unlock(su_mutex);
+}
+
static struct configfs_item_operations uvcg_extension_item_ops = {
.release = uvcg_extension_release,
+ .allow_link = uvcg_extension_allow_link,
+ .drop_link = uvcg_extension_drop_link,
};
static const struct config_item_type uvcg_extension_type = {
diff --git a/drivers/usb/gadget/function/uvc_configfs.h b/drivers/usb/gadget/function/uvc_configfs.h
index 5557813bcca9..c6a690158138 100644
--- a/drivers/usb/gadget/function/uvc_configfs.h
+++ b/drivers/usb/gadget/function/uvc_configfs.h
@@ -163,6 +163,7 @@ struct uvcg_extension_unit_descriptor {
struct uvcg_extension {
struct config_item item;
struct list_head list;
+ u8 string_descriptor_index;
struct uvcg_extension_unit_descriptor desc;
};
]
|
| 155/4 |
2026/07/30 00:22 |
llm |
codeexpert |
0m
Model:
Tokens:
input: 34310
output: 810
thoughts: 385
Thoughts: **Analyzing the UVC Configfs Extension Drop Vulnerability**
Okay, perfect. It looks like I'm dealing with a clean slate here β no conflicting commits modifying `uvcg_extension_drop`, which simplifies things significantly. My focus needs to be on crafting a precise and easily understandable summary of the issue and the proposed resolution.
Let's break this down:
* **Root Cause: The problem lies in the `drivers/usb/gadget/function/uvc_configfs.c` file, specifically within the `uvcg_extension_drop()` function.** It seems the order of operations is causing a memory safety issue. The `config_item_put(item)` function call, which decrements the reference count of the configfs item, is executed *before* crucial cleanup steps. This triggers `uvcg_extension_release()`, effectively freeing the `uvcg_extension` object (`xu`) prematurely. Subsequent attempts to access `xu->list`, `xu->desc.baSourceID`, and `xu->desc.bmControls` within the same `uvcg_extension_drop()` function lead to a textbook slab-use-after-free vulnerability. This is a critical flaw.
* **Fix: The solution is straightforward and logically sound.** We need to reorder the code execution. The `config_item_put(item)` call needs to be moved to the *very* end of `uvcg_extension_drop()`. It must come *after* the `list_del(&xu->list)` operation, freeing `xu->desc.baSourceID` and `xu->desc.bmControls` with `kfree()`, and, importantly, after the `opts->lock` mutex is unlocked. This mirrors the pattern observed in other drop callbacks within the same file, such as `uvcg_frame_drop()`, providing consistency and a more reliable design.
Now, I need to craft a response that is crystal clear, highly detailed, and presented in a professional manner. Accuracy and clarity are paramount here.
First, let's call `set-results` to record the thought process.
|
| 156/4 |
2026/07/30 00:22 |
tool |
set-results |
0m
Args: map[Answer:### Root Cause Analysis
In `drivers/usb/gadget/function/uvc_configfs.c`, the function `uvcg_extension_drop()` is responsible for dropping an extension unit configfs item. The implementation of `uvcg_extension_drop()` is as follows:
```c
static void uvcg_extension_drop(struct config_group *group, struct config_item *item)
{
struct uvcg_extension *xu = container_of(item, struct uvcg_extension, item);
struct config_item *opts_item;
struct f_uvc_opts *opts;
opts_item = group->cg_item.ci_parent->ci_parent;
opts = to_f_uvc_opts(opts_item);
mutex_lock(&opts->lock);
config_item_put(item);
list_del(&xu->list);
kfree(xu->desc.baSourceID);
kfree(xu->desc.bmControls);
mutex_unlock(&opts->lock);
}
```
When `config_item_put(item)` is called, it decrements the reference count of the configfs item. If this is the last reference to the item, it triggers the release callback `uvcg_extension_release()`:
```c
static void uvcg_extension_release(struct config_item *item)
{
struct uvcg_extension *xu = container_of(item, struct uvcg_extension, item);
kfree(xu);
}
```
This immediately frees the `uvcg_extension` object `xu`.
However, `uvcg_extension_drop()` continues to execute and accesses fields of the freed `xu` object:
1. `list_del(&xu->list)` accesses `xu->list`.
2. `kfree(xu->desc.baSourceID)` accesses `xu->desc.baSourceID`.
3. `kfree(xu->desc.bmControls)` accesses `xu->desc.bmControls`.
This results in a slab-use-after-free (UAF) bug in `__list_del_entry_valid_or_report` and other subsequent memory accesses.
---
### Solution
To resolve this issue, the call to `config_item_put(item)` must be deferred to the very end of `uvcg_extension_drop()`, after all fields of the `xu` structure have been accessed and freed, and after the mutex `opts->lock` has been unlocked. This is consistent with other drop callbacks in the same file (e.g., `uvcg_frame_drop()`).
The corrected `uvcg_extension_drop()` function should look like this:
```c
static void uvcg_extension_drop(struct config_group *group, struct config_item *item)
{
struct uvcg_extension *xu = container_of(item, struct uvcg_extension, item);
struct config_item *opts_item;
struct f_uvc_opts *opts;
opts_item = group->cg_item.ci_parent->ci_parent;
opts = to_f_uvc_opts(opts_item);
mutex_lock(&opts->lock);
list_del(&xu->list);
kfree(xu->desc.baSourceID);
kfree(xu->desc.bmControls);
mutex_unlock(&opts->lock);
config_item_put(item);
}
```]
Results: map[Answer:### Root Cause Analysis
In `drivers/usb/gadget/function/uvc_configfs.c`, the function `uvcg_extension_drop()` is responsible for dropping an extension unit configfs item. The implementation of `uvcg_extension_drop()` is as follows:
```c
static void uvcg_extension_drop(struct config_group *group, struct config_item *item)
{
struct uvcg_extension *xu = container_of(item, struct uvcg_extension, item);
struct config_item *opts_item;
struct f_uvc_opts *opts;
opts_item = group->cg_item.ci_parent->ci_parent;
opts = to_f_uvc_opts(opts_item);
mutex_lock(&opts->lock);
config_item_put(item);
list_del(&xu->list);
kfree(xu->desc.baSourceID);
kfree(xu->desc.bmControls);
mutex_unlock(&opts->lock);
}
```
When `config_item_put(item)` is called, it decrements the reference count of the configfs item. If this is the last reference to the item, it triggers the release callback `uvcg_extension_release()`:
```c
static void uvcg_extension_release(struct config_item *item)
{
struct uvcg_extension *xu = container_of(item, struct uvcg_extension, item);
kfree(xu);
}
```
This immediately frees the `uvcg_extension` object `xu`.
However, `uvcg_extension_drop()` continues to execute and accesses fields of the freed `xu` object:
1. `list_del(&xu->list)` accesses `xu->list`.
2. `kfree(xu->desc.baSourceID)` accesses `xu->desc.baSourceID`.
3. `kfree(xu->desc.bmControls)` accesses `xu->desc.bmControls`.
This results in a slab-use-after-free (UAF) bug in `__list_del_entry_valid_or_report` and other subsequent memory accesses.
---
### Solution
To resolve this issue, the call to `config_item_put(item)` must be deferred to the very end of `uvcg_extension_drop()`, after all fields of the `xu` structure have been accessed and freed, and after the mutex `opts->lock` has been unlocked. This is consistent with other drop callbacks in the same file (e.g., `uvcg_frame_drop()`).
The corrected `uvcg_extension_drop()` function should look like this:
```c
static void uvcg_extension_drop(struct config_group *group, struct config_item *item)
{
struct uvcg_extension *xu = container_of(item, struct uvcg_extension, item);
struct config_item *opts_item;
struct f_uvc_opts *opts;
opts_item = group->cg_item.ci_parent->ci_parent;
opts = to_f_uvc_opts(opts_item);
mutex_lock(&opts->lock);
list_del(&xu->list);
kfree(xu->desc.baSourceID);
kfree(xu->desc.bmControls);
mutex_unlock(&opts->lock);
config_item_put(item);
}
```]
|
| 157/2 |
2026/07/30 00:22 |
llm |
expert |
0m
Model:
Tokens:
input: 26218
output: 35
thoughts: 408
Thoughts: **Analyzing a Potential Use-After-Free Vulnerability**
Okay, so `config_item_put` is the culprit, freeing an object and then immediately, within the same function, `list_del` attempts to access it. My mind immediately jumps to exploitation possibilities. It seems like a classic use-after-free scenario. This UAF occurs practically instantly after `kfree(xu)`. The fact that `kfree` and `list_del` are within the same function is important, no opportunity for a context switch, right?
But then I recall that with `CONFIG_PREEMPT` enabled, the `kfree` operation *can* be interrupted by a context switch. And this bug report explicitly mentions `PREEMPT(full)`. So, if preemption happens right *after* `kfree(xu)`, another thread could swoop in, allocate the freed memory, and overwrite the `xu` object. Subsequently, the `list_del(&xu->list)` operation is going to use these overwritten `next` and `prev` pointers.
The `list_del` macro itself, well, it does the standard list unlinking: `next->prev = prev; prev->next = next;`. If an attacker can control those overwritten pointers, they can achieve a write-what-where primitive β writing `prev` to `next->prev` and `next` to `prev->next`. That's a classic list unlinking exploit primitive, and those are usually highly exploitable.
Wait a second, there's `__list_del_entry_valid`! It checks `next->prev == entry` and `prev->next == entry`. So, if `CONFIG_LIST_HARDENED` or `CONFIG_DEBUG_LIST` are enabled, these checks will prevent the write, panicking or warning instead. The bug report mentions `__list_del_entry_valid_or_report`, which implies `CONFIG_DEBUG_LIST` is indeed active here. But, in production builds, this is often disabled to reduce overhead.
But, hang on... doesn't `list_del` call `__list_del_entry_valid` *even without* `CONFIG_DEBUG_LIST`? I need to double-check that. Let's look at `include/linux/list.h`.
|
| 158/2 |
2026/07/30 00:22 |
tool |
read-file |
0m
Args: map[File:include/linux/list.h FirstLine:120 LineCount:40]
Results: map[Contents: 120: struct list_head *next = entry->next;
121:
122: /*
123: * With the hardening version, elide checking if next and prev
124: * are NULL, LIST_POISON1 or LIST_POISON2, since the immediate
125: * dereference of them below would result in a fault.
126: */
127: if (likely(prev->next == entry && next->prev == entry))
128: return true;
129: ret = false;
130: }
131:
132: ret &= __list_del_entry_valid_or_report(entry);
133: return ret;
134: }
135: #else
136: static inline bool __list_add_valid(struct list_head *new,
137: struct list_head *prev,
138: struct list_head *next)
139: {
140: return true;
141: }
142: static inline bool __list_del_entry_valid(struct list_head *entry)
143: {
144: return true;
145: }
146: #endif
147:
148: /*
149: * Insert a new entry between two known consecutive entries.
150: *
151: * This is only for internal list manipulation where we know
152: * the prev/next entries already!
153: */
154: static inline void __list_add(struct list_head *new,
155: struct list_head *prev,
156: struct list_head *next)
157: {
158: if (!__list_add_valid(new, prev, next))
159: return;
]
|
| 159/2 |
2026/07/30 00:22 |
llm |
expert |
0m
Model:
Tokens:
input: 27693
output: 28
thoughts: 1323
Thoughts: **Analyzing a Potential Use-After-Free Vulnerability**
Okay, so I'm looking at this code and thinking, without `CONFIG_DEBUG_LIST` or `CONFIG_LIST_HARDENED`, `__list_del_entry_valid` just returns true without any checks. Hang on, is `CONFIG_LIST_HARDENED` even a thing now? I should check if it's enabled by default, or if it's used in a production environment. Usually, `CONFIG_DEBUG_LIST` is disabled in production. If `CONFIG_LIST_HARDENED` is disabled, `list_del` will just perform the write directly.
I'm seeing `list_del` do a pretty standard `__list_del` operation: `next->prev = prev; WRITE_ONCE(prev->next, next);`. If an attacker can control `next` and `prev`, that looks like an arbitrary write primitive; `prev` is written to `next->prev` and `next` is written to `prev->next`. That's a classic setup for an arbitrary write. But, can an attacker even *win* this race?
The race window is between `kfree(xu)` and `list_del(&xu->list)`. It's literally two consecutive C statements: call `config_item_put` which then calls `kfree(xu)`, followed by some assembly to load `xu->list.prev` and `xu->list.next`. To win the race, an interrupt *must* occur *precisely* after `kfree` returns, and that interrupt handler *must* allocate the object and overwrite it, or, preemption *must* occur, switching to another thread that allocates and overwrites the object. I have to ask myself, is this practically exploitable?
Winning a race that fast is extremely difficult but not impossible. You could try timer interrupts, page faults, but that wouldn't work here since it's kernel memory, or maybe hyperthreading, if the allocator uses a shared freelist, but SLUB uses per-CPU freelists. Thinking more about it, If the attacker *can't* reliably win the race, they might just crash the kernel instead.
But wait, if they don't win the race, `list_del` will remove the object from the list using the *original* `next` and `prev` pointers. The object was just freed, so its `next` and `prev` are still intact (unless SLUB overwrote them with the freelist pointer). So where *does* SLUB put the freelist pointer? Modern kernels place the freelist pointer in the middle of the object to prevent overwriting the first few bytes, which are often used after free by buggy code. Let's check where the freelist pointer is for `kmalloc-192`. `xu->list` is at offset 80. If the freelist pointer is at offset 0, it would overwrite `item` (which is 80 bytes).
`xu->list` is NOT overwritten by the freelist pointer. So, if the race isn't won, `list_del` will work fine, removing the object with valid pointers. Then, `kfree(xu->desc.baSourceID)` and `kfree(xu->desc.bmControls)` will get called. Wait a second... Those pointers are also being read from the freed object! If the race *isn't* won, these pointers will be the original ones, which were already freed? Hold on, `uvcg_extension_release` only calls `kfree(xu)`. It does *not* free `baSourceID` or `bmControls`. So `kfree(xu->desc.baSourceID)` will actually free the correct memory, even though `xu` has already been freed. So, if the race is *not* won, the code should execute successfully without crashing, assuming KASAN is disabled!
Wait, though, `list_del` *modifies* `next->prev` and `prev->next`. Since `xu` is freed, it's not in the list anymore. But `list_del` is removing it from the list. Wait, if `xu` is freed, it shouldn't be in the list anyway. But hold on! Does `list_del` write to `xu->list.next` and `xu->list.prev`? No, `list_del` modifies `next->prev` and `prev->next`. It doesn't write to `xu->list` (unless `LIST_POISON` is enabled, which is a debug feature).
Oh wait! `list_del` does this: `__list_del_entry(entry); entry->next = LIST_POISON1;
|
| 160/2 |
2026/07/30 00:22 |
tool |
grepper |
0m
Args: map[Expression:freepointer PathPrefix:mm/slub.c]
Results: map[Output:mm/slub.c=530=static inline void *freelist_ptr_decode(const struct kmem_cache *s,
--
mm/slub.c-542-
mm/slub.c:543:static inline void *get_freepointer(struct kmem_cache *s, void *object)
mm/slub.c-544-{
--
mm/slub.c-553-
mm/slub.c:554:static inline void set_freepointer(struct kmem_cache *s, void *object, void *fp)
mm/slub.c-555-{
--
mm/slub.c=925=static void __fill_map(unsigned long *obj_map, struct kmem_cache *s,
--
mm/slub.c-932-
mm/slub.c:933: for (p = slab->freelist; p; p = get_freepointer(s, p))
mm/slub.c-934- set_bit(__obj_to_index(s, addr, p), obj_map);
--
mm/slub.c=1175=static void print_trailer(struct kmem_cache *s, struct slab *slab, u8 *p)
--
mm/slub.c-1184- pr_err("Object 0x%p @offset=%tu fp=0x%p\n\n",
mm/slub.c:1185: p, p - addr, get_freepointer(s, p));
mm/slub.c-1186-
--
mm/slub.c=1310=check_bytes_and_report(struct kmem_cache *s, struct slab *slab,
--
mm/slub.c-1353- * - Object payload bytes.
mm/slub.c:1354: * - If the freepointer may overlap the object, it is stored inside
mm/slub.c-1355- * the object (typically near the middle).
--
mm/slub.c=1470=static int check_object(struct kmem_cache *s, struct slab *slab,
--
mm/slub.c-1531- /*
mm/slub.c:1532: * Cannot check freepointer while object is allocated if
mm/slub.c:1533: * object and freepointer overlap.
mm/slub.c-1534- */
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-1542- */
mm/slub.c:1543: set_freepointer(s, p, NULL);
mm/slub.c-1544- ret = 0;
--
mm/slub.c=1584=static bool on_freelist(struct kmem_cache *s, struct slab *slab, void *search)
--
mm/slub.c-1598- "Freechain corrupt");
mm/slub.c:1599: set_freepointer(s, object, NULL);
mm/slub.c-1600- break;
--
mm/slub.c-1609- object = fp;
mm/slub.c:1610: fp = get_freepointer(s, object);
mm/slub.c-1611- nr++;
--
mm/slub.c=2596=bool slab_free_hook(struct kmem_cache *s, void *x, bool init,
--
mm/slub.c-2654- *
mm/slub.c:2655: * The object's freepointer is also avoided if stored outside the
mm/slub.c-2656- * object.
--
mm/slub.c=2681=bool slab_free_freelist_hook(struct kmem_cache *s, void **head, void **tail,
--
mm/slub.c-2702- object = next;
mm/slub.c:2703: next = get_freepointer(s, object);
mm/slub.c-2704-
--
mm/slub.c-2707- /* Move object to the new freelist */
mm/slub.c:2708: set_freepointer(s, object, *head);
mm/slub.c-2709- *head = object;
--
mm/slub.c=3547=static void *alloc_single_from_partial(struct kmem_cache *s,
--
mm/slub.c-3563- object = slab->freelist;
mm/slub.c:3564: slab->freelist = get_freepointer(s, object);
mm/slub.c-3565- slab->inuse++;
--
mm/slub.c=3606=static inline void build_slab_freelist(struct kmem_cache *s, struct slab *slab,
--
mm/slub.c-3622- next = next_slab_obj(s, iter);
mm/slub.c:3623: set_freepointer(s, cur, next);
mm/slub.c-3624- cur = next;
--
mm/slub.c-3626-
mm/slub.c:3627: set_freepointer(s, cur, NULL);
mm/slub.c-3628-}
--
mm/slub.c=3667=static void *alloc_single_from_new_slab(struct kmem_cache *s, struct slab *slab,
--
mm/slub.c-3683-
mm/slub.c:3684: /* alloc_debug_processing() always expects a valid freepointer */
mm/slub.c:3685: set_freepointer(s, object, slab->freelist);
mm/slub.c-3686-
--
mm/slub.c=3798=static void *get_from_partial_node(struct kmem_cache *s,
--
mm/slub.c-3843-
mm/slub.c:3844: new.freelist = get_freepointer(s, old.freelist);
mm/slub.c-3845- new.counters = old.counters;
--
mm/slub.c=4169=static inline bool free_debug_processing(struct kmem_cache *s,
--
mm/slub.c-4205- if (object != tail) {
mm/slub.c:4206: object = get_freepointer(s, object);
mm/slub.c-4207- goto next_object;
--
mm/slub.c=5537=static noinline void free_to_partial_list(
--
mm/slub.c-5561- slab->inuse -= cnt;
mm/slub.c:5562: set_freepointer(s, tail, prior);
mm/slub.c-5563- slab->freelist = head;
--
mm/slub.c=5638=static void __slab_free(struct kmem_cache *s, struct slab *slab,
--
mm/slub.c-5664-
mm/slub.c:5665: set_freepointer(s, tail, old.freelist);
mm/slub.c-5666-
--
mm/slub.c=6316=static void free_deferred_objects(struct irq_work *work)
--
mm/slub.c-6337- /*
mm/slub.c:6338: * We used freepointer in 'x' to link 'x' into df->objects.
mm/slub.c-6339- * Clear it to NULL to avoid false positive detection
--
mm/slub.c-6341- */
mm/slub.c:6342: set_freepointer(s, x, NULL);
mm/slub.c-6343-
--
mm/slub.c=7042=int build_detached_freelist(struct kmem_cache *s, size_t size,
--
mm/slub.c-7076-
mm/slub.c:7077: set_freepointer(df->s, object, NULL);
mm/slub.c-7078-
--
mm/slub.c-7084- /* Opportunity build freelist */
mm/slub.c:7085: set_freepointer(df->s, object, df->freelist);
mm/slub.c-7086- df->freelist = object;
--
mm/slub.c=7155=__refill_objects_node(struct kmem_cache *s, void **p, gfp_t gfp, unsigned int min,
--
mm/slub.c-7181- p[refilled] = object;
mm/slub.c:7182: object = get_freepointer(s, object);
mm/slub.c-7183- maybe_wipe_obj_freeptr(s, p[refilled]);
--
mm/slub.c-7205- tail = object;
mm/slub.c:7206: object = get_freepointer(s, object);
mm/slub.c-7207- } while (object);
]
|
| 161/2 |
2026/07/30 00:22 |
llm |
expert |
0m
Model:
Tokens:
input: 32611
output: 36
thoughts: 838
Thoughts: **Analyzing the Race Condition in `uvcg_extension_drop`**
Okay, so I'm digging into this `uvcg_extension_drop` function, trying to understand this potential use-after-free vulnerability. The core of the issue seems to be this: the freepointer is usually in the middle of a `kmalloc-192` object, with an `s->offset` of 96. I see that the `xu->list` member is at offset 80, spanning 16 bytes. That means the freepointer sits *right after* `xu->list`, at offset 96.
If the `list_del` operation loses the race, it writes `LIST_POISON1` and `LIST_POISON2` to `xu->list.next` and `xu->list.prev`, which is a use-after-free write, because the object has already been freed by the preceding `config_item_put(item)` call, which calls `kfree(xu)`. Those poison values are `0xdead000000000100` and `0xdead000000000122`, respectively.
But I'm thinking, this only overwrites memory within the freed object. Since the object is freed, this memory will be used for the next allocation. The new user will see these "poison" values, but they'll likely initialize the object themselves (like with `kzalloc`), so it's a harmless UAF write unless KASAN is enabled. However, KASAN would immediately flag this, and point out the UAF write.
Now, what if we win the race? We've got two C statements: `config_item_put(item)` then `list_del(&xu->list)`. If an attacker can win, they can control the object before `list_del` is called. The race window is literally two instructions. No context switch is possible. If an interrupt happens, then the attacker can allocate from `kmalloc-192` to overwrite. With `PREEMPT` enabled, the scheduler could preempt and context switch at this point. That makes it easier, but still difficult.
Interrupt timing is tricky, requiring a perfect interrupt using timer events or perf events. The interrupt handler also needs to allocate from the same `kmalloc-192` cache on the *same* CPU to overwrite. That is also difficult, as SLUB uses per-CPU freelists. The prize would be a potentially arbitrary write primitive. It's a tight race.
Crucially, this bug is accessible only to root, or a user with `CAP_SYS_ADMIN` in the initial namespace, because it requires creating directories in `configfs` under `/sys/kernel/config/usb_gadget`. Wait... that implies this is triggered when a directory is removed (`rmdir`) or when `mkdir` fails.
Since `configfs` is only accessible to root, this bug can't be exploited from an unprivileged process. Just to be sure, I should check whether `configfs` can be mounted in a user namespace. The `FS_USERNS_MOUNT` flag seems not to be there. I will verify it by checking `fs/configfs/mount.c`.
|
| 162/2 |
2026/07/30 00:22 |
tool |
read-file |
0m
Args: map[File:fs/configfs/mount.c FirstLine:120 LineCount:30]
Results: map[Contents: 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:
]
|
| 163/2 |
2026/07/30 00:22 |
llm |
expert |
0m
Model:
Tokens:
input: 34622
output: 34
thoughts: 12
Thoughts: Okay, let's dive into this. Here's how I'd approach that snippet, considering my expert perspective:
**Checking `configfs_fs_type`**
Right, let's take a look at `configfs_fs_type`. This seems like a crucial point, and based on context, probably indicates the intended file system type for something related to the `configfs` framework. I know `configfs` well, and I'm instantly thinking about the potential implications. I need to understand why we're focusing on this particular type. Is this for mounting, for detection, or something else entirely? A simple check is unlikely to reveal much on its own, so I need to know why. Are we looking to confirm that `configfs` is correctly initialized? That it's the target file system? Or are we verifying a security constraint, or checking if it's the correct filesystem that is being worked with? My experience tells me there's more context needed before a conclusion can be reached. This snippet, in isolation, is just a starting point, and I need far more information to understand the rationale and purpose behind this particular check.
|
| 164/2 |
2026/07/30 00:23 |
tool |
grepper |
0m
Args: map[Expression:configfs_fs_type PathPrefix:fs/configfs/mount.c]
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");
]
|
| 165/2 |
2026/07/30 00:23 |
llm |
expert |
0m
Model:
Tokens:
input: 29598
output: 36
thoughts: 0
|
| 166/2 |
2026/07/30 00:23 |
tool |
read-file |
0m
Args: map[File:fs/configfs/mount.c FirstLine:100 LineCount:30]
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:
]
|
| 167/2 |
2026/07/30 00:23 |
llm |
expert |
0m
Model:
Tokens:
input: 35809
output: 59
thoughts: 752
Thoughts: **My Assessment of the UVC Gadget Use-After-Free Vulnerability**
Okay, so the initial check confirms what I suspected: `FS_USERNS_MOUNT` isn't present in `configfs_fs_type.fs_flags`. That nails down the first point β configfs *can't* be mounted within a user namespace. This immediately means unprivileged users are out of the picture; they can't access or manipulate configfs, which is mounted by root in the initial namespace. The good news is the bug isn't a problem for unprivileged processes or user namespaces.
Now, the critical question: how *is* `uvcg_extension_drop` triggered? It's buried in the USB gadget UVC function. My initial thought goes to the plug-in of a USB device. But wait... this is the *gadget* framework, meaning the Linux box is *acting* as a USB device, not a host. The gadget is configured via configfs by user-space, likely a root-run script (think `libusbgx` or a shell script). So, it's a local administrator configuring the gadget, not a standard USB host plug-in. Not a host, not a remote trigger.
Could the host send some USB control requests that trigger it? No, the code is a configfs callback. It's only called when a configfs item is *dropped* β think `rmdir` or a failed `mkdir`. Not in response to USB traffic. This narrows the scope significantly.
Is it *exploitable*? Even with root access, we need to consider it. The race window is incredibly tight, two instructions. If you *lose* the race, it's just harmless `LIST_POISON` writes (unless KASAN is enabled, which would likely catch it). However, if you *win* the race? Arbitrary write primitive. Winning that race is practically impossible without some advanced techniques, and even then, it's incredibly unreliable. But, given the kernel context, a use-after-free with potential arbitrary write is generally exploitable, despite the race. However, because only root can trigger it, is the exploitability moot? Yes. Root already has full control.
Wait, what about systems with restricted root, like Secure Boot or lockdown? Could this be a bypass? Yes, potentially, if the race can be won. A 2-instruction race is tough, but it's possible. I'll mark it as `Exploitable = True` because it's a UAF write, but `Unprivileged = False`, `UserNamespace = False`.
Let's double-check; are there other triggers? Could a malicious USB host somehow trigger it? The Linux gadget connects to a host and receives USB requests. Does the UVC gadget driver create or drop configfs items in response to those requests? No. Configfs items are created/dropped by local user-space *before* the gadget is bound to the UDC. Configuration is usually static after binding. Let's look and see if `uvcg_extension_drop` is called anywhere else.
|
| 168/2 |
2026/07/30 00:23 |
tool |
codesearch-find-references |
0m
Args: map[ContextFile:drivers/usb/gadget/function/uvc_configfs.c IncludeSnippetLines:0 Name:uvcg_extension_drop SourceTreePrefix:]
Results: map[References:[map[ReferenceKind:takes-address-of ReferencingEntityKind:global_variable ReferencingEntityName:uvcg_extensions_grp_ops SourceFile:drivers/usb/gadget/function/uvc_configfs.c SourceLine:1303]] TruncatedOutput:false]
|
| 169/2 |
2026/07/30 00:23 |
llm |
expert |
0m
Model:
Tokens:
input: 30901
output: 49
thoughts: 0
|
| 170/2 |
2026/07/30 00:23 |
tool |
codeexpert |
1m
Args: map[Question:Is configfs mountable by unprivileged users in a user namespace? I don't see FS_USERNS_MOUNT flag in configfs_fs_type.]
Results: map[Answer:No, **configfs is not mountable by unprivileged users in a user namespace**. Your observation is completely correct.
### Why configfs cannot be mounted in a user namespace
1. **Missing `FS_USERNS_MOUNT` Flag**:
In `fs/configfs/mount.c`, the `configfs_fs_type` struct is defined as follows:
```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,
};
```
Because the `.fs_flags` field is omitted, it defaults to `0`. Consequently, it does not have the `FS_USERNS_MOUNT` flag (which is required to allow mounting by a non-initial user namespace root).
2. **VFS Capability Checks**:
When a mount request is made (via either the legacy `mount` syscall or the new mount API), the VFS 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);
}
```
Since `configfs_fs_type.fs_flags` does not contain `FS_USERNS_MOUNT`, `mount_capable()` falls back to checking `capable(CAP_SYS_ADMIN)`.
3. **Unprivileged User Restrictions**:
The `capable(CAP_SYS_ADMIN)` helper checks for the `CAP_SYS_ADMIN` capability in the **initial** user namespace (`&init_user_ns`). An unprivileged user inside a non-initial user namespace only possesses capabilities within their own user namespace (and nested ones), but not in the initial user namespace. As a result, `capable(CAP_SYS_ADMIN)` returns `false`, and the mount attempt is rejected with `-EPERM`.]
|
| 171/3 |
2026/07/30 00:23 |
agent |
codeexpert |
1m
Model:
Results: map[Answer:No, **configfs is not mountable by unprivileged users in a user namespace**. Your observation is completely correct.
### Why configfs cannot be mounted in a user namespace
1. **Missing `FS_USERNS_MOUNT` Flag**:
In `fs/configfs/mount.c`, the `configfs_fs_type` struct is defined as follows:
```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,
};
```
Because the `.fs_flags` field is omitted, it defaults to `0`. Consequently, it does not have the `FS_USERNS_MOUNT` flag (which is required to allow mounting by a non-initial user namespace root).
2. **VFS Capability Checks**:
When a mount request is made (via either the legacy `mount` syscall or the new mount API), the VFS 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);
}
```
Since `configfs_fs_type.fs_flags` does not contain `FS_USERNS_MOUNT`, `mount_capable()` falls back to checking `capable(CAP_SYS_ADMIN)`.
3. **Unprivileged User Restrictions**:
The `capable(CAP_SYS_ADMIN)` helper checks for the `CAP_SYS_ADMIN` capability in the **initial** user namespace (`&init_user_ns`). An unprivileged user inside a non-initial user namespace only possesses capabilities within their own user namespace (and nested ones), but not in the initial user namespace. As a result, `capable(CAP_SYS_ADMIN)` returns `false`, and the mount attempt is rejected with `-EPERM`.]
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 configfs mountable by unprivileged users in a user namespace? I don't see FS_USERNS_MOUNT flag in configfs_fs_type.
|
| 172/4 |
2026/07/30 00:23 |
llm |
codeexpert |
0m
Model:
Tokens:
input: 3896
output: 38
thoughts: 87
Thoughts: **Investigating ConfigFS and User Namespaces**
Okay, so the question is: can unprivileged users mount configfs within a user namespace? That's a good one. I don't recall seeing any explicit `FS_USERNS_MOUNT` flag associated with `configfs_fs_type`. That's the first thing I need to confirm.
Time to dive into the kernel source. Let's start by pinpointing the definition of `configfs_fs_type`. My usual tools for this kind of investigation would be `codesearch-definition-source` or `grepper`. I'll fire one of those up and get the definition immediately. Once I have the definition, I can start looking for the presence or absence of that crucial user namespace mount flag. I'm guessing I'll need to then look for related code that might allow such a mount even without a dedicated flag. This will be interesting.
|
| 173/4 |
2026/07/30 00:23 |
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]
|
| 174/4 |
2026/07/30 00:23 |
llm |
codeexpert |
0m
Model:
Tokens:
input: 4349
output: 35
thoughts: 77
Thoughts: Okay, let's dive into this. I need to understand the mounting behavior of configfs. My initial thought is to examine `fs/configfs/mount.c`, specifically around line 115. I'm looking for a comprehensive picture. I'm searching for additional places within the code where it might be mounted, or potentially alternative methods for mounting. Also, I need to check if there are other file_system_types involved in the mounting process. I need to understand all the potential entry points. Right, time to read that section of the file.
|
| 175/4 |
2026/07/30 00:23 |
tool |
read-file |
0m
Args: map[File:fs/configfs/mount.c FirstLine:1 LineCount:130]
Results: map[Contents: 1: // SPDX-License-Identifier: GPL-2.0-or-later
2: /*
3: * mount.c - operations for initializing and mounting configfs.
4: *
5: * Based on sysfs:
6: * sysfs is Copyright (C) 2001, 2002, 2003 Patrick Mochel
7: *
8: * configfs Copyright (C) 2005 Oracle. All rights reserved.
9: */
10:
11: #include <linux/fs.h>
12: #include <linux/module.h>
13: #include <linux/mount.h>
14: #include <linux/fs_context.h>
15: #include <linux/pagemap.h>
16: #include <linux/init.h>
17: #include <linux/slab.h>
18:
19: #include <linux/configfs.h>
20: #include "configfs_internal.h"
21:
22: /* Random magic number */
23: #define CONFIGFS_MAGIC 0x62656570
24:
25: static struct vfsmount *configfs_mount = NULL;
26: struct kmem_cache *configfs_dir_cachep;
27: static int configfs_mnt_count = 0;
28:
29:
30: static void configfs_free_inode(struct inode *inode)
31: {
32: if (S_ISLNK(inode->i_mode))
33: kfree(inode->i_link);
34: free_inode_nonrcu(inode);
35: }
36:
37: static const struct super_operations configfs_ops = {
38: .statfs = simple_statfs,
39: .drop_inode = inode_just_drop,
40: .free_inode = configfs_free_inode,
41: };
42:
43: static struct config_group configfs_root_group = {
44: .cg_item = {
45: .ci_namebuf = "root",
46: .ci_name = configfs_root_group.cg_item.ci_namebuf,
47: },
48: };
49:
50: int configfs_is_root(struct config_item *item)
51: {
52: return item == &configfs_root_group.cg_item;
53: }
54:
55: static struct configfs_dirent configfs_root = {
56: .s_sibling = LIST_HEAD_INIT(configfs_root.s_sibling),
57: .s_children = LIST_HEAD_INIT(configfs_root.s_children),
58: .s_element = &configfs_root_group.cg_item,
59: .s_type = CONFIGFS_ROOT,
60: .s_iattr = NULL,
61: };
62:
63: static int configfs_fill_super(struct super_block *sb, struct fs_context *fc)
64: {
65: struct inode *inode;
66: struct dentry *root;
67:
68: sb->s_blocksize = PAGE_SIZE;
69: sb->s_blocksize_bits = PAGE_SHIFT;
70: sb->s_magic = CONFIGFS_MAGIC;
71: sb->s_op = &configfs_ops;
72: sb->s_time_gran = 1;
73:
74: inode = configfs_new_inode(S_IFDIR | S_IRWXU | S_IRUGO | S_IXUGO,
75: &configfs_root, sb);
76: if (inode) {
77: inode->i_op = &configfs_root_inode_operations;
78: inode->i_fop = &configfs_dir_operations;
79: /* directory inodes start off with i_nlink == 2 (for "." entry) */
80: inc_nlink(inode);
81: } else {
82: pr_debug("could not get root inode\n");
83: return -ENOMEM;
84: }
85:
86: root = d_make_root(inode);
87: if (!root) {
88: pr_debug("%s: could not get root dentry!\n",__func__);
89: return -ENOMEM;
90: }
91: config_group_init(&configfs_root_group);
92: configfs_root_group.cg_item.ci_dentry = root;
93: root->d_fsdata = &configfs_root;
94: sb->s_root = root;
95: set_default_d_op(sb, &configfs_dentry_ops); /* the rest get that */
96: sb->s_d_flags |= DCACHE_DONTCACHE;
97: return 0;
98: }
99:
100: static int configfs_get_tree(struct fs_context *fc)
]
|
| 176/4 |
2026/07/30 00:23 |
llm |
codeexpert |
0m
Model:
Tokens:
input: 6067
output: 36
thoughts: 10
Thoughts: Okay, I'm ready to dive into this. Here's a summary from my perspective:
**Assessing the Landscape**
Alright, I've got the initial prompt, and now it's time to understand the full context. My immediate thought is to delve deeper. I'll need to absorb the entirety of the information presented. I need to read the rest of the file. This complete picture will be crucial for any insightful analysis or response.
|
| 177/4 |
2026/07/30 00:23 |
tool |
read-file |
0m
Args: map[File:fs/configfs/mount.c FirstLine:101 LineCount:50]
Results: map[Contents: 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:
150: err = register_filesystem(&configfs_fs_type);
]
|
| 178/4 |
2026/07/30 00:23 |
llm |
codeexpert |
0m
Model:
Tokens:
input: 6931
output: 21
thoughts: 92
Thoughts: Okay, here's my breakdown of what I'm thinking about right now, focusing on that `FS_USERNS_MOUNT` flag:
**Analyzing `FS_USERNS_MOUNT` and ConfigFS**
Alright, I'm diving into this `FS_USERNS_MOUNT` situation. First, I need to understand its relationship with `configfs`. Is there another file system type associated with `configfs` other than the obvious `configfs_fs_type`? I'm pretty sure there isn't but it never hurts to verify. My next step here is to thoroughly refresh my memory on what `FS_USERNS_MOUNT` actually signifies. Then, I need to get a broader view. I'll need to go through the codebase and find all the instances where this `FS_USERNS_MOUNT` is used. That will also provide me with the list of filesystems that use it, which is crucial. This should give me a solid understanding of its impact and intended use within the file system ecosystem.
|
| 179/4 |
2026/07/30 00:23 |
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-};
]
|
| 180/4 |
2026/07/30 00:23 |
llm |
codeexpert |
0m
Model:
Tokens:
input: 10106
output: 33
thoughts: 49
Thoughts: Okay, let's dive into this. Here's how I'd approach this dissection of `fs/super.c`:
**Dissecting `fs/super.c` for `FS_USERNS_MOUNT` Handling**
Alright, my internal radar is pinging, and I want to zoom in on `fs/super.c`. Specifically, let's take a gander around line 695. I'm going to focus on how the kernel handles the `FS_USERNS_MOUNT` feature flag. The core of my interest lies in understanding the interplay between `mount_capable` and `sget_fc`. I need to figure out how these functions specifically test for and utilize `FS_USERNS_MOUNT`. My expertise tells me these are likely central to any security checks or permissions related to user namespace mounts, so I'll be meticulously examining how they're used and what implications their behavior has. I'll need to confirm if they're the only checks to verify this, or if any other functions must be considered, and verify their relationship to each other.
|
| 181/4 |
2026/07/30 00:23 |
tool |
read-file |
0m
Args: map[File:fs/super.c FirstLine:690 LineCount:70]
Results: map[Contents: 690: }
691: }
692:
693: EXPORT_SYMBOL(generic_shutdown_super);
694:
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: }
702:
703: /**
704: * sget_fc - Find or create a superblock
705: * @fc: Filesystem context.
706: * @test: Comparison callback
707: * @set: Setup callback
708: *
709: * Create a new superblock or find an existing one.
710: *
711: * The @test callback is used to find a matching existing superblock.
712: * Whether or not the requested parameters in @fc are taken into account
713: * is specific to the @test callback that is used. They may even be
714: * completely ignored.
715: *
716: * If an extant superblock is matched, it will be returned unless:
717: *
718: * (1) the namespace the filesystem context @fc and the extant
719: * superblock's namespace differ
720: *
721: * (2) the filesystem context @fc has requested that reusing an extant
722: * superblock is not allowed
723: *
724: * In both cases EBUSY will be returned.
725: *
726: * If no match is made, a new superblock will be allocated and basic
727: * initialisation will be performed (s_type, s_fs_info and s_id will be
728: * set and the @set callback will be invoked), the superblock will be
729: * published and it will be returned in a partially constructed state
730: * with SB_BORN and SB_ACTIVE as yet unset.
731: *
732: * Return: On success, an extant or newly created superblock is
733: * returned. On failure an error pointer is returned.
734: */
735: struct super_block *sget_fc(struct fs_context *fc,
736: int (*test)(struct super_block *, struct fs_context *),
737: int (*set)(struct super_block *, struct fs_context *))
738: {
739: struct super_block *s = NULL;
740: struct super_block *old;
741: struct user_namespace *user_ns = fc->global ? &init_user_ns : fc->user_ns;
742: int err;
743:
744: /*
745: * Never allow s_user_ns != &init_user_ns when FS_USERNS_MOUNT or
746: * FS_USERNS_DELEGATABLE is not set, as the filesystem is likely
747: * unprepared to handle it. This can happen when fsconfig() is called
748: * from init_user_ns with an fs_fd opened in another user namespace.
749: */
750: if (user_ns != &init_user_ns &&
751: !(fc->fs_type->fs_flags & (FS_USERNS_MOUNT | FS_USERNS_DELEGATABLE))) {
752: errorfc(fc, "VFS: Mounting from non-initial user namespace is not allowed");
753: return ERR_PTR(-EPERM);
754: }
755:
756: retry:
757: spin_lock(&sb_lock);
758: if (test) {
759: hlist_for_each_entry(old, &fc->fs_type->fs_supers, s_instances) {
]
|
| 182/4 |
2026/07/30 00:23 |
llm |
codeexpert |
0m
Model:
Tokens:
input: 11559
output: 21
thoughts: 26
Thoughts: Okay, here's my summary, presented as a stream of thought:
**Tracing the `mount_capable` Function**
Alright, let's get down to brass tacks. I need to understand the call hierarchy for this `mount_capable` function. My primary goal here is to determine *where* and *how* it's being used. So, the first logical step is to perform a targeted search within the codebase. We want to find every instance where `mount_capable` is actually *called*. I'll need to use some robust search tools to identify all the direct references. This should give me a map of the function's immediate context. This information is key to understanding its purpose and potential impact, especially in a system like this where such a function is likely tied to some critical capability. I anticipate this will be a good starting point to understanding why it's behaving the way it is.
|
| 183/4 |
2026/07/30 00:23 |
tool |
codesearch-find-references |
0m
Args:
Results: map[References:[map[ReferenceKind:calls ReferencingEntityKind:function ReferencingEntityName:do_new_mount SourceFile:fs/namespace.c SourceLine:3838] map[ReferenceKind:calls ReferencingEntityKind:function ReferencingEntityName:vfs_cmd_create SourceFile:fs/fsopen.c SourceLine:225]] TruncatedOutput:false]
|
| 184/4 |
2026/07/30 00:23 |
llm |
codeexpert |
0m
Model:
Tokens:
input: 11914
output: 34
thoughts: 19
Thoughts: Here's a summary of my thought process, as requested:
**Diving into `fs/fsopen.c`**
Okay, I need to get into the nitty-gritty of the `fsopen` functionality. Let's see... the code's right here. Based on what I'm looking at, my initial focus will be around line 225. I need to understand what's happening there, specifically within the context of the larger file system opening procedure. I'm expecting to find the core logic for the initialization or validation of an open file structure. Depending on the design, this might involve looking at permissions checks, resource allocation, and or potential interactions with the VFS layer. Let's start there and see what we can find.
|
| 185/4 |
2026/07/30 00:23 |
tool |
read-file |
0m
Args: map[File:fs/fsopen.c FirstLine:210 LineCount:30]
Results: map[Contents: 210: put_fs_context(fc);
211: err_path:
212: path_put(&target);
213: err:
214: return ret;
215: }
216:
217: static int vfs_cmd_create(struct fs_context *fc, bool exclusive)
218: {
219: struct super_block *sb;
220: int ret;
221:
222: if (fc->phase != FS_CONTEXT_CREATE_PARAMS)
223: return -EBUSY;
224:
225: if (!mount_capable(fc))
226: return -EPERM;
227:
228: fc->phase = FS_CONTEXT_CREATING;
229: fc->exclusive = exclusive;
230:
231: ret = vfs_get_tree(fc);
232: if (ret) {
233: fc->phase = FS_CONTEXT_FAILED;
234: return ret;
235: }
236:
237: sb = fc->root->d_sb;
238: ret = security_sb_kern_mount(sb);
239: if (unlikely(ret)) {
]
|
| 186/4 |
2026/07/30 00:23 |
llm |
codeexpert |
0m
Model:
Tokens:
input: 12518
output: 34
thoughts: 19
Thoughts: Here's my take on the thought process, as requested:
**Diving into `fs/namespace.c`**
Alright, time to dig in. The problem at hand feels like it might be rooted in how namespaces are handled, so let's jump right into `fs/namespace.c`. Specifically, I'm thinking around line 3838 is a good place to start. This area likely deals with the core logic for... well, likely either creating or managing a namespace, or perhaps even resolving a name within a specific context. I'll need to refresh my memory on what that section of the code does specifically, but I'm betting that's where the relevant logic lives. Let's see what's happening there.
|
| 187/4 |
2026/07/30 00:23 |
tool |
read-file |
0m
Args: map[File:fs/namespace.c FirstLine:3820 LineCount:30]
Results: map[Contents:3820:
3821: fc = fs_context_for_mount(type, sb_flags);
3822: put_filesystem(type);
3823: if (IS_ERR(fc))
3824: return PTR_ERR(fc);
3825:
3826: /*
3827: * Indicate to the filesystem that the mount request is coming
3828: * from the legacy mount system call.
3829: */
3830: fc->oldapi = true;
3831:
3832: if (subtype)
3833: err = vfs_parse_fs_string(fc, "subtype", subtype);
3834: if (!err && name)
3835: err = vfs_parse_fs_string(fc, "source", name);
3836: if (!err)
3837: err = parse_monolithic_mount_data(fc, data);
3838: if (!err && !mount_capable(fc))
3839: err = -EPERM;
3840: if (!err)
3841: err = do_new_mount_fc(fc, path, mnt_flags);
3842:
3843: put_fs_context(fc);
3844: return err;
3845: }
3846:
3847: static void lock_mount_exact(const struct path *path,
3848: struct pinned_mountpoint *mp, bool copy_mount,
3849: unsigned int copy_flags)
]
|
| 188/4 |
2026/07/30 00:23 |
llm |
codeexpert |
0m
Model:
Tokens:
input: 13170
output: 24
thoughts: 306
Thoughts: **Analyzing Configfs Mount Capabilities**
Okay, so I'm digging into how `mount_capable(fc)` works, specifically in the context of mounting filesystems. The code first checks if `FS_USERNS_MOUNT` is set in the filesystem's flags (`fc->fs_type->fs_flags`). If it *isn't* set, the code calls `capable(CAP_SYS_ADMIN)`. This is where it gets interesting. `capable(CAP_SYS_ADMIN)` essentially checks for the `CAP_SYS_ADMIN` capability in the *initial* user namespace (`&init_user_ns`). An unprivileged user within their own user namespace, or any nested one, *doesn't* typically have `CAP_SYS_ADMIN` in the initial namespace, right? They only have that capability within their own, isolated namespace.
Therefore, it looks like if `configfs` doesn't have `FS_USERNS_MOUNT` set, an unprivileged user in a user namespace can't mount it. That's a pretty straightforward conclusion based on this code path.
But wait, I need to be *absolutely* sure. This is an area where subtle implementation details can bite you. I'm going to quickly verify if there's any other way `configfs` *could* be mounted that I'm missing. Is there another filesystem type that's somehow involved? Are there any other references to `configfs_fs_type` lurking around in the codebase that might change the game? I need to perform a search and confirm there are no other potential mount entry points or mechanisms that circumvent this behavior. Let's start by searching for all references to `configfs_fs_type`. Gotta be thorough here.
|
| 189/4 |
2026/07/30 00:23 |
tool |
codesearch-find-references |
0m
Args: map[Name:configfs_fs_type]
Results: map[References:<nil> TruncatedOutput:false]
|
| 190/4 |
2026/07/30 00:23 |
llm |
codeexpert |
0m
Model:
Tokens:
input: 13888
output: 20
thoughts: 27
Thoughts: Here's my thought process, summarized as you requested:
**Debugging a Missing Reference**
Okay, so `codesearch-find-references` drew a blank. That's unusual. My immediate reaction is to widen the net. If a more robust search didn't turn up anything, it's time to try something moreβ¦ brute force. Let me try `grepper`. It's generally more comprehensive, and Iβm hoping it can pick up something `codesearch` missed. Maybe there's a subtly different naming convention or some kind of obfuscation that's thwarting the more targeted approach. I'm crossing my fingers that `grepper` yields some results, even if they're a bit noisy. That would give me something to work with.
|
| 191/4 |
2026/07/30 00:23 |
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");
]
|
| 192/4 |
2026/07/30 00:23 |
llm |
codeexpert |
0m
Model:
Tokens:
input: 14502
output: 24
thoughts: 130
Thoughts: **My Analysis of the configfs Filesystem**
Okay, so I see that `configfs_fs_type` is declared as static within `fs/configfs/mount.c`. That means it's pretty much self-contained within that file, which simplifies things a bit. First, let me quickly verify that there aren't any *other* `file_system_type` structs related to configfs that are registered anywhere else in the kernel. A quick scan should confirm this... Yep, it looks like `configfs_fs_type` is the only one registered, specifically through `register_filesystem(&configfs_fs_type)`. Good, that's what I expected.
Now, a slightly broader question arises: is there *any* other mechanism by which configfs is mounted or made accessible? I should investigate `configfs_pin_fs`. It looks like that function is called from various parts of the kernel. Let's trace all of the call sites for `configfs_pin_fs` to understand precisely how configfs is utilized beyond the standard mount point. This will give me a comprehensive understanding of its use cases.
|
| 193/4 |
2026/07/30 00:24 |
tool |
codesearch-find-references |
0m
Args: map[Name:configfs_pin_fs]
Results: map[References:[map[ReferenceKind:calls ReferencingEntityKind:function ReferencingEntityName:configfs_depend_item SourceFile:fs/configfs/dir.c SourceLine:1155] map[ReferenceKind:calls ReferencingEntityKind:function ReferencingEntityName:configfs_register_subsystem SourceFile:fs/configfs/dir.c SourceLine:1885]] TruncatedOutput:false]
|
| 194/4 |
2026/07/30 00:24 |
llm |
codeexpert |
0m
Model:
Tokens:
input: 15023
output: 34
thoughts: 442
Thoughts: **Analyzing Configfs and User Namespaces**
Okay, so `configfs_pin_fs` is the key internal function that configfs uses to keep itself mounted, particularly when a subsystem or an item is registered. I need to understand this better. First, let me make sure I've got the mounting process nailed down: regular filesystem mounts go through `vfs_get_tree`, `do_new_mount`, `fsopen`, and then the `mount` syscall. The crucial check there is `mount_capable(fc)`.
Since `configfs_fs_type` doesn't have `FS_USERNS_MOUNT` flagged, `mount_capable(fc)` boils down to checking if the process has `CAP_SYS_ADMIN`. And that means you'd need that capability in the *initial* user namespace. Meaning, a totally unprivileged user in their *own* user namespace β even if they *have* `CAP_SYS_ADMIN` in *their* namespace β can't mount configfs. That's clear.
But, before I conclude, let's just make sure there aren't any other gotchas. Let me check what other filesystems *can* be mounted in a user namespace. Right, there are filesystems like `sysfs`, `proc`, `ramfs`, `tmpfs` (shmem), `devpts`, `binderfs`, `fuse`, `overlayfs`, and those *do* have `FS_USERNS_MOUNT` set, which makes them mountable by unprivileged users (though some might have additional restrictions like `mount_too_revealing` with sysfs/proc). Got it! Configfs is *not* like those filesystems.
Now, I wonder if there's any history or discussion around this. Let's see if anyone ever tried to add `FS_USERNS_MOUNT` to configfs, was it rejected, or is there any context around the design decision. Let's use `git-log` to look for commits related to "configfs" and "userns" or "FS_USERNS_MOUNT" in the commit messages.
|
| 195/4 |
2026/07/30 00:24 |
tool |
git-log |
0m
Args: map[MessageRegexps:[configfs userns] PathPrefix:fs/configfs]
Results: map[Output:69552c0c50f3 userns: Convert configfs to use kuid and kgid where appropriate
]
|
| 196/4 |
2026/07/30 00:24 |
llm |
codeexpert |
0m
Model:
Tokens:
input: 15997
output: 27
thoughts: 26
Thoughts: Here's a summary of my thought process:
**Diving into Commit History**
Okay, time to get a look at commit `69552c0c50f3`. I need to understand what changes were introduced with that particular commit. I'm going to pull up the diff and really scrutinize it. I need to quickly grasp the implications of those modifications. I'll need to assess how it interacts with the rest of the codebase and if it might have introduced any regressions or conflicts. Let's see what we've got.
|
| 197/4 |
2026/07/30 00:24 |
tool |
git-show |
0m
Args:
Results: map[Output:commit 69552c0c50f3f950f304fb07a4320e46f7f60c21
Author: Eric W. Biederman <ebiederm@xmission.com>
Date: Tue Feb 7 16:28:09 2012 -0800
userns: Convert configfs to use kuid and kgid where appropriate
Cc: Joel Becker <jlbec@evilplan.org>
Acked-by: Serge Hallyn <serge.hallyn@canonical.com>
Signed-off-by: Eric W. Biederman <ebiederm@xmission.com>
diff --git a/fs/configfs/inode.c b/fs/configfs/inode.c
index 0074362d9f7f..a9d35b0e06cf 100644
--- a/fs/configfs/inode.c
+++ b/fs/configfs/inode.c
@@ -79,8 +79,8 @@ int configfs_setattr(struct dentry * dentry, struct iattr * iattr)
return -ENOMEM;
/* assign default attributes */
sd_iattr->ia_mode = sd->s_mode;
- sd_iattr->ia_uid = 0;
- sd_iattr->ia_gid = 0;
+ sd_iattr->ia_uid = GLOBAL_ROOT_UID;
+ sd_iattr->ia_gid = GLOBAL_ROOT_GID;
sd_iattr->ia_atime = sd_iattr->ia_mtime = sd_iattr->ia_ctime = CURRENT_TIME;
sd->s_iattr = sd_iattr;
}
diff --git a/init/Kconfig b/init/Kconfig
index ed6310b6042b..33d231cd3cc6 100644
--- a/init/Kconfig
+++ b/init/Kconfig
@@ -950,7 +950,6 @@ config UIDGID_CONVERTED
depends on CEPH_FS = n
depends on CIFS = n
depends on CODA_FS = n
- depends on CONFIGFS_FS = n
depends on CRAMFS = n
depends on ECRYPT_FS = n
depends on EFS_FS = n
]
|
| 198/4 |
2026/07/30 00:24 |
llm |
codeexpert |
0m
Model:
Tokens:
input: 16780
output: 34
thoughts: 86
Thoughts: **Investigating Configfs and User Namespaces**
Okay, so configfs has been updated to use kuid/kgid, which is great, but it seems like nobody ever explicitly flagged it with `FS_USERNS_MOUNT`. That's... interesting. I need to dig into this further. My gut feeling is that this might have implications for how configfs behaves or is expected to behave within user namespaces, which I need to understand completely given its role in system configuration.
Let's start by seeing if there's any existing discussion or related commits about configfs and `FS_USERNS_MOUNT` or generally mounting it within user namespaces. I'll need to see if this has been addressed, perhaps overlooked, or intentionally avoided for some reason. The history of this sort of file system behavior is what I'm looking for.
I'll run a `git log` search specifically in `fs/configfs/` looking for occurrences of `FS_USERNS_MOUNT`. That should hopefully reveal any prior work, patches, or discussions related to user namespaces and this filesystem. I need to get the full picture here. This is a bit of a breadcrumb trail I hope to follow.
|
| 199/4 |
2026/07/30 00:24 |
tool |
git-log |
0m
Args: map[CodeRegexp:FS_USERNS_MOUNT PathPrefix:fs/configfs]
Results:
|
| 200/4 |
2026/07/30 00:24 |
llm |
codeexpert |
0m
Model:
Tokens:
input: 17166
output: 29
thoughts: 249
Thoughts: **Investigating User Namespace Mounting of configfs**
Okay, so I'm looking at `fs/configfs` and I can see there are *zero* commits where `FS_USERNS_MOUNT` is even mentioned. That tells me straight away that `configfs` wasn't explicitly designed to be mounted within a user namespace using that specific flag. Interesting.
Now, my mind starts to wander. Is there *any* other way this could be achieved? Could a user namespace access it if it was *already* mounted on the host? Hmm... Could an unprivileged user, given the right circumstances, somehow bind-mount or move the existing mount point within their namespace?
Ah, yes, that's it! If they have `CAP_SYS_ADMIN` capability *within* their user namespace and the target mount is accessible to them, then they can bind-mount or move the existing configfs mount. But, and this is crucial, they *can't* create a *new* configfs mount from scratch. No `mount("none", "/mnt", "configfs", 0, NULL)` allowed for them.
Okay, let's just make sure I have all the rules in order. Time to hit the documentation and refresh my memory on user namespace mounting in detail. I should start with `Documentation/filesystems/sharedsubtree.rst`, that's usually a good starting point. I'll also scan the rest of `Documentation/filesystems/` and `Documentation/admin-guide/` and search for keywords like "user namespace" and "userns" to see if there's anything else relevant. I need to be absolutely sure about all the rules in play here.
|
| 201/4 |
2026/07/30 00:24 |
tool |
grepper |
0m
Error:
Args: map[Expression:FS_USERNS_MOUNT PathPrefix:Documentation/]
Results:
|
| 202/4 |
2026/07/30 00:24 |
llm |
codeexpert |
0m
Model:
Tokens:
input: 17817
output: 27
thoughts: 51
Thoughts: Okay, let's see. My first instinct is to hunt down instances of `FS_USERNS_MOUNT` across the entire documentation directory. *Hmm*, that's odd, `grepper` came up empty. Okay, back to the drawing board. If that specific term isn't yielding results, I'll broaden the net. Let's try searching for more general terms, such as "user namespace" or the more concise "userns", but this time, focusing our search specifically within the `Documentation/` directory. That should give me a good starting point.
|
| 203/4 |
2026/07/30 00:24 |
tool |
grepper |
0m
Args: map[Expression:user namespace PathPrefix:Documentation/filesystems/]
Results: map[Output:Documentation/filesystems/fscrypt.rst=694=required that either the specified key has been added by the current
Documentation/filesystems/fscrypt.rst:695:user or that the caller has CAP_FOWNER in the initial user namespace.
Documentation/filesystems/fscrypt.rst-696-(This is needed to prevent a user from encrypting their data with
--
Documentation/filesystems/fscrypt.rst=877=as follows:
--
Documentation/filesystems/fscrypt.rst-884- To add this type of key, the calling process must have the
Documentation/filesystems/fscrypt.rst:885: CAP_SYS_ADMIN capability in the initial user namespace.
Documentation/filesystems/fscrypt.rst-886-
--
Documentation/filesystems/fscrypt.rst=943=FS_IOC_ADD_ENCRYPTION_KEY can fail with the following errors:
--
Documentation/filesystems/fscrypt.rst-946- caller does not have the CAP_SYS_ADMIN capability in the initial
Documentation/filesystems/fscrypt.rst:947: user namespace; or the key was specified by Linux key ID but the
Documentation/filesystems/fscrypt.rst-948- process lacks Search permission on the key.
--
Documentation/filesystems/fscrypt.rst=1047=This structure must be zeroed, then initialized as follows:
--
Documentation/filesystems/fscrypt.rst-1054- calling process must have the CAP_SYS_ADMIN capability in the
Documentation/filesystems/fscrypt.rst:1055: initial user namespace.
Documentation/filesystems/fscrypt.rst-1056-
--
Documentation/filesystems/fscrypt.rst=1094=FS_IOC_REMOVE_ENCRYPTION_KEY can fail with the following errors:
--
Documentation/filesystems/fscrypt.rst-1097- was specified, but the caller does not have the CAP_SYS_ADMIN
Documentation/filesystems/fscrypt.rst:1098: capability in the initial user namespace
Documentation/filesystems/fscrypt.rst-1099-- ``EINVAL``: invalid key specifier type, or reserved bits were set
--
Documentation/filesystems/fuse/fuse.rst=305=mounts, it can relax the last limitation in several ways:
--
Documentation/filesystems/fuse/fuse.rst-312- an unprivileged user - normally restricted from mounting with
Documentation/filesystems/fuse/fuse.rst:313: 'allow_other' - could do so in a user namespace where they're
Documentation/filesystems/fuse/fuse.rst-314- privileged. If any process could access such an 'allow_other' mount
Documentation/filesystems/fuse/fuse.rst-315- this would give the mounting user the ability to manipulate
Documentation/filesystems/fuse/fuse.rst:316: processes in user namespaces where they're unprivileged. For this
Documentation/filesystems/fuse/fuse.rst-317- reason 'allow_other' restricts access to users in the same userns
--
Documentation/filesystems/fuse/fuse.rst-321- set, super user's processes have unrestricted access to mounts
Documentation/filesystems/fuse/fuse.rst:322: irrespective of allow_other setting or user namespace of the
Documentation/filesystems/fuse/fuse.rst-323- mounting user.
--
Documentation/filesystems/idmappings.rst=154=A kernel id is always created by an idmapping. Such idmappings are associated
Documentation/filesystems/idmappings.rst:155:with user namespaces. Since we mainly care about how idmappings work we're not
Documentation/filesystems/idmappings.rst-156-going to be concerned with how idmappings are created nor how they are used
--
Documentation/filesystems/idmappings.rst=158=namespaces.
Documentation/filesystems/idmappings.rst-159-
Documentation/filesystems/idmappings.rst:160:The initial user namespace is special. It always has an idmapping of the
Documentation/filesystems/idmappings.rst-161-following form::
--
Documentation/filesystems/idmappings.rst=166=system.
Documentation/filesystems/idmappings.rst-167-
Documentation/filesystems/idmappings.rst:168:Other user namespaces usually have non-identity idmappings such as::
Documentation/filesystems/idmappings.rst-169-
--
Documentation/filesystems/idmappings.rst=174=immediately translated into a kernel id according to the idmapping associated
Documentation/filesystems/idmappings.rst:175:with the relevant user namespace.
Documentation/filesystems/idmappings.rst-176-
--
Documentation/filesystems/idmappings.rst=178=owned by ``u1000``:
Documentation/filesystems/idmappings.rst-179-
Documentation/filesystems/idmappings.rst:180:- If a filesystem were to be mounted in the initial user namespaces (as most
Documentation/filesystems/idmappings.rst-181- filesystems are) then the initial idmapping will be used. As we saw this is
--
Documentation/filesystems/idmappings.rst=377=From the implementation point it's worth mentioning how idmappings are represented.
Documentation/filesystems/idmappings.rst:378:All idmappings are taken from the corresponding user namespace.
Documentation/filesystems/idmappings.rst-379-
--
Documentation/filesystems/idmappings.rst=604=If the user is lucky, they are dealing with a filesystem that is mountable
Documentation/filesystems/idmappings.rst:605:inside user namespaces. But this would also change ownership globally and the
Documentation/filesystems/idmappings.rst-606-change in ownership is tied to the lifetime of the filesystem mount, i.e. the
Documentation/filesystems/idmappings.rst=607=superblock. The only way to change ownership is to completely unmount the
Documentation/filesystems/idmappings.rst:608:filesystem and mount it again in another user namespace. This is usually
Documentation/filesystems/idmappings.rst-609-impossible because it would mean that all users currently accessing the
--
Documentation/filesystems/idmappings.rst=618=They allow to expose the same set of dentries with different ownership at
Documentation/filesystems/idmappings.rst:619:different mounts. This is achieved by marking the mounts with a user namespace
Documentation/filesystems/idmappings.rst-620-through the ``mount_setattr()`` system call. The idmapping associated with it
--
Documentation/filesystems/idmappings.rst=629=Filesystems that support idmapped mounts don't have any real reason to support
Documentation/filesystems/idmappings.rst:630:being mountable inside user namespaces. A filesystem could be exposed
Documentation/filesystems/idmappings.rst-631-completely under an idmapped mount to get the same effect. This has the
Documentation/filesystems/idmappings.rst=632=advantage that filesystems can leave the creation of the superblock to
Documentation/filesystems/idmappings.rst:633:privileged users in the initial user namespace.
Documentation/filesystems/idmappings.rst-634-
Documentation/filesystems/idmappings.rst=635=However, it is perfectly possible to combine idmapped mounts with filesystems
Documentation/filesystems/idmappings.rst:636:mountable inside user namespaces. We will touch on this further below.
Documentation/filesystems/idmappings.rst-637-
--
Documentation/filesystems/porting.rst=705=d_alloc_parallel() for that; look for in-tree examples.
--
Documentation/filesystems/porting.rst-717-->setxattr() and xattr_handler.set() get dentry and inode passed separately.
Documentation/filesystems/porting.rst:718:The xattr_handler.set() gets passed the user namespace of the mount the inode
Documentation/filesystems/porting.rst-719-is seen from so filesystems can idmap the i_uid and i_gid accordingly.
--
Documentation/filesystems/proc.rst=2438=so will give an `-EBUSY` error).
--
Documentation/filesystems/proc.rst-2442-
Documentation/filesystems/proc.rst:2443:If user namespaces are in use, the kernel additionally checks the instances of
Documentation/filesystems/proc.rst-2444-procfs available to the mounter and will not allow procfs to be mounted if:
--
Documentation/filesystems/sharedsubtree.rst=205=building blocks to solve the following problems:
Documentation/filesystems/sharedsubtree.rst-206-
Documentation/filesystems/sharedsubtree.rst:207:C) Per-user namespace
Documentation/filesystems/sharedsubtree.rst-208-
--
Documentation/filesystems/tmpfs.rst=155=normally can be.
Documentation/filesystems/tmpfs.rst-156-
Documentation/filesystems/tmpfs.rst:157:Note that tmpfs quotas do not support user namespaces so no uid/gid
Documentation/filesystems/tmpfs.rst:158:translation is done if quotas are enabled inside user namespaces.
Documentation/filesystems/tmpfs.rst-159-
]
|
| 204/4 |
2026/07/30 00:24 |
llm |
codeexpert |
0m
Model:
Tokens:
input: 20292
output: 23
thoughts: 737
Thoughts: **Analyzing Configfs and User Namespaces for Mount Capabilities**
Okay, let's break this down systematically. My immediate thought is to check the `idmappings.rst` documentation, specifically lines 604 and 629. The document clearly states that a filesystem needs to be explicitly marked as mountable within user namespaces, using the `FS_USERNS_MOUNT` flag in its `file_system_type` structure. The document further points out a key design consideration: filesystems supporting idmapped mounts don't inherently *need* to be user-namespaced mountable, and delegating superblock creation to a privileged user is an architectural choice.
So, the question is, does `configfs` play by these rules? Let's verify. I recall its source code, specifically `fs/configfs/mount.c`. We need to examine how the `file_system_type` for `configfs` is defined. Aha! There it is:
```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,
};
```
Right, no `.fs_flags` member explicitly initialized. Since it's not set, it defaults to zero. This confirms that `configfs` *does not* have the `FS_USERNS_MOUNT` flag set.
Now, let's consider what happens when someone attempts to mount `configfs` inside a user namespace. If a process calls `mount("none", "/mnt", "configfs", 0, NULL)` or uses the new mount API ( `fsopen("configfs", 0)`) the following steps will be executed:
1. The `mount` system call or `fsopen` will look up the `"configfs"` filesystem type.
2. It finds `configfs_fs_type`.
3. It allocates a filesystem context (`fc`).
4. Crucially, it calls `mount_capable(fc)`.
Within `mount_capable(fc)`, the code checks for `FS_USERNS_MOUNT`:
```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);
}
```
Since the `configfs_fs_type.fs_flags` does *not* contain `FS_USERNS_MOUNT`, `mount_capable(fc)` effectively simplifies to checking `capable(CAP_SYS_ADMIN)`. This check boils down to whether the calling process has `CAP_SYS_ADMIN` capability within the *initial* user namespace (`&init_user_ns`). An unprivileged process, or a process only privileged within a *non-initial* user namespace, *will not* have this capability, thus `capable(CAP_SYS_ADMIN)` returns `false`.
Therefore, the `mount_capable(fc)` function returns `false`, and the mount attempt will ultimately fail with an `-EPERM` error, as we might expect.
Hold on, let's just make certain, are there any *other* flags that influence user namespace delegation, something like `FS_USERNS_DELEGATABLE` perhaps? Time to search the codebase.
|
| 205/4 |
2026/07/30 00:24 |
tool |
grepper |
0m
Args: map[Expression:FS_USERNS_DELEGATABLE]
Results: map[Output:fs/nfs/fs_context.c=1766=struct file_system_type nfs_fs_type = {
--
fs/nfs/fs_context.c-1773- FS_BINARY_MOUNTDATA |
fs/nfs/fs_context.c:1774: FS_USERNS_DELEGATABLE,
fs/nfs/fs_context.c-1775-};
--
fs/nfs/fs_context.c=1780=struct file_system_type nfs4_fs_type = {
--
fs/nfs/fs_context.c-1787- FS_BINARY_MOUNTDATA |
fs/nfs/fs_context.c:1788: FS_USERNS_DELEGATABLE,
fs/nfs/fs_context.c-1789-};
--
fs/super.c=735=struct super_block *sget_fc(struct fs_context *fc,
--
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-747- * unprepared to handle it. This can happen when fsconfig() is called
--
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");
--
include/linux/fs.h=2280=struct file_system_type {
--
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 */
include/linux/fs.h-2294-#define FS_RENAME_DOES_D_MOVE 32768 /* FS will handle d_move() during rename() internally. */
]
|
| 206/4 |
2026/07/30 00:24 |
llm |
codeexpert |
0m
Model:
Tokens:
input: 22441
output: 33
thoughts: 187
Thoughts: **Investigating User Namespace Mount Capabilities**
Okay, so `FS_USERNS_DELEGATABLE` is specifically tied to NFS, allowing mounting within a user namespace from an external context β think of it as a process in the initial namespace managing a superblock for a non-initial namespace. Interesting. I recall that configfs doesn't expose either `FS_USERNS_MOUNT` or `FS_USERNS_DELEGATABLE`, but I need to be certain.
Let me take a broader view: are there *any* other filesystems I should be aware of, or are there any less obvious checks in place?
The main area to look at should be around where filesystems are registered. A quick check of `fs/filesystems.c` (or a similar location) should reveal if there's a global list or some override mechanism in place. Ah, right, `register_filesystem` simply registers the `file_system_type` struct.
To be absolutely certain, I'm going to scan the source of `fs/filesystems.c`. Best to leave no stone unturned when it comes to security.
|
| 207/4 |
2026/07/30 00:24 |
tool |
read-file |
0m
Args: map[File:fs/filesystems.c FirstLine:1 LineCount:100]
Results: map[Contents: 1: // SPDX-License-Identifier: GPL-2.0
2: /*
3: * linux/fs/filesystems.c
4: *
5: * Copyright (C) 1991, 1992 Linus Torvalds
6: *
7: * table of configured filesystems
8: */
9:
10: #include <linux/syscalls.h>
11: #include <linux/fs.h>
12: #include <linux/proc_fs.h>
13: #include <linux/seq_file.h>
14: #include <linux/kmod.h>
15: #include <linux/init.h>
16: #include <linux/module.h>
17: #include <linux/slab.h>
18: #include <linux/uaccess.h>
19: #include <linux/fs_parser.h>
20: #include <linux/rculist.h>
21:
22: /*
23: * Read-mostly filesystem drivers list.
24: *
25: * Readers walk under rcu_read_lock(); writers take file_systems_lock
26: * and publish via _rcu hlist primitives. unregister_filesystem()
27: * synchronize_rcu()s after unlock so the embedded file_system_type
28: * can't go away under a reader. To keep using a filesystem after
29: * the RCU section ends, take a module reference via try_module_get().
30: */
31: static HLIST_HEAD(file_systems);
32: static DEFINE_SPINLOCK(file_systems_lock);
33:
34: #ifdef CONFIG_PROC_FS
35: /*
36: * Cache a stringified version of the filesystem list.
37: *
38: * The fs list gets queried a lot by userspace because of libselinux, including
39: * rather surprising programs (would you guess *sed* is on the list?). In order
40: * to reduce the overhead we cache the resulting string, which normally hangs
41: * around below 512 bytes in size.
42: *
43: * As the list almost never changes, its creation is not particularly optimized
44: * to keep things simple.
45: *
46: * We sort it out on read in order to not introduce a failure point for fs
47: * registration (in principle we may be unable to alloc memory for the list).
48: */
49: struct file_systems_string {
50: struct rcu_head rcu;
51: unsigned long gen;
52: size_t len;
53: char string[];
54: };
55:
56: static unsigned long file_systems_gen;
57: static struct file_systems_string __read_mostly __rcu *file_systems_string;
58:
59: static void invalidate_filesystems_string(void);
60: #else
61: static inline void invalidate_filesystems_string(void) { }
62: #endif
63:
64: /* WARNING: This can be used only if we _already_ own a reference */
65: struct file_system_type *get_filesystem(struct file_system_type *fs)
66: {
67: __module_get(fs->owner);
68: return fs;
69: }
70:
71: void put_filesystem(struct file_system_type *fs)
72: {
73: module_put(fs->owner);
74: }
75:
76: static struct file_system_type *find_filesystem(const char *name, unsigned len)
77: {
78: struct file_system_type *fs;
79:
80: hlist_for_each_entry_rcu(fs, &file_systems, list,
81: lockdep_is_held(&file_systems_lock))
82: if (strncmp(fs->name, name, len) == 0 && !fs->name[len])
83: return fs;
84: return NULL;
85: }
86:
87: /**
88: * register_filesystem - register a new filesystem
89: * @fs: the file system structure
90: *
91: * Adds the file system passed to the list of file systems the kernel
92: * is aware of for mount and other syscalls. Returns 0 on success,
93: * or a negative errno code on an error.
94: *
95: * The &struct file_system_type that is passed is linked into the kernel
96: * structures and must not be freed until the file system has been
97: * unregistered.
98: */
99: int register_filesystem(struct file_system_type *fs)
100: {
]
|
| 208/4 |
2026/07/30 00:24 |
llm |
codeexpert |
0m
Model:
Tokens:
input: 24307
output: 514
thoughts: 768
Thoughts: **Configfs Mountability in User Namespaces: A Detailed Explanation**
Okay, so I've looked into this, and the question is: can an unprivileged user mount `configfs` within a user namespace? The answer is a definitive "no."
Let me break this down definitively, referencing the code:
* **The Core Issue:** You're absolutely correct; `configfs_fs_type` defined in `fs/configfs/mount.c` *does not* have the `FS_USERNS_MOUNT` flag set. In fact, the `.fs_flags` field is omitted entirely, defaulting to 0. This is the crux of the matter.
* **The VFS Verification:** The Virtual File System (VFS) layer, in `fs/super.c`, uses a function called `mount_capable()` during mount operations (whether it's the older `mount` syscall or the newer `fsopen`/`fsmount` approach). This is where the permission is checked.
Here's what `mount_capable()` does:
```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);
}
```
See that `if` statement? Because `configfs` *lacks* the `FS_USERNS_MOUNT` flag, `mount_capable()` evaluates to `capable(CAP_SYS_ADMIN)`. `capable()` in this context means checking if the process has the specified capability. In the case of `configfs` without the `FS_USERNS_MOUNT` flag, this capability check is performed against the *initial* user namespace (`&init_user_ns`).
* **Namespaces and Privileges:** Now, here's where it all falls apart for unprivileged users in user namespaces: Unprivileged users *within a user namespace* do *not* have `CAP_SYS_ADMIN` (or any other capabilities) in the *initial* user namespace. They only have these capabilities within *their own* user namespace. Because the `mount_capable()` check is made against the initial namespace (due to the missing flag), it will fail and return `-EPERM`. Therefore, configfs can not be mounted.
So, in short, because the `FS_USERNS_MOUNT` flag isn't set, the mount operation requires `CAP_SYS_ADMIN` in the initial namespace, which unprivileged users in user namespaces lack. That's why they can't mount it. The user's observation is spot-on. I'll go ahead and 'set-results' with this accurate explanation.
|
| 209/4 |
2026/07/30 00:25 |
tool |
set-results |
0m
Args: map[Answer:No, **configfs is not mountable by unprivileged users in a user namespace**. Your observation is completely correct.
### Why configfs cannot be mounted in a user namespace
1. **Missing `FS_USERNS_MOUNT` Flag**:
In `fs/configfs/mount.c`, the `configfs_fs_type` struct is defined as follows:
```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,
};
```
Because the `.fs_flags` field is omitted, it defaults to `0`. Consequently, it does not have the `FS_USERNS_MOUNT` flag (which is required to allow mounting by a non-initial user namespace root).
2. **VFS Capability Checks**:
When a mount request is made (via either the legacy `mount` syscall or the new mount API), the VFS 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);
}
```
Since `configfs_fs_type.fs_flags` does not contain `FS_USERNS_MOUNT`, `mount_capable()` falls back to checking `capable(CAP_SYS_ADMIN)`.
3. **Unprivileged User Restrictions**:
The `capable(CAP_SYS_ADMIN)` helper checks for the `CAP_SYS_ADMIN` capability in the **initial** user namespace (`&init_user_ns`). An unprivileged user inside a non-initial user namespace only possesses capabilities within their own user namespace (and nested ones), but not in the initial user namespace. As a result, `capable(CAP_SYS_ADMIN)` returns `false`, and the mount attempt is rejected with `-EPERM`.]
Results: map[Answer:No, **configfs is not mountable by unprivileged users in a user namespace**. Your observation is completely correct.
### Why configfs cannot be mounted in a user namespace
1. **Missing `FS_USERNS_MOUNT` Flag**:
In `fs/configfs/mount.c`, the `configfs_fs_type` struct is defined as follows:
```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,
};
```
Because the `.fs_flags` field is omitted, it defaults to `0`. Consequently, it does not have the `FS_USERNS_MOUNT` flag (which is required to allow mounting by a non-initial user namespace root).
2. **VFS Capability Checks**:
When a mount request is made (via either the legacy `mount` syscall or the new mount API), the VFS 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);
}
```
Since `configfs_fs_type.fs_flags` does not contain `FS_USERNS_MOUNT`, `mount_capable()` falls back to checking `capable(CAP_SYS_ADMIN)`.
3. **Unprivileged User Restrictions**:
The `capable(CAP_SYS_ADMIN)` helper checks for the `CAP_SYS_ADMIN` capability in the **initial** user namespace (`&init_user_ns`). An unprivileged user inside a non-initial user namespace only possesses capabilities within their own user namespace (and nested ones), but not in the initial user namespace. As a result, `capable(CAP_SYS_ADMIN)` returns `false`, and the mount attempt is rejected with `-EPERM`.]
|
| 210/2 |
2026/07/30 00:25 |
llm |
expert |
0m
Model:
Tokens:
input: 37931
output: 66
thoughts: 777
Thoughts: **My Assessment of the UAF Vulnerability**
Alright, let's break this down systematically. First, this isn't something a regular user can trigger; it's definitely not accessible from user namespaces. Okay, so we're looking at a Denial of Service scenario here, clearly a Use-After-Free (UAF). If KASAN (Kernel Address Sanitizer) is enabled, we'll see an immediate panic or warning, that's straightforward. If KASAN is disabled, we're writing `LIST_POISON` to a freed object.
Now, does writing `LIST_POISON` cause a crash *on its own*? No, not immediately, the object is freed, not actively in use at that moment. The real issue is when the memory is reallocated. When that happens, the new user will *usually* overwrite those `LIST_POISON` values. But wait, what if the memory isn't immediately reallocated? The SLUB allocator might try to use the freepointer, which is at offset 96. And the vuln is at offset 80 (size 16), so the freepointer is NOT overwritten and this is a classic UAF.
Then, the re-allocation: what if the new user expects zeroed memory? If they use `kzalloc`, it will get zeroed out, and the `LIST_POISON` is gone. But, if they use `kmalloc` and don't initialize, then we potentially still have the `LIST_POISON` values lurking around, which *could* lead to a crash later. Either way, it's a standard UAF write, leading to a potential DoS and potentially exploitable.
However, the real limiter here is the access. Since root is required to interact with configfs, the impact is significantly reduced.
Let's review the dimensions:
* **Exploitable:** Yes (UAF write, list unlinking primitive if race is won).
* **Denial of Service:** Yes (kernel crash if race is won or if KASAN is enabled, or if reallocated object uses uninitialized memory).
* **Accessible From Unprivileged Processes:** No (root required to access configfs).
* **Accessible From User Namespaces:** No (configfs can't be mounted in userns).
* **VM Guest/Host Trigger in The Confidential Computing Context:** No.
* **Ethernet Network/Remote/Peripheral/Malicious Filesystem Trigger:** No.
Okay, let's double-check other triggering mechanisms for `uvcg_extension_drop`. Can a malicious USB host somehow trigger it? No, configfs is for *local* gadget configuration, not remote control. What about a malicious filesystem? Again, no, configfs is a pseudo-filesystem, not backed by a block device. Only through direct configfs operations.
And the only other point of entry for this bug would be to trigger `uvcg_extension_make`. But a look at the code confirms this too is also through `uvcg_extensions_grp_ops`, which is only accessible through configfs.
Therefore, the bug is strictly limited to root (or users with `CAP_SYS_ADMIN` in the initial namespace). I'll mark the results accordingly.
|
| 211/2 |
2026/07/30 00:25 |
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]
|
| 212/2 |
2026/07/30 00:25 |
llm |
expert |
0m
Model:
Tokens:
input: 39539
output: 695
thoughts: 387
Thoughts: **My Assessment of the UVC Gadget Extension Bug**
Okay, the analysis is complete, and it's time to solidify my conclusions. I've got a use-after-free vulnerability locked down within the USB gadget UVC function, specifically in `uvcg_extension_drop`. The crux of the matter is the order of operations: `config_item_put(item)` is executed *before* `list_del(&xu->list)`. Essentially, `config_item_put` prematurely releases the `uvcg_extension` object, triggering its release in `uvcg_extension_release`, which frees the memory of `xu`. The subsequent call to `list_del(&xu->list)` then attempts to access this freed memory. That's a textbook use-after-free write, plain and simple.
The dangerous part is that `list_del` is writing `LIST_POISON1` and `LIST_POISON2` to the `next` and `prev` pointers of the list node, which reside within the freed object. If an adversary were incredibly skilled (and lucky), and could somehow reallocate that exact memory block in the infinitesimally small window between the free and the execution of `list_del`, they could potentially control those pointers, resulting in a primitive for arbitrary memory writes. It's a race condition of the most delicate kind.
However, after considering the attack surface, the security impact seems significantly muted. This vulnerability is *only* exploitable through `configfs` operations β think failures during `mkdir` or `rmdir` actions on the extension item. The `configfs` filesystem is typically mounted at `/sys/kernel/config` and, as you know, is restricted to the root user. Moreover, since `configfs` does *not* possess the `FS_USERNS_MOUNT` flag, it cannot be mounted by unprivileged users within a user namespace. Meaning this vulnerability is entirely out of reach for any unprivileged process, and completely inaccessible from within a user namespace.
In essence, because root access is required, the security impact is minimal. The very entity that could trigger the bug β root β already has total control over the system. This vulnerability isn't remotely exploitable via the network, external peripherals, or even malicious filesystems. I'd assess this as a functional bug with limited security implications.
|
| 213/1 |
2026/07/30 00:25 |
action |
format-explanation |
0m
Results: map[Explanation:**Root Cause Analysis**
The bug is a slab-use-after-free (UAF) in the USB gadget UVC (USB Video Class)
function, specifically in `uvcg_extension_drop()` located in
`drivers/usb/gadget/function/uvc_configfs.c`.
When an extension configfs item is dropped (e.g., via `rmdir` or when `mkdir`
fails and cleans up), `uvcg_extension_drop()` is invoked. The function is
implemented as follows:
```c
static void uvcg_extension_drop(struct config_group *group, struct config_item
*item)
{
struct uvcg_extension *xu = container_of(item, struct uvcg_extension,
item);
// ...
config_item_put(item);
list_del(&xu->list);
kfree(xu->desc.baSourceID);
kfree(xu->desc.bmControls);
// ...
}
```
Calling `config_item_put(item)` decrements the reference count of the item. If
it reaches zero, it triggers the release callback (`uvcg_extension_release()`),
which immediately calls `kfree(xu)`.
However, the function continues to execute and calls `list_del(&xu->list)`, as
well as freeing other members of `xu`. This results in a use-after-free read and
write on the `xu` object.
**Exploitability**
If an attacker can win the extremely tight race condition (literally two C
statements without a context switch) by having an interrupt or preemption occur
exactly after `kfree(xu)` and reallocating the object, they could control the
`next` and `prev` pointers before `list_del()` is called. This would lead to a
classic list unlinking arbitrary write primitive (`next->prev = prev; prev->next
= next;`), which is highly exploitable.
If the race is not won, `list_del()` writes `LIST_POISON1` and `LIST_POISON2`
into the freed object, which can cause a kernel crash (Denial of Service) if the
memory is later reallocated and used uninitialized, or if debug tools like
KASAN/DEBUG_LIST are enabled.
**Access and Trigger**
Despite being technically exploitable, this bug is **not accessible to
unprivileged users**.
The vulnerable code is triggered exclusively via `configfs` operations (creating
or removing directories under `/sys/kernel/config/usb_gadget/`).
- `configfs` is typically mounted by root and its directories are owned by root
with restricted permissions.
- `configfs` lacks the `FS_USERNS_MOUNT` flag in its file system type
definition, meaning it **cannot be mounted inside a user namespace** by an
unprivileged user.
- The bug is triggered by local administrative configuration of the USB gadget,
not by plugging in a malicious USB device (peripheral) or by processing remote
network traffic.
Therefore, an attacker must already possess `CAP_SYS_ADMIN` in the initial
namespace (i.e., root privileges) to reach this code, making the practical
security impact negligible.]
|