| 0/0 |
2026/08/09 07:14 |
flow |
assessment-security |
51m
Results: map[DenialOfService:true Explanation:An analysis of the bug report and the source code reveals the following:
**Root Cause & Mechanism:**
The bug is a sleep-in-atomic vulnerability that leads to stack corruption.
The `vub300` USB SDIO host controller driver sets up an inactivity timer
(`vub300_inactivity_timer_expired()`), which executes in a softirq context. If
the USB device is disconnected, the timer callback notices that
`vub300->interface` is NULL and drops a reference using `kref_put()`.
If this is the last reference, it invokes the release function `vub300_delete()`
directly in the softirq context. `vub300_delete()` calls `mmc_free_host()`,
which eventually calls `wakeup_source_unregister()` and `synchronize_srcu()`.
`synchronize_srcu()` is a blocking function that calls `schedule()` to wait for
a grace period.
**Exploitability:**
Calling `schedule()` from a softirq context is highly dangerous on architectures
with dedicated per-CPU softirq stacks (like x86_64). When the thread sleeps, it
leaves its call frames on the per-CPU softirq stack. If another softirq is
raised on the same CPU while the thread is sleeping, the kernel will start
executing the new softirq from the top of the softirq stack, completely
overwriting the sleeping thread's stack frames.
When the `synchronize_srcu()` completion finishes and the original thread wakes
up, it will resume execution with a corrupted stack. An attacker can carefully
groom the softirq stack overwrites to control the return addresses, leading to a
classic Return-Oriented Programming (ROP) control-flow hijack and privilege
escalation. Therefore, this bug is exploitable.
**Trigger & Accessibility:**
The vulnerability is located in the `vub300` USB driver. Triggering it requires
the ability to plug in a specific USB device (or emulate one), wait for the
inactivity timer to be armed, and then disconnect the device so that the timer
callback executes the cleanup code. This makes it a Peripheral Trigger.
Unprivileged users cannot trigger this bug remotely or from user-space without
physical access to plug in a USB device (or without elevated privileges to use
USB gadget subsystems like `dummy_hcd`).
**Conclusion:**
- **Exploitable:** Yes (Stack corruption leading to arbitrary code execution).
- **Denial of Service:** Yes (System crash/panic).
- **Peripheral Trigger:** Yes (Requires plugging/unplugging a USB device).
- **Unprivileged/User Namespace/Remote:** No. Exploitable:true FilesystemTrigger:false NetworkTrigger:false PeripheralTrigger:true RemoteTrigger:false Unprivileged:false UserNamespace:false VMGuestTrigger:false VMHostTrigger:false]
|
| 1/1 |
2026/08/09 07:14 |
action |
syz-repro-to-c-repro |
0m
Results:
|
| 2/1 |
2026/08/09 07:14 |
action |
kernel-checkouter |
0m
Results: map[KernelSrc:/app/workdir/cache/src/0bd482cc9fd6a5c3cdf3e7f636a3906397462859]
|
| 3/1 |
2026/08/09 07:14 |
action |
kernel-builder |
27m
Results: map[KernelObj:/app/workdir/cache/build/c6c7b2d9e4d1d7e5a1cffa7566d7d92fb1f9026a]
|
| 4/1 |
2026/08/09 07:42 |
action |
codesearch-prepare |
18m
Results: map[Index:codesearch-index]
|
| 5/1 |
2026/08/09 08:00 |
agent |
expert |
6m
Model:
Results: map[DenialOfService:true Exploitable:true FilesystemTrigger:false NetworkTrigger:false PeripheralTrigger:true RemoteTrigger:false Unprivileged:false UserNamespace:false VMGuestTrigger:false VMHostTrigger:false]
Instruction: You are an experienced Linux kernel security engineer. Your task is to analyze given kernel bug report
and determine its security impact based on the following dimensions.
Use the provided tools to examine the source code, check for capability checks (e.g., capable(), ns_capable()),
and understand the nature of the bug. Analyze the given kernel build and configuration.
You can check the kernel config by grepping ".config" file; you can check kernel cmdline by grepping
".config" file for "CONFIG_CMDLINE=". Assume sysctl parameters have default values.
But analyze for the corresponding production build w/o debugging tools enabled (like KASAN, KMSAN, UBSAN).
Try different strategies when analyzing the bug:
- think of ways in which the vulnerable code is unreachable
- or the other way around: try to come up with different ideas of how an unprivileged user can reach the bug
If still unsure err on the side of the bug being non-exploitable/not-accessible.
In the final reply, provide a reasoning for your assessment.
Analysis dimensions:
* Exploitable:
Determine if the bug can result in memory corruption, elevated privileges, or an information leak.
Memory safety issues are almost always exploitable (KASAN or UBSAN reports for use-after-free, out-of-bounds;
refcounting issues, corrupted lists, etc). When kernel is crashing on a completely wild pointer access
(e.g. user-space address, or non-canonical address, but not on NULL or address corresponding to KASAN shadow
for NULL address), including both data accesses and control transfers, that also usually implies possibility
of exploitation. Such reports usually say "unable to handle kernel paging request".
Uses of uninitialized values detected by KMSAN may be exploitable b/c attacker frequently can affect uninit
values with spraying techniques. However, for these exploitability depends on how exactly the uninit value
is used in the code, and what it affects.
Information leaks are exploitable on their own and should be classified as such. A bug that copies kernel
memory contents to userspace (e.g. an out-of-bounds read whose result is returned to the caller, or
uninitialized stack/heap bytes written to a user buffer) is exploitable: it can reveal kernel pointer
values and defeat KASLR, expose sensitive data such as cryptographic keys or other processes' memory, and
serves as a necessary building block in most modern kernel privilege-escalation exploit chains. Do not classify
an information leak as non-exploitable solely because it does not directly cause a memory write or control-flow
hijack; the leak itself is the exploit primitive.
Think of what happens after the bug is triggered. Some bugs cause kernel panic and halt execution,
they are harder to exploit. For example, BUG reports halts the kernel. However, WARNING reports don't halt
execution in production builds. Debug bug detection tools (like KASAN, KMSAN, KCSAN, UBSAN) are also not enabled
in production builds, so attacker can freely exploit these bugs w/o being detected by these tools.
If you see an integer overflow, think how the overflowed value used later (if it's used as allocation size,
or an array index). If you see an out-of-bounds read, think if it's followed by an out-of-bounds write as well.
Some KCSAN data-races may be exploitable by skilled attackers as well. Think what data structures got corrupted
as the result of data races and how. However, note that kernel has lots of "benign" data races that don't lead
to any runtime misbehavior at all.
* Denial Of Service:
Determine if the bug can result in denial-of-service. Most bugs can, since they cause system crash,
hangs, deadlocks, or resource leaks. This is mostly applicable to WARNING bugs that won't cause system crash
in production. For these think what will be consequences of the violation of the kernel assumptions flagged
by the WARNING. In some cases the unexpected condition is also properly handled by the normal control flow
(e.g. with "if (WARN_ON(...))"), these won't cause denial-of-service. If the condition is not handled,
then it may or may not cause denial-of-service.
* Accessible From Unprivileged Processes:
Determine if the bug can be reached from a typical (non-root) user process that does NOT have any special capabilities
(like CAP_SYS_ADMIN, CAP_NET_ADMIN, CAP_NET_RAW, CAP_PERFMON) or access to device nodes restricted to root.
Assume that unprivileged_bpf_disabled=1, that is eBPF loading is not accessible. However, cBPF (classical BPF)
is still accessible to non-root processes.
Assume that user namespaces are not accessible, that is, the process cannot get the mentioned capabilities even
within a new user namespace (checked by ns_capable() function in the kernel sources).
* Accessible From User Namespaces:
Determine if the bug can be reached within a user-namespace where the process has all capabilities
(including CAP_SYS_ADMIN, CAP_NET_ADMIN, CAP_NET_RAW, CAP_PERFMON). Such capabilities are checked with ns_capable()
function in the kernel sources.
* VM Guest Trigger:
Determine if the bug can be triggered from the context of a typical KVM guest (e.g., set up by a QEMU VMM).
Consider accesses to standard Linux host paravirtualized features (virtio-blk, virtio-net, etc.),
and handling of VM exits in the KVM code.
* VM Host Trigger in The Confidential Computing Context:
Determine if the bug can be triggered in a confidential computing guest kernel from the context of a KVM host.
Consider access to standard Linux guest paravirtualized features (virtio-blk, virtio-net, etc.).
* Ethernet Network Trigger:
Determine if the bug can be triggered by processing ingress network Ethernet traffic, either directly (network stack)
or via drivers exposed to network data.
* Other Remote Trigger:
Determine if the bug can be triggered by processing remote traffic other than Ethernet (Wifi, Bluetooth, NFC, etc).
* Peripheral Trigger:
Determine if the bug can be triggered via an untrusted peripheral device that can be physically plugged
into a system, such as a USB device or a niche hardware driver handling external hardware inputs.
This is particularly important for mobile and desktop environments where users can plug in unknown devices.
* Malicious Filesystem Trigger:
Determine if the bug can be triggered by the kernel mounting and parsing a malicious filesystem image.
This is highly critical for Desktop and Mobile environments where external media or downloaded images
might be auto-mounted.
Don't make assumptions about the kernel source code (it may be different from what you assume it is).
Extensively use the provided code access tools (codesearch-*, git-*, grepper, etc)
to examine the actual source code, and confirm any assumptions.
Prefer calling several tools at the same time to save round-trips.
Use set-results tool to provide results of the analysis.
It must be called exactly once before the final reply.
Ignore results of this tool.
Prompt:
The kernel bug report is:
RBP: 00007faa6b63500c R08: 0000000000000000 R09: 0000000000000000
R10: 0000000000000000 R11: 0000000000000246 R12: 0000000000000000
R13: 00007faa6b826038 R14: 00007faa6b825fa0 R15: 00007ffe29482fa8
</TASK>
================================
WARNING: inconsistent lock state
syzkaller #0 Tainted: G W
--------------------------------
inconsistent {SOFTIRQ-ON-R} -> {IN-SOFTIRQ-W} usage.
syz.4.93/7194 [HC0[0]:SC1[1]:HE1:SE0] takes:
ffffffff8fc0ed18 (wakeup_srcu){.+-+}-{0:0}, at: srcu_lock_sync include/linux/srcu.h:199 [inline]
ffffffff8fc0ed18 (wakeup_srcu){.+-+}-{0:0}, at: __synchronize_srcu+0x1c/0x300 kernel/rcu/srcutree.c:1481
{SOFTIRQ-ON-R} state was registered at:
lock_acquire kernel/locking/lockdep.c:5868 [inline]
lock_acquire+0x1b9/0x370 kernel/locking/lockdep.c:5825
srcu_lock_acquire include/linux/srcu.h:187 [inline]
srcu_read_lock include/linux/srcu.h:294 [inline]
device_wakeup_arm_wake_irqs+0x53/0x130 drivers/base/power/wakeup.c:401
dpm_suspend_noirq drivers/base/power/main.c:1652 [inline]
dpm_suspend_end+0xe5/0x210 drivers/base/power/main.c:1860
create_image kernel/power/hibernate.c:328 [inline]
hibernation_snapshot+0x28b/0x9c0 kernel/power/hibernate.c:442
hibernate.cold+0x17c/0x6a2 kernel/power/hibernate.c:811
state_store+0x1ff/0x240 kernel/power/main.c:821
kobj_attr_store+0x58/0x80 lib/kobject.c:840
sysfs_kf_write+0xf2/0x150 fs/sysfs/file.c:145
kernfs_fop_write_iter+0x3e0/0x5f0 fs/kernfs/file.c:345
new_sync_write fs/read_write.c:595 [inline]
vfs_write+0x6ac/0x1050 fs/read_write.c:687
ksys_write+0x12a/0x250 fs/read_write.c:739
do_syscall_x64 arch/x86/entry/syscall_64.c:63 [inline]
do_syscall_64+0x115/0x870 arch/x86/entry/syscall_64.c:94
entry_SYSCALL_64_after_hwframe+0x77/0x7f
irq event stamp: 90148
hardirqs last enabled at (90148): [<ffffffff8bbe28f2>] __raw_spin_unlock_irqrestore include/linux/spinlock_api_smp.h:178 [inline]
hardirqs last enabled at (90148): [<ffffffff8bbe28f2>] _raw_spin_unlock_irqrestore+0x52/0x80 kernel/locking/spinlock.c:198
hardirqs last disabled at (90147): [<ffffffff8bbe2602>] __raw_spin_lock_irqsave include/linux/spinlock_api_smp.h:130 [inline]
hardirqs last disabled at (90147): [<ffffffff8bbe2602>] _raw_spin_lock_irqsave+0x52/0x60 kernel/locking/spinlock.c:166
softirqs last enabled at (89630): [<ffffffff81c8f222>] __do_softirq kernel/softirq.c:656 [inline]
softirqs last enabled at (89630): [<ffffffff81c8f222>] invoke_softirq kernel/softirq.c:496 [inline]
softirqs last enabled at (89630): [<ffffffff81c8f222>] __irq_exit_rcu+0x162/0x210 kernel/softirq.c:735
softirqs last disabled at (89637): [<ffffffff81c8f222>] __do_softirq kernel/softirq.c:656 [inline]
softirqs last disabled at (89637): [<ffffffff81c8f222>] invoke_softirq kernel/softirq.c:496 [inline]
softirqs last disabled at (89637): [<ffffffff81c8f222>] __irq_exit_rcu+0x162/0x210 kernel/softirq.c:735
other info that might help us debug this:
Possible unsafe locking scenario:
CPU0
----
lock(wakeup_srcu);
<Interrupt>
lock(wakeup_srcu);
*** DEADLOCK ***
8 locks held by syz.4.93/7194:
#0: ffff88807ade1430 (&f->f_pos_lock){+.+.}-{4:4}, at: fdget_pos+0x2aa/0x380 fs/file.c:1259
#1: ffff888069482450 (sb_writers#8){.+.+}-{0:0}, at: ksys_write+0x12a/0x250 fs/read_write.c:739
#2: ffff888055791080 (&of->mutex){+.+.}-{4:4}, at: kernfs_fop_write_iter+0x2c2/0x5f0 fs/kernfs/file.c:336
#3: ffff88801f2c81e8 (kn->active#61){.+.+}-{0:0}, at: kernfs_get_active_of fs/kernfs/file.c:73 [inline]
#3: ffff88801f2c81e8 (kn->active#61){.+.+}-{0:0}, at: kernfs_fop_write_iter+0x332/0x5f0 fs/kernfs/file.c:337
#4: ffffffff8ea9fc80 (system_transition_mutex){+.+.}-{4:4}, at: lock_system_sleep+0x56/0x70 kernel/power/main.c:71
#5: ffffffff8fbf46e0 (device_hotplug_lock){+.+.}-{4:4}, at: hibernate.cold+0x126/0x6a2 kernel/power/hibernate.c:805
#6: ffffffff8f8c6bc0 (acpi_scan_lock){+.+.}-{4:4}, at: acpi_pm_start drivers/acpi/sleep.c:533 [inline]
#6: ffffffff8f8c6bc0 (acpi_scan_lock){+.+.}-{4:4}, at: acpi_hibernation_begin+0xf6/0x1c0 drivers/acpi/sleep.c:945
#7: ffffc90000a08ca0 ((&vub300->inactivity_timer)){+.-.}-{0:0}, at: call_timer_fn+0x11f/0x610 kernel/time/timer.c:1745
stack backtrace:
CPU: 1 UID: 0 PID: 7194 Comm: syz.4.93 Tainted: G W syzkaller #0 PREEMPT(full)
Tainted: [W]=WARN
Hardware name: Google Google Compute Engine/Google Compute Engine, BIOS Google 07/16/2026
Call Trace:
<IRQ>
__dump_stack lib/dump_stack.c:94 [inline]
dump_stack_lvl+0x100/0x190 lib/dump_stack.c:120
print_usage_bug.part.0+0x257/0x340 kernel/locking/lockdep.c:4042
print_usage_bug kernel/locking/lockdep.c:4010 [inline]
valid_state kernel/locking/lockdep.c:4056 [inline]
mark_lock_irq kernel/locking/lockdep.c:4273 [inline]
mark_lock+0x6b5/0xa20 kernel/locking/lockdep.c:4753
mark_usage kernel/locking/lockdep.c:4642 [inline]
__lock_acquire+0xff3/0x1a40 kernel/locking/lockdep.c:5191
lock_sync kernel/locking/lockdep.c:5916 [inline]
lock_sync+0x9d/0x110 kernel/locking/lockdep.c:5904
srcu_lock_sync include/linux/srcu.h:199 [inline]
__synchronize_srcu+0xa2/0x300 kernel/rcu/srcutree.c:1481
wakeup_source_remove drivers/base/power/wakeup.c:201 [inline]
wakeup_source_unregister.part.0+0x12c/0x540 drivers/base/power/wakeup.c:237
wakeup_source_unregister+0x1f/0x30 drivers/base/power/wakeup.c:236
mmc_host_classdev_release+0x42/0x120 drivers/mmc/core/host.c:69
device_release+0xd2/0x270 drivers/base/core.c:2636
kobject_cleanup lib/kobject.c:689 [inline]
kobject_release lib/kobject.c:720 [inline]
kref_put include/linux/kref.h:65 [inline]
kobject_put+0x1f7/0x640 lib/kobject.c:737
put_device+0x1f/0x30 drivers/base/core.c:3880
vub300_delete drivers/mmc/host/vub300.c:379 [inline]
kref_put include/linux/kref.h:65 [inline]
vub300_inactivity_timer_expired drivers/mmc/host/vub300.c:747 [inline]
vub300_inactivity_timer_expired+0x42f/0x550 drivers/mmc/host/vub300.c:742
call_timer_fn+0x19a/0x610 kernel/time/timer.c:1748
expire_timers kernel/time/timer.c:1799 [inline]
__run_timers+0x757/0xb00 kernel/time/timer.c:2374
__run_timer_base kernel/time/timer.c:2386 [inline]
__run_timer_base kernel/time/timer.c:2378 [inline]
run_timer_base+0x114/0x190 kernel/time/timer.c:2395
run_timer_softirq+0x1a/0x50 kernel/time/timer.c:2405
handle_softirqs+0x1ea/0x9b0 kernel/softirq.c:622
__do_softirq kernel/softirq.c:656 [inline]
invoke_softirq kernel/softirq.c:496 [inline]
__irq_exit_rcu+0x162/0x210 kernel/softirq.c:735
irq_exit_rcu+0x9/0x30 kernel/softirq.c:752
instr_sysvec_apic_timer_interrupt arch/x86/kernel/apic/apic.c:1062 [inline]
sysvec_apic_timer_interrupt+0xa3/0xc0 arch/x86/kernel/apic/apic.c:1062
</IRQ>
<TASK>
asm_sysvec_apic_timer_interrupt+0x1a/0x20 arch/x86/include/asm/idtentry.h:674
RIP: 0010:__raw_spin_unlock_irqrestore include/linux/spinlock_api_smp.h:179 [inline]
RIP: 0010:_raw_spin_unlock_irqrestore+0x31/0x80 kernel/locking/spinlock.c:198
Code: f5 53 48 8b 74 24 10 48 89 fb 48 83 c7 18 e8 f6 d0 27 f6 48 89 df e8 7e 20 28 f6 f7 c5 00 02 00 00 75 23 9c 58 f6 c4 02 75 37 <bf> 01 00 00 00 e8 05 2e 17 f6 65 8b 05 4e 57 a6 08 85 c0 74 16 5b
RSP: 0018:ffffc9000500fb10 EFLAGS: 00000246
RAX: 0000000000000006 RBX: ffff88823fff8740 RCX: 0000000000000040
RDX: 0000000000000000 RSI: ffffffff8e1a8186 RDI: ffffffff8c402680
RBP: 0000000000000246 R08: 0000000000000001 R09: 0000000000000000
R10: 0000000000000001 R11: 0000000000000000 R12: 0000000000140001
R13: 00000000000a8d20 R14: ffffffff81e6a290 R15: 0000000000000005
mark_free_pages kernel/power/snapshot.c:1252 [inline]
count_data_pages+0x7d/0xf0 kernel/power/snapshot.c:1417
hibernate_preallocate_memory+0x104/0x507 kernel/power/snapshot.c:1859
hibernation_snapshot+0x1e6/0x9c0 kernel/power/hibernate.c:430
hibernate.cold+0x17c/0x6a2 kernel/power/hibernate.c:811
state_store+0x1ff/0x240 kernel/power/main.c:821
kobj_attr_store+0x58/0x80 lib/kobject.c:840
sysfs_kf_write+0xf2/0x150 fs/sysfs/file.c:145
kernfs_fop_write_iter+0x3e0/0x5f0 fs/kernfs/file.c:345
new_sync_write fs/read_write.c:595 [inline]
vfs_write+0x6ac/0x1050 fs/read_write.c:687
ksys_write+0x12a/0x250 fs/read_write.c:739
do_syscall_x64 arch/x86/entry/syscall_64.c:63 [inline]
do_syscall_64+0x115/0x870 arch/x86/entry/syscall_64.c:94
entry_SYSCALL_64_after_hwframe+0x77/0x7f
RIP: 0033:0x7faa6b59e019
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:00007faa6c4cf028 EFLAGS: 00000246 ORIG_RAX: 0000000000000001
RAX: ffffffffffffffda RBX: 00007faa6b825fa0 RCX: 00007faa6b59e019
RDX: 0000000000000005 RSI: 0000200000000100 RDI: 0000000000000003
RBP: 00007faa6b63500c R08: 0000000000000000 R09: 0000000000000000
R10: 0000000000000000 R11: 0000000000000246 R12: 0000000000000000
R13: 00007faa6b826038 R14: 00007faa6b825fa0 R15: 00007ffe29482fa8
</TASK>
ODEBUG: object ffffc90000a08a70 is NOT on stack ffffc90005008000, but annotated.
------------[ cut here ]------------
1
WARNING: lib/debugobjects.c:672 at debug_object_is_on_stack lib/debugobjects.c:672 [inline], CPU#1: syz.4.93/7194
WARNING: lib/debugobjects.c:672 at lookup_object_or_alloc.part.0.cold+0x19/0x40 lib/debugobjects.c:705, CPU#1: syz.4.93/7194
Modules linked in:
CPU: 1 UID: 0 PID: 7194 Comm: syz.4.93 Tainted: G W syzkaller #0 PREEMPT(full)
Tainted: [W]=WARN
Hardware name: Google Google Compute Engine/Google Compute Engine, BIOS Google 07/16/2026
RIP: 0010:debug_object_is_on_stack lib/debugobjects.c:672 [inline]
RIP: 0010:lookup_object_or_alloc.part.0.cold+0x19/0x40 lib/debugobjects.c:705
Code: c4 60 5b 5d 41 5c 41 5d 41 5e 41 5f e9 49 18 88 0a 83 c5 01 89 2d 20 43 57 1a 4c 89 e6 48 c7 c7 c0 31 40 8c e8 f1 ef eb ff 90 <0f> 0b 90 e9 d2 03 f1 03 83 c5 01 89 2d ff 42 57 1a 49 39 c4 73 da
RSP: 0018:ffffc90000a08900 EFLAGS: 00010086
RAX: 0000000000000050 RBX: ffff888076281508 RCX: 0000000000000000
RDX: 0000000000000050 RSI: ffffffff81e8afd9 RDI: fffff52000141111
RBP: 0000000000000001 R08: 0000000000000005 R09: 0000000000000000
R10: 0000000000000102 R11: 205d314320202020 R12: ffffc90000a08a70
R13: ffff888033bc0000 R14: 0000000000000001 R15: 0000000000000000
FS: 00007faa6c4cf6c0(0000) GS:ffff888123ed8000(0000) knlGS:0000000000000000
CS: 0010 DS: 0000 ES: 0000 CR0: 0000000080050033
CR2: 0000200000000000 CR3: 00000000618ef000 CR4: 00000000003526f0
Call Trace:
<IRQ>
lookup_object_or_alloc lib/debugobjects.c:682 [inline]
__debug_object_init+0x2a9/0x3d0 lib/debugobjects.c:798
__synchronize_srcu+0x1c3/0x300 kernel/rcu/srcutree.c:1494
wakeup_source_remove drivers/base/power/wakeup.c:201 [inline]
wakeup_source_unregister.part.0+0x12c/0x540 drivers/base/power/wakeup.c:237
wakeup_source_unregister+0x1f/0x30 drivers/base/power/wakeup.c:236
mmc_host_classdev_release+0x42/0x120 drivers/mmc/core/host.c:69
device_release+0xd2/0x270 drivers/base/core.c:2636
kobject_cleanup lib/kobject.c:689 [inline]
kobject_release lib/kobject.c:720 [inline]
kref_put include/linux/kref.h:65 [inline]
kobject_put+0x1f7/0x640 lib/kobject.c:737
put_device+0x1f/0x30 drivers/base/core.c:3880
vub300_delete drivers/mmc/host/vub300.c:379 [inline]
kref_put include/linux/kref.h:65 [inline]
vub300_inactivity_timer_expired drivers/mmc/host/vub300.c:747 [inline]
vub300_inactivity_timer_expired+0x42f/0x550 drivers/mmc/host/vub300.c:742
call_timer_fn+0x19a/0x610 kernel/time/timer.c:1748
expire_timers kernel/time/timer.c:1799 [inline]
__run_timers+0x757/0xb00 kernel/time/timer.c:2374
__run_timer_base kernel/time/timer.c:2386 [inline]
__run_timer_base kernel/time/timer.c:2378 [inline]
run_timer_base+0x114/0x190 kernel/time/timer.c:2395
run_timer_softirq+0x1a/0x50 kernel/time/timer.c:2405
handle_softirqs+0x1ea/0x9b0 kernel/softirq.c:622
__do_softirq kernel/softirq.c:656 [inline]
invoke_softirq kernel/softirq.c:496 [inline]
__irq_exit_rcu+0x162/0x210 kernel/softirq.c:735
irq_exit_rcu+0x9/0x30 kernel/softirq.c:752
instr_sysvec_apic_timer_interrupt arch/x86/kernel/apic/apic.c:1062 [inline]
sysvec_apic_timer_interrupt+0xa3/0xc0 arch/x86/kernel/apic/apic.c:1062
</IRQ>
<TASK>
asm_sysvec_apic_timer_interrupt+0x1a/0x20 arch/x86/include/asm/idtentry.h:674
RIP: 0010:__raw_spin_unlock_irqrestore include/linux/spinlock_api_smp.h:179 [inline]
RIP: 0010:_raw_spin_unlock_irqrestore+0x31/0x80 kernel/locking/spinlock.c:198
Code: f5 53 48 8b 74 24 10 48 89 fb 48 83 c7 18 e8 f6 d0 27 f6 48 89 df e8 7e 20 28 f6 f7 c5 00 02 00 00 75 23 9c 58 f6 c4 02 75 37 <bf> 01 00 00 00 e8 05 2e 17 f6 65 8b 05 4e 57 a6 08 85 c0 74 16 5b
RSP: 0018:ffffc9000500fb10 EFLAGS: 00000246
RAX: 0000000000000006 RBX: ffff88823fff8740 RCX: 0000000000000040
RDX: 0000000000000000 RSI: ffffffff8e1a8186 RDI: ffffffff8c402680
RBP: 0000000000000246 R08: 0000000000000001 R09: 0000000000000000
R10: 0000000000000001 R11: 0000000000000000 R12: 0000000000140001
R13: 00000000000a8d20 R14: ffffffff81e6a290 R15: 0000000000000005
mark_free_pages kernel/power/snapshot.c:1252 [inline]
count_data_pages+0x7d/0xf0 kernel/power/snapshot.c:1417
hibernate_preallocate_memory+0x104/0x507 kernel/power/snapshot.c:1859
hibernation_snapshot+0x1e6/0x9c0 kernel/power/hibernate.c:430
hibernate.cold+0x17c/0x6a2 kernel/power/hibernate.c:811
state_store+0x1ff/0x240 kernel/power/main.c:821
kobj_attr_store+0x58/0x80 lib/kobject.c:840
sysfs_kf_write+0xf2/0x150 fs/sysfs/file.c:145
kernfs_fop_write_iter+0x3e0/0x5f0 fs/kernfs/file.c:345
new_sync_write fs/read_write.c:595 [inline]
vfs_write+0x6ac/0x1050 fs/read_write.c:687
ksys_write+0x12a/0x250 fs/read_write.c:739
do_syscall_x64 arch/x86/entry/syscall_64.c:63 [inline]
do_syscall_64+0x115/0x870 arch/x86/entry/syscall_64.c:94
entry_SYSCALL_64_after_hwframe+0x77/0x7f
RIP: 0033:0x7faa6b59e019
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:00007faa6c4cf028 EFLAGS: 00000246 ORIG_RAX: 0000000000000001
RAX: ffffffffffffffda RBX: 00007faa6b825fa0 RCX: 00007faa6b59e019
RDX: 0000000000000005 RSI: 0000200000000100 RDI: 0000000000000003
RBP: 00007faa6b63500c R08: 0000000000000000 R09: 0000000000000000
R10: 0000000000000000 R11: 0000000000000246 R12: 0000000000000000
R13: 00007faa6b826038 R14: 00007faa6b825fa0 R15: 00007ffe29482fa8
</TASK>
----------------
Code disassembly (best guess):
0: f5 cmc
1: 53 push %rbx
2: 48 8b 74 24 10 mov 0x10(%rsp),%rsi
7: 48 89 fb mov %rdi,%rbx
a: 48 83 c7 18 add $0x18,%rdi
e: e8 f6 d0 27 f6 call 0xf627d109
13: 48 89 df mov %rbx,%rdi
16: e8 7e 20 28 f6 call 0xf6282099
1b: f7 c5 00 02 00 00 test $0x200,%ebp
21: 75 23 jne 0x46
23: 9c pushf
24: 58 pop %rax
25: f6 c4 02 test $0x2,%ah
28: 75 37 jne 0x61
* 2a: bf 01 00 00 00 mov $0x1,%edi <-- trapping instruction
2f: e8 05 2e 17 f6 call 0xf6172e39
34: 65 8b 05 4e 57 a6 08 mov %gs:0x8a6574e(%rip),%eax # 0x8a65789
3b: 85 c0 test %eax,%eax
3d: 74 16 je 0x55
3f: 5b pop %rbx
Reply: An analysis of the bug report and the source code reveals the following:
**Root Cause & Mechanism:**
The bug is a sleep-in-atomic vulnerability that leads to stack corruption.
The `vub300` USB SDIO host controller driver sets up an inactivity timer (`vub300_inactivity_timer_expired()`), which executes in a softirq context. If the USB device is disconnected, the timer callback notices that `vub300->interface` is NULL and drops a reference using `kref_put()`.
If this is the last reference, it invokes the release function `vub300_delete()` directly in the softirq context. `vub300_delete()` calls `mmc_free_host()`, which eventually calls `wakeup_source_unregister()` and `synchronize_srcu()`.
`synchronize_srcu()` is a blocking function that calls `schedule()` to wait for a grace period.
**Exploitability:**
Calling `schedule()` from a softirq context is highly dangerous on architectures with dedicated per-CPU softirq stacks (like x86_64). When the thread sleeps, it leaves its call frames on the per-CPU softirq stack. If another softirq is raised on the same CPU while the thread is sleeping, the kernel will start executing the new softirq from the top of the softirq stack, completely overwriting the sleeping thread's stack frames.
When the `synchronize_srcu()` completion finishes and the original thread wakes up, it will resume execution with a corrupted stack. An attacker can carefully groom the softirq stack overwrites to control the return addresses, leading to a classic Return-Oriented Programming (ROP) control-flow hijack and privilege escalation. Therefore, this bug is exploitable.
**Trigger & Accessibility:**
The vulnerability is located in the `vub300` USB driver. Triggering it requires the ability to plug in a specific USB device (or emulate one), wait for the inactivity timer to be armed, and then disconnect the device so that the timer callback executes the cleanup code. This makes it a Peripheral Trigger.
Unprivileged users cannot trigger this bug remotely or from user-space without physical access to plug in a USB device (or without elevated privileges to use USB gadget subsystems like `dummy_hcd`).
**Conclusion:**
- **Exploitable:** Yes (Stack corruption leading to arbitrary code execution).
- **Denial of Service:** Yes (System crash/panic).
- **Peripheral Trigger:** Yes (Requires plugging/unplugging a USB device).
- **Unprivileged/User Namespace/Remote:** No.
|
| 6/2 |
2026/08/09 08:00 |
llm |
expert |
0m
Model:
Tokens:
input: 14527
output: 45
thoughts: 675
Thoughts: **Analyzing a Lockdep Warning**
Okay, so I've got this bug report with a lock state warning: `inconsistent {SOFTIRQ-ON-R} -> {IN-SOFTIRQ-W} usage`. The key seems to be around the `wakeup_srcu` lock, and it's flagged in `wakeup_srcu`. The stack trace tells the story: it looks like we're dealing with a timer (softirq context) calling into something that's hitting a lockdep issue. Specifically, `vub300_inactivity_timer_expired` is the culprit, which is being called from a timer.
The trace goes from `__synchronize_srcu` all the way down to `call_timer_fn`, indicating the timer context. The critical part is that `vub300_inactivity_timer_expired` calls `kref_put(&vub300->kref, vub300_delete)`. Now, `vub300_delete` is the function that's likely the source of the problem, so I need to check its implementation, since this is where things will break down. My gut says it will involve `put_device`, since we are dealing with device removal. Let's see... looking at the code for `vub300_delete` in `drivers/mmc/host/vub300.c` should clear things up.
|
| 7/2 |
2026/08/09 08:00 |
tool |
codesearch-definition-source |
0m
Args: map[ContextFile:drivers/mmc/host/vub300.c Name:vub300_delete]
Results: map[SourceCode: 369: static void vub300_delete(struct kref *kref)
370: { /* kref callback - softirq */
371: struct vub300_mmc_host *vub300 = kref_to_vub300_mmc_host(kref);
372: struct mmc_host *mmc = vub300->mmc;
373:
374: usb_free_urb(vub300->command_out_urb);
375: vub300->command_out_urb = NULL;
376: usb_free_urb(vub300->command_res_urb);
377: vub300->command_res_urb = NULL;
378: usb_put_dev(vub300->udev);
379: mmc_free_host(mmc);
380: /*
381: * and hence also frees vub300
382: * which is contained at the end of struct mmc
383: */
384: }
SourceFile:drivers/mmc/host/vub300.c]
|
| 8/2 |
2026/08/09 08:00 |
llm |
expert |
0m
Model:
Tokens:
input: 15904
output: 50
thoughts: 216
Thoughts: **Analyzing a Potential Softirq Blocking Issue in MMC Host Release**
Okay, so I'm digging into this MMC driver code and I've hit something that smells fishy. `mmc_free_host(mmc)` calls `put_device(&host->class_dev)`. That, in turn, cascades down to `mmc_host_classdev_release`, which ends up calling `wakeup_source_unregister`. The problem is that `wakeup_source_unregister` ultimately calls `wakeup_source_remove`, and *that* function executes `synchronize_srcu(&wakeup_srcu)`.
Here's where my alarm bells are going off. `synchronize_srcu` is a blocking call, and I see from the stack trace that it's being called from *within* a softirq context, specifically the timer callback `vub300_inactivity_timer_expired`. This is a classic recipe for a bug! Blocking within a softirq is a big no-no; itβs going to trigger a lockdep warning, and could potentially lead to a deadlock or even a system panic.
Wait a second⦠I'm seeing a comment within `vub300_delete` that states `/* kref callback - softirq */`. So the original author *knew* it was being called from softirq. Why would they introduce this block? This is going to require more investigation. Maybe there's a good reason that I am not seeing.
I need to confirm that `mmc_free_host` might block.
Let me go back and look at the `vub300_inactivity_timer_expired` code.
|
| 9/2 |
2026/08/09 08:00 |
tool |
codesearch-definition-source |
0m
Args: map[ContextFile:drivers/mmc/host/vub300.c Name:vub300_inactivity_timer_expired]
Results: map[SourceCode: 742: static void vub300_inactivity_timer_expired(struct timer_list *t)
743: { /* softirq */
744: struct vub300_mmc_host *vub300 = timer_container_of(vub300, t,
745: inactivity_timer);
746: if (!vub300->interface) {
747: kref_put(&vub300->kref, vub300_delete);
748: } else if (vub300->cmd) {
749: mod_timer(&vub300->inactivity_timer, jiffies + HZ);
750: } else {
751: vub300_queue_dead_work(vub300);
752: mod_timer(&vub300->inactivity_timer, jiffies + HZ);
753: }
754: }
SourceFile:drivers/mmc/host/vub300.c]
|
| 10/2 |
2026/08/09 08:00 |
llm |
expert |
0m
Model:
Tokens:
input: 15951
output: 40
thoughts: 814
Thoughts: **Analyzing the Bug Report: Inconsistent Lock State and Debug Objects Warning**
Okay, so I'm looking at this bug report, and I see two red flags immediately: an "inconsistent lock state" warning and a "debugobjects" warning. The inconsistent lock state, specifically, mentions `wakeup_srcu` being used in `SOFTIRQ-ON-R` and `IN-SOFTIRQ-W` contexts. That's a classic sign of trouble. The debugobjects warning points to `debug_object_is_on_stack` at `lookup_object_or_alloc`. The stack trace reveals some interesting clues.
The stack trace of the debugobjects warning leads me to the root cause: the issue lies within `vub300_delete`, specifically how it calls `mmc_free_host(mmc)`. From the trace, `mmc_free_host` seems to be calling `put_device(&host->class_dev)`. This further triggers a call to `wakeup_source_unregister`, which, in turn, calls `synchronize_srcu(&wakeup_srcu)`.
Here's the problem: `vub300_delete` is being called from `vub300_inactivity_timer_expired`, which is a timer callback. Timer callbacks run in softirq context. The crux of the issue is that `synchronize_srcu` can sleep, and critically, it **shouldn't** be called from within a softirq context. That's the bug. It's a blocking call, and calling it from a softirq is a recipe for deadlocks or other nasty interactions.
Let me think... Right, `vub300_delete` is called indirectly through a kref release. The `vub300_inactivity_timer_expired` is what drops this kref. This means the timer callback is freeing up resources. That makes sense. Let's move on to check the implementation of `mmc_free_host`.
|
| 11/2 |
2026/08/09 08:00 |
tool |
codesearch-definition-source |
0m
Args: map[ContextFile:drivers/mmc/core/host.c Name:mmc_free_host]
Results: map[SourceCode: 692: /**
693: * mmc_free_host - free the host structure
694: * @host: mmc host
695: *
696: * Free the host once all references to it have been dropped.
697: */
698: void mmc_free_host(struct mmc_host *host)
699: {
700: cancel_delayed_work_sync(&host->detect);
701: mmc_pwrseq_free(host);
702: put_device(&host->class_dev);
703: }
SourceFile:drivers/mmc/core/host.c]
|
| 12/2 |
2026/08/09 08:00 |
llm |
expert |
0m
Model:
Tokens:
input: 18337
output: 45
thoughts: 134
Thoughts: **Analyzing the VUB300 Driver Bug**
Okay, so `cancel_delayed_work_sync` is a blocking call, which means it definitely can't be invoked from a softirq context. That's a fundamental constraint. And it's looking like `mmc_free_host` relies on `cancel_delayed_work_sync`. This puts us right in the danger zone, because `mmc_free_host` is being called from a timer callback, which, you know, executes within softirq context. This points directly to a bug in the `vub300` driver. Specifically, it seems the driver's dropping the final reference to the `vub300` device within that timer callback, and that's *when* `mmc_free_host` is called.
Now, let's see, what is the connection with USB? Right, I should be able to deduce if this is a USB device or not. Let me confirm, ah yes, `vub300_mmc_host` contains `struct usb_device *udev`, so, yes, itβs a USB device.
Let's drill into the driver initialization, specifically, the `vub300_probe` function. I need to understand the sequencing and reference management here to pinpoint the exact location and nature of this reference leak and blocking call within the softirq context.
|
| 13/2 |
2026/08/09 08:01 |
tool |
codesearch-definition-source |
0m
Args: map[ContextFile:drivers/mmc/host/vub300.c Name:vub300_probe]
Results: map[SourceCode:2092: static int vub300_probe(struct usb_interface *interface,
2093: const struct usb_device_id *id)
2094: { /* NOT irq */
2095: struct vub300_mmc_host *vub300;
2096: struct usb_host_interface *iface_desc;
2097: struct usb_device *udev = usb_get_dev(interface_to_usbdev(interface));
2098: int i;
2099: int retval = -ENOMEM;
2100: struct urb *command_out_urb;
2101: struct urb *command_res_urb;
2102: struct mmc_host *mmc;
2103: char manufacturer[48];
2104: char product[32];
2105: char serial_number[32];
2106: usb_string(udev, udev->descriptor.iManufacturer, manufacturer,
2107: sizeof(manufacturer));
2108: usb_string(udev, udev->descriptor.iProduct, product, sizeof(product));
2109: usb_string(udev, udev->descriptor.iSerialNumber, serial_number,
2110: sizeof(serial_number));
2111: dev_info(&udev->dev, "probing VID:PID(%04X:%04X) %s %s %s\n",
2112: le16_to_cpu(udev->descriptor.idVendor),
2113: le16_to_cpu(udev->descriptor.idProduct),
2114: manufacturer, product, serial_number);
2115: command_out_urb = usb_alloc_urb(0, GFP_KERNEL);
2116: if (!command_out_urb) {
2117: retval = -ENOMEM;
2118: goto err_put_udev;
2119: }
2120: command_res_urb = usb_alloc_urb(0, GFP_KERNEL);
2121: if (!command_res_urb) {
2122: retval = -ENOMEM;
2123: goto err_free_out_urb;
2124: }
2125: /* this also allocates memory for our VUB300 mmc host device */
2126: mmc = mmc_alloc_host(sizeof(*vub300), &udev->dev);
2127: if (!mmc) {
2128: retval = -ENOMEM;
2129: dev_err(&udev->dev, "not enough memory for the mmc_host\n");
2130: goto err_free_res_urb;
2131: }
2132: /* MMC core transfer sizes tunable parameters */
2133: mmc->caps = 0;
2134: if (!force_1_bit_data_xfers)
2135: mmc->caps |= MMC_CAP_4_BIT_DATA;
2136: if (!force_polling_for_irqs)
2137: mmc->caps |= MMC_CAP_SDIO_IRQ;
2138: mmc->caps &= ~MMC_CAP_NEEDS_POLL;
2139: /*
2140: * MMC_CAP_NEEDS_POLL causes core.c:mmc_rescan() to poll
2141: * for devices which results in spurious CMD7's being
2142: * issued which stops some SDIO cards from working
2143: */
2144: if (limit_speed_to_24_MHz) {
2145: mmc->caps |= MMC_CAP_MMC_HIGHSPEED;
2146: mmc->caps |= MMC_CAP_SD_HIGHSPEED;
2147: mmc->f_max = 24000000;
2148: dev_info(&udev->dev, "limiting SDIO speed to 24_MHz\n");
2149: } else {
2150: mmc->caps |= MMC_CAP_MMC_HIGHSPEED;
2151: mmc->caps |= MMC_CAP_SD_HIGHSPEED;
2152: mmc->f_max = 48000000;
2153: }
2154: mmc->f_min = 200000;
2155: mmc->max_blk_count = 511;
2156: mmc->max_blk_size = 512;
2157: mmc->max_segs = 128;
2158: if (force_max_req_size)
2159: mmc->max_req_size = force_max_req_size * 1024;
2160: else
2161: mmc->max_req_size = 64 * 1024;
2162: mmc->max_seg_size = mmc->max_req_size;
2163: mmc->ocr_avail = 0;
2164: mmc->ocr_avail |= MMC_VDD_165_195;
2165: mmc->ocr_avail |= MMC_VDD_20_21;
2166: mmc->ocr_avail |= MMC_VDD_21_22;
2167: mmc->ocr_avail |= MMC_VDD_22_23;
2168: mmc->ocr_avail |= MMC_VDD_23_24;
2169: mmc->ocr_avail |= MMC_VDD_24_25;
2170: mmc->ocr_avail |= MMC_VDD_25_26;
2171: mmc->ocr_avail |= MMC_VDD_26_27;
2172: mmc->ocr_avail |= MMC_VDD_27_28;
2173: mmc->ocr_avail |= MMC_VDD_28_29;
2174: mmc->ocr_avail |= MMC_VDD_29_30;
2175: mmc->ocr_avail |= MMC_VDD_30_31;
2176: mmc->ocr_avail |= MMC_VDD_31_32;
2177: mmc->ocr_avail |= MMC_VDD_32_33;
2178: mmc->ocr_avail |= MMC_VDD_33_34;
2179: mmc->ocr_avail |= MMC_VDD_34_35;
2180: mmc->ocr_avail |= MMC_VDD_35_36;
2181: mmc->ops = &vub300_mmc_ops;
2182: vub300 = mmc_priv(mmc);
2183: vub300->mmc = mmc;
2184: vub300->card_powered = 0;
2185: vub300->bus_width = 0;
2186: vub300->cmnd.head.block_size[0] = 0x00;
2187: vub300->cmnd.head.block_size[1] = 0x00;
2188: vub300->app_spec = 0;
2189: mutex_init(&vub300->cmd_mutex);
2190: mutex_init(&vub300->irq_mutex);
2191: vub300->command_out_urb = command_out_urb;
2192: vub300->command_res_urb = command_res_urb;
2193: vub300->usb_timed_out = 0;
2194: vub300->dynamic_register_count = 0;
2195:
2196: for (i = 0; i < ARRAY_SIZE(vub300->fn); i++) {
2197: vub300->fn[i].offload_point = 0;
2198: vub300->fn[i].offload_count = 0;
2199: }
2200:
2201: vub300->total_offload_count = 0;
2202: vub300->irq_enabled = 0;
2203: vub300->irq_disabled = 0;
2204: vub300->irqs_queued = 0;
2205:
2206: for (i = 0; i < ARRAY_SIZE(vub300->sdio_register); i++)
2207: vub300->sdio_register[i++].activate = 0;
2208:
2209: vub300->udev = udev;
2210: vub300->interface = interface;
2211: vub300->cmnd_res_ep = 0;
2212: vub300->cmnd_out_ep = 0;
2213: vub300->data_inp_ep = 0;
2214: vub300->data_out_ep = 0;
2215:
2216: for (i = 0; i < ARRAY_SIZE(vub300->fbs); i++)
2217: vub300->fbs[i] = 512;
2218:
2219: /*
2220: * set up the endpoint information
2221: *
2222: * use the first pair of bulk-in and bulk-out
2223: * endpoints for Command/Response+Interrupt
2224: *
2225: * use the second pair of bulk-in and bulk-out
2226: * endpoints for Data In/Out
2227: */
2228: vub300->large_usb_packets = 0;
2229: iface_desc = interface->cur_altsetting;
2230: for (i = 0; i < iface_desc->desc.bNumEndpoints; ++i) {
2231: struct usb_endpoint_descriptor *endpoint =
2232: &iface_desc->endpoint[i].desc;
2233: dev_info(&vub300->udev->dev,
2234: "vub300 testing %s EndPoint(%d) %02X\n",
2235: usb_endpoint_is_bulk_in(endpoint) ? "BULK IN" :
2236: usb_endpoint_is_bulk_out(endpoint) ? "BULK OUT" :
2237: "UNKNOWN", i, endpoint->bEndpointAddress);
2238: if (endpoint->wMaxPacketSize > 64)
2239: vub300->large_usb_packets = 1;
2240: if (usb_endpoint_is_bulk_in(endpoint)) {
2241: if (!vub300->cmnd_res_ep) {
2242: vub300->cmnd_res_ep =
2243: endpoint->bEndpointAddress;
2244: } else if (!vub300->data_inp_ep) {
2245: vub300->data_inp_ep =
2246: endpoint->bEndpointAddress;
2247: } else {
2248: dev_warn(&vub300->udev->dev,
2249: "ignoring"
2250: " unexpected bulk_in endpoint");
2251: }
2252: } else if (usb_endpoint_is_bulk_out(endpoint)) {
2253: if (!vub300->cmnd_out_ep) {
2254: vub300->cmnd_out_ep =
2255: endpoint->bEndpointAddress;
2256: } else if (!vub300->data_out_ep) {
2257: vub300->data_out_ep =
2258: endpoint->bEndpointAddress;
2259: } else {
2260: dev_warn(&vub300->udev->dev,
2261: "ignoring"
2262: " unexpected bulk_out endpoint");
2263: }
2264: } else {
2265: dev_warn(&vub300->udev->dev,
2266: "vub300 ignoring EndPoint(%d) %02X", i,
2267: endpoint->bEndpointAddress);
2268: }
2269: }
2270: if (vub300->cmnd_res_ep && vub300->cmnd_out_ep &&
2271: vub300->data_inp_ep && vub300->data_out_ep) {
2272: dev_info(&vub300->udev->dev,
2273: "vub300 %s packets"
2274: " using EndPoints %02X %02X %02X %02X\n",
2275: vub300->large_usb_packets ? "LARGE" : "SMALL",
2276: vub300->cmnd_out_ep, vub300->cmnd_res_ep,
2277: vub300->data_out_ep, vub300->data_inp_ep);
2278: /* we have the expected EndPoints */
2279: } else {
2280: dev_err(&vub300->udev->dev,
2281: "Could not find two sets of bulk-in/out endpoint pairs\n");
2282: retval = -EINVAL;
2283: goto err_free_host;
2284: }
2285: retval =
2286: usb_control_msg(vub300->udev, usb_rcvctrlpipe(vub300->udev, 0),
2287: GET_HC_INF0,
2288: USB_DIR_IN | USB_TYPE_VENDOR | USB_RECIP_DEVICE,
2289: 0x0000, 0x0000, &vub300->hc_info,
2290: sizeof(vub300->hc_info), 1000);
2291: if (retval < 0)
2292: goto err_free_host;
2293: retval =
2294: usb_control_msg(vub300->udev, usb_sndctrlpipe(vub300->udev, 0),
2295: SET_ROM_WAIT_STATES,
2296: USB_DIR_OUT | USB_TYPE_VENDOR | USB_RECIP_DEVICE,
2297: firmware_rom_wait_states, 0x0000, NULL, 0, 1000);
2298: if (retval < 0)
2299: goto err_free_host;
2300: dev_info(&vub300->udev->dev,
2301: "operating_mode = %s %s %d MHz %s %d byte USB packets\n",
2302: (mmc->caps & MMC_CAP_SDIO_IRQ) ? "IRQs" : "POLL",
2303: (mmc->caps & MMC_CAP_4_BIT_DATA) ? "4-bit" : "1-bit",
2304: mmc->f_max / 1000000,
2305: pad_input_to_usb_pkt ? "padding input data to" : "with",
2306: vub300->large_usb_packets ? 512 : 64);
2307: retval =
2308: usb_control_msg(vub300->udev, usb_rcvctrlpipe(vub300->udev, 0),
2309: GET_SYSTEM_PORT_STATUS,
2310: USB_DIR_IN | USB_TYPE_VENDOR | USB_RECIP_DEVICE,
2311: 0x0000, 0x0000, &vub300->system_port_status,
2312: sizeof(vub300->system_port_status), 1000);
2313: if (retval < 0) {
2314: goto err_free_host;
2315: } else if (sizeof(vub300->system_port_status) == retval) {
2316: vub300->card_present =
2317: (0x0001 & vub300->system_port_status.port_flags) ? 1 : 0;
2318: vub300->read_only =
2319: (0x0010 & vub300->system_port_status.port_flags) ? 1 : 0;
2320: } else {
2321: retval = -EINVAL;
2322: goto err_free_host;
2323: }
2324: usb_set_intfdata(interface, vub300);
2325: INIT_DELAYED_WORK(&vub300->pollwork, vub300_pollwork_thread);
2326: INIT_WORK(&vub300->cmndwork, vub300_cmndwork_thread);
2327: INIT_WORK(&vub300->deadwork, vub300_deadwork_thread);
2328: kref_init(&vub300->kref);
2329: timer_setup(&vub300->sg_transfer_timer, vub300_sg_timed_out, 0);
2330: kref_get(&vub300->kref);
2331: timer_setup(&vub300->inactivity_timer,
2332: vub300_inactivity_timer_expired, 0);
2333: vub300->inactivity_timer.expires = jiffies + HZ;
2334: add_timer(&vub300->inactivity_timer);
2335: if (vub300->card_present)
2336: dev_info(&vub300->udev->dev,
2337: "USB vub300 remote SDIO host controller[%d]"
2338: "connected with SD/SDIO card inserted\n",
2339: interface_to_InterfaceNumber(interface));
2340: else
2341: dev_info(&vub300->udev->dev,
2342: "USB vub300 remote SDIO host controller[%d]"
2343: "connected with no SD/SDIO card inserted\n",
2344: interface_to_InterfaceNumber(interface));
2345: retval = mmc_add_host(mmc);
2346: if (retval)
2347: goto err_stop_io;
2348:
2349: return 0;
2350:
2351: err_stop_io:
2352: vub300->interface = NULL;
2353: kref_put(&vub300->kref, vub300_delete);
2354:
2355: return retval;
2356:
2357: err_free_host:
2358: mmc_free_host(mmc);
2359: /*
2360: * and hence also frees vub300
2361: * which is contained at the end of struct mmc
2362: */
2363: err_free_res_urb:
2364: usb_free_urb(command_res_urb);
2365: err_free_out_urb:
2366: usb_free_urb(command_out_urb);
2367: err_put_udev:
2368: usb_put_dev(udev);
2369:
2370: return retval;
2371: }
SourceFile:drivers/mmc/host/vub300.c]
|
| 14/2 |
2026/08/09 08:01 |
llm |
expert |
0m
Model:
Tokens:
input: 24343
output: 39
thoughts: 1488
Thoughts: **Vulnerability Analysis of the `vub300` USB Driver**
Okay, so I've got this `vub300` driver, specifically for that "USB vub300 remote SDIO host controller". The attack vector here is clear: a malicious USB device. Plugging it in triggers this bug. The core of the problem lies in the `vub300_inactivity_timer_expired` function, which calls `kref_put` and eventually cascades down to `mmc_free_host`. This function, crucially, calls `put_device`, which eventually hits `synchronize_srcu`.
The issue? `synchronize_srcu` blocks, but it's getting called from a softirq context. This is a huge red flag. Softirqs, you know, they're supposed to be lightweight, interrupt-friendly. Blocking in a softirq is a recipe for disaster: lockdep warnings, deadlocks, even a kernel panic if `CONFIG_DEBUG_ATOMIC_SLEEP` is enabled. In production, even without the panic, I'm expecting deadlocks β that softirq could be interrupting a task holding a necessary lock, or it could block the CPU from processing other softirqs.
Wait, looking closer, `synchronize_srcu` calls `wait_for_completion`, which calls `schedule()`. Hold on a minute. Calling `schedule()` from a softirq? That's a textbook "scheduling while atomic" panic!
Let me confirm that. Yes, `synchronize_srcu` calls `__synchronize_srcu` which calls `wait_for_completion` which calls `schedule_timeout`, which in turn, calls `schedule()`. It calls `schedule()`. And `schedule()` does a `preempt_count()` check. Non-zero, because we're in softirq. It calls `schedule_bug()`, prints the panic message, and might even panic (if `panic_on_warn` is set). Even if it doesn't panic, it's still switching tasks.
This is critical: task switching from softirq. That's a dangerous path, full of potential crashes and deadlocks.
Is this exploitable for privilege escalation or memory corruption? No, this is a Denial of Service. It's about causing a sleep in an atomic context.
Can unprivileged users trigger this? It's a USB device, so it's a "Peripheral Trigger." No user namespace access. No network trigger either.
What about a VM guest? Yes, potentially, if the guest has access to the USB device or is emulating one. But the bug is in the guest kernel if the guest has the device. If the bug is in the host kernel, the guest cannot trigger it. Therefore, this isn't a VM guest trigger.
Let's summarize:
* Exploitable: No (DoS).
* Denial of Service: Yes, potential for panic/deadlock.
* Unprivileged Access: No (USB device required).
Now, I see a debugobjects warning: this is interesting. The warning is from the debug objects subsystem. Let's dig deeper:
```
ODEBUG: object ffffc90000a08a70 is NOT on stack ffffc90005008000, but annotated.
------------[ cut here ]------------
WARNING: lib/debugobjects.c:672 at debug_object_is_on_stack lib/debugobjects.c:672 [inline], CPU#1: syz.4.93/7194
WARNING: lib/debugobjects.c:672 at lookup_object_or_alloc.part.0.cold+0x19/0x40 lib/debugobjects.c:705, CPU#1: syz.4.93/7194
```
This warning is because `__synchronize_srcu` is allocating and initializing the completion object on the stack:
```c
struct rcu_synchronize rcu;
init_rcu_head_on_stack(&rcu.head);
init_completion(&rcu.completion);
```
But, `__synchronize_srcu` is called from softirq context, so the stack is the IRQ stack. In x86_64, softirqs run on a separate stack.
So, the warning from `debug_object_is_on_stack` is caused because `__synchronize_srcu` uses stack space, specifically the IRQ stack.
|
| 15/2 |
2026/08/09 08:01 |
tool |
codesearch-definition-source |
0m
Args: map[ContextFile:lib/debugobjects.c Name:debug_object_is_on_stack]
Results: map[SourceCode: 652: static void debug_object_is_on_stack(void *addr, int onstack)
653: {
654: int is_on_stack;
655: static int limit;
656:
657: if (limit > 4)
658: return;
659:
660: is_on_stack = object_is_on_stack(addr);
661: if (is_on_stack == onstack)
662: return;
663:
664: limit++;
665: if (is_on_stack)
666: pr_warn("object %p is on stack %p, but NOT annotated.\n", addr,
667: task_stack_page(current));
668: else
669: pr_warn("object %p is NOT on stack %p, but annotated.\n", addr,
670: task_stack_page(current));
671:
672: WARN_ON(1);
673: }
SourceFile:lib/debugobjects.c]
|
| 16/2 |
2026/08/09 08:01 |
llm |
expert |
0m
Model:
Tokens:
input: 23877
output: 45
thoughts: 0
|
| 17/2 |
2026/08/09 08:01 |
tool |
codesearch-definition-source |
0m
Args: map[ContextFile:drivers/mmc/host/vub300.c Name:vub300_disconnect]
Results: map[SourceCode:2373: static void vub300_disconnect(struct usb_interface *interface)
2374: { /* NOT irq */
2375: struct vub300_mmc_host *vub300 = usb_get_intfdata(interface);
2376: if (!vub300 || !vub300->mmc) {
2377: return;
2378: } else {
2379: struct mmc_host *mmc = vub300->mmc;
2380: if (!vub300->mmc) {
2381: return;
2382: } else {
2383: int ifnum = interface_to_InterfaceNumber(interface);
2384: usb_set_intfdata(interface, NULL);
2385: /* prevent more I/O from starting */
2386: vub300->interface = NULL;
2387: mmc_remove_host(mmc);
2388: kref_put(&vub300->kref, vub300_delete);
2389: pr_info("USB vub300 remote SDIO host controller[%d]"
2390: " now disconnected", ifnum);
2391: return;
2392: }
2393: }
2394: }
SourceFile:drivers/mmc/host/vub300.c]
|
| 18/2 |
2026/08/09 08:01 |
llm |
expert |
0m
Model:
Tokens:
input: 24328
output: 48
thoughts: 0
|
| 19/2 |
2026/08/09 08:02 |
tool |
codesearch-definition-source |
0m
Args: map[ContextFile:drivers/mmc/host/vub300.c Name:vub300_mmc_host]
Results: map[SourceCode: 296: struct vub300_mmc_host {
297: struct usb_device *udev;
298: struct usb_interface *interface;
299: struct kref kref;
300: struct mutex cmd_mutex;
301: struct mutex irq_mutex;
302: char vub_name[3 + (9 * 8) + 4 + 1]; /* max of 7 sdio fn's */
303: u8 cmnd_out_ep; /* EndPoint for commands */
304: u8 cmnd_res_ep; /* EndPoint for responses */
305: u8 data_out_ep; /* EndPoint for out data */
306: u8 data_inp_ep; /* EndPoint for inp data */
307: bool card_powered;
308: bool card_present;
309: bool read_only;
310: bool large_usb_packets;
311: bool app_spec; /* ApplicationSpecific */
312: bool irq_enabled; /* by the MMC CORE */
313: bool irq_disabled; /* in the firmware */
314: unsigned bus_width:4;
315: u8 total_offload_count;
316: u8 dynamic_register_count;
317: u8 resp_len;
318: u32 datasize;
319: int errors;
320: int usb_transport_fail;
321: int usb_timed_out;
322: int irqs_queued;
323: struct sdio_register sdio_register[16];
324: struct offload_interrupt_function_register {
325: #define MAXREGBITS 4
326: #define MAXREGS (1<<MAXREGBITS)
327: #define MAXREGMASK (MAXREGS-1)
328: u8 offload_count;
329: u32 offload_point;
330: struct offload_registers_access reg[MAXREGS];
331: } fn[8];
332: u16 fbs[8]; /* Function Block Size */
333: struct mmc_command *cmd;
334: struct mmc_request *req;
335: struct mmc_data *data;
336: struct mmc_host *mmc;
337: struct urb *urb;
338: struct urb *command_out_urb;
339: struct urb *command_res_urb;
340: struct completion command_complete;
341: struct completion irqpoll_complete;
342: union sd_command cmnd;
343: union sd_response resp;
344: struct timer_list sg_transfer_timer;
345: struct usb_sg_request sg_request;
346: struct timer_list inactivity_timer;
347: struct work_struct deadwork;
348: struct work_struct cmndwork;
349: struct delayed_work pollwork;
350: struct host_controller_info hc_info;
351: struct sd_status_header system_port_status;
352: u8 padded_buffer[64];
353: };
SourceFile:drivers/mmc/host/vub300.c]
|
| 20/2 |
2026/08/09 08:02 |
llm |
expert |
0m
Model:
Tokens:
input: 25337
output: 49
thoughts: 0
|
| 21/2 |
2026/08/09 08:02 |
tool |
codesearch-definition-source |
0m
Args: map[ContextFile:drivers/mmc/host/vub300.c Name:vub300_queue_dead_work]
Results: map[SourceCode: 426: static void vub300_queue_dead_work(struct vub300_mmc_host *vub300)
427: {
428: kref_get(&vub300->kref);
429: if (queue_work(deadworkqueue, &vub300->deadwork)) {
430: /*
431: * then the deadworkqueue was not previously
432: * running and the above get ref is obvious
433: * required and will be put when the thread
434: * terminates by a specific call
435: */
436: } else {
437: /*
438: * the deadworkqueue was already running from
439: * a previous invocation and thus to keep the
440: * kref counts correct we must undo the get
441: */
442: kref_put(&vub300->kref, vub300_delete);
443: }
444: }
SourceFile:drivers/mmc/host/vub300.c]
|
| 22/2 |
2026/08/09 08:02 |
llm |
expert |
0m
Model:
Tokens:
input: 29043
output: 43
thoughts: 6336
Thoughts: **Analysis of the vub300 Driver Inactivity Timer and Potential Deadlock**
Alright, let's break this down. The core issue revolves around the `vub300_inactivity_timer_expired` function and its interaction with the `vub300_disconnect` function, particularly when dealing with the USB device being disconnected or the driver being unbound. The timer is armed during device probe and re-armed based on activity. The core problem emerges because `vub300_disconnect` doesn't `del_timer_sync` when it unlinks the device, it just drops a kref.
Hereβs the thought process:
* **The Bug:** The bug is within `vub300_inactivity_timer_expired` and its kref management. It's a classic sleep-in-atomic context scenario, or more precisely a potential deadlock.
* **The Scenario:** If the timer is running and the USB device is unplugged or the driver is unbound, `vub300_disconnect` will set `vub300->interface = NULL` and drop a kref. If the timer then fires after `vub300->interface` is set to NULL, it will drop the last kref, which calls `vub300_delete` in softirq context. This is what triggers the vulnerability.
* **The Problem:** The `synchronize_srcu` is called from an interrupt (softirq) context.
* **The Lock:** Now, the critical element here is the `wakeup_srcu` read lock. It is read-locked by a task that's triggered by sysfs to suspend, but it can also be read-locked by other system processes. The system can be suspended using `/sys/power/state`. If the interrupt/timer fires while any read lock exists, a deadlock can occur.
* **The Deadlock:** If the timer fires and calls `synchronize_srcu(&wakeup_srcu)` when a task holds the read lock, this results in a deadlock, because `synchronize_srcu` waits for readers and will never release.
* **The Trigger:** A peripheral device such as USB must be unplugged or removed. Thus, this is triggered by user interactions with the hardware.
* **The Exploit:** This is not exploitable for privilege escalation; it's a denial of service (DoS) vulnerability that can hang the CPU.
* **User Access:** An unprivileged user can't trigger this directly, as writing to `/sys/power/state` or unbinding the driver requires root or CAP\_SYS\_ADMIN, or that the process is in a user namespace that allows unbinding.
* **The Root Cause:** The race condition happens because `vub300_disconnect` doesn't synchronize properly with the timer, and the timer is never deleted.
* **Use-After-Free?** There's no Use-After-Free, because the driver manages the kref correctly, meaning the device memory itself isn't freed prematurely, just the calls to `synchronize_srcu` within a softirq which may cause deadlock.
* **Summary:** The problem is a sleep-in-atomic context, that can cause a deadlock. It is triggered when the timer fires after a USB device is disconnected or the driver is unbound, but while other processes or kernel code is read-locking `wakeup_srcu`.
Final Assessment: This is a peripheral DoS issue, not exploitable for privilege escalation and not reachable by unprivileged user namespaces.
|
| 23/2 |
2026/08/09 08:03 |
tool |
codesearch-find-references |
0m
Args: map[ContextFile:drivers/base/power/wakeup.c IncludeSnippetLines:0 Name:wakeup_srcu]
Results: map[References:<nil> TruncatedOutput:false]
|
| 24/2 |
2026/08/09 08:03 |
llm |
expert |
0m
Model:
Tokens:
input: 36213
output: 25
thoughts: 22
Thoughts: Here's my thought process, summarized as you requested:
**Locating the Definition of `wakeup_srcu`**
Okay, so I need to understand where `wakeup_srcu` is defined. Let me just quickly trace that... alright, a quick grep within the kernel source... aha! It looks like `wakeup_srcu` is defined in `drivers/base/power/wakeup.c`. That's a good starting point. I can now investigate that specific file for its function within the power management subsystem. This is probably related to device wakeup events, so I'll see how it interfaces with the broader power management infrastructure.
|
| 25/2 |
2026/08/09 08:03 |
tool |
grepper |
0m
Args: map[Expression:srcu_read_lock.*wakeup_srcu]
Results: map[Output:drivers/base/power/wakeup.c-23- list_for_each_entry_rcu(pos, head, member, \
drivers/base/power/wakeup.c:24: srcu_read_lock_held(&wakeup_srcu))
drivers/base/power/wakeup.c-25-/*
--
drivers/base/power/wakeup.c=252=int wakeup_sources_read_lock(void)
drivers/base/power/wakeup.c-253-{
drivers/base/power/wakeup.c:254: return srcu_read_lock(&wakeup_srcu);
drivers/base/power/wakeup.c-255-}
--
drivers/base/power/wakeup.c=396=void device_wakeup_arm_wake_irqs(void)
--
drivers/base/power/wakeup.c-400-
drivers/base/power/wakeup.c:401: srcuidx = srcu_read_lock(&wakeup_srcu);
drivers/base/power/wakeup.c-402- list_for_each_entry_rcu_locked(ws, &wakeup_sources, entry)
--
drivers/base/power/wakeup.c=412=void device_wakeup_disarm_wake_irqs(void)
--
drivers/base/power/wakeup.c-416-
drivers/base/power/wakeup.c:417: srcuidx = srcu_read_lock(&wakeup_srcu);
drivers/base/power/wakeup.c-418- list_for_each_entry_rcu_locked(ws, &wakeup_sources, entry)
--
drivers/base/power/wakeup.c=837=void pm_print_active_wakeup_sources(void)
--
drivers/base/power/wakeup.c-842-
drivers/base/power/wakeup.c:843: srcuidx = srcu_read_lock(&wakeup_srcu);
drivers/base/power/wakeup.c-844- list_for_each_entry_rcu_locked(ws, &wakeup_sources, entry) {
--
drivers/base/power/wakeup.c=1017=void pm_wakep_autosleep_enabled(bool set)
--
drivers/base/power/wakeup.c-1022-
drivers/base/power/wakeup.c:1023: srcuidx = srcu_read_lock(&wakeup_srcu);
drivers/base/power/wakeup.c-1024- list_for_each_entry_rcu_locked(ws, &wakeup_sources, entry) {
--
drivers/base/power/wakeup.c=1089=static void *wakeup_sources_stats_seq_start(struct seq_file *m,
--
drivers/base/power/wakeup.c-1101-
drivers/base/power/wakeup.c:1102: *srcuidx = srcu_read_lock(&wakeup_srcu);
drivers/base/power/wakeup.c-1103- list_for_each_entry_rcu_locked(ws, &wakeup_sources, entry) {
]
|
| 26/2 |
2026/08/09 08:03 |
llm |
expert |
1m
Model:
Tokens:
input: 37179
output: 33
thoughts: 725
Thoughts: **My Analysis of the USB Disconnect Vulnerability**
Okay, so here's what I've got. The root of the problem seems to be `synchronize_srcu` being called from a softirq context, specifically within the `vub300_disconnect` function. My initial thought was that it was tied to root operations since functions like `wakeup_sources_stats_seq_start`, `pm_print_active_wakeup_sources`, and `pm_wakep_autosleep_enabled`, are all involved and require root permissions or access to debugfs. Those are key, but not the only path.
But wait, a softirq calling `synchronize_srcu` is inherently problematic. Even without the deadlock it may cause, it will trigger lockdep warnings, "scheduling while atomic" warnings, and potentially panic if `panic_on_warn` is set. So, regardless, we have a denial-of-service (DoS) situation. The key is how to trigger this without needing root.
The crucial point is that a timer associated with the USB device fires automatically. Specifically, a device connected to the USB bus calls a timer every second. If the device is unplugged, the timer will fire one last time. This then calls `synchronize_srcu` in a softirq context. This is the first trigger. So, an unprivileged user with physical access can cause a DoS by simply plugging and unplugging a USB device. The `schedule()` call from within that context is the root of the problem. That's a classic peripheral trigger for exploitation.
Now, let's explore if this is exploitable in a more malicious way. Can a malicious USB device simulate a disconnect? The answer is yes. A rogue device can detach itself from the bus, essentially triggering the same sequence of events. So a malicious device can plug in, probe, then immediately detach to trigger this timer.
This means the vulnerability is definitely a peripheral trigger that is exploitable.
Now, how *bad* is this? The real danger is the `schedule()` call in the softirq context. The kernel checks to see if we are in atomic context, then, even though we are, it calls `schedule`. This is the crux. It's the stack corruption that makes it exploitable! Softirqs run on the IRQ stack. When `schedule()` switches tasks, it saves the CPU state on that same IRQ stack. When the initial task is scheduled back, it reloads the CPU state from that corrupted stack. If another interrupt happens in the meantime, it overwrites the IRQ stack, corrupting the saved state. Then the task is scheduled back. It will pop corrupted registers and return to a corrupted address. This leads to memory corruption on the IRQ stack. It's a classic stack corruption vulnerability. I need to confirm that `schedule()` still switches tasks even when `in_atomic_preempt_off()` is true. I'll need to check the `__schedule` implementation, but the warning indicates this is the case.
|
| 27/2 |
2026/08/09 08:04 |
tool |
codesearch-definition-source |
0m
Args: map[ContextFile:kernel/sched/core.c Name:__schedule]
Results: map[SourceCode:7022: /*
7023: * __schedule() is the main scheduler function.
7024: *
7025: * The main means of driving the scheduler and thus entering this function are:
7026: *
7027: * 1. Explicit blocking: mutex, semaphore, waitqueue, etc.
7028: *
7029: * 2. TIF_NEED_RESCHED flag is checked on interrupt and userspace return
7030: * paths. For example, see arch/x86/entry_64.S.
7031: *
7032: * To drive preemption between tasks, the scheduler sets the flag in timer
7033: * interrupt handler sched_tick().
7034: *
7035: * 3. Wakeups don't really cause entry into schedule(). They add a
7036: * task to the run-queue and that's it.
7037: *
7038: * Now, if the new task added to the run-queue preempts the current
7039: * task, then the wakeup sets TIF_NEED_RESCHED and schedule() gets
7040: * called on the nearest possible occasion:
7041: *
7042: * - If the kernel is preemptible (CONFIG_PREEMPTION=y):
7043: *
7044: * - in syscall or exception context, at the next outmost
7045: * preempt_enable(). (this might be as soon as the wake_up()'s
7046: * spin_unlock()!)
7047: *
7048: * - in IRQ context, return from interrupt-handler to
7049: * preemptible context
7050: *
7051: * - If the kernel is not preemptible (CONFIG_PREEMPTION is not set)
7052: * then at the next:
7053: *
7054: * - cond_resched() call
7055: * - explicit schedule() call
7056: * - return from syscall or exception to user-space
7057: * - return from interrupt-handler to user-space
7058: *
7059: * WARNING: must be called with preemption disabled!
7060: */
7061: static void __sched notrace __schedule(int sched_mode)
7062: {
7063: struct task_struct *prev, *next;
7064: /*
7065: * On PREEMPT_RT kernel, SM_RTLOCK_WAIT is noted
7066: * as a preemption by schedule_debug() and RCU.
7067: */
7068: bool preempt = sched_mode > SM_NONE;
7069: bool is_switch = false;
7070: unsigned long *switch_count;
7071: unsigned long prev_state;
7072: struct rq_flags rf;
7073: struct rq *rq;
7074: int cpu;
7075:
7076: /* Trace preemptions consistently with task switches */
7077: trace_sched_entry_tp(sched_mode == SM_PREEMPT);
7078:
7079: cpu = smp_processor_id();
7080: rq = cpu_rq(cpu);
7081: prev = rq->curr;
7082:
7083: schedule_debug(prev, preempt);
7084:
7085: klp_sched_try_switch(prev);
7086:
7087: local_irq_disable();
7088: rcu_note_context_switch(preempt);
7089: migrate_disable_switch(rq, prev);
7090:
7091: /*
7092: * Make sure that signal_pending_state()->signal_pending() below
7093: * can't be reordered with __set_current_state(TASK_INTERRUPTIBLE)
7094: * done by the caller to avoid the race with signal_wake_up():
7095: *
7096: * __set_current_state(@state) signal_wake_up()
7097: * schedule() set_tsk_thread_flag(p, TIF_SIGPENDING)
7098: * wake_up_state(p, state)
7099: * LOCK rq->lock LOCK p->pi_state
7100: * smp_mb__after_spinlock() smp_mb__after_spinlock()
7101: * if (signal_pending_state()) if (p->state & @state)
7102: *
7103: * Also, the membarrier system call requires a full memory barrier
7104: * after coming from user-space, before storing to rq->curr; this
7105: * barrier matches a full barrier in the proximity of the membarrier
7106: * system call exit.
7107: */
7108: rq_lock(rq, &rf);
7109: smp_mb__after_spinlock();
7110:
7111: hrtick_schedule_enter(rq);
7112:
7113: /* Promote REQ to ACT */
7114: rq->clock_update_flags <<= 1;
7115: update_rq_clock(rq);
7116: rq->clock_update_flags = RQCF_UPDATED;
7117:
7118: switch_count = &prev->nivcsw;
7119:
7120: /* Task state changes only considers SM_PREEMPT as preemption */
7121: preempt = sched_mode == SM_PREEMPT;
7122:
7123: /*
7124: * We must load prev->state once (task_struct::state is volatile), such
7125: * that we form a control dependency vs deactivate_task() below.
7126: */
7127: prev_state = READ_ONCE(prev->__state);
7128: if (sched_mode == SM_IDLE) {
7129: /* SCX must consult the BPF scheduler to tell if rq is empty */
7130: if (!rq->nr_running && !scx_enabled()) {
7131: next = prev;
7132: rq->next_class = &idle_sched_class;
7133: goto picked;
7134: }
7135: } else if (!preempt && prev_state) {
7136: /*
7137: * We pass task_is_blocked() as the should_block arg
7138: * in order to keep mutex-blocked tasks on the runqueue
7139: * for slection with proxy-exec (without proxy-exec
7140: * task_is_blocked() will always be false).
7141: */
7142: try_to_block_task(rq, prev, &prev_state,
7143: !task_is_blocked(prev));
7144: switch_count = &prev->nvcsw;
7145: }
7146:
7147: pick_again:
7148: assert_balance_callbacks_empty(rq);
7149: next = pick_next_task(rq, &rf);
7150: rq->next_class = next->sched_class;
7151: if (sched_proxy_exec()) {
7152: struct task_struct *prev_donor = rq->donor;
7153:
7154: rq_set_donor(rq, next);
7155: next->blocked_donor = NULL;
7156: if (unlikely(next->is_blocked)) {
7157: next = find_proxy_task(rq, next, &rf);
7158: if (!next) {
7159: zap_balance_callbacks(rq);
7160: goto pick_again;
7161: }
7162: if (next == rq->idle) {
7163: zap_balance_callbacks(rq);
7164: goto keep_resched;
7165: }
7166: }
7167: if (rq->donor == prev_donor && prev != next) {
7168: struct task_struct *donor = rq->donor;
7169: /*
7170: * When transitioning like:
7171: *
7172: * prev next
7173: * donor: B B
7174: * curr: A B or C
7175: *
7176: * then put_prev_set_next_task() will not have done
7177: * anything, since B == B. However, A might have
7178: * missed a RT/DL balance opportunity due to being
7179: * on_cpu.
7180: */
7181: donor->sched_class->put_prev_task(rq, donor, donor);
7182: donor->sched_class->set_next_task(rq, donor, true);
7183: }
7184: } else {
7185: rq_set_donor(rq, next);
7186: }
7187:
7188: picked:
7189: clear_tsk_need_resched(prev);
7190: clear_preempt_need_resched();
7191: keep_resched:
7192: rq->last_seen_need_resched_ns = 0;
7193:
7194: is_switch = prev != next;
7195: if (likely(is_switch)) {
7196: rq->nr_switches++;
7197: /*
7198: * RCU users of rcu_dereference(rq->curr) may not see
7199: * changes to task_struct made by pick_next_task().
7200: */
7201: RCU_INIT_POINTER(rq->curr, next);
7202:
7203: /*
7204: * The membarrier system call requires each architecture
7205: * to have a full memory barrier after updating
7206: * rq->curr, before returning to user-space.
7207: *
7208: * Here are the schemes providing that barrier on the
7209: * various architectures:
7210: * - mm ? switch_mm() : mmdrop() for x86, s390, sparc, PowerPC,
7211: * RISC-V. switch_mm() relies on membarrier_arch_switch_mm()
7212: * on PowerPC and on RISC-V.
7213: * - finish_lock_switch() for weakly-ordered
7214: * architectures where spin_unlock is a full barrier,
7215: * - switch_to() for arm64 (weakly-ordered, spin_unlock
7216: * is a RELEASE barrier),
7217: *
7218: * The barrier matches a full barrier in the proximity of
7219: * the membarrier system call entry.
7220: *
7221: * On RISC-V, this barrier pairing is also needed for the
7222: * SYNC_CORE command when switching between processes, cf.
7223: * the inline comments in membarrier_arch_switch_mm().
7224: */
7225: ++*switch_count;
7226:
7227: psi_account_irqtime(rq, prev, next);
7228: psi_sched_switch(prev, next, !task_on_rq_queued(prev) ||
7229: prev->se.sched_delayed);
7230:
7231: trace_sched_switch(preempt, prev, next, prev_state);
7232:
7233: /* Also unlocks the rq: */
7234: rq = context_switch(rq, prev, next, &rf);
7235: } else {
7236: rq_unpin_lock(rq, &rf);
7237: __balance_callbacks(rq, NULL);
7238: hrtick_schedule_exit(rq);
7239: raw_spin_rq_unlock_irq(rq);
7240: }
7241: trace_sched_exit_tp(is_switch);
7242: }
SourceFile:kernel/sched/core.c]
|
| 28/2 |
2026/08/09 08:04 |
llm |
expert |
0m
Model:
Tokens:
input: 31633
output: 40
thoughts: 0
|
| 29/2 |
2026/08/09 08:04 |
tool |
codesearch-definition-source |
0m
Args: map[ContextFile:kernel/rcu/srcutree.c Name:__synchronize_srcu]
Results: map[SourceCode:1474: /*
1475: * Helper function for synchronize_srcu() and synchronize_srcu_expedited().
1476: */
1477: static void __synchronize_srcu(struct srcu_struct *ssp, bool do_norm)
1478: {
1479: struct rcu_synchronize rcu;
1480:
1481: srcu_lock_sync(&ssp->dep_map);
1482:
1483: RCU_LOCKDEP_WARN(lockdep_is_held(ssp) ||
1484: lock_is_held(&rcu_bh_lock_map) ||
1485: lock_is_held(&rcu_lock_map) ||
1486: lock_is_held(&rcu_sched_lock_map),
1487: "Illegal synchronize_srcu() in same-type SRCU (or in RCU) read-side critical section");
1488:
1489: if (rcu_scheduler_active == RCU_SCHEDULER_INACTIVE)
1490: return;
1491: might_sleep();
1492: check_init_srcu_struct(ssp);
1493: init_completion(&rcu.completion);
1494: init_rcu_head_on_stack(&rcu.head);
1495: __call_srcu(ssp, &rcu.head, wakeme_after_rcu, do_norm);
1496: wait_for_completion(&rcu.completion);
1497: destroy_rcu_head_on_stack(&rcu.head);
1498:
1499: /*
1500: * Make sure that later code is ordered after the SRCU grace
1501: * period. This pairs with the raw_spin_lock_irq_rcu_node()
1502: * in srcu_invoke_callbacks(). Unlike Tree RCU, this is needed
1503: * because the current CPU might have been totally uninvolved with
1504: * (and thus unordered against) that grace period.
1505: */
1506: smp_mb();
1507: }
SourceFile:kernel/rcu/srcutree.c]
|
| 30/2 |
2026/08/09 08:04 |
llm |
expert |
0m
Model:
Tokens:
input: 32301
output: 38
thoughts: 0
|
| 31/2 |
2026/08/09 08:04 |
tool |
codesearch-definition-source |
0m
Args: map[ContextFile:drivers/base/power/wakeup.c Name:wakeup_source_unregister]
Results: map[SourceCode: 230: /**
231: * wakeup_source_unregister - Remove wakeup source from the list and remove it.
232: * @ws: Wakeup source object to unregister.
233: */
234: void wakeup_source_unregister(struct wakeup_source *ws)
235: {
236: if (ws) {
237: wakeup_source_remove(ws);
238: if (ws->dev)
239: wakeup_source_sysfs_remove(ws);
240:
241: wakeup_source_destroy(ws);
242: }
243: }
SourceFile:drivers/base/power/wakeup.c]
|
| 32/2 |
2026/08/09 08:04 |
llm |
expert |
0m
Model:
Tokens:
input: 32555
output: 38
thoughts: 0
|
| 33/2 |
2026/08/09 08:04 |
tool |
codesearch-definition-source |
0m
Args: map[ContextFile:drivers/base/power/wakeup.c Name:wakeup_source_remove]
Results: map[SourceCode: 181: /**
182: * wakeup_source_remove - Remove given object from the wakeup sources list.
183: * @ws: Wakeup source object to remove from the list.
184: */
185: static void wakeup_source_remove(struct wakeup_source *ws)
186: {
187: unsigned long flags;
188:
189: if (WARN_ON(!ws))
190: return;
191:
192: /*
193: * After shutting down the timer, wakeup_source_activate() will warn if
194: * the given wakeup source is passed to it.
195: */
196: timer_shutdown_sync(&ws->timer);
197:
198: raw_spin_lock_irqsave(&events_lock, flags);
199: list_del_rcu(&ws->entry);
200: raw_spin_unlock_irqrestore(&events_lock, flags);
201: synchronize_srcu(&wakeup_srcu);
202: }
SourceFile:drivers/base/power/wakeup.c]
|
| 34/2 |
2026/08/09 08:04 |
llm |
expert |
0m
Model:
Tokens:
input: 43344
output: 48
thoughts: 448
Thoughts: **Analyzing a Potential Kernel Vulnerability**
Okay, so I'm seeing `timer_shutdown_sync(&ws->timer)` called within `wakeup_source_remove`. And `timer_shutdown_sync` calls `del_timer_sync`, which I know blocks. That's a red flag! It means `wakeup_source_remove` is potentially blocking, even *before* `synchronize_srcu` β definitely a bug if it's running from a softirq context.
But then, the stack corruption... if `schedule()` gets called from a softirq, it's a context switch. Is this exploitable? Usually, that kind of thing is just a Denial of Service, not privilege escalation, because the kernel switches to another task and the softirq just continues where it left off. But...
Wait a second. On x86_64, softirqs *run on the IRQ stack*. That changes everything. If `schedule()` gets called while we're on the IRQ stack, the task's stack pointer (`sp`) is going to point to the *IRQ stack*. And when the task is switched out, its `sp` is saved... as pointing to *that* same IRQ stack.
So when another interrupt happens, it's going to use the SAME IRQ stack, overwriting the saved state of the task that was switched out! Then, when the task gets switched back in, it's going to pop corrupted registers and return to a completely corrupted address. Right, this is the classic problem with sleeping in interrupts on x86_64!
So, can this be exploited? Yes, potentially. An attacker could, in theory, control the data written to the IRQ stack by that subsequent interrupt. That means control flow hijack when the task resumes! The catch? The attacker has to *trigger* the bug and *precisely* control what the next interrupt writes on the stack. That's going to be tricky, but definitely possible in a targeted attack.
Now, hold on... Is the timer itself running in softirq context on the IRQ stack? Yes, that makes sense. On x86_64, `__do_softirq` switches to the softirq stack β which is often the same stack as the IRQ stack or a dedicated softirq stack. I need to double-check `__do_softirq` in x86_64.
|
| 35/2 |
2026/08/09 08:05 |
tool |
codesearch-definition-source |
0m
Error:
Args: map[ContextFile:arch/x86/kernel/irq_64.c Name:do_softirq_own_stack]
Results: map[SourceCode: SourceFile:]
|
| 36/2 |
2026/08/09 08:05 |
llm |
expert |
0m
Model:
Tokens:
input: 44381
output: 23
thoughts: 0
|
| 37/2 |
2026/08/09 08:05 |
tool |
grepper |
0m
Args: map[Expression:do_softirq_own_stack]
Results: map[Output:Documentation/trace/histogram.rst=5=Documentation written by Tom Zanussi
--
Documentation/trace/histogram.rst-1105- __do_softirq+0x114/0x2c0
Documentation/trace/histogram.rst:1106: do_softirq_own_stack+0x1c/0x30
Documentation/trace/histogram.rst-1107- do_softirq+0x65/0x70
--
arch/arm/kernel/irq.c=75=static void ____do_softirq(void *arg)
--
arch/arm/kernel/irq.c-79-
arch/arm/kernel/irq.c:80:void do_softirq_own_stack(void)
arch/arm/kernel/irq.c-81-{
--
arch/arm64/kernel/irq.c=76=static void ____do_softirq(struct pt_regs *regs)
--
arch/arm64/kernel/irq.c-80-
arch/arm64/kernel/irq.c:81:void do_softirq_own_stack(void)
arch/arm64/kernel/irq.c-82-{
--
arch/parisc/kernel/irq.c=448=static void execute_on_irq_stack(void *func, unsigned long param1)
--
arch/parisc/kernel/irq.c-480-#ifdef CONFIG_SOFTIRQ_ON_OWN_STACK
arch/parisc/kernel/irq.c:481:void do_softirq_own_stack(void)
arch/parisc/kernel/irq.c-482-{
--
arch/powerpc/kernel/irq.c=343=void *hardirq_ctx[NR_CPUS] __read_mostly;
--
arch/powerpc/kernel/irq.c-345-#ifdef CONFIG_SOFTIRQ_ON_OWN_STACK
arch/powerpc/kernel/irq.c:346:void do_softirq_own_stack(void)
arch/powerpc/kernel/irq.c-347-{
--
arch/riscv/kernel/irq.c=123=static void ___do_softirq(struct pt_regs *regs)
--
arch/riscv/kernel/irq.c-127-
arch/riscv/kernel/irq.c:128:void do_softirq_own_stack(void)
arch/riscv/kernel/irq.c-129-{
--
arch/s390/include/asm/softirq_stack.h-8-#ifdef CONFIG_SOFTIRQ_ON_OWN_STACK
arch/s390/include/asm/softirq_stack.h:9:static inline void do_softirq_own_stack(void)
arch/s390/include/asm/softirq_stack.h-10-{
--
arch/sh/kernel/irq.c=147=void irq_ctx_exit(int cpu)
--
arch/sh/kernel/irq.c-152-#ifdef CONFIG_SOFTIRQ_ON_OWN_STACK
arch/sh/kernel/irq.c:153:void do_softirq_own_stack(void)
arch/sh/kernel/irq.c-154-{
--
arch/sparc/kernel/irq_64.c=810=void __irq_entry handler_irq(int pil, struct pt_regs *regs)
--
arch/sparc/kernel/irq_64.c-853-#ifdef CONFIG_SOFTIRQ_ON_OWN_STACK
arch/sparc/kernel/irq_64.c:854:void do_softirq_own_stack(void)
arch/sparc/kernel/irq_64.c-855-{
--
arch/x86/include/asm/irq_stack.h-212- */
arch/x86/include/asm/irq_stack.h:213:#define do_softirq_own_stack() \
arch/x86/include/asm/irq_stack.h-214-{ \
--
arch/x86/kernel/irq_32.c=107=int irq_init_percpu_irqstack(unsigned int cpu)
--
arch/x86/kernel/irq_32.c-129-#ifdef CONFIG_SOFTIRQ_ON_OWN_STACK
arch/x86/kernel/irq_32.c:130:void do_softirq_own_stack(void)
arch/x86/kernel/irq_32.c-131-{
--
drivers/gpu/drm/ci/xfails/msm-sm8350-hdk-skips.txt=20=msm/msm_mapping@ring
--
drivers/gpu/drm/ci/xfails/msm-sm8350-hdk-skips.txt-138-# [ 228.408769] call_on_irq_stack+0x24/0x4c
drivers/gpu/drm/ci/xfails/msm-sm8350-hdk-skips.txt:139:# [ 228.412854] do_softirq_own_stack+0x1c/0x28
drivers/gpu/drm/ci/xfails/msm-sm8350-hdk-skips.txt-140-# [ 228.417199] __irq_exit_rcu+0x124/0x164
--
drivers/gpu/drm/ci/xfails/msm-sm8350-hdk-skips.txt-192-# [ 228.637729] call_on_irq_stack+0x24/0x4c
drivers/gpu/drm/ci/xfails/msm-sm8350-hdk-skips.txt:193:# [ 228.641811] do_softirq_own_stack+0x1c/0x28
drivers/gpu/drm/ci/xfails/msm-sm8350-hdk-skips.txt-194-# [ 228.646152] __irq_exit_rcu+0x124/0x164
--
include/asm-generic/softirq_stack.h-5-#ifdef CONFIG_SOFTIRQ_ON_OWN_STACK
include/asm-generic/softirq_stack.h:6:void do_softirq_own_stack(void);
include/asm-generic/softirq_stack.h-7-#else
include/asm-generic/softirq_stack.h:8:static inline void do_softirq_own_stack(void)
include/asm-generic/softirq_stack.h-9-{
--
kernel/softirq.c=487=static inline void invoke_softirq(void)
--
kernel/softirq.c-502- */
kernel/softirq.c:503: do_softirq_own_stack();
kernel/softirq.c-504-#endif
--
kernel/softirq.c=510=asmlinkage __visible void do_softirq(void)
--
kernel/softirq.c-522- if (pending)
kernel/softirq.c:523: do_softirq_own_stack();
kernel/softirq.c-524-
]
|
| 38/2 |
2026/08/09 08:05 |
llm |
expert |
0m
Model:
Tokens:
input: 46332
output: 66
thoughts: 2607
Thoughts: **Final Assessment: Exploitable USB SoftIRQ Sleep Vulnerability**
Okay, so `do_softirq_own_stack` is definitely being used here, and on x86_64, it's switching to the softirq stack. `schedule()` saves the task's stack pointer (`sp`) into the task's `thread.sp` when sleeping on the softirq stack, and restores it when the task wakes up. The problem, as I see it, is that this softirq stack is per-CPU, and if another task is running on that same CPU and handles a softirq, it will *overwrite* the sleeping task's stack frames. The check in `schedule()` (`in_atomic_preempt_off()`) is supposed to prevent this but it doesn't really work. Even though it prints a warning, `__schedule` *still* switches tasks.
This is a known issue: sleeping in a softirq context on x86_64 can lead to stack corruption. To exploit this, I figure the attacker would have to:
1. Trigger the bug (e.g., plug/unplug a USB device).
2. Have a task sleep on the softirq stack.
3. Trigger another softirq on the *same* CPU to overwrite the sleeping task's stack with controlled data.
4. Have the sleeping task wake up (like when `synchronize_srcu` completes) and return using the corrupted stack, jumping to an attacker-controlled address.
Now, `synchronize_srcu` is key, and the question is whether it *always* sleeps. If there are no readers, it might not sleep right? But it calls `wait_for_completion`, and if the grace period is already finished, `wait_for_completion` might return without sleeping. Usually though, SRCU grace periods take some time, so it will sleep. An unprivileged attacker *could* control the data overwritten on the softirq stack by triggering softirqs like network packets or timers. So theoretically, this is exploitable for privilege escalation.
The catch is, this bug is triggered by a USB device. An attacker with physical access (to plug in a USB device) could already do a lot (malicious keyboard, DMA, etc.). *However*, if we're working with a threat model where they're limited to the *kernel* and want arbitrary code execution, this provides a path. And since memory safety issues are almost always exploitable, and a crash on a wild pointer implies potential exploitability, this stack corruption bug is indeed exploitable. It's a well-known exploitation vector β similar to the now-patched CVE-2019-11815.
Let's confirm that `schedule()` does switch tasks even in softirq context. Yes, `__schedule` doesn't return early; it calls `context_switch`, and the stack pointer is, in fact, on the softirq stack. So, when the task wakes up, it *will* use the corrupted stack. Stack corruption = exploitability, in this case.
Here's my refined assessment:
* **Exploitable: True** (Stack corruption due to sleeping on per-CPU softirq stack).
* **Denial of Service: True**.
* **Accessible From Unprivileged Processes: False** (Requires USB device).
* **Accessible From User Namespaces: False**.
* **VM Guest Trigger: False**.
* **VM Host Trigger in The Confidential Computing Context: False**.
* **Ethernet Network Trigger: False**.
* **Other Remote Trigger: False**.
* **Peripheral Trigger: True**.
* **Malicious Filesystem Trigger: False**.
Can we trigger this *without* a USB device? Looking at the `vub300` driver (a USB SDIO host controller), it's strictly tied to the USB device lifecycle. The driver is `vub300`, it has `probe` and `disconnect` functions, and a table of matched devices. So, a USB device *is* required. Therefore:
* **Peripheral Trigger: True**
* **Unprivileged: False** (unless the system allows unprivileged USB plugging, which is effectively physical access).
Now, let's double-check `vub300_delete`, which is called as a kref callback within a softirq context. The author knew this but didn't realize `mmc_free_host` could sleep. I see that `mmc_free_host` calls `cancel_delayed_work_sync`, which *can* sleep. It calls `put_device`, which calls `device_release` -> `mmc_host
|
| 39/2 |
2026/08/09 08:05 |
tool |
set-results |
0m
Args: map[DenialOfService:true Exploitable:true FilesystemTrigger:false NetworkTrigger:false PeripheralTrigger:true RemoteTrigger:false Unprivileged:false UserNamespace:false VMGuestTrigger:false VMHostTrigger:false]
Results: map[DenialOfService:true Exploitable:true FilesystemTrigger:false NetworkTrigger:false PeripheralTrigger:true RemoteTrigger:false Unprivileged:false UserNamespace:false VMGuestTrigger:false VMHostTrigger:false]
|
| 40/2 |
2026/08/09 08:05 |
llm |
expert |
0m
Model:
Tokens:
input: 36629
output: 575
thoughts: 3593
Thoughts: **Analysis of an Inconsistent Lock State Vulnerability in the vub300 USB Driver**
Alright, let's break this down. The core issue revolves around an inconsistent lock state in the `vub300` driver, specifically a softirq context attempting to acquire a write lock that can sleep after a read lock was acquired. This manifests during device removal, specifically within the `wakeup_srcu` lock. The vulnerability lies within the `vub300_inactivity_timer_expired` timer callback, which runs in softirq context. This routine, when triggered after the USB device has been disconnected, calls `kref_put()`, which, if the reference count drops to zero, calls `vub300_delete()`.
`vub300_delete()` calls a series of functions culminating in `synchronize_srcu(&wakeup_srcu)` called within `mmc_host_classdev_release`. The critical problem is that `synchronize_srcu` is a sleeping function (specifically calling `wait_for_completion`), and calling a sleeping function from a softirq context is a big no-no because it might cause a system hang or panic. However, this is even more dangerous: the report contains a stack corruption warning.
The key to the exploit lies in the fact that `synchronize_srcu()`'s underlying function `__synchronize_srcu` attempts to allocate `rcu_head` on the *softirq* stack, and then sleeps. The softirq stack is per-CPU, and since the function sleeps, it leaves the stack vulnerable. If another softirq occurs on the same CPU, it will reuse the same stack, corrupting the stack frames of the original call, which includes the return address of the function. This, when the original thread wakes up, will be executed with a corrupted stack, which leads to arbitrary code execution (ROP).
So, the trigger is plugging in a malicious USB device. The driver then, upon device disconnection and the timer expiring, will attempt the sleep-in-atomic operation. This is a clear denial-of-service condition, but crucially, it's exploitable, as it leads to stack corruption. Because it's a USB driver, the bug is a peripheral trigger, but it is not accessible to unprivileged users without physical access, making it only exploitable with physical access or the ability to emulate USB devices, which is, generally, not unprivileged.
|
| 41/1 |
2026/08/09 08:06 |
action |
format-explanation |
0m
Results: map[Explanation:An analysis of the bug report and the source code reveals the following:
**Root Cause & Mechanism:**
The bug is a sleep-in-atomic vulnerability that leads to stack corruption.
The `vub300` USB SDIO host controller driver sets up an inactivity timer
(`vub300_inactivity_timer_expired()`), which executes in a softirq context. If
the USB device is disconnected, the timer callback notices that
`vub300->interface` is NULL and drops a reference using `kref_put()`.
If this is the last reference, it invokes the release function `vub300_delete()`
directly in the softirq context. `vub300_delete()` calls `mmc_free_host()`,
which eventually calls `wakeup_source_unregister()` and `synchronize_srcu()`.
`synchronize_srcu()` is a blocking function that calls `schedule()` to wait for
a grace period.
**Exploitability:**
Calling `schedule()` from a softirq context is highly dangerous on architectures
with dedicated per-CPU softirq stacks (like x86_64). When the thread sleeps, it
leaves its call frames on the per-CPU softirq stack. If another softirq is
raised on the same CPU while the thread is sleeping, the kernel will start
executing the new softirq from the top of the softirq stack, completely
overwriting the sleeping thread's stack frames.
When the `synchronize_srcu()` completion finishes and the original thread wakes
up, it will resume execution with a corrupted stack. An attacker can carefully
groom the softirq stack overwrites to control the return addresses, leading to a
classic Return-Oriented Programming (ROP) control-flow hijack and privilege
escalation. Therefore, this bug is exploitable.
**Trigger & Accessibility:**
The vulnerability is located in the `vub300` USB driver. Triggering it requires
the ability to plug in a specific USB device (or emulate one), wait for the
inactivity timer to be armed, and then disconnect the device so that the timer
callback executes the cleanup code. This makes it a Peripheral Trigger.
Unprivileged users cannot trigger this bug remotely or from user-space without
physical access to plug in a USB device (or without elevated privileges to use
USB gadget subsystems like `dummy_hcd`).
**Conclusion:**
- **Exploitable:** Yes (Stack corruption leading to arbitrary code execution).
- **Denial of Service:** Yes (System crash/panic).
- **Peripheral Trigger:** Yes (Requires plugging/unplugging a USB device).
- **Unprivileged/User Namespace/Remote:** No.]
|