| 0/0 |
2026/08/02 00:29 |
flow |
moderation |
39m
Results: map[Actionable:true Explanation:An analysis of the KASAN bug report and the Linux kernel source code confirms
that this report is **fully consistent, highly actionable, and points directly
to a clear root cause**.
### 1. Root Cause Analysis
The bug is a classic use-after-free (UAF) in `uvcg_extension_drop()` within
`drivers/usb/gadget/function/uvc_configfs.c`.
When an extension unit directory is removed via configfs,
`uvcg_extension_drop()` is invoked:
```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); // <--- [1] Drops the last reference,
freeing 'xu'
list_del(&xu->list); // <--- [2] Use-after-free of 'xu'
kfree(xu->desc.baSourceID); // <--- [3] Use-after-free of 'xu'
kfree(xu->desc.bmControls); // <--- [4] Use-after-free of 'xu'
mutex_unlock(&opts->lock);
}
```
1. **At `[1]`**: `config_item_put(item)` decrements the reference count of the
config item. Since this is the last reference, it triggers the release callback
`uvcg_extension_release()`, which frees the parent structure `struct
uvcg_extension *xu` via `kfree(xu)`.
2. **At `[2]`**: The code immediately attempts to delete `xu->list` from the
list of extension units using `list_del(&xu->list)`. Since `xu` has already been
freed, this results in a use-after-free.
3. **At `[3]` and `[4]`**: The code also attempts to free members of `xu->desc`
(`baSourceID` and `bmControls`), which are further use-after-free accesses.
---
### 2. Consistency of the Bug Report
The KASAN report is perfectly aligned with this analysis:
* **Bad Access Stack**: Shows the crash occurring in `list_del` inside
`uvcg_extension_drop+0x81/0x1f0`.
* **Allocation Stack**: Shows the object was allocated in
`uvcg_extension_make+0x95/0x300`.
* **Free Stack**: Shows the object was freed via `config_item_release` ->
`config_item_cleanup` -> `uvcg_extension_drop+0x75/0x1f0` (which corresponds to
the `config_item_put(item)` call at line 1259).
* **Memory Offset Match**:
* The structure layout of `struct uvcg_extension` is:
```c
struct uvcg_extension {
struct config_item item; // Offset 0, Size 80 bytes
struct list_head list; // Offset 80, Size 16 bytes
(next at 80, prev at 88)
struct uvcg_extension_unit_descriptor desc;
};
```
* KASAN reports a read of size 8 at offset **88** inside the freed 192-byte
region. This corresponds exactly to accessing `xu->list.prev` during the
`list_del` operation.
---
### 3. Recommended Fix
To resolve the issue, `config_item_put(item)` must be deferred to the very end
of `uvcg_extension_drop()`, after all operations on `xu` and its members are
completed:
```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);
}
```]
|
| 1/1 |
2026/08/02 00:29 |
action |
kernel-checkouter |
0m
Results: map[KernelSrc:/app/workdir/cache/src/cde1b9ef9bc0fd8ebc2b773db8e023ba15b86bfc]
|
| 2/1 |
2026/08/02 00:29 |
action |
kernel-builder |
22m
Results: map[KernelObj:/app/workdir/cache/build/2e9b6314714c2f0b1c38d672c3dc5b2fe47f626d]
|
| 3/1 |
2026/08/02 00:51 |
action |
codesearch-prepare |
15m
Results: map[Index:codesearch-index]
|
| 4/1 |
2026/08/02 01:07 |
agent |
expert |
1m
Model:
Results:
Instruction: You are an experienced Linux kernel developer tasked with determining if the given kernel bug
report is actionable or not. Actionable means that it contains enough info to root cause
the underlying bug, and that the report is self-consistent and makes sense, rather than
e.g. a one-off nonsensical crash induced by a previous memory corruption.
The bug report is about a use-after-free bug generated by KASAN tool.
It should contain 3 stack traces: the bad memory access stack, the heap block allocation stack,
and the heap block free stack. If the report does not contain 3 stacks, it's not actionable.
All 3 stack traces should be related to the same object type,
and usually be in the same kernel subsystem (at least leaf stack frames).
An example of an actionable and consistent report would be: first access stack relates
to an access to a field of struct Foo, allocation/free stacks relate to allocation/free
of the struct Foo.
In inconsistent/nonsensical reports an access may be to a struct Foo, but allocation
stack allocates a different structure in a different subsystem.
Look for other suspicious signals/inconsistencies that can make this report hard to
debug/understand.
In the final reply explain why you think the report is self-consistent and actionable,
or why it's inconsistent and/or not actionable.
Use the provided tools to confirm any assumptions, variables/fields being accessed, etc.
Don't make assumptions about the kernel source code (it may be different from what you assume it is).
Extensively use the provided code access tools (codesearch-*, git-*, grepper, etc)
to examine the actual source code, and confirm any assumptions.
Prefer calling several tools at the same time to save round-trips.
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 bug report is:
==================================================================
BUG: KASAN: slab-use-after-free in __list_del_entry_valid_or_report+0x31/0x190 lib/list_debug.c:49
Read of size 8 at addr ffff8880343c1158 by task syz.4.33/5952
CPU: 0 UID: 0 PID: 5952 Comm: syz.4.33 Not tainted syzkaller #0 PREEMPT_{RT,(full)}
Hardware name: Google Google Compute Engine/Google Compute Engine, BIOS Google 07/24/2026
Call Trace:
<TASK>
dump_stack_lvl+0xe8/0x150 lib/dump_stack.c:120
print_address_description+0x55/0x1e0 mm/kasan/report.c:378
print_report+0x58/0x70 mm/kasan/report.c:482
kasan_report+0x117/0x150 mm/kasan/report.c:595
__list_del_entry_valid_or_report+0x31/0x190 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+0x81/0x1f0 drivers/usb/gadget/function/uvc_configfs.c:1260
configfs_mkdir+0x7e9/0x9e0 fs/configfs/dir.c:1443
vfs_mkdir+0x402/0x620 fs/namei.c:5276
filename_mkdirat+0x289/0x520 fs/namei.c:5309
__do_sys_mkdirat fs/namei.c:5330 [inline]
__se_sys_mkdirat+0x35/0x150 fs/namei.c:5327
do_syscall_x64 arch/x86/entry/syscall_64.c:63 [inline]
do_syscall_64+0x174/0x580 arch/x86/entry/syscall_64.c:94
entry_SYSCALL_64_after_hwframe+0x77/0x7f
RIP: 0033:0x7f3013e4df99
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:00007f30120a6028 EFLAGS: 00000246 ORIG_RAX: 0000000000000102
RAX: ffffffffffffffda RBX: 00007f30140d5fa0 RCX: 00007f3013e4df99
RDX: 00000000000001c0 RSI: 0000200000000340 RDI: ffffffffffffff9c
RBP: 00007f3013ee4ec4 R08: 0000000000000000 R09: 0000000000000000
R10: 0000000000000000 R11: 0000000000000246 R12: 0000000000000000
R13: 00007f30140d6038 R14: 00007f30140d5fa0 R15: 00007ffc51c27a98
</TASK>
Allocated by task 5952:
kasan_save_stack mm/kasan/common.c:57 [inline]
kasan_save_track+0x3e/0x80 mm/kasan/common.c:78
poison_kmalloc_redzone mm/kasan/common.c:398 [inline]
__kasan_kmalloc+0x93/0xb0 mm/kasan/common.c:415
kasan_kmalloc include/linux/kasan.h:263 [inline]
__kmalloc_cache_noprof+0x3d2/0x6b0 mm/slub.c:5489
_kmalloc_noprof include/linux/slab.h:988 [inline]
_kzalloc_noprof include/linux/slab.h:1309 [inline]
uvcg_extension_make+0x95/0x300 drivers/usb/gadget/function/uvc_configfs.c:1276
configfs_mkdir+0x580/0x9e0 fs/configfs/dir.c:1369
vfs_mkdir+0x402/0x620 fs/namei.c:5276
filename_mkdirat+0x289/0x520 fs/namei.c:5309
__do_sys_mkdirat fs/namei.c:5330 [inline]
__se_sys_mkdirat+0x35/0x150 fs/namei.c:5327
do_syscall_x64 arch/x86/entry/syscall_64.c:63 [inline]
do_syscall_64+0x174/0x580 arch/x86/entry/syscall_64.c:94
entry_SYSCALL_64_after_hwframe+0x77/0x7f
Freed by task 5952:
kasan_save_stack mm/kasan/common.c:57 [inline]
kasan_save_track+0x3e/0x80 mm/kasan/common.c:78
kasan_save_free_info+0x40/0x50 mm/kasan/generic.c:584
poison_slab_object mm/kasan/common.c:253 [inline]
__kasan_slab_free+0x5c/0x80 mm/kasan/common.c:285
kasan_slab_free include/linux/kasan.h:235 [inline]
slab_free_hook mm/slub.c:2677 [inline]
slab_free mm/slub.c:6377 [inline]
kfree+0x1c5/0x6c0 mm/slub.c:6692
config_item_cleanup fs/configfs/item.c:128 [inline]
config_item_release+0x13a/0x2d0 fs/configfs/item.c:137
uvcg_extension_drop+0x75/0x1f0 drivers/usb/gadget/function/uvc_configfs.c:1259
configfs_mkdir+0x7e9/0x9e0 fs/configfs/dir.c:1443
vfs_mkdir+0x402/0x620 fs/namei.c:5276
filename_mkdirat+0x289/0x520 fs/namei.c:5309
__do_sys_mkdirat fs/namei.c:5330 [inline]
__se_sys_mkdirat+0x35/0x150 fs/namei.c:5327
do_syscall_x64 arch/x86/entry/syscall_64.c:63 [inline]
do_syscall_64+0x174/0x580 arch/x86/entry/syscall_64.c:94
entry_SYSCALL_64_after_hwframe+0x77/0x7f
The buggy address belongs to the object at ffff8880343c1100
which belongs to the cache kmalloc-192 of size 192
The buggy address is located 88 bytes inside of
freed 192-byte region [ffff8880343c1100, ffff8880343c11c0)
The buggy address belongs to the physical page:
page: refcount:0 mapcount:0 mapping:0000000000000000 index:0x0 pfn:0x343c1
flags: 0x80000000000000(node=0|zone=1)
page_type: f5(slab)
raw: 0080000000000000 ffff88813ffb13c0 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 28098951672, free_ts 0
set_page_owner include/linux/page_owner.h:32 [inline]
post_alloc_hook+0x1f9/0x250 mm/page_alloc.c:1859
prep_new_page mm/page_alloc.c:1867 [inline]
get_page_from_freelist+0x262a/0x26a0 mm/page_alloc.c:3946
__alloc_frozen_pages_noprof+0x18d/0x380 mm/page_alloc.c:5304
alloc_slab_page mm/slub.c:3266 [inline]
allocate_slab+0x79/0x5e0 mm/slub.c:3380
new_slab mm/slub.c:3426 [inline]
refill_objects+0x2d8/0x350 mm/slub.c:7310
refill_sheaf mm/slub.c:2804 [inline]
__pcs_replace_empty_main+0x330/0x690 mm/slub.c:4675
alloc_from_pcs mm/slub.c:4773 [inline]
slab_alloc_node mm/slub.c:4905 [inline]
__kmalloc_cache_noprof+0x468/0x6b0 mm/slub.c:5485
_kmalloc_noprof include/linux/slab.h:988 [inline]
_kzalloc_noprof include/linux/slab.h:1309 [inline]
call_usermodehelper_setup+0x8e/0x270 kernel/umh.c:362
kobject_uevent_env+0x665/0x9e0 lib/kobject_uevent.c:631
kernel_add_sysfs_param+0xb1/0xe0 kernel/params.c:801
param_sysfs_builtin+0x199/0x250 kernel/params.c:836
param_sysfs_builtin_init+0x23/0x30 kernel/params.c:972
do_one_initcall+0x250/0x870 init/main.c:1347
do_initcall_level+0x10a/0x1a0 init/main.c:1409
do_initcalls+0x59/0xa0 init/main.c:1425
kernel_init_freeable+0x29d/0x3e0 init/main.c:1658
page_owner free stack trace missing
Memory state around the buggy address:
ffff8880343c1000: 00 00 00 00 00 00 00 00 00 00 00 00 00 00 00 00
ffff8880343c1080: 00 00 00 00 fc fc fc fc fc fc fc fc fc fc fc fc
>ffff8880343c1100: fa fb fb fb fb fb fb fb fb fb fb fb fb fb fb fb
^
ffff8880343c1180: fb fb fb fb fb fb fb fb fc fc fc fc fc fc fc fc
ffff8880343c1200: fa fb fb fb fb fb fb fb fb fb fb fb fb fb fb fb
==================================================================
Reply: An analysis of the KASAN bug report and the Linux kernel source code confirms that this report is **fully consistent, highly actionable, and points directly to a clear root cause**.
### 1. Root Cause Analysis
The bug is a classic use-after-free (UAF) in `uvcg_extension_drop()` within `drivers/usb/gadget/function/uvc_configfs.c`.
When an extension unit directory is removed via configfs, `uvcg_extension_drop()` is invoked:
```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); // <--- [1] Drops the last reference, freeing 'xu'
list_del(&xu->list); // <--- [2] Use-after-free of 'xu'
kfree(xu->desc.baSourceID); // <--- [3] Use-after-free of 'xu'
kfree(xu->desc.bmControls); // <--- [4] Use-after-free of 'xu'
mutex_unlock(&opts->lock);
}
```
1. **At `[1]`**: `config_item_put(item)` decrements the reference count of the config item. Since this is the last reference, it triggers the release callback `uvcg_extension_release()`, which frees the parent structure `struct uvcg_extension *xu` via `kfree(xu)`.
2. **At `[2]`**: The code immediately attempts to delete `xu->list` from the list of extension units using `list_del(&xu->list)`. Since `xu` has already been freed, this results in a use-after-free.
3. **At `[3]` and `[4]`**: The code also attempts to free members of `xu->desc` (`baSourceID` and `bmControls`), which are further use-after-free accesses.
---
### 2. Consistency of the Bug Report
The KASAN report is perfectly aligned with this analysis:
* **Bad Access Stack**: Shows the crash occurring in `list_del` inside `uvcg_extension_drop+0x81/0x1f0`.
* **Allocation Stack**: Shows the object was allocated in `uvcg_extension_make+0x95/0x300`.
* **Free Stack**: Shows the object was freed via `config_item_release` -> `config_item_cleanup` -> `uvcg_extension_drop+0x75/0x1f0` (which corresponds to the `config_item_put(item)` call at line 1259).
* **Memory Offset Match**:
* The structure layout of `struct uvcg_extension` is:
```c
struct uvcg_extension {
struct config_item item; // Offset 0, Size 80 bytes
struct list_head list; // Offset 80, Size 16 bytes (next at 80, prev at 88)
struct uvcg_extension_unit_descriptor desc;
};
```
* KASAN reports a read of size 8 at offset **88** inside the freed 192-byte region. This corresponds exactly to accessing `xu->list.prev` during the `list_del` operation.
---
### 3. Recommended Fix
To resolve the issue, `config_item_put(item)` must be deferred to the very end of `uvcg_extension_drop()`, after all operations on `xu` and its members are completed:
```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);
}
```
|
| 5/2 |
2026/08/02 01:07 |
llm |
expert |
0m
Model:
Tokens:
input: 7553
output: 46
thoughts: 851
Thoughts: **Analyzing a Kernel Bug Report: Actionable or Not?**
Alright, let's dive into this kernel bug report. The core issue is a KASAN slab-use-after-free, specifically in `__list_del_entry_valid_or_report`. The report pinpoints a read of size 8 at a particular address by a specific task, `syz.4.33/5952`. My immediate task is to assess if this is something we can actually *do* something about.
I need to analyze the stack traces. There are three key stacks presented here, I'll examine them closely. First, we have the "Bad memory access stack" which shows the exact point of failure, starting in `__list_del_entry_valid_or_report` and tracing back through `__list_del_entry`, `list_del`, and then into `uvcg_extension_drop` within the USB gadget UVC configfs driver code. From there, it moves into `configfs_mkdir` and down to the VFS layers.
Next, we have the "Allocation stack," showing where the memory was *originally* allocated. This looks to be inside the `uvcg_extension_make` function, which then calls `configfs_mkdir`, and through similar VFS functions. Finally, there's the "Free stack" showing the deallocation path through `config_item_release` and `config_item_cleanup`, finally arriving back in `uvcg_extension_drop`.
Hold on, this is where it gets interesting. Looking at the code flow, within `uvcg_extension_drop` at line 1259, it calls a function that releases the `config_item`, which leads to the object being freed (through `config_item_release` -> `config_item_cleanup` -> `kfree`). Immediately *after* that, at line 1260, it calls `list_del`! It's a classic use-after-free scenario. This is a very clear and actionable bug. The free happens *before* the list manipulation. This is probably going to be a quick one to fix.
To confirm my suspicion, I need to check the source code. Let me check the code for `uvcg_extension_drop` and `uvcg_extension_make` in `drivers/usb/gadget/function/uvc_configfs.c` using the kernel source. I'm going to look for any other instances of similar memory management issues.
|
| 6/2 |
2026/08/02 01:07 |
tool |
codesearch-definition-source |
0m
Args: map[ContextFile:drivers/usb/gadget/function/uvc_configfs.c 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]
|
| 7/2 |
2026/08/02 01:07 |
llm |
expert |
0m
Model:
Tokens:
input: 9311
output: 46
thoughts: 443
Thoughts: **Analyzing a Potential Use-After-Free Vulnerability**
Okay, let's take a look at this `uvcg_extension_drop` function. Line 1248 introduces the function, and it seems like we're dealing with dropping a `uvcg_extension`. The structure of the function indicates it's cleaning up resources associated with the extension, and specifically how they are handled within a `config_group` and `config_item` context. I see the usual container-of pattern to retrieve the containing `uvcg_extension` structure.
Now, here's where things get interesting. Line 1259 calls `config_item_put(item)`. I immediately know that this is a critical point. If this drops the *last* reference to the `config_item`, it's going to trigger the release sequence, which goes `config_item_release` down to `config_item_cleanup` and ultimately, `kfree(xu)`. We’re freeing the `xu` struct here.
The problem, and my immediate concern, is line 1260: `list_del(&xu->list)`. We're attempting to remove the extension from a list *after* the memory for `xu` has been freed. This, my friend, is a textbook use-after-free vulnerability! We're dereferencing a pointer (`xu`) that's no longer valid. That’s a clear bug.
To get a complete picture, I need to check how this `xu` is allocated and initialized in the first place. I'm going to look at `uvcg_extension_make` next.
|
| 8/2 |
2026/08/02 01:07 |
tool |
codesearch-definition-source |
0m
Args: map[ContextFile:drivers/usb/gadget/function/uvc_configfs.c 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]
|
| 9/2 |
2026/08/02 01:07 |
llm |
expert |
0m
Model:
Tokens:
input: 10723
output: 46
thoughts: 17
Thoughts: Okay, let's get down to brass tacks.
**Examining `uvcg_extension_type` and Its Release Function**
Right, so I need to take a look at the `uvcg_extension_type` and, specifically, how it's released. This probably means I'm dealing with some kind of resource management, memory allocation, or connection handling related to this extension. I need to understand the lifecycle of this object; where it's created, how it's used, and, critically, how it's disposed of to prevent leaks, deadlocks, or other nasties. I'm assuming there's a corresponding release function, and that's where the critical logic resides. Is it a simple deallocation, or are there more complex operations involved, like closing connections, releasing locks, or cleaning up associated data structures? I need to analyze this release function meticulously to ensure everything is handled correctly and that it's robust in different scenarios, including potential error conditions. Let's see what we've got.
|
| 10/2 |
2026/08/02 01:07 |
tool |
codesearch-find-references |
0m
Args: map[ContextFile:drivers/usb/gadget/function/uvc_configfs.c Name:uvcg_extension_type]
Results: map[References:<nil> TruncatedOutput:false]
|
| 11/2 |
2026/08/02 01:07 |
llm |
expert |
0m
Model:
Tokens:
input: 11022
output: 46
thoughts: 25
Thoughts: Here's my take on the thought process, summarized as you requested:
**Deconstructing the `uvcg_extension_type` Definition**
Okay, time to get to the heart of this. I need to understand the `uvcg_extension_type` definition. Given the context, I'm going to start by going straight to the source. Let's navigate to around line 1248 of the file. That should be a good starting point to locate the relevant code and get a clearer picture of how this `uvcg_extension_type` is defined and used within the broader system. I'm expecting this will provide the contextual definition I need.
|
| 12/2 |
2026/08/02 01:07 |
tool |
read-file |
0m
Args: map[File:drivers/usb/gadget/function/uvc_configfs.c FirstLine:1200 LineCount:100]
Results: map[Contents:1200: if (!strings || tgt->ci_parent->ci_parent != strings) {
1201: ret = -EINVAL;
1202: goto put_strings;
1203: }
1204:
1205: string = to_gadget_string(tgt);
1206: xu->string_descriptor_index = string->usb_string.id;
1207:
1208: put_strings:
1209: config_item_put(strings);
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;
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: {
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:
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: }
]
|
| 13/2 |
2026/08/02 01:07 |
llm |
expert |
0m
Model:
Tokens:
input: 12777
output: 46
thoughts: 13
Thoughts: Okay, let's dive into this. Here's my take on those thoughts, as if they're my own:
**Analyzing `uvcg_extension_release`**
Alright, I'm going to take a closer look at this `uvcg_extension_release` now. Considering my expertise, I'm already mentally dissecting what that nomenclature implies. The "uvcg" likely stands for something specific within the context, probably a framework or module I'm familiar with. "Extension" suggests this is adding functionality or modifying an existing component, and "release" clearly points to a new version, a deployment, or an update. I'm immediately running through related codebases and projects, trying to place this name within a familiar architecture. I need to understand what this release entails and what its impact will be. Is it a minor bug fix, or a major overhaul? What specific components are being touched? My brain is already starting to formulate questions about compatibility, dependencies, and potential regressions. Let's see what the documentation tells me.
|
| 14/2 |
2026/08/02 01:07 |
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]
|
| 15/2 |
2026/08/02 01:07 |
llm |
expert |
0m
Model:
Tokens:
input: 13184
output: 39
thoughts: 437
Thoughts: **Analyzing a Use-After-Free Vulnerability**
Okay, this code is remarkably straightforward, and the bug jumps out pretty quickly. Let's break down what's happening. First, when a directory is created, `uvcg_extension_make` is called. This allocates a `struct uvcg_extension` called `xu` using `kzalloc`. That's standard and sensible. It initializes `xu->item` and adds `xu->list` to a list associated with the parent options structure. Then it returns `&xu->item`. So far, so good.
Now, later, when the directory is removed, the `uvcg_extension_drop` function comes into play. It retrieves `xu` from `item`, and locks `opts->lock`. Then, crucially, it calls `config_item_put(item)`. Now, `config_item_put` handles reference counting and eventually leads to a call to `uvcg_extension_release` which *kfree*s `xu` . The problem is, it does this *before* it removes `xu->list` from the `opts->extension_units` list using `list_del(&xu->list)`. It's classic use-after-free! The call to `list_del` accesses the freed `xu` struct, specifically its `next` and `prev` pointers. This should definitely trigger KASAN's slab-use-after-free detection in `__list_del_entry_valid_or_report`.
But, I need to make sure this isn't isolated. Is there another way that `config_item_put` is called on the object before the `list_del` call? To get a good grip on this, I should examine other `_drop` functions in `uvc_configfs.c` to see if they follow the same unsafe pattern or, hopefully, if they've implemented it correctly. Gotta be thorough.
|
| 16/2 |
2026/08/02 01:07 |
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=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=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-{
]
|
| 17/2 |
2026/08/02 01:07 |
llm |
expert |
0m
Model:
Tokens:
input: 14364
output: 46
thoughts: 13
Thoughts: Okay, let's dive into this. Here's how I'd approach summarizing the "thoughts" related to `uvcg_frame_drop`, speaking as an expert:
**Analyzing `uvcg_frame_drop`**
Alright, I see we're focusing on `uvcg_frame_drop`. That immediately piques my interest. My initial questions are: what system or module is this function or variable associated with? Is this within the context of video processing, graphics rendering, data acquisition, or something else entirely? The name itself suggests we're dealing with dropped frames, which implies a potential bottleneck or performance issue related to the transfer or display of visual information.
Now, my mind starts to race through possible causes. Is it a bandwidth limitation on the data transfer path? Perhaps the processing pipeline is struggling to keep up with the frame rate? Are we dealing with synchronization problems between different components, like the capture device and the display?
I'd want to examine the surrounding code. What conditions trigger a frame drop? Is there any logging associated with `uvcg_frame_drop` that gives clues about why frames are being missed? Are there any error codes or timestamps that can correlate the drops with other system events?
Depending on the context, I'd also start formulating potential debugging strategies. Could it be a simple buffer overflow issue? Are there any adjustable parameters – buffer sizes, timeout values, frame rate targets – that could be tweaked? Perhaps we need to profile the code to pinpoint exactly where the performance bottleneck lies. I'd be looking for instrumentation points, performance counters, and any metrics that provide visibility into the data flow and processing times.
Ultimately, understanding the function and its behavior are crucial steps toward diagnosing, isolating, and solving any issues. I need to understand this to determine the next step toward a resolution.
|
| 18/2 |
2026/08/02 01:07 |
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]
|
| 19/2 |
2026/08/02 01:07 |
llm |
expert |
0m
Model:
Tokens:
input: 15265
output: 38
thoughts: 85
Thoughts: Okay, here's my take on those thoughts, framed as if they're mine:
**Debugging a UVC Frame Drop - A Hypothesis and Investigation**
Alright, so I'm digging into this frame drop issue in the UVC driver. My gut feeling tells me something's off in the cleanup procedures, specifically in `uvcg_frame_drop`. That `config_item_put(item)` call, it's sitting there at the very end of the function. Now, that could be entirely normal, but my experience screams "check that last!". Let's see... I've got a hunch it's related to some late resource release.
Let's quickly check the other `_drop` functions in `uvc_configfs.c`. It's a bit of a pattern match, but maybe I missed something in `uvc_configfs.c` so let me grep the code for all the `_drop` instances in the same file. I'll need to see if the timing is consistent or if I can spot any discrepancies. This could pinpoint the root cause of the drops. Perhaps the cleanup routines are happening at the wrong time and are preventing some resource from being used. We'll see...
|
| 20/2 |
2026/08/02 01:07 |
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-};
]
|
| 21/2 |
2026/08/02 01:07 |
llm |
expert |
0m
Model:
Tokens:
input: 18321
output: 39
thoughts: 54
Thoughts: Okay, let's dive into this code.
**Investigating Drop Function Implementations**
Hmm, I need to understand the behavior of these drop functions. I should start by getting a wider view of the code base. Let's see... are there other instances of functions that might be named something like `_drop` or `_drop_item`? That would be a helpful starting point to grasp the design pattern.
Wait, hold on a second. Based on what I have been doing with USB gadget functions, a good place to start would be a specific location within the kernel tree. Yes, `drivers/usb/gadget/function/uvc_configfs.c` seems like a likely candidate. I should search for implementations of `drop_item` within that file, as that's directly related to the USB gadget function configfs. Let me narrow my search to there. That's probably a smarter first step than a completely open search. This should give me some context right away.
|
| 22/2 |
2026/08/02 01:07 |
tool |
grepper |
0m
Args: map[Expression:drop_item 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-};
]
|
| 23/2 |
2026/08/02 01:07 |
llm |
expert |
0m
Model:
Tokens:
input: 19217
output: 44
thoughts: 471
Thoughts: **Analyzing a Use-After-Free Vulnerability**
Okay, so I've identified that `uvcg_extension_drop` is the only other function besides the primary one that implements `drop_item`. And, as I suspected, there's a clear bug: `config_item_put(item)` is called before `list_del(&xu->list)`. This is a classic recipe for trouble. Now, let's make sure this is the *only* issue and that it's actionable. We want to be certain before we commit to a fix.
Let me review the report's details thoroughly. It confirms a slab-use-after-free vulnerability, specifically in `__list_del_entry_valid_or_report`, and there's a read of size 8 at `ffff8880343c1158`. This address is 88 bytes into a 192-byte freed region. The allocation originated in `uvcg_extension_make` by task 5952, and the free happens in `config_item_release` within `uvcg_extension_drop`, both by the same task. The bad access is right in `uvcg_extension_drop` at the `list_del(&xu->list)` call.
Now, to make sure this all lines up, I'm going to quickly check the `struct uvcg_extension` layout. We need to confirm that `list` is indeed at offset 88. Let me grab the structure definition. I'll use `codesearch-struct-layout` or `codesearch-definition-source` to get the structure definition for `struct uvcg_extension` and confirm the offset. Once that's done, we're ready to proceed with a fix.
|
| 24/2 |
2026/08/02 01:07 |
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]
|
| 25/2 |
2026/08/02 01:07 |
llm |
expert |
0m
Model:
Tokens:
input: 20239
output: 44
thoughts: 25
Thoughts: Here's my thought process, as requested:
**Examining the uvcg_extension Structure Layout**
Okay, so I need to get a handle on the internals of `struct uvcg_extension`. My first instinct is to dive into the low-level details. The easiest way to get a clear picture of the structure's memory layout is to use `codesearch-struct-layout`. This will show me the offsets, data types, and any padding within the structure. Understanding the precise arrangement of members is crucial. This will tell me how various fields relate to each other in memory, so I can understand how data is organized, accessed, and potentially manipulated at a lower level. Let's see what that command reveals.
|
| 26/2 |
2026/08/02 01:08 |
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]]]
|
| 27/2 |
2026/08/02 01:08 |
llm |
expert |
0m
Model:
Tokens:
input: 20555
output: 46
thoughts: 272
Thoughts: **KASAN Report Debugging: A Perfect Match**
Wow, check this out! The KASAN report is screaming at me, and I think I've cracked it. The `item` is 640 bits, which is 80 bytes. So, `list` starts at that exact offset. And `list`, as we know from the `struct list_head` definition, has those two crucial pointers: `next` and `prev`. `next` is predictably at the 80-byte offset, and `prev` is... aha! 88 bytes.
The KASAN report itself nails it: "Read of size 8 at addr ffff8880343c1158". And then the kicker: "The buggy address is located 88 bytes inside of freed 192-byte region [ffff8880343c1100, ffff8880343c11c0)". 88 bytes *inside* that region... that's exactly where `xu->list.prev` should be! This is textbook stuff. It's a perfect alignment of the reported memory corruption with the structure layout. I love when things click like this.
Okay, let's trace this back to the source. Time to use `git log` or `git blame` on `drivers/usb/gadget/function/uvc_configfs.c`, likely around line 1259. Let's find out when this nasty little bug crept in.
|
| 28/2 |
2026/08/02 01:08 |
tool |
git-blame |
0m
Args: map[End:1265 File:drivers/usb/gadget/function/uvc_configfs.c Start:1255]
Results: map[Output: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) }
]
|
| 29/2 |
2026/08/02 01:08 |
llm |
expert |
0m
Model:
Tokens:
input: 21526
output: 28
thoughts: 23
Thoughts: Here's my attempt at summarizing the thought process, tailored for an expert audience:
**Analyzing Commit 0525210c98402**
Alright, let's dive into this specific commit: `0525210c98402`. My immediate thought is to understand the context. What changes were introduced? Was this a feature addition, a bug fix, a refactor, or something else entirely? The commit hash itself is useless without that. My reflex is to use `git show` or `git log -p -1 0525210c98402` to get the diff. I'll need to check the commit message first, though. Hopefully, the author provided a clear and concise description that gives me the *why* behind the change, not just the *what*. If the message is lacking, then the diff itself is going to have to do the heavy lifting in telling the story. Ultimately, I'm expecting to determine the scope and impact of this code update, and how it aligns with the overall architecture. I will need to look for potential regressions and dependencies; this is essential, not just for reviewing, but for confirming that this change can be incorporated into production. Finally, I’ll need to understand the reasoning behind the change and see if the reasoning is sound, or if this change is a sign of deeper architectural problems.
|
| 30/2 |
2026/08/02 01:08 |
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 */
]
|
| 31/2 |
2026/08/02 01:08 |
llm |
expert |
0m
Model:
Tokens:
input: 29501
output: 15
thoughts: 319
Thoughts: **Bug Analysis and Remediation**
Wow, this is a pretty significant find! It looks like a classic use-after-free vulnerability, and it's stemming from commit `0525210c9840229e42c6b68e886c72a75a67cf8e` in the USB gadget UVC subsystem. Specifically, the issue is within `uvcg_extension_drop`. The code calls `config_item_put(item)` before removing the extension unit (`xu`) from its list and freeing associated memory.
My immediate thought is, this is definitely a race condition that could lead to crashes or worse. `config_item_put` is dropping the last reference to the item, effectively freeing `xu` through `uvcg_extension_release`. Then, the subsequent `list_del(&xu->list)` and the `kfree` calls on `xu->desc.baSourceID` and `xu->desc.bmControls` attempt to access memory that has already been deallocated, resulting in a use-after-free! The descriptor fields are also affected by the same vulnerability!
The fix is straightforward, but critical. I need to move `config_item_put(item)` to the very end of `uvcg_extension_drop`, *after* the `list_del` and the `kfree` calls, which is the only way to ensure the memory isn't accessed after it's freed.
I also need to make sure I call the `set-results` tool. Considering the severity of the use-after-free, I need to call the tool with `Actionable: true`.
|
| 32/2 |
2026/08/02 01:08 |
tool |
set-results |
0m
Args:
Results:
|
| 33/2 |
2026/08/02 01:08 |
llm |
expert |
0m
Model:
Tokens:
input: 30208
output: 1099
thoughts: 510
Thoughts: **Analyzing the Bug Report: A Deep Dive**
Okay, let's break this down. My initial assessment is that this report is exceptionally well-structured and, based on what I'm seeing, entirely actionable. It nails the key components of a robust bug report, hitting all the necessary points for a quick resolution.
First, the report *clearly* identifies the problem: a Use-After-Free (UAF) vulnerability within the `uvcg_extension_drop` function, triggered during the deletion of an extension unit directory via configfs. No ambiguity there.
Then, we have the heart of the matter - the root cause. It describes, with impressive precision, the sequence of events leading to the UAF. The call to `config_item_put(item)` immediately followed by the attempt to access freed memory is the smoking gun. It's concise and points directly to the flawed logic. The callback chain, from `config_item_put` down to `uvcg_extension_release` and the subsequent `kfree(xu)` call, is crystal clear. I can see the memory being freed, and then, right afterwards, the code attempts to interact with the now-invalid memory region. That's a textbook UAF.
The report also demonstrates internal consistency. The stack traces provided paint a cohesive picture. The crash location in `list_del` perfectly aligns with the function where the memory access violation occurs, the allocation point is where the vulnerable object is instantiated, and the freeing stack trace confirms the release happens via `config_item_release`. Critically, the offset mismatch – the reported offset of the bad access (88 bytes) aligning with the offset of `xu->list.prev` – is excellent. That's a crucial detail that confirms the identified location of the vulnerability.
Finally, the report proposes a simple, elegant solution: move `config_item_put(item)` to the very *end* of `uvcg_extension_drop`, ensuring the cleanup happens *after* the operations on the memory. This avoids the UAF. This is a straightforward fix, and it's well-reasoned. This report is ready for action.
|
| 34/1 |
2026/08/02 01:08 |
action |
format-explanation |
0m
Results: map[Explanation:An analysis of the KASAN bug report and the Linux kernel source code confirms
that this report is **fully consistent, highly actionable, and points directly
to a clear root cause**.
### 1. Root Cause Analysis
The bug is a classic use-after-free (UAF) in `uvcg_extension_drop()` within
`drivers/usb/gadget/function/uvc_configfs.c`.
When an extension unit directory is removed via configfs,
`uvcg_extension_drop()` is invoked:
```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); // <--- [1] Drops the last reference,
freeing 'xu'
list_del(&xu->list); // <--- [2] Use-after-free of 'xu'
kfree(xu->desc.baSourceID); // <--- [3] Use-after-free of 'xu'
kfree(xu->desc.bmControls); // <--- [4] Use-after-free of 'xu'
mutex_unlock(&opts->lock);
}
```
1. **At `[1]`**: `config_item_put(item)` decrements the reference count of the
config item. Since this is the last reference, it triggers the release callback
`uvcg_extension_release()`, which frees the parent structure `struct
uvcg_extension *xu` via `kfree(xu)`.
2. **At `[2]`**: The code immediately attempts to delete `xu->list` from the
list of extension units using `list_del(&xu->list)`. Since `xu` has already been
freed, this results in a use-after-free.
3. **At `[3]` and `[4]`**: The code also attempts to free members of `xu->desc`
(`baSourceID` and `bmControls`), which are further use-after-free accesses.
---
### 2. Consistency of the Bug Report
The KASAN report is perfectly aligned with this analysis:
* **Bad Access Stack**: Shows the crash occurring in `list_del` inside
`uvcg_extension_drop+0x81/0x1f0`.
* **Allocation Stack**: Shows the object was allocated in
`uvcg_extension_make+0x95/0x300`.
* **Free Stack**: Shows the object was freed via `config_item_release` ->
`config_item_cleanup` -> `uvcg_extension_drop+0x75/0x1f0` (which corresponds to
the `config_item_put(item)` call at line 1259).
* **Memory Offset Match**:
* The structure layout of `struct uvcg_extension` is:
```c
struct uvcg_extension {
struct config_item item; // Offset 0, Size 80 bytes
struct list_head list; // Offset 80, Size 16 bytes
(next at 80, prev at 88)
struct uvcg_extension_unit_descriptor desc;
};
```
* KASAN reports a read of size 8 at offset **88** inside the freed 192-byte
region. This corresponds exactly to accessing `xu->list.prev` during the
`list_del` operation.
---
### 3. Recommended Fix
To resolve the issue, `config_item_put(item)` must be deferred to the very end
of `uvcg_extension_drop()`, after all operations on `xu` and its members are
completed:
```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);
}
```]
|