| 0/0 |
2026/08/09 10:33 |
flow |
assessment-security |
3m
Results: map[DenialOfService:true Explanation:**Bug Analysis:**
The bug is a Use-After-Free (UAF) vulnerability in the RedRat3 USB infrared
transceiver driver (`drivers/media/rc/redrat3.c`).
During the device initialization in `redrat3_dev_probe()`, the driver allocates
a `redrat3_dev` structure (`rr3`). It then successfully registers a Remote
Controller (`rc`) device and its associated `input` device by calling
`redrat3_init_rc_dev()`. As part of this registration, the input device's `name`
pointer is set to point to `rr3->name`.
Immediately after, the driver calls `redrat3_enable_detector(rr3)`. If this
function fails (e.g., the USB device returns an unexpected status or a URB
submission fails), the probe function jumps to the error handling path
(`led_free` -> `redrat_free`). In this error path, the driver frees the `rr3`
structure via `redrat3_delete()`, but it **fails to unregister the `rc`
device**.
As a result, the `input` device remains registered and exposed to userspace, but
its `name` attribute now points to freed memory. When userspace reads the
`/sys/class/input/inputX/name` sysfs file, the kernel calls
`input_dev_show_name()`, which reads the dangling pointer and triggers the UAF.
As seen in the crash report, this is often triggered automatically by `udevd`
when it processes the device addition uevent.
**Security Impact:**
* **Exploitable:** **True**. The bug acts as an information leak primitive. The
freed object is a 2048-byte slab object (`kmalloc-2k`). An attacker can spray
the heap to reallocate this memory with an object containing sensitive data
(e.g., kernel pointers, keys). Reading the sysfs file will then leak this data
to userspace until a null byte is encountered, which can be used to defeat KASLR
or leak secrets.
* **Denial Of Service:** **True**. If the freed slab page is returned to the
page allocator and unmapped, the subsequent read by `udevd` or an attacker will
cause a fatal page fault and kernel panic.
* **Accessible From Unprivileged Processes & User Namespaces:** **True**. The
sysfs attribute `/sys/class/input/inputX/name` is world-readable (`-r--r--r--`).
Once the vulnerable device is plugged in and the UAF is created, any
unprivileged user can read the file to exploit the information leak.
* **Peripheral Trigger:** **True**. The bug is triggered by plugging in a
physical (or emulated) USB device that intentionally or accidentally fails the
`redrat3_enable_detector()` initialization step.
* **VM Host Trigger in The Confidential Computing Context:** **True**. A
malicious hypervisor can hotplug a virtual USB device to a confidential guest
and manipulate the USB responses to fail the probe. The guest's `udevd` will
automatically read the sysfs file, triggering the UAF and crashing the guest
kernel (DoS).
* **VM Guest Trigger:** **False**. The vulnerability is in a host USB driver,
which is not exposed to the guest via standard paravirtualization.
* **Network / Remote / Filesystem Trigger:** **False**. The bug is strictly tied
to the USB subsystem and requires peripheral access. Exploitable:true FilesystemTrigger:false NetworkTrigger:false PeripheralTrigger:true RemoteTrigger:false Unprivileged:true UserNamespace:true VMGuestTrigger:false VMHostTrigger:true]
|
| 1/1 |
2026/08/09 10:33 |
action |
syz-repro-to-c-repro |
0m
Results:
|
| 2/1 |
2026/08/09 10:33 |
action |
kernel-checkouter |
0m
Results: map[KernelSrc:/app/workdir/cache/src/0bd482cc9fd6a5c3cdf3e7f636a3906397462859]
|
| 3/1 |
2026/08/09 10:33 |
action |
kernel-builder |
0m
Results: map[KernelObj:/app/workdir/cache/build/594e41fd48f519e183d63038fd61240ce1feb3e5]
|
| 4/1 |
2026/08/09 10:33 |
action |
codesearch-prepare |
0m
Results: map[Index:codesearch-index]
|
| 5/1 |
2026/08/09 10:33 |
agent |
expert |
2m
Model:
Results: map[DenialOfService:true Exploitable:true FilesystemTrigger:false NetworkTrigger:false PeripheralTrigger:true RemoteTrigger:false Unprivileged:true UserNamespace:true VMGuestTrigger:false VMHostTrigger:true]
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 string_nocheck lib/vsprintf.c:648 [inline]
BUG: KASAN: slab-use-after-free in string+0x216/0x2d0 lib/vsprintf.c:730
Read of size 1 at addr ffff888051fda758 by task udevd/7464
CPU: 0 UID: 0 PID: 7464 Comm: udevd Not tainted syzkaller #0 PREEMPT(full)
Hardware name: Google Google Compute Engine/Google Compute Engine, BIOS Google 07/16/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
string_nocheck lib/vsprintf.c:648 [inline]
string+0x216/0x2d0 lib/vsprintf.c:730
vsnprintf+0x74a/0xef0 lib/vsprintf.c:2945
vscnprintf+0x41/0x90 lib/vsprintf.c:3014
sysfs_emit+0x10e/0x180 fs/sysfs/file.c:761
input_dev_show_name+0x58/0x70 drivers/input/input.c:1282
dev_attr_show+0x58/0xc0 drivers/base/core.c:2487
sysfs_kf_seq_show+0x331/0x490 fs/sysfs/file.c:65
seq_read_iter+0x463/0xca0 fs/seq_file.c:231
new_sync_read fs/read_write.c:493 [inline]
vfs_read+0x595/0xa80 fs/read_write.c:574
ksys_read+0x150/0x270 fs/read_write.c:716
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:0x7fe991ca7407
Code: 48 89 fa 4c 89 df e8 38 aa 00 00 8b 93 08 03 00 00 59 5e 48 83 f8 fc 74 1a 5b c3 0f 1f 84 00 00 00 00 00 48 8b 44 24 10 0f 05 <5b> c3 0f 1f 80 00 00 00 00 83 e2 39 83 fa 08 75 de e8 23 ff ff ff
RSP: 002b:00007ffc9989ac00 EFLAGS: 00000202 ORIG_RAX: 0000000000000000
RAX: ffffffffffffffda RBX: 00007fe9924aa880 RCX: 00007fe991ca7407
RDX: 0000000000001000 RSI: 00007ffc9989b0e0 RDI: 0000000000000008
RBP: 0000000000000008 R08: 0000000000000000 R09: 0000000000000000
R10: 0000000000000000 R11: 0000000000000202 R12: 0000562578d85a20
R13: 00007ffc9989ace0 R14: 00007ffc9989b0e0 R15: 0000562548648bcc
</TASK>
Allocated by task 10:
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+0x32d/0x660 mm/slub.c:5489
_kmalloc_noprof include/linux/slab.h:988 [inline]
_kzalloc_noprof include/linux/slab.h:1309 [inline]
redrat3_dev_probe+0x477/0x2570 drivers/media/rc/redrat3.c:1023
usb_probe_interface+0x653/0xc60 drivers/usb/core/driver.c:396
call_driver_probe drivers/base/dd.c:-1 [inline]
really_probe+0x254/0xae0 drivers/base/dd.c:706
__driver_probe_device+0x1e8/0x360 drivers/base/dd.c:868
driver_probe_device+0x4f/0x240 drivers/base/dd.c:898
__device_attach_driver+0x270/0x410 drivers/base/dd.c:1026
bus_for_each_drv+0x258/0x2f0 drivers/base/bus.c:500
__device_attach+0x2c4/0x450 drivers/base/dd.c:1098
device_initial_probe+0xa1/0xd0 drivers/base/dd.c:1153
bus_probe_device+0x12a/0x220 drivers/base/bus.c:620
device_add+0x7d7/0xb80 drivers/base/core.c:3772
usb_set_configuration+0x1ad8/0x2180 drivers/usb/core/message.c:2268
usb_generic_driver_probe+0x8d/0x150 drivers/usb/core/generic.c:250
usb_probe_device+0x1c3/0x3b0 drivers/usb/core/driver.c:291
call_driver_probe drivers/base/dd.c:-1 [inline]
really_probe+0x254/0xae0 drivers/base/dd.c:706
__driver_probe_device+0x1e8/0x360 drivers/base/dd.c:868
driver_probe_device+0x4f/0x240 drivers/base/dd.c:898
__device_attach_driver+0x270/0x410 drivers/base/dd.c:1026
bus_for_each_drv+0x258/0x2f0 drivers/base/bus.c:500
__device_attach+0x2c4/0x450 drivers/base/dd.c:1098
device_initial_probe+0xa1/0xd0 drivers/base/dd.c:1153
bus_probe_device+0x12a/0x220 drivers/base/bus.c:620
device_add+0x7d7/0xb80 drivers/base/core.c:3772
usb_new_device+0x99d/0x1620 drivers/usb/core/hub.c:2695
hub_port_connect drivers/usb/core/hub.c:5567 [inline]
hub_port_connect_change drivers/usb/core/hub.c:5707 [inline]
port_event drivers/usb/core/hub.c:5871 [inline]
hub_event+0x28e8/0x4d30 drivers/usb/core/hub.c:5953
process_one_work kernel/workqueue.c:3322 [inline]
process_scheduled_works+0xa8e/0x14e0 kernel/workqueue.c:3405
worker_thread+0xa47/0xfb0 kernel/workqueue.c:3486
kthread+0x388/0x470 kernel/kthread.c:436
ret_from_fork+0x514/0xb70 arch/x86/kernel/process.c:158
ret_from_fork_asm+0x1a/0x30 arch/x86/entry/entry_64.S:245
Freed by task 10:
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/0x640 mm/slub.c:6692
redrat3_delete drivers/media/rc/redrat3.c:466 [inline]
redrat3_dev_probe+0x1bf4/0x2570 drivers/media/rc/redrat3.c:1124
usb_probe_interface+0x653/0xc60 drivers/usb/core/driver.c:396
call_driver_probe drivers/base/dd.c:-1 [inline]
really_probe+0x254/0xae0 drivers/base/dd.c:706
__driver_probe_device+0x1e8/0x360 drivers/base/dd.c:868
driver_probe_device+0x4f/0x240 drivers/base/dd.c:898
__device_attach_driver+0x270/0x410 drivers/base/dd.c:1026
bus_for_each_drv+0x258/0x2f0 drivers/base/bus.c:500
__device_attach+0x2c4/0x450 drivers/base/dd.c:1098
device_initial_probe+0xa1/0xd0 drivers/base/dd.c:1153
bus_probe_device+0x12a/0x220 drivers/base/bus.c:620
device_add+0x7d7/0xb80 drivers/base/core.c:3772
usb_set_configuration+0x1ad8/0x2180 drivers/usb/core/message.c:2268
usb_generic_driver_probe+0x8d/0x150 drivers/usb/core/generic.c:250
usb_probe_device+0x1c3/0x3b0 drivers/usb/core/driver.c:291
call_driver_probe drivers/base/dd.c:-1 [inline]
really_probe+0x254/0xae0 drivers/base/dd.c:706
__driver_probe_device+0x1e8/0x360 drivers/base/dd.c:868
driver_probe_device+0x4f/0x240 drivers/base/dd.c:898
__device_attach_driver+0x270/0x410 drivers/base/dd.c:1026
bus_for_each_drv+0x258/0x2f0 drivers/base/bus.c:500
__device_attach+0x2c4/0x450 drivers/base/dd.c:1098
device_initial_probe+0xa1/0xd0 drivers/base/dd.c:1153
bus_probe_device+0x12a/0x220 drivers/base/bus.c:620
device_add+0x7d7/0xb80 drivers/base/core.c:3772
usb_new_device+0x99d/0x1620 drivers/usb/core/hub.c:2695
hub_port_connect drivers/usb/core/hub.c:5567 [inline]
hub_port_connect_change drivers/usb/core/hub.c:5707 [inline]
port_event drivers/usb/core/hub.c:5871 [inline]
hub_event+0x28e8/0x4d30 drivers/usb/core/hub.c:5953
process_one_work kernel/workqueue.c:3322 [inline]
process_scheduled_works+0xa8e/0x14e0 kernel/workqueue.c:3405
worker_thread+0xa47/0xfb0 kernel/workqueue.c:3486
kthread+0x388/0x470 kernel/kthread.c:436
ret_from_fork+0x514/0xb70 arch/x86/kernel/process.c:158
ret_from_fork_asm+0x1a/0x30 arch/x86/entry/entry_64.S:245
The buggy address belongs to the object at ffff888051fda000
which belongs to the cache kmalloc-2k of size 2048
The buggy address is located 1880 bytes inside of
freed 2048-byte region [ffff888051fda000, ffff888051fda800)
The buggy address belongs to the physical page:
page: refcount:0 mapcount:0 mapping:0000000000000000 index:0x0 pfn:0x51fd8
head: order:3 mapcount:0 entire_mapcount:0 nr_pages_mapped:0 pincount:0
flags: 0xfff00000000040(head|node=0|zone=1|lastcpupid=0x7ff)
page_type: f5(slab)
raw: 00fff00000000040 ffff88801b002000 dead000000000100 dead000000000122
raw: 0000000000000000 0000000800080008 00000000f5000000 0000000000000000
head: 00fff00000000040 ffff88801b002000 dead000000000100 dead000000000122
head: 0000000000000000 0000000800080008 00000000f5000000 0000000000000000
head: 00fff00000000003 fffffffffffffe01 00000000ffffffff 00000000ffffffff
head: ffffffffffffffff 0000000000000000 00000000ffffffff 0000000000000008
page dumped because: kasan: bad access detected
page_owner tracks the page as allocated
page last allocated via order 3, migratetype Unmovable, gfp_mask 0xd20c0(__GFP_IO|__GFP_FS|__GFP_NOWARN|__GFP_NORETRY|__GFP_COMP|__GFP_NOMEMALLOC), pid 1094, tgid 1094 (kworker/u8:7), ts 96851722653, free_ts 25791140797
set_page_owner include/linux/page_owner.h:32 [inline]
post_alloc_hook+0x1f9/0x250 mm/page_alloc.c:1859
prep_new_page mm/page_alloc.c:1867 [inline]
get_page_from_freelist+0x21fa/0x2270 mm/page_alloc.c:3946
__alloc_frozen_pages_noprof+0x18d/0x380 mm/page_alloc.c:5304
alloc_slab_page mm/slub.c:3266 [inline]
allocate_slab+0x79/0x5e0 mm/slub.c:3380
new_slab mm/slub.c:3426 [inline]
refill_objects+0x2d5/0x350 mm/slub.c:7310
refill_sheaf mm/slub.c:2804 [inline]
__pcs_replace_empty_main+0x2bf/0x6b0 mm/slub.c:4675
alloc_from_pcs mm/slub.c:4773 [inline]
slab_alloc_node mm/slub.c:4905 [inline]
__do_kmalloc_node mm/slub.c:5333 [inline]
__kmalloc_node_track_caller_noprof+0x557/0x730 mm/slub.c:5471
kmalloc_reserve net/core/skbuff.c:637 [inline]
__alloc_skb+0x2bd/0x7a0 net/core/skbuff.c:715
alloc_skb include/linux/skbuff.h:1384 [inline]
mld_newpack+0x165/0xcb0 net/ipv6/mcast.c:1789
add_grhead net/ipv6/mcast.c:1900 [inline]
add_grec+0x119f/0x19d0 net/ipv6/mcast.c:2039
mld_send_initial_cr+0x288/0x550 net/ipv6/mcast.c:2284
ipv6_mc_dad_complete+0x87/0x410 net/ipv6/mcast.c:2295
addrconf_dad_completed+0x8af/0xe60 net/ipv6/addrconf.c:4375
addrconf_dad_work+0xcf8/0x15c0 net/ipv6/addrconf.c:-1
process_one_work kernel/workqueue.c:3322 [inline]
process_scheduled_works+0xa8e/0x14e0 kernel/workqueue.c:3405
worker_thread+0xa47/0xfb0 kernel/workqueue.c:3486
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+0x960/0xa60 mm/page_alloc.c:1451
__free_contig_range_common+0x174/0x340 mm/page_alloc.c:6897
destroy_args+0x4e5/0x570 mm/debug_vm_pgtable.c:993
debug_vm_pgtable+0x3f8/0x410 mm/debug_vm_pgtable.c:1368
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
kernel_init+0x1d/0x1d0 init/main.c:1548
ret_from_fork+0x514/0xb70 arch/x86/kernel/process.c:158
ret_from_fork_asm+0x1a/0x30 arch/x86/entry/entry_64.S:245
Memory state around the buggy address:
ffff888051fda600: fb fb fb fb fb fb fb fb fb fb fb fb fb fb fb fb
ffff888051fda680: fb fb fb fb fb fb fb fb fb fb fb fb fb fb fb fb
>ffff888051fda700: fb fb fb fb fb fb fb fb fb fb fb fb fb fb fb fb
^
ffff888051fda780: fb fb fb fb fb fb fb fb fb fb fb fb fb fb fb fb
ffff888051fda800: fc fc fc fc fc fc fc fc fc fc fc fc fc fc fc fc
==================================================================
Reply: **Bug Analysis:**
The bug is a Use-After-Free (UAF) vulnerability in the RedRat3 USB infrared transceiver driver (`drivers/media/rc/redrat3.c`).
During the device initialization in `redrat3_dev_probe()`, the driver allocates a `redrat3_dev` structure (`rr3`). It then successfully registers a Remote Controller (`rc`) device and its associated `input` device by calling `redrat3_init_rc_dev()`. As part of this registration, the input device's `name` pointer is set to point to `rr3->name`.
Immediately after, the driver calls `redrat3_enable_detector(rr3)`. If this function fails (e.g., the USB device returns an unexpected status or a URB submission fails), the probe function jumps to the error handling path (`led_free` -> `redrat_free`). In this error path, the driver frees the `rr3` structure via `redrat3_delete()`, but it **fails to unregister the `rc` device**.
As a result, the `input` device remains registered and exposed to userspace, but its `name` attribute now points to freed memory. When userspace reads the `/sys/class/input/inputX/name` sysfs file, the kernel calls `input_dev_show_name()`, which reads the dangling pointer and triggers the UAF. As seen in the crash report, this is often triggered automatically by `udevd` when it processes the device addition uevent.
**Security Impact:**
* **Exploitable:** **True**. The bug acts as an information leak primitive. The freed object is a 2048-byte slab object (`kmalloc-2k`). An attacker can spray the heap to reallocate this memory with an object containing sensitive data (e.g., kernel pointers, keys). Reading the sysfs file will then leak this data to userspace until a null byte is encountered, which can be used to defeat KASLR or leak secrets.
* **Denial Of Service:** **True**. If the freed slab page is returned to the page allocator and unmapped, the subsequent read by `udevd` or an attacker will cause a fatal page fault and kernel panic.
* **Accessible From Unprivileged Processes & User Namespaces:** **True**. The sysfs attribute `/sys/class/input/inputX/name` is world-readable (`-r--r--r--`). Once the vulnerable device is plugged in and the UAF is created, any unprivileged user can read the file to exploit the information leak.
* **Peripheral Trigger:** **True**. The bug is triggered by plugging in a physical (or emulated) USB device that intentionally or accidentally fails the `redrat3_enable_detector()` initialization step.
* **VM Host Trigger in The Confidential Computing Context:** **True**. A malicious hypervisor can hotplug a virtual USB device to a confidential guest and manipulate the USB responses to fail the probe. The guest's `udevd` will automatically read the sysfs file, triggering the UAF and crashing the guest kernel (DoS).
* **VM Guest Trigger:** **False**. The vulnerability is in a host USB driver, which is not exposed to the guest via standard paravirtualization.
* **Network / Remote / Filesystem Trigger:** **False**. The bug is strictly tied to the USB subsystem and requires peripheral access.
|
| 6/2 |
2026/08/09 10:33 |
llm |
expert |
0m
Model:
Tokens:
input: 11451
output: 21
thoughts: 228
Thoughts: **KASAN Slab-Use-After-Free Deep Dive**
Okay, so I'm looking at this bug report, and it's screaming KASAN slab-use-after-free, right in `string_nocheck` inside `lib/vsprintf.c`. That's not good. Tracing the call stack, we've got `vsnprintf` kicking this off, which calls `vscnprintf`, then `sysfs_emit`, which eventually goes through `input_dev_show_name`, then `dev_attr_show`, all the way down to `sysfs_kf_seq_show`, then `seq_read_iter` and finally `vfs_read`. Seems we're reading a sysfs attribute, specifically the input device's name.
Now, the killer is where the allocation and deallocation happens. The object in question was initially allocated during `redrat3_dev_probe`. The problem is it's freed in `redrat3_delete`, which gets called from within `redrat3_dev_probe` - either the probe's error path or possibly during a disconnect. It's really the classic allocation and deallocation mismatch. Wait, just double-checking: allocation in `redrat3_dev_probe`, free in `redrat3_delete`, which IS called from `redrat3_dev_probe` (line 1124). That's a textbook race condition waiting to happen.
Time to dive into `drivers/media/rc/redrat3.c` and figure out how these probe and delete functions are interacting. Need to see if a race condition is present.
|
| 7/2 |
2026/08/09 10:33 |
tool |
grepper |
0m
Args: map[Expression:redrat3_dev_probe]
Results: map[Output:drivers/media/rc/redrat3.c=924=static struct rc_dev *redrat3_init_rc_dev(struct redrat3_dev *rr3)
--
drivers/media/rc/redrat3.c-970-
drivers/media/rc/redrat3.c:971:static int redrat3_dev_probe(struct usb_interface *intf,
drivers/media/rc/redrat3.c-972- const struct usb_device_id *id)
--
drivers/media/rc/redrat3.c=1166=static struct usb_driver redrat3_dev_driver = {
drivers/media/rc/redrat3.c-1167- .name = DRIVER_NAME,
drivers/media/rc/redrat3.c:1168: .probe = redrat3_dev_probe,
drivers/media/rc/redrat3.c-1169- .disconnect = redrat3_dev_disconnect,
]
|
| 8/2 |
2026/08/09 10:33 |
llm |
expert |
0m
Model:
Tokens:
input: 12318
output: 40
thoughts: 28
Thoughts: Okay, let's dive into this. Here's how I'd approach that kernel module:
**Analyzing the `redrat3_dev_probe` function**
Alright, my first step is to zero in on `redrat3_dev_probe`. I know this is located in `drivers/media/rc/redrat3.c`. My expert understanding tells me that this is likely where the probe function for a RedRat3 device is implemented. As a seasoned professional, I immediately start running through my mental checklist: is this a platform driver? A USB driver? And is the probing part of the device initialization? And how are the resources, such as memory regions and IRQs, handled? I'm going to want to check the device-tree data (if applicable), its configuration, and how itβs being used to map and control the underlying hardware.
|
| 9/2 |
2026/08/09 10:34 |
tool |
read-file |
0m
Args: map[File:drivers/media/rc/redrat3.c FirstLine:971 LineCount:100]
Results: map[Contents: 971: static int redrat3_dev_probe(struct usb_interface *intf,
972: const struct usb_device_id *id)
973: {
974: struct usb_device *udev = interface_to_usbdev(intf);
975: struct device *dev = &intf->dev;
976: struct usb_host_interface *uhi;
977: struct redrat3_dev *rr3;
978: struct usb_endpoint_descriptor *ep;
979: struct usb_endpoint_descriptor *ep_narrow = NULL;
980: struct usb_endpoint_descriptor *ep_wide = NULL;
981: struct usb_endpoint_descriptor *ep_out = NULL;
982: u8 addr, attrs;
983: int pipe, i;
984: int retval = -ENOMEM;
985:
986: uhi = intf->cur_altsetting;
987:
988: /* find our bulk-in and bulk-out endpoints */
989: for (i = 0; i < uhi->desc.bNumEndpoints; ++i) {
990: ep = &uhi->endpoint[i].desc;
991: addr = ep->bEndpointAddress;
992: attrs = ep->bmAttributes;
993:
994: if (((addr & USB_ENDPOINT_DIR_MASK) == USB_DIR_IN) &&
995: ((attrs & USB_ENDPOINT_XFERTYPE_MASK) ==
996: USB_ENDPOINT_XFER_BULK)) {
997: dev_dbg(dev, "found bulk-in endpoint at 0x%02x\n",
998: ep->bEndpointAddress);
999: /* data comes in on 0x82, 0x81 is for learning */
1000: if (ep->bEndpointAddress == RR3_NARROW_IN_EP_ADDR)
1001: ep_narrow = ep;
1002: if (ep->bEndpointAddress == RR3_WIDE_IN_EP_ADDR)
1003: ep_wide = ep;
1004: }
1005:
1006: if ((ep_out == NULL) &&
1007: ((addr & USB_ENDPOINT_DIR_MASK) == USB_DIR_OUT) &&
1008: ((attrs & USB_ENDPOINT_XFERTYPE_MASK) ==
1009: USB_ENDPOINT_XFER_BULK)) {
1010: dev_dbg(dev, "found bulk-out endpoint at 0x%02x\n",
1011: ep->bEndpointAddress);
1012: ep_out = ep;
1013: }
1014: }
1015:
1016: if (!ep_narrow || !ep_out || !ep_wide) {
1017: dev_err(dev, "Couldn't find all endpoints\n");
1018: retval = -ENODEV;
1019: goto no_endpoints;
1020: }
1021:
1022: /* allocate memory for our device state and initialize it */
1023: rr3 = kzalloc_obj(*rr3);
1024: if (!rr3)
1025: goto no_endpoints;
1026:
1027: rr3->dev = &intf->dev;
1028: rr3->ep_narrow = ep_narrow;
1029: rr3->ep_out = ep_out;
1030: rr3->udev = udev;
1031:
1032: /* set up bulk-in endpoint */
1033: rr3->narrow_urb = usb_alloc_urb(0, GFP_KERNEL);
1034: if (!rr3->narrow_urb)
1035: goto redrat_free;
1036:
1037: rr3->wide_urb = usb_alloc_urb(0, GFP_KERNEL);
1038: if (!rr3->wide_urb)
1039: goto redrat_free;
1040:
1041: rr3->bulk_in_buf = usb_alloc_coherent(udev,
1042: le16_to_cpu(ep_narrow->wMaxPacketSize),
1043: GFP_KERNEL, &rr3->dma_in);
1044: if (!rr3->bulk_in_buf)
1045: goto redrat_free;
1046:
1047: pipe = usb_rcvbulkpipe(udev, ep_narrow->bEndpointAddress);
1048: usb_fill_bulk_urb(rr3->narrow_urb, udev, pipe, rr3->bulk_in_buf,
1049: le16_to_cpu(ep_narrow->wMaxPacketSize),
1050: redrat3_handle_async, rr3);
1051: rr3->narrow_urb->transfer_dma = rr3->dma_in;
1052: rr3->narrow_urb->transfer_flags |= URB_NO_TRANSFER_DMA_MAP;
1053:
1054: pipe = usb_rcvbulkpipe(udev, ep_wide->bEndpointAddress);
1055: usb_fill_bulk_urb(rr3->wide_urb, udev, pipe, rr3->bulk_in_buf,
1056: le16_to_cpu(ep_narrow->wMaxPacketSize),
1057: redrat3_handle_async, rr3);
1058: rr3->wide_urb->transfer_dma = rr3->dma_in;
1059: rr3->wide_urb->transfer_flags |= URB_NO_TRANSFER_DMA_MAP;
1060:
1061: redrat3_reset(rr3);
1062: redrat3_get_firmware_rev(rr3);
1063:
1064: /* default.. will get overridden by any sends with a freq defined */
1065: rr3->carrier = 38000;
1066:
1067: atomic_set(&rr3->flash, 0);
1068: rr3->flash_urb = usb_alloc_urb(0, GFP_KERNEL);
1069: if (!rr3->flash_urb)
1070: goto redrat_free;
]
|
| 10/2 |
2026/08/09 10:34 |
llm |
expert |
0m
Model:
Tokens:
input: 14327
output: 41
thoughts: 16
Thoughts: Okay, let's dive into this. Here's my thought process as I approach reading `redrat3_dev_probe`:
**Analyzing `redrat3_dev_probe`**
Alright, the instruction is clear: let's go through the rest of the contents of `redrat3_dev_probe`. My immediate goal is to understand its purpose and how it's implemented. I'm already anticipating needing to assess the code's functionality β probably looking for things like device detection, initialization routines, error handling, and perhaps even some hardware-specific logic. Given the name, I'd expect it to relate to some sort of probing or discovery process related to a device, possibly something specific to the redrat3 platform or a related development board. I'll need to pay close attention to the data structures it uses, the function calls it makes, and any interaction with hardware registers. I need to get a high-level understanding of what it accomplishes first, before I drill down into any specifics.
|
| 11/2 |
2026/08/09 10:34 |
tool |
read-file |
0m
Args: map[File:drivers/media/rc/redrat3.c FirstLine:1071 LineCount:100]
Results: map[Contents:1071:
1072: /* learn urb */
1073: rr3->learn_urb = usb_alloc_urb(0, GFP_KERNEL);
1074: if (!rr3->learn_urb)
1075: goto redrat_free;
1076:
1077: /* setup packet is 'c0 b2 0000 0000 0001' */
1078: rr3->learn_control.bRequestType = 0xc0;
1079: rr3->learn_control.bRequest = RR3_MODSIG_CAPTURE;
1080: rr3->learn_control.wLength = cpu_to_le16(1);
1081:
1082: usb_fill_control_urb(rr3->learn_urb, udev, usb_rcvctrlpipe(udev, 0),
1083: (unsigned char *)&rr3->learn_control,
1084: &rr3->learn_buf, sizeof(rr3->learn_buf),
1085: redrat3_learn_complete, rr3);
1086:
1087: /* setup packet is 'c0 b9 0000 0000 0001' */
1088: rr3->flash_control.bRequestType = 0xc0;
1089: rr3->flash_control.bRequest = RR3_BLINK_LED;
1090: rr3->flash_control.wLength = cpu_to_le16(1);
1091:
1092: usb_fill_control_urb(rr3->flash_urb, udev, usb_rcvctrlpipe(udev, 0),
1093: (unsigned char *)&rr3->flash_control,
1094: &rr3->flash_in_buf, sizeof(rr3->flash_in_buf),
1095: redrat3_led_complete, rr3);
1096:
1097: /* led control */
1098: rr3->led.name = "redrat3:red:feedback";
1099: rr3->led.default_trigger = "rc-feedback";
1100: rr3->led.brightness_set = redrat3_brightness_set;
1101: retval = led_classdev_register(&intf->dev, &rr3->led);
1102: if (retval)
1103: goto redrat_free;
1104:
1105: rr3->rc = redrat3_init_rc_dev(rr3);
1106: if (!rr3->rc) {
1107: retval = -ENOMEM;
1108: goto led_free;
1109: }
1110:
1111: /* might be all we need to do? */
1112: retval = redrat3_enable_detector(rr3);
1113: if (retval < 0)
1114: goto led_free;
1115:
1116: /* we can register the device now, as it is ready */
1117: usb_set_intfdata(intf, rr3);
1118:
1119: return 0;
1120:
1121: led_free:
1122: led_classdev_unregister(&rr3->led);
1123: redrat_free:
1124: redrat3_delete(rr3, rr3->udev);
1125:
1126: no_endpoints:
1127: return retval;
1128: }
1129:
1130: static void redrat3_dev_disconnect(struct usb_interface *intf)
1131: {
1132: struct usb_device *udev = interface_to_usbdev(intf);
1133: struct redrat3_dev *rr3 = usb_get_intfdata(intf);
1134: struct rc_dev *rc = rr3->rc;
1135:
1136: usb_set_intfdata(intf, NULL);
1137: rc_unregister_device(rc);
1138: led_classdev_unregister(&rr3->led);
1139: redrat3_delete(rr3, udev);
1140: rc_free_device(rc);
1141: }
1142:
1143: static int redrat3_dev_suspend(struct usb_interface *intf, pm_message_t message)
1144: {
1145: struct redrat3_dev *rr3 = usb_get_intfdata(intf);
1146:
1147: led_classdev_suspend(&rr3->led);
1148: usb_kill_urb(rr3->narrow_urb);
1149: usb_kill_urb(rr3->wide_urb);
1150: usb_kill_urb(rr3->flash_urb);
1151: return 0;
1152: }
1153:
1154: static int redrat3_dev_resume(struct usb_interface *intf)
1155: {
1156: struct redrat3_dev *rr3 = usb_get_intfdata(intf);
1157:
1158: if (usb_submit_urb(rr3->narrow_urb, GFP_NOIO))
1159: return -EIO;
1160: if (usb_submit_urb(rr3->wide_urb, GFP_NOIO))
1161: return -EIO;
1162: led_classdev_resume(&rr3->led);
1163: return 0;
1164: }
1165:
1166: static struct usb_driver redrat3_dev_driver = {
1167: .name = DRIVER_NAME,
1168: .probe = redrat3_dev_probe,
1169: .disconnect = redrat3_dev_disconnect,
1170: .suspend = redrat3_dev_suspend,
]
|
| 12/2 |
2026/08/09 10:34 |
llm |
expert |
0m
Model:
Tokens:
input: 16268
output: 40
thoughts: 90
Thoughts: Okay, let me break this down.
**Diving into this Kernel Bug**
Alright, so I've got a use-after-free situation on my hands, specifically within `sysfs_emit`. It looks like it's reading `input_dev_show_name` after the memory has been released. The object that's been freed is `rr3` β a `struct redrat3_dev`, which is 2048 bytes in size. Hmm, that name... `redrat3_init_rc_dev`... Wait, I vaguely recall that function registering an rc device. Let me check that function more closely. Let's see what's going on in `redrat3_init_rc_dev`. That should provide the crucial context.
|
| 13/2 |
2026/08/09 10:34 |
tool |
read-file |
0m
Args: map[File:drivers/media/rc/redrat3.c FirstLine:924 LineCount:100]
Results: map[Contents: 924: static struct rc_dev *redrat3_init_rc_dev(struct redrat3_dev *rr3)
925: {
926: struct device *dev = rr3->dev;
927: struct rc_dev *rc;
928: int ret;
929: u16 prod = le16_to_cpu(rr3->udev->descriptor.idProduct);
930:
931: rc = rc_allocate_device(RC_DRIVER_IR_RAW);
932: if (!rc)
933: return NULL;
934:
935: snprintf(rr3->name, sizeof(rr3->name),
936: "RedRat3%s Infrared Remote Transceiver",
937: prod == USB_RR3IIUSB_PRODUCT_ID ? "-II" : "");
938:
939: usb_make_path(rr3->udev, rr3->phys, sizeof(rr3->phys));
940:
941: rc->device_name = rr3->name;
942: rc->input_phys = rr3->phys;
943: usb_to_input_id(rr3->udev, &rc->input_id);
944: rc->dev.parent = dev;
945: rc->priv = rr3;
946: rc->allowed_protocols = RC_PROTO_BIT_ALL_IR_DECODER;
947: rc->min_timeout = MS_TO_US(RR3_RX_MIN_TIMEOUT);
948: rc->max_timeout = MS_TO_US(RR3_RX_MAX_TIMEOUT);
949: rc->timeout = redrat3_get_timeout(rr3);
950: rc->s_timeout = redrat3_set_timeout;
951: rc->tx_ir = redrat3_transmit_ir;
952: rc->s_tx_carrier = redrat3_set_tx_carrier;
953: rc->s_carrier_report = redrat3_wideband_receiver;
954: rc->driver_name = DRIVER_NAME;
955: rc->rx_resolution = 2;
956: rc->map_name = RC_MAP_HAUPPAUGE;
957:
958: ret = rc_register_device(rc);
959: if (ret < 0) {
960: dev_err(dev, "remote dev registration failed\n");
961: goto out;
962: }
963:
964: return rc;
965:
966: out:
967: rc_free_device(rc);
968: return NULL;
969: }
970:
971: static int redrat3_dev_probe(struct usb_interface *intf,
972: const struct usb_device_id *id)
973: {
974: struct usb_device *udev = interface_to_usbdev(intf);
975: struct device *dev = &intf->dev;
976: struct usb_host_interface *uhi;
977: struct redrat3_dev *rr3;
978: struct usb_endpoint_descriptor *ep;
979: struct usb_endpoint_descriptor *ep_narrow = NULL;
980: struct usb_endpoint_descriptor *ep_wide = NULL;
981: struct usb_endpoint_descriptor *ep_out = NULL;
982: u8 addr, attrs;
983: int pipe, i;
984: int retval = -ENOMEM;
985:
986: uhi = intf->cur_altsetting;
987:
988: /* find our bulk-in and bulk-out endpoints */
989: for (i = 0; i < uhi->desc.bNumEndpoints; ++i) {
990: ep = &uhi->endpoint[i].desc;
991: addr = ep->bEndpointAddress;
992: attrs = ep->bmAttributes;
993:
994: if (((addr & USB_ENDPOINT_DIR_MASK) == USB_DIR_IN) &&
995: ((attrs & USB_ENDPOINT_XFERTYPE_MASK) ==
996: USB_ENDPOINT_XFER_BULK)) {
997: dev_dbg(dev, "found bulk-in endpoint at 0x%02x\n",
998: ep->bEndpointAddress);
999: /* data comes in on 0x82, 0x81 is for learning */
1000: if (ep->bEndpointAddress == RR3_NARROW_IN_EP_ADDR)
1001: ep_narrow = ep;
1002: if (ep->bEndpointAddress == RR3_WIDE_IN_EP_ADDR)
1003: ep_wide = ep;
1004: }
1005:
1006: if ((ep_out == NULL) &&
1007: ((addr & USB_ENDPOINT_DIR_MASK) == USB_DIR_OUT) &&
1008: ((attrs & USB_ENDPOINT_XFERTYPE_MASK) ==
1009: USB_ENDPOINT_XFER_BULK)) {
1010: dev_dbg(dev, "found bulk-out endpoint at 0x%02x\n",
1011: ep->bEndpointAddress);
1012: ep_out = ep;
1013: }
1014: }
1015:
1016: if (!ep_narrow || !ep_out || !ep_wide) {
1017: dev_err(dev, "Couldn't find all endpoints\n");
1018: retval = -ENODEV;
1019: goto no_endpoints;
1020: }
1021:
1022: /* allocate memory for our device state and initialize it */
1023: rr3 = kzalloc_obj(*rr3);
]
|
| 14/2 |
2026/08/09 10:34 |
llm |
expert |
0m
Model:
Tokens:
input: 18247
output: 39
thoughts: 311
Thoughts: **My Analysis of the RedRat3 Device Driver's Resource Management**
Okay, so I'm looking at this `redrat3_dev_probe` function, and my immediate concern is how the remote control (rc) device is handled in the error paths. I see that the `redrat3_init_rc_dev` function sets up the device name and physical path for the remote control and then calls `rc_register_device`. Seems standard enough. But then, if `redrat3_enable_detector` fails during the probe, it jumps to `led_free`.
Now, here's where my alarm bells start ringing. The code within `led_free` *unregisters the LED class device* and then calls `redrat3_delete`. However, it *doesn't* unregister the `rc` device! There's no call to `rc_unregister_device` in this specific error path. This is a classic resource leak scenario, right? The driver registers the `rc` device, and if the hardware isn't quite ready, the registration sticks around, even when the rest of the probe is cleaned up. This would lead to resource exhaustion over time, especially with hotplugging. I need to dig deeper into the `redrat3_delete` function to see if it implicitly cleans up the `rc` device. I really hope it does! Otherwise, this is a serious bug that needs immediate attention.
|
| 15/2 |
2026/08/09 10:34 |
tool |
read-file |
0m
Args: map[File:drivers/media/rc/redrat3.c FirstLine:444 LineCount:50]
Results: map[Contents: 444:
445: ret = usb_submit_urb(rr3->wide_urb, GFP_KERNEL);
446: if (ret)
447: dev_err(rr3->dev, "wide band urb failed: %d", ret);
448:
449: return ret;
450: }
451:
452: static inline void redrat3_delete(struct redrat3_dev *rr3,
453: struct usb_device *udev)
454: {
455: usb_kill_urb(rr3->narrow_urb);
456: usb_kill_urb(rr3->wide_urb);
457: usb_kill_urb(rr3->flash_urb);
458: usb_kill_urb(rr3->learn_urb);
459: usb_free_urb(rr3->narrow_urb);
460: usb_free_urb(rr3->wide_urb);
461: usb_free_urb(rr3->flash_urb);
462: usb_free_urb(rr3->learn_urb);
463: usb_free_coherent(udev, le16_to_cpu(rr3->ep_narrow->wMaxPacketSize),
464: rr3->bulk_in_buf, rr3->dma_in);
465:
466: kfree(rr3);
467: }
468:
469: static u32 redrat3_get_timeout(struct redrat3_dev *rr3)
470: {
471: __be32 *tmp;
472: u32 timeout = MS_TO_US(150); /* a sane default, if things go haywire */
473: int len, ret, pipe;
474:
475: len = sizeof(*tmp);
476: tmp = kzalloc(len, GFP_KERNEL);
477: if (!tmp)
478: return timeout;
479:
480: pipe = usb_rcvctrlpipe(rr3->udev, 0);
481: ret = usb_control_msg(rr3->udev, pipe, RR3_GET_IR_PARAM,
482: USB_TYPE_VENDOR | USB_RECIP_DEVICE | USB_DIR_IN,
483: RR3_IR_IO_SIG_TIMEOUT, 0, tmp, len, 5000);
484: if (ret != len)
485: dev_warn(rr3->dev, "Failed to read timeout from hardware\n");
486: else {
487: timeout = redrat3_len_to_us(be32_to_cpup(tmp));
488:
489: dev_dbg(rr3->dev, "Got timeout of %d ms\n", timeout / 1000);
490: }
491:
492: kfree(tmp);
493:
]
|
| 16/2 |
2026/08/09 10:34 |
llm |
expert |
0m
Model:
Tokens:
input: 19753
output: 39
thoughts: 191
Thoughts: **Use-After-Free Vulnerability in `redrat3_delete`**
Okay, so I've been tracing through this code, and it's looking like we have a potential use-after-free situation. Specifically, `redrat3_delete` calls `kfree(rr3)` and that looks like it frees `rr3` from memory, right? Fine. But here's the kicker: `rr3->rc` is registered, and that `rc` device is holding onto pointers to both `rr3->name` and `rr3->phys`. Now, when `sysfs_emit` tries to read `input_dev_show_name`, it's going to access `dev->name`, which is *pointing* to `rr3->name`. Since `rr3` is already gone, that's a classic use-after-free, plain and simple.
But wait, there's another angle to this. `rc_register_device` registers the input device and exposes it to userspace. If `redrat3_enable_detector` *fails* somewhere down the line, `rr3` gets freed, but the `rc` device remains registered, still exposed to userspace. Then userspace, something like `udevd`, could read the `name` attribute of the input device, and that, once again, would point to `rr3->name`, which we know is a freed pointer. That's even worse β a race condition where a failure in initialization leads to the vulnerability being exposed.
I need to dive into `redrat3_enable_detector` and see how this can happen. That's the key.
|
| 17/2 |
2026/08/09 10:34 |
tool |
read-file |
0m
Args: map[File:drivers/media/rc/redrat3.c FirstLine:400 LineCount:50]
Results: map[Contents: 400: data = kzalloc(sizeof(u8), GFP_KERNEL);
401: if (!data)
402: return -ENOMEM;
403:
404: udev = rr3->udev;
405: res = usb_control_msg(udev, usb_rcvctrlpipe(udev, 0), cmd,
406: USB_TYPE_VENDOR | USB_RECIP_DEVICE | USB_DIR_IN,
407: 0x0000, 0x0000, data, sizeof(u8), 10000);
408:
409: if (res < 0) {
410: dev_err(rr3->dev, "%s: Error sending rr3 cmd res %d, data %d",
411: __func__, res, *data);
412: res = -EIO;
413: } else
414: res = data[0];
415:
416: kfree(data);
417:
418: return res;
419: }
420:
421: /* Enables the long range detector and starts async receive */
422: static int redrat3_enable_detector(struct redrat3_dev *rr3)
423: {
424: struct device *dev = rr3->dev;
425: int ret;
426:
427: ret = redrat3_send_cmd(RR3_RC_DET_ENABLE, rr3);
428: if (ret != 0)
429: dev_dbg(dev, "%s: unexpected ret of %d\n",
430: __func__, ret);
431:
432: ret = redrat3_send_cmd(RR3_RC_DET_STATUS, rr3);
433: if (ret != 1) {
434: dev_err(dev, "%s: detector status: %d, should be 1\n",
435: __func__, ret);
436: return -EIO;
437: }
438:
439: ret = usb_submit_urb(rr3->narrow_urb, GFP_KERNEL);
440: if (ret) {
441: dev_err(rr3->dev, "narrow band urb failed: %d", ret);
442: return ret;
443: }
444:
445: ret = usb_submit_urb(rr3->wide_urb, GFP_KERNEL);
446: if (ret)
447: dev_err(rr3->dev, "wide band urb failed: %d", ret);
448:
449: return ret;
]
|
| 18/2 |
2026/08/09 10:34 |
llm |
expert |
0m
Model:
Tokens:
input: 21123
output: 40
thoughts: 1864
Thoughts: **Analysis of the Redrat3 USB Driver Vulnerability**
Okay, here's what I've got. This looks like a solid use-after-free (UAF) vulnerability in the Redrat3 USB driver. My initial assessment is that it's exploitable and can be triggered under specific circumstances.
First, the core issue is that `redrat3_enable_detector` can fail. When it does, due to an error during USB communication (specifically a non-1 return from `RR3_RC_DET_STATUS` or a `usb_submit_urb` failure), the driver cleans up partially. It unregisters the LED, and deletes the `rr3` structure. However, it fails to unregister the remote control (`rc`) device registered with `rc_register_device`. This leaves a dangling pointer.
The `rc` device is tied to an input device and uses `rr3->name` as the device name. Since `rr3` gets freed via `redrat3_delete`, `rr3->name` is no longer valid. When `udevd` or any process later tries to access `/sys/class/input/inputX/name` (which points to this dangling memory), it will read freed memory. The important part is that the sysfs attribute is world-readable, meaning any unprivileged user can trigger this.
The question of exploitability hinges on whether this is just a read of freed memory or something more. It's a `sysfs_emit` read, which is a string read, limited to `PAGE_SIZE`. In most cases, it will leak whatever is in the freed slab object. Since it's a read, the primary impact is information leakage. The leaked information could be sensitive kernel pointers or other data, if the memory has been reallocated with something that contains pointers. It's not likely to cause an immediate kernel panic (because it's a slab object, and it's probably reallocated memory), but it is a security risk. I'm going to mark this as exploitable.
Can it be triggered? It's definitely a **Peripheral Trigger**. A malicious USB device (or a malfunctioning one that fails the `RR3_RC_DET_STATUS` check) is required to trigger the failure.
Remotely? No, not in the typical sense. Physical access (or a USB/IP setup) is needed.
VM Guest Trigger? I think this is a **VM Host Trigger** in the confidential computing context, since a malicious host can hotplug a virtual USB device to the guest, and then trigger the vulnerability in the guest kernel. It's a standard paravirtualized feature, which means it is applicable.
Unprivileged Access? This is where it gets interesting. While creating the dangling pointer is a hardware event or requires root, reading the sysfs file is accessible to *any* user. I'll mark "Accessible From Unprivileged Processes" as True.
The missing `rc_unregister_device` call in the error path is the root cause. A simple missing cleanup on error. The `rc` device registration persists indefinitely after a failed probe, leading to the UAF. This is classic error-handling mistake.
|
| 19/2 |
2026/08/09 10:34 |
tool |
codesearch-definition-source |
0m
Args: map[ContextFile:drivers/media/rc/rc-main.c Name:rc_unregister_device]
Results: map[SourceCode:2016: void rc_unregister_device(struct rc_dev *dev)
2017: {
2018: if (!dev)
2019: return;
2020:
2021: if (dev->driver_type == RC_DRIVER_IR_RAW)
2022: ir_raw_event_unregister(dev);
2023:
2024: timer_delete_sync(&dev->timer_keyup);
2025: timer_delete_sync(&dev->timer_repeat);
2026:
2027: mutex_lock(&dev->lock);
2028: if (dev->users && dev->close)
2029: dev->close(dev);
2030: dev->registered = false;
2031: mutex_unlock(&dev->lock);
2032:
2033: rc_free_rx_device(dev);
2034:
2035: /*
2036: * lirc device should be freed with dev->registered = false, so
2037: * that userspace polling will get notified.
2038: */
2039: if (dev->allowed_protocols != RC_PROTO_BIT_CEC)
2040: lirc_unregister(dev);
2041:
2042: device_del(&dev->dev);
2043:
2044: ida_free(&rc_ida, dev->minor);
2045: }
SourceFile:drivers/media/rc/rc-main.c]
|
| 20/2 |
2026/08/09 10:34 |
llm |
expert |
0m
Model:
Tokens:
input: 24140
output: 38
thoughts: 62
Thoughts: Okay, here's my interpretation of that.
**My Analysis of the Device Registration Issue**
Right, so `rc_unregister_device` *should* handle the unregistration of the device. That's its entire purpose. But, the problem is that it's just not being called. The code path isn't reaching that function, which means the device is remaining registered in the system. Consequently, it persists within the `/sys` filesystem.
Specifically, I'm thinking about how `udevd`, or perhaps another process, is likely querying the device's name via a read to `/sys/class/input/inputX/name`. That read is going to trigger a call to `input_dev_show_name`. And that call is happening, but the device is still registered, so it's still being returned, even though it *should* be gone. We're getting stale information. That read from `/sys/class/input/inputX/name` is the tell. It reveals the issue.
|
| 21/2 |
2026/08/09 10:35 |
tool |
codesearch-definition-source |
0m
Args: map[ContextFile:drivers/input/input.c Name:input_dev_show_name]
Results: map[SourceCode:1282: INPUT_DEV_STRING_ATTR_SHOW(name);
SourceFile:drivers/input/input.c]
|
| 22/2 |
2026/08/09 10:35 |
llm |
expert |
0m
Model:
Tokens:
input: 24507
output: 24
thoughts: 15
Thoughts: Here's a summary of my thought process:
**Diving into Attribute Display**
Okay, time to get to work. First, I need to take a look at `INPUT_DEV_STRING_ATTR_SHOW`. This seems like a key piece in this process, so I'll start there. I need to understand what this represents, its function, and how it interacts with other parts of the system. I'll need to remember to consider the context of the larger problem I'm working on and how this element fits in with my current goals. So, I'll start digging in.
|
| 23/2 |
2026/08/09 10:35 |
tool |
grepper |
0m
Args: map[Expression:define INPUT_DEV_STRING_ATTR_SHOW]
Results: map[Output:drivers/input/input.c=1267=static inline void input_proc_exit(void) { }
--
drivers/input/input.c-1269-
drivers/input/input.c:1270:#define INPUT_DEV_STRING_ATTR_SHOW(name) \
drivers/input/input.c-1271-static ssize_t input_dev_show_##name(struct device *dev, \
]
|
| 24/2 |
2026/08/09 10:35 |
llm |
expert |
0m
Model:
Tokens:
input: 24790
output: 36
thoughts: 8
Thoughts: Okay, here's my interpretation of "Let's read the macro definition", rendered as a first-person, expert-level summary:
**Analyzing the Macro Definition**
Alright, so the initial prompt is straightforward: "Let's read the macro definition." That immediately signals a focus on the mechanics of code generation, potentially within a preprocessor or a macro expansion system. My mind immediately starts to work. I need to understand the *purpose* of the macro. Is it for code abstraction? Performance optimization? Conditional compilation? The *syntax* is also critical; are we dealing with a simple textual substitution, or a more sophisticated construct that allows for parameterization, conditional logic, or even recursive expansion? I'm already mentally running through the possible macro languages I'm familiar with, considering the implications of each, and anticipating common pitfalls like unintended name capture or subtle side effects. I'm ready to dive into the specifics and pick apart its inner workings, with my mental toolbox of macro analysis techniques at the ready. I'm looking for its strengths and weaknesses, and how it fits into the broader context of the system.
|
| 25/2 |
2026/08/09 10:35 |
tool |
read-file |
0m
Args: map[File:drivers/input/input.c FirstLine:1270 LineCount:15]
Results: map[Contents:1270: #define INPUT_DEV_STRING_ATTR_SHOW(name) \
1271: static ssize_t input_dev_show_##name(struct device *dev, \
1272: struct device_attribute *attr, \
1273: char *buf) \
1274: { \
1275: struct input_dev *input_dev = to_input_dev(dev); \
1276: \
1277: return sysfs_emit(buf, "%s\n", \
1278: input_dev->name ? input_dev->name : ""); \
1279: } \
1280: static DEVICE_ATTR(name, S_IRUGO, input_dev_show_##name, NULL)
1281:
1282: INPUT_DEV_STRING_ATTR_SHOW(name);
1283: INPUT_DEV_STRING_ATTR_SHOW(phys);
1284: INPUT_DEV_STRING_ATTR_SHOW(uniq);
]
|
| 26/2 |
2026/08/09 10:35 |
llm |
expert |
0m
Model:
Tokens:
input: 25354
output: 40
thoughts: 37
Thoughts: Okay, here's my interpretation of that thought process, assuming I'm an expert in device driver internals:
**Diving into the Device Naming Logic**
Alright, I'm tracing this `input_dev->name` reference. It's a fundamental thing, the name associated with the input device. I know I see it used, but I need to figure out *where* that string is actually set, initialized. Let me quickly think... my first instinct is to check `rc_register_device`. Given that `rc` likely refers to a remote control device, `rc_register_device` is the most logical place to look for the device to be registered. Okay, `rc_register_device` it is. Let's dig into that function and see if the name assignment happens there. I know I'm on the right track; the input device must get its name somewhere, and registration seems like the prime candidate.
|
| 27/2 |
2026/08/09 10:35 |
tool |
codesearch-definition-source |
0m
Args: map[ContextFile:drivers/media/rc/rc-main.c Name:rc_register_device]
Results: map[SourceCode:1891: int rc_register_device(struct rc_dev *dev)
1892: {
1893: const char *path;
1894: int attr = 0;
1895: int minor;
1896: int rc;
1897:
1898: if (!dev)
1899: return -EINVAL;
1900:
1901: minor = ida_alloc_max(&rc_ida, RC_DEV_MAX - 1, GFP_KERNEL);
1902: if (minor < 0)
1903: return minor;
1904:
1905: dev->minor = minor;
1906: dev_set_name(&dev->dev, "rc%u", dev->minor);
1907: dev_set_drvdata(&dev->dev, dev);
1908:
1909: dev->dev.groups = dev->sysfs_groups;
1910: if (dev->driver_type == RC_DRIVER_SCANCODE && !dev->change_protocol)
1911: dev->sysfs_groups[attr++] = &rc_dev_ro_protocol_attr_grp;
1912: else if (dev->driver_type != RC_DRIVER_IR_RAW_TX)
1913: dev->sysfs_groups[attr++] = &rc_dev_rw_protocol_attr_grp;
1914: if (dev->s_filter)
1915: dev->sysfs_groups[attr++] = &rc_dev_filter_attr_grp;
1916: if (dev->s_wakeup_filter)
1917: dev->sysfs_groups[attr++] = &rc_dev_wakeup_filter_attr_grp;
1918: dev->sysfs_groups[attr++] = NULL;
1919:
1920: if (dev->driver_type == RC_DRIVER_IR_RAW) {
1921: rc = ir_raw_event_prepare(dev);
1922: if (rc < 0)
1923: goto out_minor;
1924: }
1925:
1926: if (dev->driver_type != RC_DRIVER_IR_RAW_TX) {
1927: rc = rc_prepare_rx_device(dev);
1928: if (rc)
1929: goto out_raw;
1930: }
1931:
1932: dev->registered = true;
1933:
1934: rc = device_add(&dev->dev);
1935: if (rc)
1936: goto out_rx_free;
1937:
1938: path = kobject_get_path(&dev->dev.kobj, GFP_KERNEL);
1939: dev_info(&dev->dev, "%s as %s\n",
1940: dev->device_name ?: "Unspecified device", path ?: "N/A");
1941: kfree(path);
1942:
1943: /*
1944: * once the input device is registered in rc_setup_rx_device,
1945: * userspace can open the input device and rc_open() will be called
1946: * as a result. This results in driver code being allowed to submit
1947: * keycodes with rc_keydown, so lirc must be registered first.
1948: */
1949: if (dev->allowed_protocols != RC_PROTO_BIT_CEC) {
1950: rc = lirc_register(dev);
1951: if (rc < 0)
1952: goto out_dev;
1953: }
1954:
1955: if (dev->driver_type != RC_DRIVER_IR_RAW_TX) {
1956: rc = rc_setup_rx_device(dev);
1957: if (rc)
1958: goto out_lirc;
1959: }
1960:
1961: if (dev->driver_type == RC_DRIVER_IR_RAW) {
1962: rc = ir_raw_event_register(dev);
1963: if (rc < 0)
1964: goto out_rx;
1965: }
1966:
1967: dev_dbg(&dev->dev, "Registered rc%u (driver: %s)\n", dev->minor,
1968: dev->driver_name ? dev->driver_name : "unknown");
1969:
1970: return 0;
1971:
1972: out_rx:
1973: rc_free_rx_device(dev);
1974: out_lirc:
1975: if (dev->allowed_protocols != RC_PROTO_BIT_CEC)
1976: lirc_unregister(dev);
1977: out_dev:
1978: device_del(&dev->dev);
1979: out_rx_free:
1980: ir_free_table(&dev->rc_map);
1981: out_raw:
1982: ir_raw_event_free(dev);
1983: out_minor:
1984: ida_free(&rc_ida, minor);
1985: return rc;
1986: }
SourceFile:drivers/media/rc/rc-main.c]
|
| 28/2 |
2026/08/09 10:35 |
llm |
expert |
0m
Model:
Tokens:
input: 27127
output: 42
thoughts: 16
Thoughts: Here's my thought process, as it relates to the given information:
**Understanding the Device Setup**
Okay, so I'm focusing on device initialization... specifically, this `rc_setup_rx_device` function. I see that it's responsible for *registering* the input device. Right, so it's not just *creating* or *allocating* the resources; it's actually making it known to the system, making it available for use. This registration step is key. It's the point where the operating system, or at least the relevant driver infrastructure, becomes aware of the hardware. Good, this solidifies my understanding of the flow.
|
| 29/2 |
2026/08/09 10:35 |
tool |
codesearch-definition-source |
0m
Args: map[ContextFile:drivers/media/rc/rc-main.c Name:rc_setup_rx_device]
Results: map[SourceCode:1848: static int rc_setup_rx_device(struct rc_dev *dev)
1849: {
1850: int rc;
1851:
1852: /* rc_open will be called here */
1853: rc = input_register_device(dev->input_dev);
1854: if (rc)
1855: return rc;
1856:
1857: /*
1858: * Default delay of 250ms is too short for some protocols, especially
1859: * since the timeout is currently set to 250ms. Increase it to 500ms,
1860: * to avoid wrong repetition of the keycodes. Note that this must be
1861: * set after the call to input_register_device().
1862: */
1863: if (dev->allowed_protocols == RC_PROTO_BIT_CEC)
1864: dev->input_dev->rep[REP_DELAY] = 0;
1865: else
1866: dev->input_dev->rep[REP_DELAY] = 500;
1867:
1868: /*
1869: * As a repeat event on protocols like RC-5 and NEC take as long as
1870: * 110/114ms, using 33ms as a repeat period is not the right thing
1871: * to do.
1872: */
1873: dev->input_dev->rep[REP_PERIOD] = 125;
1874:
1875: return 0;
1876: }
SourceFile:drivers/media/rc/rc-main.c]
|
| 30/2 |
2026/08/09 10:35 |
llm |
expert |
0m
Model:
Tokens:
input: 27806
output: 40
thoughts: 33
Thoughts: Okay, here's my interpretation of those thoughts, presented as a personal thought process, as requested:
**Device Driver Initialization Thoughts**
Alright, let's see. The code flow is looking correct. I've confirmed that the `input_register_device` function *is* definitely being called in the initialization sequence. That's a critical step, obviously. Without that, nothing else will work correctly.
And, if I remember correctly, the device name, that crucial identifying string, is being properly assigned to `dev->input_dev->name`. Yes, it's being set within the `rc_allocate_device` function, as expected. So the input device is not being initialised with a null or uninitialised name. So far, so good, everything is lining up as it should. This ensures the device's identity and allows the input system to recognize and manage the device. We are now well on our way to success.
|
| 31/2 |
2026/08/09 10:35 |
tool |
codesearch-definition-source |
0m
Args: map[ContextFile:drivers/media/rc/rc-main.c Name:rc_allocate_device]
Results: map[SourceCode:1701: struct rc_dev *rc_allocate_device(enum rc_driver_type type)
1702: {
1703: struct rc_dev *dev;
1704:
1705: dev = kzalloc_obj(*dev);
1706: if (!dev)
1707: return NULL;
1708:
1709: if (type != RC_DRIVER_IR_RAW_TX) {
1710: dev->input_dev = input_allocate_device();
1711: if (!dev->input_dev) {
1712: kfree(dev);
1713: return NULL;
1714: }
1715:
1716: dev->input_dev->getkeycode = ir_getkeycode;
1717: dev->input_dev->setkeycode = ir_setkeycode;
1718: input_set_drvdata(dev->input_dev, dev);
1719:
1720: dev->timeout = IR_DEFAULT_TIMEOUT;
1721: timer_setup(&dev->timer_keyup, ir_timer_keyup, 0);
1722: timer_setup(&dev->timer_repeat, ir_timer_repeat, 0);
1723:
1724: spin_lock_init(&dev->rc_map.lock);
1725: spin_lock_init(&dev->keylock);
1726: }
1727: mutex_init(&dev->lock);
1728:
1729: dev->dev.type = &rc_dev_type;
1730: dev->dev.class = &rc_class;
1731: device_initialize(&dev->dev);
1732:
1733: dev->driver_type = type;
1734:
1735: __module_get(THIS_MODULE);
1736: return dev;
1737: }
SourceFile:drivers/media/rc/rc-main.c]
|
| 32/2 |
2026/08/09 10:35 |
llm |
expert |
0m
Model:
Tokens:
input: 28638
output: 42
thoughts: 27
Thoughts: Okay, here's my summarized thought process, presented as if I'm the one having these thoughts, targeting an expert audience:
**Debugging the Input Device Name Assignment**
Alright, hang on a second. I need to trace this `dev->input_dev->name` setting. Seems I've got a bit of a puzzle to solve. Where exactly is this crucial `name` field for the input device being populated? My initial hunch is that it's likely occurring within the `rc_prepare_rx_device` function. I need to confirm. I should have been more careful the first time around.
|
| 33/2 |
2026/08/09 10:35 |
tool |
codesearch-definition-source |
0m
Args: map[ContextFile:drivers/media/rc/rc-main.c Name:rc_prepare_rx_device]
Results: map[SourceCode:1784: static int rc_prepare_rx_device(struct rc_dev *dev)
1785: {
1786: int rc;
1787: struct rc_map *rc_map;
1788: u64 rc_proto;
1789:
1790: if (!dev->map_name)
1791: return -EINVAL;
1792:
1793: rc_map = rc_map_get(dev->map_name);
1794: if (!rc_map)
1795: rc_map = rc_map_get(RC_MAP_EMPTY);
1796: if (!rc_map || !rc_map->scan || rc_map->size == 0)
1797: return -EINVAL;
1798:
1799: rc = ir_setkeytable(dev, rc_map);
1800: if (rc)
1801: return rc;
1802:
1803: rc_proto = BIT_ULL(rc_map->rc_proto);
1804:
1805: if (dev->driver_type == RC_DRIVER_SCANCODE && !dev->change_protocol)
1806: dev->enabled_protocols = dev->allowed_protocols;
1807:
1808: if (dev->driver_type == RC_DRIVER_IR_RAW)
1809: ir_raw_load_modules(&rc_proto);
1810:
1811: if (dev->change_protocol) {
1812: rc = dev->change_protocol(dev, &rc_proto);
1813: if (rc < 0)
1814: goto out_table;
1815: dev->enabled_protocols = rc_proto;
1816: }
1817:
1818: /* Keyboard events */
1819: set_bit(EV_KEY, dev->input_dev->evbit);
1820: set_bit(EV_REP, dev->input_dev->evbit);
1821: set_bit(EV_MSC, dev->input_dev->evbit);
1822: set_bit(MSC_SCAN, dev->input_dev->mscbit);
1823:
1824: /* Pointer/mouse events */
1825: set_bit(INPUT_PROP_POINTING_STICK, dev->input_dev->propbit);
1826: set_bit(EV_REL, dev->input_dev->evbit);
1827: set_bit(REL_X, dev->input_dev->relbit);
1828: set_bit(REL_Y, dev->input_dev->relbit);
1829:
1830: if (dev->open)
1831: dev->input_dev->open = ir_open;
1832: if (dev->close)
1833: dev->input_dev->close = ir_close;
1834:
1835: dev->input_dev->dev.parent = &dev->dev;
1836: memcpy(&dev->input_dev->id, &dev->input_id, sizeof(dev->input_id));
1837: dev->input_dev->phys = dev->input_phys;
1838: dev->input_dev->name = dev->device_name;
1839:
1840: return 0;
1841:
1842: out_table:
1843: ir_free_table(&dev->rc_map);
1844:
1845: return rc;
1846: }
SourceFile:drivers/media/rc/rc-main.c]
|
| 34/2 |
2026/08/09 10:35 |
llm |
expert |
0m
Model:
Tokens:
input: 29852
output: 66
thoughts: 2978
Thoughts: **Analysis of the RedRat3 USB Driver Vulnerability**
Okay, so here's the breakdown of this RedRat3 USB driver bug as I see it, from an expert's perspective:
1. **The Root Cause:** The core issue is a Use-After-Free (UAF) vulnerability. The driver allocates and registers an input device (`input_dev`) and a remote control device (`rc_dev`). Critically, `input_dev->name` is assigned a pointer to `rr3->name`, which is a string. If the `redrat3_enable_detector` function fails *after* the initial device registration, the `rr3` structure (including `rr3->name`) is freed, but the `rc_dev` device remains registered, and the `input_dev`'s name field retains a dangling pointer. This means the pointer in `input_dev->name` is now dangling.
2. **Exploitation Potential:** This dangling pointer can be exploited via a simple sysfs read, specifically `/sys/class/input/inputX/name`. The `sysfs_emit` function will attempt to read this string until a null terminator, potentially reading freed memory. If the freed memory has been reallocated by something else, this read becomes an information leak. Given the `kmalloc-2k` size for the `rr3` object, this leak could expose significantly valuable data (keys, pointers, etc.). The sysfs read uses `vscnprintf`, which also supports a size limit.
3. **Denial of Service (DoS):** If the freed memory hasn't been reallocated, accessing it will cause a kernel page fault, resulting in a system crash. While slab allocation makes this slightly less likely, it's still possible, especially if the whole slab page containing the object is returned to the system (which is possible).
4. **Unprivileged Access:** The `/sys/class/input/inputX/name` file is world-readable, meaning it's accessible by any unprivileged user. This makes exploitation straightforward from a standard user process. The fact that the trigger requires device plugging is not a special privilege in itself.
5. **User Namespaces:** If unprivileged users can access it, user namespaces can too.
6. **Virtualization Considerations:** The bug is not directly triggered by host or guest paravirtualized features. However, *in a confidential computing context*, a malicious host can hotplug a RedRat3 device to a KVM guest and deliberately trigger a probe failure. The guest's `udevd` daemon will then automatically read the name attribute (as the syzkaller report confirms!), triggering the UAF. The host can then cause a DoS by causing a kernel panic when the guest accesses the dangling pointer.
7. **Triggering Scenario & Reliability:** The trigger doesn't even require a *malicious* device. A legitimate RedRat3 device that fails to initialize due to a transient error, timeout, or USB bandwidth issue (within `redrat3_enable_detector`) is sufficient. This makes this bug relatively easy to trigger in the wild, adding to its risk profile. The `udevd` daemon's automatic reading upon device registration further increases the bug's reliability. The device is registered by `rc_register_device` even if `redrat3_enable_detector` fails.
8. **Code Review & Confirmation:** A thorough examination of the driver code (especially `redrat3_init_rc_dev`, `redrat3_enable_detector`, `redrat3_delete`, and `redrat3_dev_disconnect`) confirms the vulnerability. The remote control device (`rc_dev`) registration happens before the check for `redrat3_enable_detector`. There is no mechanism to unregister the remote control device if the probing process fails, resulting in a dangling pointer. Furthermore, the `redrat3_dev_disconnect` function is not called after probe fails.
|
| 35/2 |
2026/08/09 10:36 |
tool |
set-results |
0m
Args: map[DenialOfService:true Exploitable:true FilesystemTrigger:false NetworkTrigger:false PeripheralTrigger:true RemoteTrigger:false Unprivileged:true UserNamespace:true VMGuestTrigger:false VMHostTrigger:true]
Results: map[DenialOfService:true Exploitable:true FilesystemTrigger:false NetworkTrigger:false PeripheralTrigger:true RemoteTrigger:false Unprivileged:true UserNamespace:true VMGuestTrigger:false VMHostTrigger:true]
|
| 36/2 |
2026/08/09 10:36 |
llm |
expert |
0m
Model:
Tokens:
input: 33817
output: 731
thoughts: 398
Thoughts: **Final Thoughts on the Redrat3 Driver Vulnerability**
Okay, the analysis is complete, and it's time to craft the final response. This is a classic, nasty use-after-free scenario, right in the `input_dev_show_name` function. It's all stemming from a missing cleanup in the error handling of `redrat3_dev_probe`.
Essentially, when `redrat3_enable_detector` blows up β and it's designed to blow up on failed hardware setup, I think I know how to exploit it, β the driver helpfully frees the `rr3` structure. *However*, and this is the crux of the issue, it completely forgets to unregister the remote control (`rc`) device that it had already registered via `redrat3_init_rc_dev`. This leaves the `rc` device and its associated `input` device dangling, still happily exposed to userspace.
The real kicker? The `name` attribute of the input device is pointing directly to `rr3->name` β and that memory has now been *freed*. So, when something in userspace, whether it's `udevd` innocently probing or an unprivileged user snooping around, attempts to read `/sys/class/input/inputX/name`, boom! We're reading freed memory.
This is definitely exploitable. An attacker can leak the contents of a freed `kmalloc-2k` slab object. If that memory has been reused and, let's say, contains sensitive data, that data is now ripe for the taking. Plus, it's also a denial-of-service vulnerability. If the freed memory hasn't been reallocated, reading it will just cause a kernel panic, which is never good.
And the best part? It's all entirely unprivileged. The sysfs attribute is world-readable, so any user can trigger this UAF read once the device is in this dangling state. Itβs also relatively easy to trigger. You just need to plug in a USB device that'll fail the `redrat3_enable_detector` initialization. A malicious KVM host could also trigger this by hot-plugging a virtual USB device into a Confidential Computing guest and forcing the probe to fail. Then `udevd` in the guest will do its thing, trigger the bug and crash the guest.
|
| 37/1 |
2026/08/09 10:36 |
action |
format-explanation |
0m
Results: map[Explanation:**Bug Analysis:**
The bug is a Use-After-Free (UAF) vulnerability in the RedRat3 USB infrared
transceiver driver (`drivers/media/rc/redrat3.c`).
During the device initialization in `redrat3_dev_probe()`, the driver allocates
a `redrat3_dev` structure (`rr3`). It then successfully registers a Remote
Controller (`rc`) device and its associated `input` device by calling
`redrat3_init_rc_dev()`. As part of this registration, the input device's `name`
pointer is set to point to `rr3->name`.
Immediately after, the driver calls `redrat3_enable_detector(rr3)`. If this
function fails (e.g., the USB device returns an unexpected status or a URB
submission fails), the probe function jumps to the error handling path
(`led_free` -> `redrat_free`). In this error path, the driver frees the `rr3`
structure via `redrat3_delete()`, but it **fails to unregister the `rc`
device**.
As a result, the `input` device remains registered and exposed to userspace, but
its `name` attribute now points to freed memory. When userspace reads the
`/sys/class/input/inputX/name` sysfs file, the kernel calls
`input_dev_show_name()`, which reads the dangling pointer and triggers the UAF.
As seen in the crash report, this is often triggered automatically by `udevd`
when it processes the device addition uevent.
**Security Impact:**
* **Exploitable:** **True**. The bug acts as an information leak primitive. The
freed object is a 2048-byte slab object (`kmalloc-2k`). An attacker can spray
the heap to reallocate this memory with an object containing sensitive data
(e.g., kernel pointers, keys). Reading the sysfs file will then leak this data
to userspace until a null byte is encountered, which can be used to defeat KASLR
or leak secrets.
* **Denial Of Service:** **True**. If the freed slab page is returned to the
page allocator and unmapped, the subsequent read by `udevd` or an attacker will
cause a fatal page fault and kernel panic.
* **Accessible From Unprivileged Processes & User Namespaces:** **True**. The
sysfs attribute `/sys/class/input/inputX/name` is world-readable (`-r--r--r--`).
Once the vulnerable device is plugged in and the UAF is created, any
unprivileged user can read the file to exploit the information leak.
* **Peripheral Trigger:** **True**. The bug is triggered by plugging in a
physical (or emulated) USB device that intentionally or accidentally fails the
`redrat3_enable_detector()` initialization step.
* **VM Host Trigger in The Confidential Computing Context:** **True**. A
malicious hypervisor can hotplug a virtual USB device to a confidential guest
and manipulate the USB responses to fail the probe. The guest's `udevd` will
automatically read the sysfs file, triggering the UAF and crashing the guest
kernel (DoS).
* **VM Guest Trigger:** **False**. The vulnerability is in a host USB driver,
which is not exposed to the guest via standard paravirtualization.
* **Network / Remote / Filesystem Trigger:** **False**. The bug is strictly tied
to the USB subsystem and requires peripheral access.]
|