| 0/0 |
2026/06/02 12:49 |
flow |
assessment-security |
57m
Results: map[DenialOfService:true Explanation:Based on the analysis of the bug report and the kernel source code, here is the
assessment of the vulnerability:
**Bug Analysis:**
The bug is a KASAN report indicating an invalid read of size 8 in `futex_wake()`
at `kernel/futex/waitwake.c:191`. The read corresponds to the evaluation of the
function pointer `this->wake` (which is typically `futex_wake_mark`) from a
`struct futex_q` object.
The KASAN report explicitly states that the buggy address belongs to a vmalloc
virtual mapping that was allocated as a thread stack (`alloc_thread_stack_node`)
and has since been freed (`__free_frozen_pages`, `__slab_free`). This means we
are looking at a **Use-After-Free (UAF)** on a thread stack.
In the normal lifecycle of a futex wait:
1. A thread allocates a `struct futex_q` on its stack (e.g., in `__futex_wait`).
2. It queues the `futex_q` into a futex hash bucket (`hb->chain`) and sets
`q->lock_ptr = &hb->lock`.
3. It blocks in `futex_do_wait()`.
4. When woken up (or upon timeout/signal), it calls `futex_unqueue()`, which
acquires `hb->lock`, removes the `futex_q` from the list, and allows the thread
to safely return and eventually exit (freeing its stack).
For `futex_wake()` to access a `futex_q` whose underlying stack has already been
freed, the waiting thread must have managed to return from the syscall and exit
*while* its `futex_q` was still linked in the hash bucket being iterated by
`futex_wake()`. Since `futex_wake()` holds `hb->lock` while iterating,
`futex_unqueue()` should have blocked the waiting thread from exiting. The fact
that it didn't implies a severe synchronization failureβlikely caused by memory
corruption (e.g., `q->lock_ptr` being overwritten, allowing `futex_unqueue` to
take the wrong lock or return prematurely) or a complex race condition in the
futex requeue/rehash logic leaving a dangling pointer in the list.
**Security Impact:**
* **Exploitable:** Yes. Use-After-Free vulnerabilities on thread stacks are
highly exploitable. An attacker can spray the freed vmalloc space with
controlled data, overwriting the `this->wake` function pointer. When
`futex_wake()` subsequently calls `this->wake(&wake_q, this)`, it will result in
arbitrary control-flow hijacking (RIP control), leading to local privilege
escalation (LPE).
* **Denial of Service:** Yes. Triggering the bug causes a kernel crash/panic,
leading to a denial of service.
* **Accessible From Unprivileged Processes:** Yes. The `futex` system call is a
fundamental synchronization primitive available to all unprivileged user-space
processes without any capability requirements.
* **Accessible From User Namespaces:** Yes. The bug can be triggered from within
a restricted user namespace.
* **Other Triggers:** The bug is localized to the core kernel futex subsystem
and does not require specific VM guest/host interactions, network packets,
peripheral devices, or malicious filesystems to trigger. Exploitable:true FilesystemTrigger:false NetworkTrigger:false PeripheralTrigger:false RemoteTrigger:false Unprivileged:true UserNamespace:true VMGuestTrigger:false VMHostTrigger:false]
|
| 1/1 |
2026/06/02 12:49 |
action |
syz-repro-to-c-repro |
0m
Results:
|
| 2/1 |
2026/06/02 12:49 |
action |
kernel-checkouter |
1m
Results: map[KernelSrc:/app/workdir/cache/src/9e90653914128b0c0a243c2451f414ecec48632b]
|
| 3/1 |
2026/06/02 12:51 |
action |
kernel-builder |
25m
Results: map[KernelObj:/app/workdir/cache/build/aba0d403f134124ae8bcec7f2190123b33e0ef3d]
|
| 4/1 |
2026/06/02 13:16 |
action |
codesearch-prepare |
19m
Results: map[Index:codesearch-index]
|
| 5/1 |
2026/06/02 13:35 |
agent |
expert |
10m
Model:
Results: map[DenialOfService:true Exploitable:true FilesystemTrigger:false NetworkTrigger:false PeripheralTrigger:false RemoteTrigger:false Unprivileged:true UserNamespace:true 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 or elevated privileges.
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.
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: unknown-crash in futex_wake+0x382/0x580 kernel/futex/waitwake.c:191
Read of size 8 at addr ffffc900039dfad8 by task syz.1.9500/26050
CPU: 1 UID: 0 PID: 26050 Comm: syz.1.9500 Tainted: G L syzkaller #0 PREEMPT(full)
Tainted: [L]=SOFTLOCKUP
Hardware name: Google Google Compute Engine/Google Compute Engine, BIOS Google 04/18/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
futex_wake+0x382/0x580 kernel/futex/waitwake.c:191
do_futex+0x395/0x420 kernel/futex/syscalls.c:135
__do_sys_futex kernel/futex/syscalls.c:207 [inline]
__se_sys_futex+0x3a8/0x450 kernel/futex/syscalls.c:188
do_syscall_x64 arch/x86/entry/syscall_64.c:63 [inline]
do_syscall_64+0x15f/0x560 arch/x86/entry/syscall_64.c:94
entry_SYSCALL_64_after_hwframe+0x77/0x7f
RIP: 0033:0x7f3eb3b9ce59
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:00007f3eb3f3fba8 EFLAGS: 00000246 ORIG_RAX: 00000000000000ca
RAX: ffffffffffffffda RBX: 00000000000002d0 RCX: 00007f3eb3b9ce59
RDX: 00000000000f4240 RSI: 0000000000000081 RDI: 00007f3eb3e16278
RBP: 00000000000010e9 R08: 0000000000000000 R09: 0000000000000000
R10: 00007f3eb3e15fa0 R11: 0000000000000246 R12: 0000000000000003
R13: 00007f3eb3e1627c R14: 00007f3eb3e16278 R15: 00007f3eb3e16270
</TASK>
The buggy address belongs to a vmalloc virtual mapping
The buggy address belongs to the physical page:
page: refcount:1 mapcount:0 mapping:0000000000000000 index:0x0 pfn:0xa29e7
memcg:ffff888030daa402
flags: 0xfff00000000000(node=0|zone=1|lastcpupid=0x7ff)
raw: 00fff00000000000 0000000000000000 dead000000000122 0000000000000000
raw: 0000000000000000 0000000000000000 00000001ffffffff ffff888030daa402
page dumped because: kasan: bad access detected
page_owner tracks the page as allocated
page last allocated via order 0, migratetype Unmovable, gfp_mask 0x1029c2(GFP_NOWAIT|__GFP_HIGHMEM|__GFP_IO|__GFP_FS|__GFP_ZERO|__GFP_HARDWALL), pid 26050, tgid 26050 (syz.1.9500), ts 2021430196564, free_ts 2021372410361
set_page_owner include/linux/page_owner.h:32 [inline]
post_alloc_hook+0x22d/0x280 mm/page_alloc.c:1858
prep_new_page mm/page_alloc.c:1866 [inline]
get_page_from_freelist+0x24ba/0x2540 mm/page_alloc.c:3946
__alloc_frozen_pages_noprof+0x18d/0x380 mm/page_alloc.c:5226
alloc_pages_mpol+0x235/0x490 mm/mempolicy.c:2490
alloc_frozen_pages_noprof mm/mempolicy.c:2561 [inline]
alloc_pages_noprof+0xac/0x2a0 mm/mempolicy.c:2581
vm_area_alloc_pages mm/vmalloc.c:3653 [inline]
__vmalloc_area_node mm/vmalloc.c:3878 [inline]
__vmalloc_node_range_noprof+0x7a7/0x1750 mm/vmalloc.c:4064
__vmalloc_node_noprof+0xc2/0x100 mm/vmalloc.c:4124
alloc_thread_stack_node kernel/fork.c:357 [inline]
dup_task_struct+0x298/0x840 kernel/fork.c:926
copy_process+0x89b/0x4440 kernel/fork.c:2090
kernel_clone+0x284/0x8f0 kernel/fork.c:2721
__do_sys_clone3 kernel/fork.c:3025 [inline]
__se_sys_clone3+0x33c/0x360 kernel/fork.c:3004
do_syscall_x64 arch/x86/entry/syscall_64.c:63 [inline]
do_syscall_64+0x15f/0x560 arch/x86/entry/syscall_64.c:94
entry_SYSCALL_64_after_hwframe+0x77/0x7f
page last free pid 26050 tgid 26050 stack trace:
reset_page_owner include/linux/page_owner.h:25 [inline]
__free_pages_prepare mm/page_alloc.c:1402 [inline]
__free_frozen_pages+0xbc7/0xd30 mm/page_alloc.c:2943
__slab_free+0x274/0x2c0 mm/slub.c:5613
qlink_free mm/kasan/quarantine.c:163 [inline]
qlist_free_all+0x99/0x100 mm/kasan/quarantine.c:179
kasan_quarantine_reduce+0x148/0x160 mm/kasan/quarantine.c:286
__kasan_slab_alloc+0x22/0x80 mm/kasan/common.c:350
kasan_slab_alloc include/linux/kasan.h:253 [inline]
slab_post_alloc_hook mm/slub.c:4570 [inline]
slab_alloc_node mm/slub.c:4899 [inline]
kmem_cache_alloc_noprof+0x2bc/0x650 mm/slub.c:4906
vm_area_dup+0x2b/0x680 mm/vma_init.c:123
__split_vma+0x1dc/0xa40 mm/vma.c:516
split_vma mm/vma.c:599 [inline]
vma_modify+0x88a/0x1e10 mm/vma.c:1691
vma_modify_flags+0x24b/0x330 mm/vma.c:1719
mprotect_fixup+0x62a/0xb60 mm/mprotect.c:789
do_mprotect_pkey+0x8d5/0xd20 mm/mprotect.c:967
__do_sys_mprotect mm/mprotect.c:988 [inline]
__se_sys_mprotect mm/mprotect.c:985 [inline]
__x64_sys_mprotect+0x80/0x90 mm/mprotect.c:985
do_syscall_x64 arch/x86/entry/syscall_64.c:63 [inline]
do_syscall_64+0x15f/0x560 arch/x86/entry/syscall_64.c:94
entry_SYSCALL_64_after_hwframe+0x77/0x7f
Memory state around the buggy address:
ffffc900039df980: 67 c8 a7 42 00 00 00 00 67 d8 a7 42 00 00 00 00
ffffc900039dfa00: 67 e8 a7 42 00 00 00 00 67 f8 a7 42 00 00 00 00
>ffffc900039dfa80: f1 f1 f1 f1 00 00 00 00 67 18 a8 42 00 00 00 00
^
ffffc900039dfb00: 00 00 00 00 00 f3 f3 f3 f3 f3 f3 f3 f3 f3 f3 f3
ffffc900039dfb80: 67 48 a8 42 00 00 00 00 67 58 a8 42 00 00 00 00
==================================================================
Reply: Based on the analysis of the bug report and the kernel source code, here is the assessment of the vulnerability:
**Bug Analysis:**
The bug is a KASAN report indicating an invalid read of size 8 in `futex_wake()` at `kernel/futex/waitwake.c:191`. The read corresponds to the evaluation of the function pointer `this->wake` (which is typically `futex_wake_mark`) from a `struct futex_q` object.
The KASAN report explicitly states that the buggy address belongs to a vmalloc virtual mapping that was allocated as a thread stack (`alloc_thread_stack_node`) and has since been freed (`__free_frozen_pages`, `__slab_free`). This means we are looking at a **Use-After-Free (UAF)** on a thread stack.
In the normal lifecycle of a futex wait:
1. A thread allocates a `struct futex_q` on its stack (e.g., in `__futex_wait`).
2. It queues the `futex_q` into a futex hash bucket (`hb->chain`) and sets `q->lock_ptr = &hb->lock`.
3. It blocks in `futex_do_wait()`.
4. When woken up (or upon timeout/signal), it calls `futex_unqueue()`, which acquires `hb->lock`, removes the `futex_q` from the list, and allows the thread to safely return and eventually exit (freeing its stack).
For `futex_wake()` to access a `futex_q` whose underlying stack has already been freed, the waiting thread must have managed to return from the syscall and exit *while* its `futex_q` was still linked in the hash bucket being iterated by `futex_wake()`. Since `futex_wake()` holds `hb->lock` while iterating, `futex_unqueue()` should have blocked the waiting thread from exiting. The fact that it didn't implies a severe synchronization failureβlikely caused by memory corruption (e.g., `q->lock_ptr` being overwritten, allowing `futex_unqueue` to take the wrong lock or return prematurely) or a complex race condition in the futex requeue/rehash logic leaving a dangling pointer in the list.
**Security Impact:**
* **Exploitable:** Yes. Use-After-Free vulnerabilities on thread stacks are highly exploitable. An attacker can spray the freed vmalloc space with controlled data, overwriting the `this->wake` function pointer. When `futex_wake()` subsequently calls `this->wake(&wake_q, this)`, it will result in arbitrary control-flow hijacking (RIP control), leading to local privilege escalation (LPE).
* **Denial of Service:** Yes. Triggering the bug causes a kernel crash/panic, leading to a denial of service.
* **Accessible From Unprivileged Processes:** Yes. The `futex` system call is a fundamental synchronization primitive available to all unprivileged user-space processes without any capability requirements.
* **Accessible From User Namespaces:** Yes. The bug can be triggered from within a restricted user namespace.
* **Other Triggers:** The bug is localized to the core kernel futex subsystem and does not require specific VM guest/host interactions, network packets, peripheral devices, or malicious filesystems to trigger.
|
| 6/2 |
2026/06/02 13:35 |
llm |
expert |
0m
Model:
Tokens:
input: 8425
output: 33
thoughts: 635
Thoughts: **KASAN Crash Investigation: Futex Wake-Up Gone Wrong**
Okay, so this KASAN report is showing a crash within `futex_wake`, specifically at `kernel/futex/waitwake.c:191`. The crash involves an "unknown" read of size 8 bytes, targeting memory at address `ffffc900039dfad8`. That address is apparently within a vmalloc virtual mapping. The trace points back to a thread stack allocation, originating from `alloc_thread_stack_node` via the usual `fork` sequence. So, it's a stack access, which is usually easier to debug than random heap corruption.
Looking at the memory dump around the offending address, `ffffc900039dfa80`, I see what looks like stack redzones β `f1 f1 f1 f1` and then a bunch of `f3`s. Ah, right, `f1` is the stack *left* redzone, and `f3` is the stack *right* redzone used by KASAN to detect stack overflows/underflows. The access is at `ffffc900039dfad8`, which is `0x58` bytes offset from the memory dumpβs start. Now, at `ffffc900039dfa80`, I see `f1 f1 f1 f1 00 00 00 00 67 18 a8 42 00 00 00 00`. Whatβs that `67 18 a8 42`? That's not a KASAN shadow byte. Wait a second, the value is not a byte. The memory dump shows that each byte is an individual element. That's a tag!
Therefore, the issue is an out-of-bounds access on a stack variable. Time to dive into `futex_wake` at that line in `waitwake.c` and figure out what's reaching beyond the bounds of a stack frame. This should be interesting.
|
| 7/2 |
2026/06/02 13:36 |
tool |
grepper |
0m
Args: map[Expression:futex_wake PathPrefix:kernel/futex/waitwake.c]
Results: map[Output:kernel/futex/waitwake.c-23- * The waker side modifies the user space value of the futex and calls
kernel/futex/waitwake.c:24: * futex_wake(). This function computes the hash bucket and acquires the
kernel/futex/waitwake.c-25- * hash bucket lock. Then it looks for waiters on that futex in the hash
--
kernel/futex/waitwake.c-40- * sys_futex(WAKE, futex);
kernel/futex/waitwake.c:41: * futex_wake(futex);
kernel/futex/waitwake.c-42- * if (queue_empty())
--
kernel/futex/waitwake.c-70- * | sys_futex(WAKE, futex);
kernel/futex/waitwake.c:71: * | futex_wake(futex);
kernel/futex/waitwake.c-72- * |
--
kernel/futex/waitwake.c-109-
kernel/futex/waitwake.c:110:bool __futex_wake_mark(struct futex_q *q)
kernel/futex/waitwake.c-111-{
--
kernel/futex/waitwake.c-133- */
kernel/futex/waitwake.c:134:void futex_wake_mark(struct wake_q_head *wake_q, struct futex_q *q)
kernel/futex/waitwake.c-135-{
--
kernel/futex/waitwake.c-139-
kernel/futex/waitwake.c:140: if (!__futex_wake_mark(q)) {
kernel/futex/waitwake.c-141- put_task_struct(p);
--
kernel/futex/waitwake.c-154- */
kernel/futex/waitwake.c:155:int futex_wake(u32 __user *uaddr, unsigned int flags, int nr_wake, u32 bitset)
kernel/futex/waitwake.c-156-{
--
kernel/futex/waitwake.c=202=static int futex_atomic_op_inuser(unsigned int encoded_op, u32 __user *uaddr)
--
kernel/futex/waitwake.c-215- */
kernel/futex/waitwake.c:216: pr_info_ratelimited("futex_wake_op: %s tries to shift op by %d; fix this program\n",
kernel/futex/waitwake.c-217- current->comm, oparg);
--
kernel/futex/waitwake.c-250- */
kernel/futex/waitwake.c:251:int futex_wake_op(u32 __user *uaddr1, unsigned int flags, u32 __user *uaddr2,
kernel/futex/waitwake.c-252- int nr_wake, int nr_wake2, int op)
--
kernel/futex/waitwake.c=592=int futex_wait_setup(u32 __user *uaddr, u32 val, unsigned int flags,
--
kernel/futex/waitwake.c-603- * Userspace waiter: val = var; if (cond(val)) futex_wait(&var, val);
kernel/futex/waitwake.c:604: * Userspace waker: if (cond(var)) { var = new; futex_wake(&var); }
kernel/futex/waitwake.c-605- *
]
|
| 8/2 |
2026/06/02 13:36 |
llm |
expert |
0m
Model:
Tokens:
input: 10469
output: 37
thoughts: 22
Thoughts: Here's my attempt at summarizing the thought process, tailored for an expert audience:
**Diving into the Futex Wait/Wake Logic**
Okay, time to get my hands dirty. I'm going to jump into the `kernel/futex/waitwake.c` file. Specifically, I'm targeting the code around line 191. That's usually a good area to start digging into the core wait/wake mechanisms of the futex implementation. I'm probably looking for the actual spinlock contention logic, or maybe the initial validation steps before a process enters the wait queue. I'm anticipating that line 191 will be the point where the critical section is either entered or the decision is made about whether to sleep. I expect to see the lock acquisition, followed by checks on the futex value and any related flags. From there, I'll be looking to see how the wait queue is manipulated. I might be following a signal path, or the actual logic for putting the process to sleep. I'll focus on what data structures are being used and how the interaction with the scheduler is managed. No doubt I'll be seeing the `futex_lock_wait()` or related functions that are critical to the system. It's time to follow the trail of logic that makes all this work.
|
| 9/2 |
2026/06/02 13:36 |
tool |
read-file |
0m
Args: map[File:kernel/futex/waitwake.c FirstLine:150 LineCount:50]
Results: map[Contents: 150: }
151:
152: /*
153: * Wake up waiters matching bitset queued on this futex (uaddr).
154: */
155: int futex_wake(u32 __user *uaddr, unsigned int flags, int nr_wake, u32 bitset)
156: {
157: struct futex_q *this, *next;
158: union futex_key key = FUTEX_KEY_INIT;
159: DEFINE_WAKE_Q(wake_q);
160: int ret;
161:
162: if (!bitset)
163: return -EINVAL;
164:
165: ret = get_futex_key(uaddr, flags, &key, FUTEX_READ);
166: if (unlikely(ret != 0))
167: return ret;
168:
169: if ((flags & FLAGS_STRICT) && !nr_wake)
170: return 0;
171:
172: CLASS(hb, hb)(&key);
173:
174: /* Make sure we really have tasks to wakeup */
175: if (!futex_hb_waiters_pending(hb))
176: return ret;
177:
178: spin_lock(&hb->lock);
179:
180: plist_for_each_entry_safe(this, next, &hb->chain, list) {
181: if (futex_match (&this->key, &key)) {
182: if (this->pi_state || this->rt_waiter) {
183: ret = -EINVAL;
184: break;
185: }
186:
187: /* Check if one of the bits is set in both bitsets */
188: if (!(this->bitset & bitset))
189: continue;
190:
191: this->wake(&wake_q, this);
192: if (++ret >= nr_wake)
193: break;
194: }
195: }
196:
197: spin_unlock(&hb->lock);
198: wake_up_q(&wake_q);
199: return ret;
]
|
| 10/2 |
2026/06/02 13:36 |
llm |
expert |
0m
Model:
Tokens:
input: 11496
output: 37
thoughts: 99
Thoughts: **Debugging a Kernel Crash**
Okay, so I'm looking at this crash report and the key line is 191: `this->wake(&wake_q, this);`. The crash is a `Read of size 8` on address `fffff900039dfad8`, which is being caused by `syz.1.9500/26050`. The issue seems to be centered around `this`. Hmm, `this` is a `struct futex_q *`. The important part is that `this->wake` is a function pointer. I bet there's an issue there; a bad function pointer is the most likely cause. Time to check out the definition of `struct futex_q` and figure out what's going on with that function pointer.
|
| 11/2 |
2026/06/02 13:36 |
tool |
codesearch-definition-source |
0m
Args: map[ContextFile:kernel/futex/waitwake.c Name:futex_q]
Results: map[SourceCode: 165: /**
166: * struct futex_q - The hashed futex queue entry, one per waiting task
167: * @list: priority-sorted list of tasks waiting on this futex
168: * @task: the task waiting on the futex
169: * @lock_ptr: the hash bucket lock
170: * @wake: the wake handler for this queue
171: * @wake_data: data associated with the wake handler
172: * @key: the key the futex is hashed on
173: * @pi_state: optional priority inheritance state
174: * @rt_waiter: rt_waiter storage for use with requeue_pi
175: * @requeue_pi_key: the requeue_pi target futex key
176: * @bitset: bitset for the optional bitmasked wakeup
177: * @requeue_state: State field for futex_requeue_pi()
178: * @drop_hb_ref: Waiter should drop the extra hash bucket reference if true
179: * @requeue_wait: RCU wait for futex_requeue_pi() (RT only)
180: *
181: * We use this hashed waitqueue, instead of a normal wait_queue_entry_t, so
182: * we can wake only the relevant ones (hashed queues may be shared).
183: *
184: * A futex_q has a woken state, just like tasks have TASK_RUNNING.
185: * It is considered woken when plist_node_empty(&q->list) || q->lock_ptr == 0.
186: * The order of wakeup is always to make the first condition true, then
187: * the second.
188: *
189: * PI futexes are typically woken before they are removed from the hash list via
190: * the rt_mutex code. See futex_unqueue_pi().
191: */
192: struct futex_q {
193: struct plist_node list;
194:
195: struct task_struct *task;
196: spinlock_t *lock_ptr;
197: futex_wake_fn *wake;
198: void *wake_data;
199: union futex_key key;
200: struct futex_pi_state *pi_state;
201: struct rt_mutex_waiter *rt_waiter;
202: union futex_key *requeue_pi_key;
203: u32 bitset;
204: atomic_t requeue_state;
205: bool drop_hb_ref;
206: #ifdef CONFIG_PREEMPT_RT
207: struct rcuwait requeue_wait;
208: #endif
209: } __randomize_layout;
SourceFile:kernel/futex/futex.h]
|
| 12/2 |
2026/06/02 13:36 |
llm |
expert |
0m
Model:
Tokens:
input: 12654
output: 37
thoughts: 212
Thoughts: **Debugging a Potential Futex Queue Crash**
Okay, so I'm looking at a crash that seems to involve a read of size 8. The error log points to `this->wake`, which I immediately recognize as a function pointer. Since we're dealing with a 64-bit architecture, that function pointer should be 8 bytes in size, which fits the read size. The crash occurs when trying to read this `this->wake` pointer.
`this` is a pointer to a `struct futex_q`. The address of `this->wake` is given as `ffffc900039dfad8`. Now, let me examine the memory context around `ffffc900039dfa80`. The memory state shown is `f1 f1 f1 f1 00 00 00 00 67 18 a8 42 00 00 00 00`. Hold on, I'm pretty sure `f1` represents the stack's left redzone, and that suggests that the address `ffffc900039dfa80` marks the beginning of a stack variable.
My intuition says the stack variable is probably the `struct futex_q` itself! I should verify the offset of the `wake` member within `struct futex_q` and confirm if it matches the offset implied by these memory addresses. I need to dig into the definition of that structure.
|
| 13/2 |
2026/06/02 13:36 |
tool |
codesearch-struct-layout |
0m
Args: map[ContextFile:kernel/futex/waitwake.c Name:futex_q]
Results: map[Fields:[map[Name:list OffsetBits:0 SizeBits:320] map[Name:task OffsetBits:320 SizeBits:64] map[Name:lock_ptr OffsetBits:384 SizeBits:64] map[Name:wake OffsetBits:448 SizeBits:64] map[Name:wake_data OffsetBits:512 SizeBits:64] map[Name:key OffsetBits:576 SizeBits:192] map[Name:pi_state OffsetBits:768 SizeBits:64] map[Name:rt_waiter OffsetBits:832 SizeBits:64] map[Name:requeue_pi_key OffsetBits:896 SizeBits:64] map[Name:bitset OffsetBits:960 SizeBits:32] map[Name:requeue_state OffsetBits:992 SizeBits:32] map[Name:drop_hb_ref OffsetBits:1024 SizeBits:8]]]
|
| 14/2 |
2026/06/02 13:36 |
llm |
expert |
0m
Model:
Tokens:
input: 13488
output: 31
thoughts: 1509
Thoughts: **Analyzing a KASAN Crash: A Deep Dive**
Okay, so I'm looking at this KASAN report, and my initial thought is: let's break this down. The offset of `wake` is supposedly 56 bytes, or 0x38, that gives me the base address. So, given the address of `this->wake`, I can calculate the address of `this`. Now, the problem is, I see this memory dump at `ffffc900039dfa80`, and it's full of strange values.
My immediate reaction is KASAN shadow bytes. I know 8 bytes of memory map to 1 shadow byte. But wait, those `f1` values! That looks like a 32-byte left redzone. Then, there's `00`, indicating valid memory. But what's this `67 18 a8 42`? That's definitely not a typical KASAN shadow value for valid memory. Is it a tag of some sort? Or is it something else?
Alright, let's recalibrate. The address printed here is the shadow address, not the original, correct? I can calculate the original address from the shadow address using the KASAN_SHADOW_OFFSET. The dump shows the shadow memory, and it looks like `ffffc900039dfa80` is where the shadow memory starts. And that corresponds to the original address. So, this dump is showing the shadow bytes for memory starting at the original address, and that makes sense.
So the buggy address `ffffc900039dfad8` is at an offset of 88 bytes from the start of our memory region, which corresponds to index 11 in the shadow memory. Index 11 is `42`. So the memory at the buggy address maps to the shadow byte at index 11 which is `42`? That's not a standard KASAN value. `f1` is a stack left redzone, `f3` is a stack right redzone. This is very unusual.
Then I remember `struct futex_q` and the `__randomize_layout` flag... Could the offset of `wake` be randomized? Yes, that's it! It could be anything. However, the crash is still a read of size 8 at `ffffc900039dfad8`, and the shadow byte is `42`. Wait, could `42` be something different, some sort of partial access or something?
What if the shadow memory itself is corrupted? Is that possible? Or maybe the stack is corrupted and overwriting the shadow memory? No, KASAN would report that... Then I remember that KASAN shadow memory for vmalloc is allocated. Now, if this `67 18 a8 42` are shadow values, that means the memory is partially accessible? No, it would be 1..7. The shadow memory contains the stack contents because the address is wrong? No, KASAN prints the shadow memory.
No, this is x86_64, so it's generic KASAN. Now I think about it...`67 18 a8 42` could be individual values, right? Like `0x67`, `0x18`, `0xa8`, `0x42`? Is the shadow memory unmapped, or something? The fact that it says "kasan: bad access detected" is what I need to focus on.
The page is a thread stack... and that's usually where `struct futex_q` is allocated, in `futex_wait` or similar. I've got to investigate the allocation context further. Let's see how `struct futex_q` is being allocated and what's happening around the time of the crash.
|
| 15/2 |
2026/06/02 13:36 |
tool |
grepper |
0m
Args: map[Expression:struct futex_q PathPrefix:kernel/futex/]
Results: map[Output:kernel/futex/core.c=200=static void futex_rehash_private(struct futex_private_hash *old,
--
kernel/futex/core.c-207- for (i = 0; i < slots; i++) {
kernel/futex/core.c:208: struct futex_q *this, *tmp;
kernel/futex/core.c-209-
--
kernel/futex/core.c=784=int fault_in_user_writeable(u32 __user *uaddr)
--
kernel/futex/core.c-803- */
kernel/futex/core.c:804:struct futex_q *futex_top_waiter(struct futex_hash_bucket *hb, union futex_key *key)
kernel/futex/core.c-805-{
kernel/futex/core.c:806: struct futex_q *this;
kernel/futex/core.c-807-
--
kernel/futex/core.c=822=void wait_for_owner_exiting(int ret, struct task_struct *exiting)
--
kernel/futex/core.c-851- */
kernel/futex/core.c:852:void __futex_unqueue(struct futex_q *q)
kernel/futex/core.c-853-{
--
kernel/futex/core.c-865-/* The key must be already stored in q->key. */
kernel/futex/core.c:866:void futex_q_lock(struct futex_q *q, struct futex_hash_bucket *hb)
kernel/futex/core.c-867-{
--
kernel/futex/core.c=884=void futex_q_unlock(struct futex_hash_bucket *hb)
--
kernel/futex/core.c-889-
kernel/futex/core.c:890:void __futex_queue(struct futex_q *q, struct futex_hash_bucket *hb,
kernel/futex/core.c-891- struct task_struct *task)
--
kernel/futex/core.c-920- */
kernel/futex/core.c:921:int futex_unqueue(struct futex_q *q)
kernel/futex/core.c-922-{
--
kernel/futex/core.c-965-
kernel/futex/core.c:966:void futex_q_lockptr_lock(struct futex_q *q)
kernel/futex/core.c-967-{
--
kernel/futex/core.c-987- */
kernel/futex/core.c:988:void futex_unqueue_pi(struct futex_q *q)
kernel/futex/core.c-989-{
--
kernel/futex/futex.h=162=struct futex_q;
kernel/futex/futex.h:163:typedef void (futex_wake_fn)(struct wake_q_head *wake_q, struct futex_q *q);
kernel/futex/futex.h-164-
kernel/futex/futex.h-165-/**
kernel/futex/futex.h:166: * struct futex_q - The hashed futex queue entry, one per waiting task
kernel/futex/futex.h-167- * @list: priority-sorted list of tasks waiting on this futex
--
kernel/futex/futex.h-191- */
kernel/futex/futex.h:192:struct futex_q {
kernel/futex/futex.h-193- struct plist_node list;
--
kernel/futex/futex.h-210-
kernel/futex/futex.h:211:extern const struct futex_q futex_q_init;
kernel/futex/futex.h-212-
--
kernel/futex/futex.h=218=extern int get_futex_key(u32 __user *uaddr, unsigned int flags, union futex_key *key,
kernel/futex/futex.h-219- enum futex_access rw);
kernel/futex/futex.h:220:extern void futex_q_lockptr_lock(struct futex_q *q) __acquires(q->lock_ptr);
kernel/futex/futex.h-221-extern struct hrtimer_sleeper *
--
kernel/futex/futex.h=263=extern int futex_wait_setup(u32 __user *uaddr, u32 val, unsigned int flags,
kernel/futex/futex.h:264: struct futex_q *q, union futex_key *key2,
kernel/futex/futex.h-265- struct task_struct *task);
kernel/futex/futex.h:266:extern void futex_do_wait(struct futex_q *q, struct hrtimer_sleeper *timeout);
kernel/futex/futex.h:267:extern bool __futex_wake_mark(struct futex_q *q);
kernel/futex/futex.h:268:extern void futex_wake_mark(struct wake_q_head *wake_q, struct futex_q *q);
kernel/futex/futex.h-269-
kernel/futex/futex.h=270=extern int fault_in_user_writeable(u32 __user *uaddr);
kernel/futex/futex.h:271:extern struct futex_q *futex_top_waiter(struct futex_hash_bucket *hb, union futex_key *key);
kernel/futex/futex.h-272-
--
kernel/futex/futex.h=285=static inline int futex_get_value_locked(u32 *dest, u32 __user *from)
--
kernel/futex/futex.h-290-
kernel/futex/futex.h:291:extern void __futex_unqueue(struct futex_q *q);
kernel/futex/futex.h:292:extern void __futex_queue(struct futex_q *q, struct futex_hash_bucket *hb,
kernel/futex/futex.h-293- struct task_struct *task);
kernel/futex/futex.h:294:extern int futex_unqueue(struct futex_q *q);
kernel/futex/futex.h-295-
--
kernel/futex/futex.h-310- */
kernel/futex/futex.h:311:static inline void futex_queue(struct futex_q *q, struct futex_hash_bucket *hb,
kernel/futex/futex.h-312- struct task_struct *task)
--
kernel/futex/futex.h-320-
kernel/futex/futex.h:321:extern void futex_unqueue_pi(struct futex_q *q);
kernel/futex/futex.h-322-
--
kernel/futex/futex.h=350=static inline int futex_hb_waiters_pending(struct futex_hash_bucket *hb)
--
kernel/futex/futex.h-362-
kernel/futex/futex.h:363:extern void futex_q_lock(struct futex_q *q, struct futex_hash_bucket *hb)
kernel/futex/futex.h-364- __acquires(&hb->lock)
--
kernel/futex/futex.h=379=extern void put_pi_state(struct futex_pi_state *pi_state);
kernel/futex/futex.h:380:extern int fixup_pi_owner(u32 __user *uaddr, struct futex_q *q, int locked);
kernel/futex/futex.h-381-
--
kernel/futex/futex.h=434=struct futex_vector {
kernel/futex/futex.h-435- struct futex_waitv w;
kernel/futex/futex.h:436: struct futex_q q;
kernel/futex/futex.h-437-};
--
kernel/futex/pi.c=516=int futex_lock_pi_atomic(u32 __user *uaddr, struct futex_hash_bucket *hb,
--
kernel/futex/pi.c-523- u32 uval, newval, vpid = task_pid_vnr(task);
kernel/futex/pi.c:524: struct futex_q *top_waiter;
kernel/futex/pi.c-525- int ret;
--
kernel/futex/pi.c=615=static int wake_futex_pi(u32 __user *uaddr, u32 uval,
--
kernel/futex/pi.c-673-
kernel/futex/pi.c:674:static int __fixup_pi_state_owner(u32 __user *uaddr, struct futex_q *q,
kernel/futex/pi.c-675- struct task_struct *argowner)
--
kernel/futex/pi.c-847-
kernel/futex/pi.c:848:static int fixup_pi_state_owner(u32 __user *uaddr, struct futex_q *q,
kernel/futex/pi.c-849- struct task_struct *argowner)
--
kernel/futex/pi.c-876- */
kernel/futex/pi.c:877:int fixup_pi_owner(u32 __user *uaddr, struct futex_q *q, int locked)
kernel/futex/pi.c-878-{
--
kernel/futex/pi.c=923=int futex_lock_pi(u32 __user *uaddr, unsigned int flags, ktime_t *time, int trylock)
--
kernel/futex/pi.c-927- struct rt_mutex_waiter rt_waiter;
kernel/futex/pi.c:928: struct futex_q q = futex_q_init;
kernel/futex/pi.c-929- DEFINE_WAKE_Q(wake_q);
--
kernel/futex/pi.c=1142=int futex_unlock_pi(u32 __user *uaddr, unsigned int flags)
--
kernel/futex/pi.c-1145- union futex_key key = FUTEX_KEY_INIT;
kernel/futex/pi.c:1146: struct futex_q *top_waiter;
kernel/futex/pi.c-1147- int ret;
--
kernel/futex/requeue.c=51=enum {
--
kernel/futex/requeue.c-59-
kernel/futex/requeue.c:60:const struct futex_q futex_q_init = {
kernel/futex/requeue.c-61- /* list gets initialized in futex_queue()*/
--
kernel/futex/requeue.c=75=static inline
kernel/futex/requeue.c:76:void requeue_futex(struct futex_q *q, struct futex_hash_bucket *hb1,
kernel/futex/requeue.c-77- struct futex_hash_bucket *hb2, union futex_key *key2)
--
kernel/futex/requeue.c-98-
kernel/futex/requeue.c:99:static inline bool futex_requeue_pi_prepare(struct futex_q *q,
kernel/futex/requeue.c-100- struct futex_pi_state *pi_state)
--
kernel/futex/requeue.c-131-
kernel/futex/requeue.c:132:static inline void futex_requeue_pi_complete(struct futex_q *q, int locked)
kernel/futex/requeue.c-133-{
--
kernel/futex/requeue.c-162-
kernel/futex/requeue.c:163:static inline int futex_requeue_pi_wakeup_sync(struct futex_q *q)
kernel/futex/requeue.c-164-{
--
kernel/futex/requeue.c=229=static inline
kernel/futex/requeue.c:230:void requeue_pi_wake_futex(struct futex_q *q, union futex_key *key,
kernel/futex/requeue.c-231- struct futex_hash_bucket *hb)
--
kernel/futex/requeue.c=281=futex_proxy_trylock_atomic(u32 __user *pifutex, struct futex_hash_bucket *hb1,
--
kernel/futex/requeue.c-285-{
kernel/futex/requeue.c:286: struct futex_q *top_waiter;
kernel/futex/requeue.c-287- u32 curval;
--
kernel/futex/requeue.c=382=int futex_requeue(u32 __user *uaddr1, unsigned int flags1,
--
kernel/futex/requeue.c-388- struct futex_pi_state *pi_state = NULL;
kernel/futex/requeue.c:389: struct futex_q *this, *next;
kernel/futex/requeue.c-390- DEFINE_WAKE_Q(wake_q);
--
kernel/futex/requeue.c=711=int handle_early_requeue_pi_wakeup(struct futex_hash_bucket *hb,
kernel/futex/requeue.c:712: struct futex_q *q,
kernel/futex/requeue.c-713- struct hrtimer_sleeper *timeout)
--
kernel/futex/requeue.c=784=int futex_wait_requeue_pi(u32 __user *uaddr, unsigned int flags,
--
kernel/futex/requeue.c-790- union futex_key key2 = FUTEX_KEY_INIT;
kernel/futex/requeue.c:791: struct futex_q q = futex_q_init;
kernel/futex/requeue.c-792- struct rt_mutex_base *pi_mutex;
--
kernel/futex/waitwake.c-109-
kernel/futex/waitwake.c:110:bool __futex_wake_mark(struct futex_q *q)
kernel/futex/waitwake.c-111-{
--
kernel/futex/waitwake.c-133- */
kernel/futex/waitwake.c:134:void futex_wake_mark(struct wake_q_head *wake_q, struct futex_q *q)
kernel/futex/waitwake.c-135-{
--
kernel/futex/waitwake.c=155=int futex_wake(u32 __user *uaddr, unsigned int flags, int nr_wake, u32 bitset)
kernel/futex/waitwake.c-156-{
kernel/futex/waitwake.c:157: struct futex_q *this, *next;
kernel/futex/waitwake.c-158- union futex_key key = FUTEX_KEY_INIT;
--
kernel/futex/waitwake.c=251=int futex_wake_op(u32 __user *uaddr1, unsigned int flags, u32 __user *uaddr2,
--
kernel/futex/waitwake.c-254- union futex_key key1 = FUTEX_KEY_INIT, key2 = FUTEX_KEY_INIT;
kernel/futex/waitwake.c:255: struct futex_q *this, *next;
kernel/futex/waitwake.c-256- int ret, op_ret;
--
kernel/futex/waitwake.c=334=static long futex_wait_restart(struct restart_block *restart);
--
kernel/futex/waitwake.c-340- */
kernel/futex/waitwake.c:341:void futex_do_wait(struct futex_q *q, struct hrtimer_sleeper *timeout)
kernel/futex/waitwake.c-342-{
--
kernel/futex/waitwake.c=402=int futex_wait_multiple_setup(struct futex_vector *vs, int count, int *woken)
--
kernel/futex/waitwake.c-444- u32 __user *uaddr = (u32 __user *)(unsigned long)vs[i].w.uaddr;
kernel/futex/waitwake.c:445: struct futex_q *q = &vs[i].q;
kernel/futex/waitwake.c-446- u32 val = vs[i].w.val;
--
kernel/futex/waitwake.c=592=int futex_wait_setup(u32 __user *uaddr, u32 val, unsigned int flags,
kernel/futex/waitwake.c:593: struct futex_q *q, union futex_key *key2,
kernel/futex/waitwake.c-594- struct task_struct *task)
--
kernel/futex/waitwake.c=670=int __futex_wait(u32 __user *uaddr, unsigned int flags, u32 val,
--
kernel/futex/waitwake.c-672-{
kernel/futex/waitwake.c:673: struct futex_q q = futex_q_init;
kernel/futex/waitwake.c-674- int ret;
]
|
| 16/2 |
2026/06/02 13:36 |
llm |
expert |
0m
Model:
Tokens:
input: 20497
output: 37
thoughts: 403
Thoughts: **Analyzing a Potential Race Condition in Futex Wakeup**
Okay, so I'm diving into this futex crash in `futex_wake`. My initial thought is that the problematic code is in `futex_wake` which iterates through the hash bucket chain. The crash is happening during a read of `this->wake` within the iteration loop. Clearly, `this` is a pointer to something in that chain.
Now, I'm remembering that `struct futex_q` is allocated on the stack within functions like `__futex_wait`, `futex_wait_requeue_pi`, and `futex_lock_pi`. The code I'm looking at, specifically within `__futex_wait`, confirms this:
```c
struct futex_q q = futex_q_init;
int ret;
```
If `this` *somehow* points to a stack-allocated `futex_q` from a different thread, that's immediately concerning. If the thread that created that stack frame has already exited or returned from the function where the `futex_q` was allocated (e.g., `__futex_wait`), then `this` is pointing to a freed stack frame β a classic use-after-free scenario. This is the prime suspect for the crash.
However, a nagging question arises: if a thread returns from `__futex_wait`, shouldn't the corresponding `futex_q` instance be removed from the hash bucket? That would mean the pointer is invalid. I need to understand the unqueuing process to confirm this. I need to find the specific point where the queue is unlinked to understand if the pointer is left dangling. I'll check how `__futex_wait` unqueues the structure to understand if it's properly handled in this scenario.
|
| 17/2 |
2026/06/02 13:37 |
tool |
read-file |
0m
Args: map[File:kernel/futex/waitwake.c FirstLine:670 LineCount:50]
Results: map[Contents: 670: int __futex_wait(u32 __user *uaddr, unsigned int flags, u32 val,
671: struct hrtimer_sleeper *to, u32 bitset)
672: {
673: struct futex_q q = futex_q_init;
674: int ret;
675:
676: if (!bitset)
677: return -EINVAL;
678:
679: q.bitset = bitset;
680:
681: retry:
682: /*
683: * Prepare to wait on uaddr. On success, it holds hb->lock and q
684: * is initialized.
685: */
686: ret = futex_wait_setup(uaddr, val, flags, &q, NULL, current);
687: if (ret)
688: return ret;
689:
690: /* futex_queue and wait for wakeup, timeout, or a signal. */
691: futex_do_wait(&q, to);
692:
693: /* If we were woken (and unqueued), we succeeded, whatever. */
694: if (!futex_unqueue(&q))
695: return 0;
696:
697: if (to && !to->task)
698: return -ETIMEDOUT;
699:
700: /*
701: * We expect signal_pending(current), but we might be the
702: * victim of a spurious wakeup as well.
703: */
704: if (!signal_pending(current))
705: goto retry;
706:
707: return -ERESTARTSYS;
708: }
709:
710: int futex_wait(u32 __user *uaddr, unsigned int flags, u32 val, ktime_t *abs_time, u32 bitset)
711: {
712: struct hrtimer_sleeper timeout, *to;
713: struct restart_block *restart;
714: int ret;
715:
716: to = futex_setup_timer(abs_time, &timeout, flags,
717: current->timer_slack_ns);
718:
719: ret = __futex_wait(uaddr, flags, val, to, bitset);
]
|
| 18/2 |
2026/06/02 13:37 |
llm |
expert |
0m
Model:
Tokens:
input: 22056
output: 38
thoughts: 32
Thoughts: Okay, here's my take on those thoughts, framed as an expert's internal monologue:
**Diving into Futex Unqueue**
Alright, so we're in `__futex_wait`, and the code's calling `futex_unqueue(&q)`. That's the key. I need to understand what's happening *there*. `futex_unqueue` is the function that actually pulls threads off the futex wait queue, right? It's likely handling the mechanics of releasing waiting threads. I'm focusing on it because the behavior there defines how efficient our wake-up process will be. I need to remember the subtleties of the wait queue management, potential race conditions and all that. Let's see what we find.
|
| 19/2 |
2026/06/02 13:37 |
tool |
codesearch-definition-source |
0m
Args: map[ContextFile:kernel/futex/waitwake.c Name:futex_unqueue]
Results: map[SourceCode: 910: /**
911: * futex_unqueue() - Remove the futex_q from its futex_hash_bucket
912: * @q: The futex_q to unqueue
913: *
914: * The q->lock_ptr must not be held by the caller. A call to futex_unqueue() must
915: * be paired with exactly one earlier call to futex_queue().
916: *
917: * Return:
918: * - 1 - if the futex_q was still queued (and we removed unqueued it);
919: * - 0 - if the futex_q was already removed by the waking thread
920: */
921: int futex_unqueue(struct futex_q *q)
922: {
923: spinlock_t *lock_ptr;
924: int ret = 0;
925:
926: /* RCU so lock_ptr is not going away during locking. */
927: guard(rcu)();
928: /* In the common case we don't take the spinlock, which is nice. */
929: retry:
930: /*
931: * q->lock_ptr can change between this read and the following spin_lock.
932: * Use READ_ONCE to forbid the compiler from reloading q->lock_ptr and
933: * optimizing lock_ptr out of the logic below.
934: */
935: lock_ptr = READ_ONCE(q->lock_ptr);
936: if (lock_ptr != NULL) {
937: spin_lock(lock_ptr);
938: /*
939: * q->lock_ptr can change between reading it and
940: * spin_lock(), causing us to take the wrong lock. This
941: * corrects the race condition.
942: *
943: * Reasoning goes like this: if we have the wrong lock,
944: * q->lock_ptr must have changed (maybe several times)
945: * between reading it and the spin_lock(). It can
946: * change again after the spin_lock() but only if it was
947: * already changed before the spin_lock(). It cannot,
948: * however, change back to the original value. Therefore
949: * we can detect whether we acquired the correct lock.
950: */
951: if (unlikely(lock_ptr != q->lock_ptr)) {
952: spin_unlock(lock_ptr);
953: goto retry;
954: }
955: __futex_unqueue(q);
956:
957: BUG_ON(q->pi_state);
958:
959: spin_unlock(lock_ptr);
960: ret = 1;
961: }
962:
963: return ret;
964: }
SourceFile:kernel/futex/core.c]
|
| 20/2 |
2026/06/02 13:37 |
llm |
expert |
0m
Model:
Tokens:
input: 23171
output: 39
thoughts: 116
Thoughts: **Race Condition Investigation in Futex Unqueue**
Okay, so I'm looking at `futex_unqueue` and how it interacts with the hash bucket. The core of it seems to be removing the `futex_q` entry. The thing that's nagging me is this: a return of `0` from `futex_unqueue` signifies that another thread, the "waking thread," already yanked it out. Now, that translates directly to `__futex_wait` returning 0, and the current stack frame gets nuked.
But here's where the hair on the back of my neck starts to rise. What happens if the waking thread has *just* removed the entry from the bucket, but hasn't finished its business with the `futex_q` data? The current stack frame is already gone and something could be corrupted! I have a suspicion that this is going to lead me down the path of a nasty race condition.
I need to dig deeper. I'm going to check out `futex_wake_mark` to see if there's any synchronization or atomic operation that handles this window of vulnerability. Hopefully, there's some kind of guardrail there.
|
| 21/2 |
2026/06/02 13:37 |
tool |
codesearch-definition-source |
0m
Args: map[ContextFile:kernel/futex/waitwake.c Name:futex_wake_mark]
Results: map[SourceCode: 128: /*
129: * The hash bucket lock must be held when this is called.
130: * Afterwards, the futex_q must not be accessed. Callers
131: * must ensure to later call wake_up_q() for the actual
132: * wakeups to occur.
133: */
134: void futex_wake_mark(struct wake_q_head *wake_q, struct futex_q *q)
135: {
136: struct task_struct *p = q->task;
137:
138: get_task_struct(p);
139:
140: if (!__futex_wake_mark(q)) {
141: put_task_struct(p);
142: return;
143: }
144:
145: /*
146: * Queue the task for later wakeup for after we've released
147: * the hb->lock.
148: */
149: wake_q_add_safe(wake_q, p);
150: }
SourceFile:kernel/futex/waitwake.c]
|
| 22/2 |
2026/06/02 13:37 |
llm |
expert |
0m
Model:
Tokens:
input: 23934
output: 40
thoughts: 39
Thoughts: Here's my attempt at summarizing the thought process, tailored for an expert audience:
**Futex Wake/Mark Conundrum**
Alright, hang on a second. I'm seeing something in the documentation for `futex_wake_mark` that's making me raise an eyebrow. It explicitly states, "Afterwards, the `futex_q` must not be accessed." That's... interesting, and potentially a critical detail. Let's dig deeper. The implications of that restriction are immediately apparent - it directly impacts synchronization primitives and related functions which might involve accessing the futex queue after a wake operation. It's crucial for understanding race conditions and ensuring correctness. I need to refresh my understanding of *why* that access restriction is there. Time to jump over to `__futex_wake_mark` and see what's going on under the hood. Itβs likely there's a good reason for this constraint.
|
| 23/2 |
2026/06/02 13:37 |
tool |
codesearch-definition-source |
0m
Args: map[ContextFile:kernel/futex/waitwake.c Name:__futex_wake_mark]
Results: map[SourceCode: 110: bool __futex_wake_mark(struct futex_q *q)
111: {
112: if (WARN(q->pi_state || q->rt_waiter, "refusing to wake PI futex\n"))
113: return false;
114:
115: __futex_unqueue(q);
116: /*
117: * The waiting task can free the futex_q as soon as q->lock_ptr = NULL
118: * is written, without taking any locks. This is possible in the event
119: * of a spurious wakeup, for example. A memory barrier is required here
120: * to prevent the following store to lock_ptr from getting ahead of the
121: * plist_del in __futex_unqueue().
122: */
123: smp_store_release(&q->lock_ptr, NULL);
124:
125: return true;
126: }
SourceFile:kernel/futex/waitwake.c]
|
| 24/2 |
2026/06/02 13:37 |
llm |
expert |
0m
Model:
Tokens:
input: 24519
output: 37
thoughts: 326
Thoughts: **Futex Wake Conundrum**
Okay, let's break this down. `__futex_wake_mark` is doing the work here, unqueuing from the `futex_q` and, crucially, setting `q->lock_ptr` to NULL. That NULL write is the signal; once the waiting task sees it, it's free to release the `futex_q`. This means *nothing* should touch the `futex_q` after `__futex_wake_mark` completes. That's the key.
But now I see the problem. `futex_wake` calls `this->wake(&wake_q, this);`. Normally `this->wake` will be `futex_wake_mark`. And, if so, `__futex_wake_mark` is called, which frees `this`.
My thought process is that `this->wake` is a function pointer, right? In `futex_wake`, it calls `this->wake(&wake_q, this);`. Can the function pointer be evaluated *after* the function is called? No, that's not how function pointers work. It's evaluated *before* the call. So the function call is done, then the context is lost? I'm missing something here.
Okay, let's go back to the code, and look specifically at that call to `futex_wake` at kernel/futex/waitwake.c:191. I need to re-examine what's happening *right* around that line.
|
| 25/2 |
2026/06/02 13:37 |
tool |
read-file |
0m
Args: map[File:kernel/futex/waitwake.c FirstLine:180 LineCount:20]
Results: map[Contents: 180: plist_for_each_entry_safe(this, next, &hb->chain, list) {
181: if (futex_match (&this->key, &key)) {
182: if (this->pi_state || this->rt_waiter) {
183: ret = -EINVAL;
184: break;
185: }
186:
187: /* Check if one of the bits is set in both bitsets */
188: if (!(this->bitset & bitset))
189: continue;
190:
191: this->wake(&wake_q, this);
192: if (++ret >= nr_wake)
193: break;
194: }
195: }
196:
197: spin_unlock(&hb->lock);
198: wake_up_q(&wake_q);
199: return ret;
]
|
| 26/2 |
2026/06/02 13:37 |
llm |
expert |
0m
Model:
Tokens:
input: 25501
output: 41
thoughts: 459
Thoughts: **Debugging a Use-After-Free in the Kernel**
Okay, let's break this down. Line 191, `this->wake(&wake_q, this);`, that's where things are going sideways. The crash is a read of size 8 at `this->wake`. The debugger says we're hitting `this->wake`, but it's a use-after-free. The immediate thought is that `this->wake` is being read *before* the call. But if the read *is* happening before the call, how can it be use-after-free?
Right, `this` is a pointer to a `struct futex_q`. The loop we're in uses `plist_for_each_entry_safe(this, next, &hb->chain, list)`. So, `this` is coming from a list. The obvious scenario would be that `this` was freed *before* we get to evaluate `this->wake`. But how can that happen? We're inside a protected critical section.
We know `hb->lock` is held: `spin_lock(&hb->lock);`. If we have `hb->lock` nobody can remove `this` from the list, because `futex_unqueue` takes `hb->lock` through the `q->lock_ptr`. Wait, let me check `futex_unqueue`:
```c
lock_ptr = READ_ONCE(q->lock_ptr);
if (lock_ptr != NULL) {
spin_lock(lock_ptr);
...
__futex_unqueue(q);
...
spin_unlock(lock_ptr);
}
```
Okay, so if `q->lock_ptr` isn't NULL, `futex_unqueue` grabs the spinlock. If `futex_wake` holds the lock, `futex_unqueue` should block on the `spin_lock(lock_ptr)` call. So, `this` *cannot* be unqueued by `futex_unqueue` while we're holding the lock in `futex_wake`. It should be impossible, right?
Alright, how else could `this` be freed? Hmm, could `this` be a `futex_q` from `futex_wait_multiple_setup`? Let's check `futex_wait_multiple_setup` and see how it fits into the equation. Let's start there.
|
| 27/2 |
2026/06/02 13:37 |
tool |
codesearch-definition-source |
0m
Args: map[ContextFile:kernel/futex/waitwake.c Name:futex_wait_multiple_setup]
Results: map[SourceCode: 386: /**
387: * futex_wait_multiple_setup - Prepare to wait and enqueue multiple futexes
388: * @vs: The futex list to wait on
389: * @count: The size of the list
390: * @woken: Index of the last woken futex, if any. Used to notify the
391: * caller that it can return this index to userspace (return parameter)
392: *
393: * Prepare multiple futexes in a single step and enqueue them. This may fail if
394: * the futex list is invalid or if any futex was already awoken. On success the
395: * task is ready to interruptible sleep.
396: *
397: * Return:
398: * - 1 - One of the futexes was woken by another thread
399: * - 0 - Success
400: * - <0 - -EFAULT, -EWOULDBLOCK or -EINVAL
401: */
402: int futex_wait_multiple_setup(struct futex_vector *vs, int count, int *woken)
403: {
404: bool retry = false;
405: int ret, i;
406: u32 uval;
407:
408: /*
409: * Make sure to have a reference on the private_hash such that we
410: * don't block on rehash after changing the task state below.
411: */
412: guard(private_hash)();
413:
414: /*
415: * Enqueuing multiple futexes is tricky, because we need to enqueue
416: * each futex on the list before dealing with the next one to avoid
417: * deadlocking on the hash bucket. But, before enqueuing, we need to
418: * make sure that current->state is TASK_INTERRUPTIBLE, so we don't
419: * lose any wake events, which cannot be done before the get_futex_key
420: * of the next key, because it calls get_user_pages, which can sleep.
421: * Thus, we fetch the list of futexes keys in two steps, by first
422: * pinning all the memory keys in the futex key, and only then we read
423: * each key and queue the corresponding futex.
424: *
425: * Private futexes doesn't need to recalculate hash in retry, so skip
426: * get_futex_key() when retrying.
427: */
428: retry:
429: for (i = 0; i < count; i++) {
430: if (!(vs[i].w.flags & FLAGS_SHARED) && retry)
431: continue;
432:
433: ret = get_futex_key(u64_to_user_ptr(vs[i].w.uaddr),
434: vs[i].w.flags,
435: &vs[i].q.key, FUTEX_READ);
436:
437: if (unlikely(ret))
438: return ret;
439: }
440:
441: set_current_state(TASK_INTERRUPTIBLE|TASK_FREEZABLE);
442:
443: for (i = 0; i < count; i++) {
444: u32 __user *uaddr = (u32 __user *)(unsigned long)vs[i].w.uaddr;
445: struct futex_q *q = &vs[i].q;
446: u32 val = vs[i].w.val;
447:
448: if (1) {
449: CLASS(hb, hb)(&q->key);
450:
451: futex_q_lock(q, hb);
452: ret = futex_get_value_locked(&uval, uaddr);
453:
454: if (!ret && uval == val) {
455: /*
456: * The bucket lock can't be held while dealing with the
457: * next futex. Queue each futex at this moment so hb can
458: * be unlocked.
459: */
460: futex_queue(q, hb, current);
461: continue;
462: }
463:
464: futex_q_unlock(hb);
465: __release(q->lock_ptr);
466: }
467: __set_current_state(TASK_RUNNING);
468:
469: /*
470: * Even if something went wrong, if we find out that a futex
471: * was woken, we don't return error and return this index to
472: * userspace
473: */
474: *woken = futex_unqueue_multiple(vs, i);
475: if (*woken >= 0)
476: return 1;
477:
478: if (ret) {
479: /*
480: * If we need to handle a page fault, we need to do so
481: * without any lock and any enqueued futex (otherwise
482: * we could lose some wakeup). So we do it here, after
483: * undoing all the work done so far. In success, we
484: * retry all the work.
485: */
486: if (get_user(uval, uaddr))
487: return -EFAULT;
488:
489: retry = true;
490: goto retry;
491: }
492:
493: if (uval != val)
494: return -EWOULDBLOCK;
495: }
496:
497: return 0;
498: }
SourceFile:kernel/futex/waitwake.c]
|
| 28/2 |
2026/06/02 13:37 |
llm |
expert |
0m
Model:
Tokens:
input: 28304
output: 40
thoughts: 63
Thoughts: **Analyzing `futex_wait_multiple_setup` and `futex_unqueue_multiple`**
Okay, let me think this through. Hang on, the `futex_wait_multiple_setup` function... it seems to be queuing *multiple* futexes, which is key. That implies we're managing a set of wait operations at once. So, if `futex_unqueue_multiple` gets called, it should, in theory, unqueue *all* those futexes. But the crucial detail here is the range: it specifically states it unqueues from 0 up to, but not including, `i-1`. Hmm, which index does `i` represent? That needs to be understood within the context of the larger setup. It is important to know which futexes are included and excluded. I need to take a closer look at the implementation of `futex_unqueue_multiple` to confirm my understanding and figure out the exact behavior. Let's delve into that function's code now.
|
| 29/2 |
2026/06/02 13:37 |
tool |
codesearch-definition-source |
0m
Args: map[ContextFile:kernel/futex/waitwake.c Name:futex_unqueue_multiple]
Results: map[SourceCode: 363: /**
364: * futex_unqueue_multiple - Remove various futexes from their hash bucket
365: * @v: The list of futexes to unqueue
366: * @count: Number of futexes in the list
367: *
368: * Helper to unqueue a list of futexes. This can't fail.
369: *
370: * Return:
371: * - >=0 - Index of the last futex that was awoken;
372: * - -1 - No futex was awoken
373: */
374: int futex_unqueue_multiple(struct futex_vector *v, int count)
375: {
376: int ret = -1, i;
377:
378: for (i = 0; i < count; i++) {
379: if (!futex_unqueue(&v[i].q))
380: ret = i;
381: }
382:
383: return ret;
384: }
SourceFile:kernel/futex/waitwake.c]
|
| 30/2 |
2026/06/02 13:37 |
llm |
expert |
0m
Model:
Tokens:
input: 28980
output: 54
thoughts: 30
Thoughts: Okay, here's my take on those thoughts, framed as an expert's internal monologue:
**Investigating `futex_wait_multiple_setup` and Memory Allocation**
Right, let me see... I'm looking at `futex_wait_multiple_setup` in the kernel code, and it's taking a `struct futex_vector *vs` as an argument. That's immediately got me thinking β a vector, huh? Multiple futexes potentially, all handled in one go. Efficient for a group operation. But the critical question is always memory management, isn't it? Where exactly is this `vs` *allocated*? I need to trace back where the caller is constructing that `futex_vector` structure. Is it on the stack? Is it dynamically allocated with `kmalloc`? Is it coming from some pre-allocated pool? The allocation strategy is going to impact performance β is it a fast path or potentially a slow one that could block? And, of course, memory leaks are always a concern. I'd better dig in and trace the call chain to understand the memory context fully. This is key to understanding the performance characteristics of this multi-futex wait mechanism.
|
| 31/2 |
2026/06/02 13:37 |
tool |
codesearch-find-references |
0m
Args: map[ContextFile:kernel/futex/waitwake.c IncludeSnippetLines:5 Name:futex_wait_multiple_setup SourceTreePrefix:]
Results: map[References:[map[ReferenceKind:calls ReferencingEntityKind:function ReferencingEntityName:futex_wait_multiple SourceFile:kernel/futex/waitwake.c SourceLine:546 SourceSnippet: 541:
542: if (to)
543: hrtimer_sleeper_start_expires(to, HRTIMER_MODE_ABS);
544:
545: while (1) {
546: ret = futex_wait_multiple_setup(vs, count, &hint);
547: if (ret) {
548: if (ret > 0) {
549: /* A futex was woken during setup */
550: ret = hint;
551: }
] map[ReferenceKind:calls ReferencingEntityKind:function ReferencingEntityName:io_futexv_wait SourceFile:io_uring/futex.c SourceLine:232 SourceSnippet: 227: struct io_ring_ctx *ctx = req->ctx;
228: int ret, woken = -1;
229:
230: io_ring_submit_lock(ctx, issue_flags);
231:
232: ret = futex_wait_multiple_setup(ifd->futexv, iof->futex_nr, &woken);
233:
234: /*
235: * Error case, ret is < 0. Mark the request as failed.
236: */
237: if (unlikely(ret < 0)) {
]] TruncatedOutput:false]
|
| 32/2 |
2026/06/02 13:37 |
llm |
expert |
0m
Model:
Tokens:
input: 29777
output: 39
thoughts: 36
Thoughts: Okay, here's my interpretation of that thought process, from an expert's perspective:
**Diving into Futex Implementation Details**
Right, *`io_futexv_wait`*... that rings a bell. My immediate thought is, "Ah ha! It seems like this function is where the `futex_wait_multiple_setup` function is called." That's interesting, and potentially revealing. I've got to take a closer look at `io_futexv_wait` now. Understanding how that setup function is used in this part of the kernel is going to give me a deeper understanding of the futex implementation strategy at this layer. Time to get digging.
|
| 33/2 |
2026/06/02 13:37 |
tool |
codesearch-definition-source |
0m
Args: map[ContextFile:io_uring/futex.c Name:io_futexv_wait]
Results: map[SourceCode: 223: int io_futexv_wait(struct io_kiocb *req, unsigned int issue_flags)
224: {
225: struct io_futex *iof = io_kiocb_to_cmd(req, struct io_futex);
226: struct io_futexv_data *ifd = req->async_data;
227: struct io_ring_ctx *ctx = req->ctx;
228: int ret, woken = -1;
229:
230: io_ring_submit_lock(ctx, issue_flags);
231:
232: ret = futex_wait_multiple_setup(ifd->futexv, iof->futex_nr, &woken);
233:
234: /*
235: * Error case, ret is < 0. Mark the request as failed.
236: */
237: if (unlikely(ret < 0)) {
238: io_ring_submit_unlock(ctx, issue_flags);
239: req_set_fail(req);
240: io_req_set_res(req, ret, 0);
241: io_req_async_data_free(req);
242: return IOU_COMPLETE;
243: }
244:
245: /*
246: * 0 return means that we successfully setup the waiters, and that
247: * nobody triggered a wakeup while we were doing so. If the wakeup
248: * happened post setup, the task_work will be run post this issue and
249: * under the submission lock. 1 means We got woken while setting up,
250: * let that side do the completion. Note that
251: * futex_wait_multiple_setup() will have unqueued all the futexes in
252: * this case. Mark us as having done that already, since this is
253: * different from normal wakeup.
254: */
255: if (!ret) {
256: /*
257: * If futex_wait_multiple_setup() returns 0 for a
258: * successful setup, then the task state will not be
259: * runnable. This is fine for the sync syscall, as
260: * it'll be blocking unless we already got one of the
261: * futexes woken, but it obviously won't work for an
262: * async invocation. Mark us runnable again.
263: */
264: __set_current_state(TASK_RUNNING);
265: hlist_add_head(&req->hash_node, &ctx->futex_list);
266: } else {
267: iof->futexv_unqueued = 1;
268: if (woken != -1)
269: io_req_set_res(req, woken, 0);
270: }
271:
272: io_ring_submit_unlock(ctx, issue_flags);
273: return IOU_ISSUE_SKIP_COMPLETE;
274: }
SourceFile:io_uring/futex.c]
|
| 34/2 |
2026/06/02 13:37 |
llm |
expert |
0m
Model:
Tokens:
input: 30909
output: 38
thoughts: 919
Thoughts: **Analyzing the io_uring Futexv Bug**
Okay, so `io_futexv_wait` is an io_uring command, right? And the `futexv` data is stored in `req->async_data`. My initial thought was that `io_uring` itself allocated `req->async_data`. But hold on, this bug report...
The report points to a vmalloc virtual mapping, and the address is linked to a thread stack! Not `req->async_data` at all. This means we're dealing with something allocated on the *thread* stack, likely a `struct futex_q`. The stack was created by `clone3` which means it's for a new thread.
Now, the crucial clue: the stack was *freed*. The backtrace clearly shows the freeing process, with `__free_frozen_pages`, and then slab freeing functions, including `vm_area_dup`. `vm_area_dup` allocates a `vm_area_struct` from the slab, which triggers freeing. And this included a call to `kasan_quarantine_reduce`, which is another indicator of page freeing.
So, the thread that allocated the stack has exited, and the stack itself has been freed. But that means its `struct futex_q` is still linked in the futex hash bucket, even though the stack it was on is gone. How is that possible? A thread exiting should clean up its stack and any associated resources, especially if they are thread-local. That feels wrong! I need to dig into `__futex_wait` to understand the link between `futex_q` and the thread lifecycle.
|
| 35/2 |
2026/06/02 13:38 |
tool |
codesearch-definition-source |
0m
Args: map[ContextFile:kernel/futex/waitwake.c Name:__futex_wait]
Results: map[SourceCode: 670: int __futex_wait(u32 __user *uaddr, unsigned int flags, u32 val,
671: struct hrtimer_sleeper *to, u32 bitset)
672: {
673: struct futex_q q = futex_q_init;
674: int ret;
675:
676: if (!bitset)
677: return -EINVAL;
678:
679: q.bitset = bitset;
680:
681: retry:
682: /*
683: * Prepare to wait on uaddr. On success, it holds hb->lock and q
684: * is initialized.
685: */
686: ret = futex_wait_setup(uaddr, val, flags, &q, NULL, current);
687: if (ret)
688: return ret;
689:
690: /* futex_queue and wait for wakeup, timeout, or a signal. */
691: futex_do_wait(&q, to);
692:
693: /* If we were woken (and unqueued), we succeeded, whatever. */
694: if (!futex_unqueue(&q))
695: return 0;
696:
697: if (to && !to->task)
698: return -ETIMEDOUT;
699:
700: /*
701: * We expect signal_pending(current), but we might be the
702: * victim of a spurious wakeup as well.
703: */
704: if (!signal_pending(current))
705: goto retry;
706:
707: return -ERESTARTSYS;
708: }
SourceFile:kernel/futex/waitwake.c]
|
| 36/2 |
2026/06/02 13:38 |
llm |
expert |
0m
Model:
Tokens:
input: 32780
output: 37
thoughts: 110
Thoughts: **Analyzing Futex Unqueue and Potential Race Conditions**
Okay, so I'm thinking through this `futex_unqueue(&q)` function call. If it returns 0, it means the `futex_q` was already removed by the thread that was supposed to wake it up. Hmm, if `futex_unqueue(&q)` is 0 then, `__futex_wait` is going to return 0. The thread returns to user space, and its stack frame gets destroyed. That seems straightforward enough, but that doesn't feel right. There's a nagging feeling about a race condition here. What if the thread waking up the other one has removed the queue from the hash bucket, but it's still in the middle of accessing the memory? Let me go back and re-examine the `futex_wake` function, step by step, and trace through the code again. I need to make sure I'm not missing something subtle here with the memory access and deletion.
|
| 37/2 |
2026/06/02 13:38 |
tool |
read-file |
0m
Args: map[File:kernel/futex/waitwake.c FirstLine:178 LineCount:20]
Results: map[Contents: 178: spin_lock(&hb->lock);
179:
180: plist_for_each_entry_safe(this, next, &hb->chain, list) {
181: if (futex_match (&this->key, &key)) {
182: if (this->pi_state || this->rt_waiter) {
183: ret = -EINVAL;
184: break;
185: }
186:
187: /* Check if one of the bits is set in both bitsets */
188: if (!(this->bitset & bitset))
189: continue;
190:
191: this->wake(&wake_q, this);
192: if (++ret >= nr_wake)
193: break;
194: }
195: }
196:
197: spin_unlock(&hb->lock);
]
|
| 38/2 |
2026/06/02 13:38 |
llm |
expert |
0m
Model:
Tokens:
input: 33418
output: 39
thoughts: 53
Thoughts: Okay, let's break this down.
**Diving into the `futex_wake` Process**
Alright, so I'm tracing through this `futex_wake` function, and the key thing that's jumping out at me is this call: `this->wake(&wake_q, this);`. That's where the real magic happens. Now, based on my understanding, that `this->wake` call usually resolves to `futex_wake_mark`. It's time to double-check that assumption and really focus on what's going on within `futex_wake_mark`. We need to be absolutely certain we've got the correct implementation context, so let's get back into the code.
|
| 39/2 |
2026/06/02 13:38 |
tool |
codesearch-definition-source |
0m
Args: map[ContextFile:kernel/futex/waitwake.c Name:futex_wake_mark]
Results: map[SourceCode: 128: /*
129: * The hash bucket lock must be held when this is called.
130: * Afterwards, the futex_q must not be accessed. Callers
131: * must ensure to later call wake_up_q() for the actual
132: * wakeups to occur.
133: */
134: void futex_wake_mark(struct wake_q_head *wake_q, struct futex_q *q)
135: {
136: struct task_struct *p = q->task;
137:
138: get_task_struct(p);
139:
140: if (!__futex_wake_mark(q)) {
141: put_task_struct(p);
142: return;
143: }
144:
145: /*
146: * Queue the task for later wakeup for after we've released
147: * the hb->lock.
148: */
149: wake_q_add_safe(wake_q, p);
150: }
SourceFile:kernel/futex/waitwake.c]
|
| 40/2 |
2026/06/02 13:38 |
llm |
expert |
0m
Model:
Tokens:
input: 34017
output: 41
thoughts: 482
Thoughts: **Debugging a Subtle Race Condition in Futex Wakeup**
Okay, let's break this down. I'm looking at `futex_wake_mark`, which I see calls `__futex_wake_mark(q)`. Now, `__futex_wake_mark` unqueues the `futex_q` and, importantly, sets `q->lock_ptr = NULL`. Once that NULL write is done, the waiting task should be able to return and reclaim its stack.
But, and this is crucial, `futex_wake` seems to evaluate `this->wake` *before* it actually calls `__futex_wake_mark`. That means `this->wake` is being read *before* `__futex_wake_mark` is even called. So how could this be a use-after-free scenario?
Ah, right! I'm seeing `plist_for_each_entry_safe(this, next, &hb->chain, list)`. The loop's `next` variable is evaluated *before* the loop body executes. So, `next` is read from `this->list.next`. Then it reads `this->wake`. And, then `this->wake` is called and frees the memory pointed to by `this`.
Okay, wait... if `this` is freed *inside* the loop, that is safe because `next` was already evaluated. However, the crash I'm investigating involves a read of `this->wake`. If `this` is already freed *before* `this->wake` is read, how could that happen?
Could *another* thread be the culprit? Is it possible some other thread freed `this`? If another thread unqueues it, it must have unqueued it. But `futex_wake` holds `hb->lock`, so another thread shouldn't be able to unqueue it because `futex_unqueue` takes `hb->lock`.
Wait, what if `this` is *not* unqueued by `futex_unqueue`? What if the thread that allocated `this` just exits without unqueuing it? No, that's not right. `__futex_wait` calls `futex_unqueue(&q)` before returning. And even if the thread is killed, `futex_do_wait` returns, which means `futex_unqueue` *should* be called.
What if there's an error from `futex_wait_multiple_setup`? Let me check `futex_wait_multiple_setup` again...
|
| 41/2 |
2026/06/02 13:38 |
tool |
codesearch-definition-source |
0m
Args: map[ContextFile:kernel/futex/waitwake.c Name:futex_wait_multiple_setup]
Results: map[SourceCode: 386: /**
387: * futex_wait_multiple_setup - Prepare to wait and enqueue multiple futexes
388: * @vs: The futex list to wait on
389: * @count: The size of the list
390: * @woken: Index of the last woken futex, if any. Used to notify the
391: * caller that it can return this index to userspace (return parameter)
392: *
393: * Prepare multiple futexes in a single step and enqueue them. This may fail if
394: * the futex list is invalid or if any futex was already awoken. On success the
395: * task is ready to interruptible sleep.
396: *
397: * Return:
398: * - 1 - One of the futexes was woken by another thread
399: * - 0 - Success
400: * - <0 - -EFAULT, -EWOULDBLOCK or -EINVAL
401: */
402: int futex_wait_multiple_setup(struct futex_vector *vs, int count, int *woken)
403: {
404: bool retry = false;
405: int ret, i;
406: u32 uval;
407:
408: /*
409: * Make sure to have a reference on the private_hash such that we
410: * don't block on rehash after changing the task state below.
411: */
412: guard(private_hash)();
413:
414: /*
415: * Enqueuing multiple futexes is tricky, because we need to enqueue
416: * each futex on the list before dealing with the next one to avoid
417: * deadlocking on the hash bucket. But, before enqueuing, we need to
418: * make sure that current->state is TASK_INTERRUPTIBLE, so we don't
419: * lose any wake events, which cannot be done before the get_futex_key
420: * of the next key, because it calls get_user_pages, which can sleep.
421: * Thus, we fetch the list of futexes keys in two steps, by first
422: * pinning all the memory keys in the futex key, and only then we read
423: * each key and queue the corresponding futex.
424: *
425: * Private futexes doesn't need to recalculate hash in retry, so skip
426: * get_futex_key() when retrying.
427: */
428: retry:
429: for (i = 0; i < count; i++) {
430: if (!(vs[i].w.flags & FLAGS_SHARED) && retry)
431: continue;
432:
433: ret = get_futex_key(u64_to_user_ptr(vs[i].w.uaddr),
434: vs[i].w.flags,
435: &vs[i].q.key, FUTEX_READ);
436:
437: if (unlikely(ret))
438: return ret;
439: }
440:
441: set_current_state(TASK_INTERRUPTIBLE|TASK_FREEZABLE);
442:
443: for (i = 0; i < count; i++) {
444: u32 __user *uaddr = (u32 __user *)(unsigned long)vs[i].w.uaddr;
445: struct futex_q *q = &vs[i].q;
446: u32 val = vs[i].w.val;
447:
448: if (1) {
449: CLASS(hb, hb)(&q->key);
450:
451: futex_q_lock(q, hb);
452: ret = futex_get_value_locked(&uval, uaddr);
453:
454: if (!ret && uval == val) {
455: /*
456: * The bucket lock can't be held while dealing with the
457: * next futex. Queue each futex at this moment so hb can
458: * be unlocked.
459: */
460: futex_queue(q, hb, current);
461: continue;
462: }
463:
464: futex_q_unlock(hb);
465: __release(q->lock_ptr);
466: }
467: __set_current_state(TASK_RUNNING);
468:
469: /*
470: * Even if something went wrong, if we find out that a futex
471: * was woken, we don't return error and return this index to
472: * userspace
473: */
474: *woken = futex_unqueue_multiple(vs, i);
475: if (*woken >= 0)
476: return 1;
477:
478: if (ret) {
479: /*
480: * If we need to handle a page fault, we need to do so
481: * without any lock and any enqueued futex (otherwise
482: * we could lose some wakeup). So we do it here, after
483: * undoing all the work done so far. In success, we
484: * retry all the work.
485: */
486: if (get_user(uval, uaddr))
487: return -EFAULT;
488:
489: retry = true;
490: goto retry;
491: }
492:
493: if (uval != val)
494: return -EWOULDBLOCK;
495: }
496:
497: return 0;
498: }
SourceFile:kernel/futex/waitwake.c]
|
| 42/2 |
2026/06/02 13:38 |
llm |
expert |
0m
Model:
Tokens:
input: 36856
output: 39
thoughts: 317
Thoughts: **Analyzing Futex Wait Scenarios**
Okay, let's break this down. My initial thought was about `futex_wait_multiple_setup`. If `get_user(uval, uaddr)` fails in that function, it returns `-EFAULT`. Critically, it *also* calls `futex_unqueue_multiple(vs, i)` *before* that, right? So we're unqueuing futexes before the error is returned. `futex_unqueue_multiple` handles unqueuing `vs[0]` to `vs[i-1]`. I just want to be sure I understand the error flow. What happens if `get_futex_key` fails within the loop? Okay, it returns `ret`. No futexes would have been queued at that point, so that path seems correct and safe.
Now, a new scenario pops up: What if `io_futexv_wait` is the culprit? It calls `futex_wait_multiple_setup`, which we just looked at. If `futex_wait_multiple_setup` returns 0, it adds `req` to `ctx->futex_list`. The io_uring worker thread then returns. But a key point here, isn't `vs` in `futex_wait_multiple_setup` the same as `ifd->futexv`? Where *is* `ifd->futexv` allocated? Oh, right, it's inside `req->async_data`. Okay, that makes sense. However, the crash I'm seeing is happening on a thread stack, not in the `io_uring` context, so that rules out `io_futexv_wait` as the origin.
Alright, if it's not `io_futexv_wait`, then the crash is originating from `futex_wait_multiple` or `futex_wait` or `futex_wait_requeue_pi` or `futex_lock_pi`. I'm ruling out `io_futexv_wait` and I need to start with the most complex, so let's focus our attention on `futex_wait_multiple`.
|
| 43/2 |
2026/06/02 13:38 |
tool |
codesearch-definition-source |
0m
Args: map[ContextFile:kernel/futex/waitwake.c Name:futex_wait_multiple]
Results: map[SourceCode: 523: /**
524: * futex_wait_multiple - Prepare to wait on and enqueue several futexes
525: * @vs: The list of futexes to wait on
526: * @count: The number of objects
527: * @to: Timeout before giving up and returning to userspace
528: *
529: * Entry point for the FUTEX_WAIT_MULTIPLE futex operation, this function
530: * sleeps on a group of futexes and returns on the first futex that is
531: * wake, or after the timeout has elapsed.
532: *
533: * Return:
534: * - >=0 - Hint to the futex that was awoken
535: * - <0 - On error
536: */
537: int futex_wait_multiple(struct futex_vector *vs, unsigned int count,
538: struct hrtimer_sleeper *to)
539: {
540: int ret, hint = 0;
541:
542: if (to)
543: hrtimer_sleeper_start_expires(to, HRTIMER_MODE_ABS);
544:
545: while (1) {
546: ret = futex_wait_multiple_setup(vs, count, &hint);
547: if (ret) {
548: if (ret > 0) {
549: /* A futex was woken during setup */
550: ret = hint;
551: }
552: return ret;
553: }
554:
555: futex_sleep_multiple(vs, count, to);
556:
557: __set_current_state(TASK_RUNNING);
558:
559: ret = futex_unqueue_multiple(vs, count);
560: if (ret >= 0)
561: return ret;
562:
563: if (to && !to->task)
564: return -ETIMEDOUT;
565: else if (signal_pending(current))
566: return -ERESTARTSYS;
567: /*
568: * The final case is a spurious wakeup, for
569: * which just retry.
570: */
571: }
572: }
SourceFile:kernel/futex/waitwake.c]
|
| 44/2 |
2026/06/02 13:38 |
llm |
expert |
0m
Model:
Tokens:
input: 38416
output: 52
thoughts: 44
Thoughts: Here's my analysis of the provided information, as if these were my internal thoughts:
**Dissecting futex_wait_multiple's Context**
Okay, so I'm looking at `futex_wait_multiple`, and the key data structure it uses is `struct futex_vector *vs`. That's the core. My immediate question is: where the hell is this `vs` allocated? Is it being passed in from userspace, or is it an internal kernel-space allocation? Understanding the lifecycle of this vector is absolutely crucial to grasping the behavior of the entire function.
Alright, so I need to track down the allocation. My next step is to examine the calling functions. I'll check either `sys_futex` or `do_futex`. These are the most likely candidates. Probably `sys_futex` as it looks to be the system call, but `do_futex` is a better place to see what's really happening under the hood. I know the general structure: `sys_` functions are the entry points to the kernel, and the `do_` functions handle the core logic. So, let's look at how the vector is being passed around, allocated, and freed within the context of those functions, to understand how it's being used.
|
| 45/2 |
2026/06/02 13:38 |
tool |
codesearch-find-references |
0m
Args: map[ContextFile:kernel/futex/waitwake.c IncludeSnippetLines:5 Name:futex_wait_multiple SourceTreePrefix:]
Results: map[References:[map[ReferenceKind:calls ReferencingEntityKind:function ReferencingEntityName:__do_sys_futex_waitv SourceFile:kernel/futex/syscalls.c SourceLine:345 SourceSnippet: 340: }
341:
342: ret = futex_parse_waitv(futexv, waiters, nr_futexes, futex_wake_mark,
343: NULL);
344: if (!ret)
345: ret = futex_wait_multiple(futexv, nr_futexes, timeout ? &to : NULL);
346:
347: kfree(futexv);
348:
349: destroy_timer:
350: if (timeout)
]] TruncatedOutput:false]
|
| 46/2 |
2026/06/02 13:38 |
llm |
expert |
0m
Model:
Tokens:
input: 39025
output: 42
thoughts: 99
Thoughts: **Debugging a Potential Futex Issue**
Okay, so `futexv` is allocated using `kfree(futexv)`. That means it lives on the heap, making it dynamically allocated. But the crash I'm seeing is occurring on a thread stack, and that's the key here. Given this, it's pretty clear that the problem isn't originating within the `futex_wait_multiple` function. That narrows down the possibilities significantly. My gut's telling me to rule out `futex_wait_multiple` since that uses the heap.
This points me to either `__futex_wait`, `futex_wait_requeue_pi`, or `futex_lock_pi` as the most likely suspects. Time to focus my attention on `futex_wait_requeue_pi` and see what's going on there. Let's dig into that function and see if there are any clues.
|
| 47/2 |
2026/06/02 13:38 |
tool |
codesearch-definition-source |
0m
Args: map[ContextFile:kernel/futex/requeue.c Name:futex_wait_requeue_pi]
Results: map[SourceCode: 744: /**
745: * futex_wait_requeue_pi() - Wait on uaddr and take uaddr2
746: * @uaddr: the futex we initially wait on (non-pi)
747: * @flags: futex flags (FLAGS_SHARED, FLAGS_CLOCKRT, etc.), they must be
748: * the same type, no requeueing from private to shared, etc.
749: * @val: the expected value of uaddr
750: * @abs_time: absolute timeout
751: * @bitset: 32 bit wakeup bitset set by userspace, defaults to all
752: * @uaddr2: the pi futex we will take prior to returning to user-space
753: *
754: * The caller will wait on uaddr and will be requeued by futex_requeue() to
755: * uaddr2 which must be PI aware and unique from uaddr. Normal wakeup will wake
756: * on uaddr2 and complete the acquisition of the rt_mutex prior to returning to
757: * userspace. This ensures the rt_mutex maintains an owner when it has waiters;
758: * without one, the pi logic would not know which task to boost/deboost, if
759: * there was a need to.
760: *
761: * We call schedule in futex_wait_queue() when we enqueue and return there
762: * via the following--
763: * 1) wakeup on uaddr2 after an atomic lock acquisition by futex_requeue()
764: * 2) wakeup on uaddr2 after a requeue
765: * 3) signal
766: * 4) timeout
767: *
768: * If 3, cleanup and return -ERESTARTNOINTR.
769: *
770: * If 2, we may then block on trying to take the rt_mutex and return via:
771: * 5) successful lock
772: * 6) signal
773: * 7) timeout
774: * 8) other lock acquisition failure
775: *
776: * If 6, return -EWOULDBLOCK (restarting the syscall would do the same).
777: *
778: * If 4 or 7, we cleanup and return with -ETIMEDOUT.
779: *
780: * Return:
781: * - 0 - On success;
782: * - <0 - On error
783: */
784: int futex_wait_requeue_pi(u32 __user *uaddr, unsigned int flags,
785: u32 val, ktime_t *abs_time, u32 bitset,
786: u32 __user *uaddr2)
787: {
788: struct hrtimer_sleeper timeout, *to;
789: struct rt_mutex_waiter rt_waiter;
790: union futex_key key2 = FUTEX_KEY_INIT;
791: struct futex_q q = futex_q_init;
792: struct rt_mutex_base *pi_mutex;
793: int res, ret;
794:
795: if (!IS_ENABLED(CONFIG_FUTEX_PI))
796: return -ENOSYS;
797:
798: if (uaddr == uaddr2)
799: return -EINVAL;
800:
801: if (!bitset)
802: return -EINVAL;
803:
804: to = futex_setup_timer(abs_time, &timeout, flags,
805: current->timer_slack_ns);
806:
807: /*
808: * The waiter is allocated on our stack, manipulated by the requeue
809: * code while we sleep on uaddr.
810: */
811: rt_mutex_init_waiter(&rt_waiter);
812:
813: ret = get_futex_key(uaddr2, flags, &key2, FUTEX_WRITE);
814: if (unlikely(ret != 0))
815: goto out;
816:
817: q.bitset = bitset;
818: q.rt_waiter = &rt_waiter;
819: q.requeue_pi_key = &key2;
820:
821: /*
822: * Prepare to wait on uaddr. On success, it holds hb->lock and q
823: * is initialized.
824: */
825: ret = futex_wait_setup(uaddr, val, flags, &q, &key2, current);
826: if (ret)
827: goto out;
828:
829: /* Queue the futex_q, drop the hb lock, wait for wakeup. */
830: futex_do_wait(&q, to);
831:
832: switch (futex_requeue_pi_wakeup_sync(&q)) {
833: case Q_REQUEUE_PI_IGNORE:
834: {
835: CLASS(hb, hb)(&q.key);
836: /* The waiter is still on uaddr1 */
837: spin_lock(&hb->lock);
838: ret = handle_early_requeue_pi_wakeup(hb, &q, to);
839: spin_unlock(&hb->lock);
840: }
841: break;
842:
843: case Q_REQUEUE_PI_LOCKED:
844: /* The requeue acquired the lock */
845: if (q.pi_state && (q.pi_state->owner != current)) {
846: futex_q_lockptr_lock(&q);
847: ret = fixup_pi_owner(uaddr2, &q, true);
848: /*
849: * Drop the reference to the pi state which the
850: * requeue_pi() code acquired for us.
851: */
852: put_pi_state(q.pi_state);
853: spin_unlock(q.lock_ptr);
854: /*
855: * Adjust the return value. It's either -EFAULT or
856: * success (1) but the caller expects 0 for success.
857: */
858: ret = ret < 0 ? ret : 0;
859: }
860: break;
861:
862: case Q_REQUEUE_PI_DONE:
863: /* Requeue completed. Current is 'pi_blocked_on' the rtmutex */
864: pi_mutex = &q.pi_state->pi_mutex;
865: ret = rt_mutex_wait_proxy_lock(pi_mutex, to, &rt_waiter);
866:
867: /*
868: * See futex_unlock_pi()'s cleanup: comment.
869: */
870: if (ret && !rt_mutex_cleanup_proxy_lock(pi_mutex, &rt_waiter))
871: ret = 0;
872:
873: futex_q_lockptr_lock(&q);
874: debug_rt_mutex_free_waiter(&rt_waiter);
875: /*
876: * Fixup the pi_state owner and possibly acquire the lock if we
877: * haven't already.
878: */
879: res = fixup_pi_owner(uaddr2, &q, !ret);
880: /*
881: * If fixup_pi_owner() returned an error, propagate that. If it
882: * acquired the lock, clear -ETIMEDOUT or -EINTR.
883: */
884: if (res)
885: ret = (res < 0) ? res : 0;
886:
887: futex_unqueue_pi(&q);
888: spin_unlock(q.lock_ptr);
889:
890: if (ret == -EINTR) {
891: /*
892: * We've already been requeued, but cannot restart
893: * by calling futex_lock_pi() directly. We could
894: * restart this syscall, but it would detect that
895: * the user space "val" changed and return
896: * -EWOULDBLOCK. Save the overhead of the restart
897: * and return -EWOULDBLOCK directly.
898: */
899: ret = -EWOULDBLOCK;
900: }
901: break;
902: default:
903: BUG();
904: }
905: if (q.drop_hb_ref) {
906: CLASS(hb, hb)(&q.key);
907: /* Additional reference from requeue_pi_wake_futex() */
908: futex_hash_put(hb);
909: }
910:
911: out:
912: if (to) {
913: hrtimer_cancel(&to->timer);
914: destroy_hrtimer_on_stack(&to->timer);
915: }
916: return ret;
917: }
SourceFile:kernel/futex/requeue.c]
|
| 48/2 |
2026/06/02 13:38 |
llm |
expert |
0m
Model:
Tokens:
input: 42144
output: 43
thoughts: 116
Thoughts: **Analyzing the Futex Wait and Requeue Process**
Okay, so I'm looking at `futex_wait_requeue_pi` here. The flow starts with a call to `futex_do_wait` β straightforward enough, that's the core wait operation. After that, we hit `futex_requeue_pi_wakeup_sync`. Now, this is where it gets interesting, because depending on the return value... If it returns `Q_REQUEUE_PI_IGNORE`, then we jump into `handle_early_requeue_pi_wakeup`.
Hang on a second. This `handle_early_requeue_pi_wakeup` call⦠Could it potentially unqueue the futex *within* that function? It's a possibility, and if it does, it changes the entire subsequent logic. The normal flow assumes the futex remains queued after the `futex_requeue_pi_wakeup_sync` function. It's time to dig into the implementation of `handle_early_requeue_pi_wakeup`. I need to understand that function's behavior *precisely* to ensure the integrity of the futex wait/requeue logic. This could have performance implications if it requires early re-queuing but may or may not do so.
|
| 49/2 |
2026/06/02 13:39 |
tool |
codesearch-definition-source |
0m
Args: map[ContextFile:kernel/futex/requeue.c Name:handle_early_requeue_pi_wakeup]
Results: map[SourceCode: 699: /**
700: * handle_early_requeue_pi_wakeup() - Handle early wakeup on the initial futex
701: * @hb: the hash_bucket futex_q was original enqueued on
702: * @q: the futex_q woken while waiting to be requeued
703: * @timeout: the timeout associated with the wait (NULL if none)
704: *
705: * Determine the cause for the early wakeup.
706: *
707: * Return:
708: * -EWOULDBLOCK or -ETIMEDOUT or -ERESTARTNOINTR
709: */
710: static inline
711: int handle_early_requeue_pi_wakeup(struct futex_hash_bucket *hb,
712: struct futex_q *q,
713: struct hrtimer_sleeper *timeout)
714: {
715: int ret;
716:
717: /*
718: * With the hb lock held, we avoid races while we process the wakeup.
719: * We only need to hold hb (and not hb2) to ensure atomicity as the
720: * wakeup code can't change q.key from uaddr to uaddr2 if we hold hb.
721: * It can't be requeued from uaddr2 to something else since we don't
722: * support a PI aware source futex for requeue.
723: */
724: WARN_ON_ONCE(&hb->lock != q->lock_ptr);
725:
726: /*
727: * We were woken prior to requeue by a timeout or a signal.
728: * Conditionally unqueue the futex_q and determine which it was.
729: */
730: if (!plist_node_empty(&q->list)) {
731: plist_del(&q->list, &hb->chain);
732: futex_hb_waiters_dec(hb);
733: }
734:
735: /* Handle spurious wakeups gracefully */
736: ret = -EWOULDBLOCK;
737: if (timeout && !timeout->task)
738: ret = -ETIMEDOUT;
739: else if (signal_pending(current))
740: ret = -ERESTARTNOINTR;
741: return ret;
742: }
SourceFile:kernel/futex/requeue.c]
|
| 50/2 |
2026/06/02 13:39 |
llm |
expert |
0m
Model:
Tokens:
input: 43314
output: 38
thoughts: 150
Thoughts: **Futex Requeue and the Missing Link**
Okay, so I'm digging into this futex code, specifically looking at `handle_early_requeue_pi_wakeup`. It's unqueuing from the futex queue, using the `plist_del` function and decrementing `futex_hb_waiters`. That makes sense. But... hold on a minute! I see that it doesn't set `q->lock_ptr = NULL`! That seems like a potential race condition waiting to happen. If a task wakes up and then tries to grab the lock, it could lead to unexpected behavior.
Wait a second, let me think. Is this also happening in `futex_unqueue`? Nope, that doesn't set `q->lock_ptr = NULL` either. Okay, but I remember seeing an `__futex_unqueue` somewhere. Let me check that.
That's the one! It *does* set `q->lock_ptr = NULL`. Good, that makes sense. Let me take another look at that function, just to make sure I'm not missing something else.
|
| 51/2 |
2026/06/02 13:39 |
tool |
codesearch-definition-source |
0m
Args: map[ContextFile:kernel/futex/core.c Name:__futex_unqueue]
Results: map[SourceCode: 846: /**
847: * __futex_unqueue() - Remove the futex_q from its futex_hash_bucket
848: * @q: The futex_q to unqueue
849: *
850: * The q->lock_ptr must not be NULL and must be held by the caller.
851: */
852: void __futex_unqueue(struct futex_q *q)
853: {
854: struct futex_hash_bucket *hb;
855:
856: if (WARN_ON_SMP(!q->lock_ptr) || WARN_ON(plist_node_empty(&q->list)))
857: return;
858: lockdep_assert_held(q->lock_ptr);
859:
860: hb = container_of(q->lock_ptr, struct futex_hash_bucket, lock);
861: plist_del(&q->list, &hb->chain);
862: futex_hb_waiters_dec(hb);
863: }
SourceFile:kernel/futex/core.c]
|
| 52/2 |
2026/06/02 13:39 |
llm |
expert |
0m
Model:
Tokens:
input: 44079
output: 37
thoughts: 1073
Thoughts: **Analyzing Futex Wakeup and Stack Frame Safety**
Okay, let's break this down. My initial thought was about how `__futex_unqueue` doesn't nullify `q->lock_ptr`. But wait, `__futex_wake_mark` *does* set `q->lock_ptr` to `NULL` after unqueuing. So, the waking thread is the one responsible for nulling that pointer. This is important.
Now, if `handle_early_requeue_pi_wakeup` unqueues the futex_q, it *doesn't* set `q->lock_ptr` to `NULL`. It just removes it from the list with `plist_del`. This means it *cannot* be accessed by `futex_wake` anymore, right? But hold on... what if `futex_wake` is iterating over the list and has *already* read the `next` pointer of the entry being unqueued?
But then I remember, `futex_wake` holds `hb->lock`! And `handle_early_requeue_pi_wakeup` is called *with* `hb->lock` held. So, they can't run concurrently. If one has the lock, the other must wait. Therefore, the list entry can't be freed while `futex_wake` is iterating. That's a key point.
However, what about the case when `futex_wake` is iterating and *calls* `this->wake`? Remember that `this->wake` is `futex_wake_mark`, which calls `__futex_wake_mark`, which *does* unqueue and nullify `q->lock_ptr`. Then the waiting thread wakes up and *should* be able to free its stack frame. But wait a second, `futex_wake` still has `hb->lock`. The waiting thread is blocked in `futex_do_wait`. When `wake_q_add_safe` is called, the thread isn't woken yet. It is woken up by `wake_up_q`, which happens *after* the `spin_unlock(&hb->lock)`. So the waiting thread can't free the stack frame until `futex_wake` releases the lock and calls `wake_up_q`. This is solid reasoning.
Now, what if the waiting thread is woken by a SIGNAL or TIMEOUT? If that happens, it returns from `futex_do_wait` and calls `futex_unqueue(&q)`. `futex_unqueue` tries to take `hb->lock`. If `futex_wake` is still holding the lock, then `futex_unqueue` will block! So the waiting thread *can't* free the stack frame while `futex_wake` holds `hb->lock`. But wait⦠what if `q->lock_ptr` is already NULL?
If `futex_wake` has *already* called `this->wake` (which called `__futex_wake_mark`, which sets `q->lock_ptr` to NULL), then `futex_unqueue` will read `q->lock_ptr == NULL` and *immediately* return 0. The `__futex_wait` will return 0 right away, and the waiting thread returns to user space! And the stack frame *can* be freed! But `futex_wake` still has `hb->lock`. However, `futex_wake` has *already* processed the entry; it just called `this->wake` and is moving to the next entry. It doesn't access `this` anymore! So, the waiting thread *is* safe to release its stack frame.
So, it seems we've got it. The stack frame is only safe to release if the waiting thread is woken up by `futex_wake` after `futex_wake` has released the `hb->lock`, or if the thread is woken up by a signal or timeout *after* `futex_wake` has already processed the entry, unqueued it, and set the lock pointer to NULL.
|
| 53/2 |
2026/06/02 13:39 |
tool |
read-file |
0m
Args: map[File:kernel/futex/waitwake.c FirstLine:180 LineCount:20]
Results: map[Contents: 180: plist_for_each_entry_safe(this, next, &hb->chain, list) {
181: if (futex_match (&this->key, &key)) {
182: if (this->pi_state || this->rt_waiter) {
183: ret = -EINVAL;
184: break;
185: }
186:
187: /* Check if one of the bits is set in both bitsets */
188: if (!(this->bitset & bitset))
189: continue;
190:
191: this->wake(&wake_q, this);
192: if (++ret >= nr_wake)
193: break;
194: }
195: }
196:
197: spin_unlock(&hb->lock);
198: wake_up_q(&wake_q);
199: return ret;
]
|
| 54/2 |
2026/06/02 13:39 |
llm |
expert |
0m
Model:
Tokens:
input: 46367
output: 38
thoughts: 460
Thoughts: **Debugging a Memory Access Crash in `futex_wake`**
Okay, so I'm looking at this crash in `futex_wake`, specifically at line 191: `this->wake(&wake_q, this);`. The debugger is telling me there's a read of size 8 at that location, meaning we're trying to read `this->wake`, but `this` is already *gone*! How the heck did `this` get freed before this line was even executed?
The code seems straightforward enough. It iterates through a queue, waking threads. I see `this->wake(&wake_q, this);` within the loop, and there's a check `if (++ret >= nr_wake) break;` That `plist_for_each_entry_safe` uses `next`. The crucial point is that `next` is evaluated BEFORE the wake function. So, we're not accessing `this` after the next pointer is set.
But this isn't right... the crash IS happening inside `this->wake`. That read is happening at line 191, which is `this->wake(&wake_q, this);`! Which means the `this` pointer *must* be invalid at that point. How could the waiting thread have freed the stack frame before we even get there? If the waiting thread freed the stack, it shouldn't be in the list anymore. If it's still in the list, it should be blocked by `hb->lock` in `futex_unqueue`, right? Unless... wait... could `q->lock_ptr` be NULL?
But why would `q->lock_ptr` be NULL? We've already removed it from the list. The code `__futex_wake_mark` is supposed to set `q->lock_ptr = NULL` *after* removing from the list. It's supposed to do a release store! So, while it's in the list, `q->lock_ptr` shouldn't be NULL.
So, if it isn't `NULL` something must be changing it! Could `futex_requeue` change `q->lock_ptr`? Let's check `futex_requeue`...
|
| 55/2 |
2026/06/02 13:39 |
tool |
codesearch-definition-source |
0m
Args: map[ContextFile:kernel/futex/requeue.c Name:requeue_futex]
Results: map[SourceCode: 68: /**
69: * requeue_futex() - Requeue a futex_q from one hb to another
70: * @q: the futex_q to requeue
71: * @hb1: the source hash_bucket
72: * @hb2: the target hash_bucket
73: * @key2: the new key for the requeued futex_q
74: */
75: static inline
76: void requeue_futex(struct futex_q *q, struct futex_hash_bucket *hb1,
77: struct futex_hash_bucket *hb2, union futex_key *key2)
78: {
79:
80: /*
81: * If key1 and key2 hash to the same bucket, no need to
82: * requeue.
83: */
84: if (likely(&hb1->chain != &hb2->chain)) {
85: plist_del(&q->list, &hb1->chain);
86: futex_hb_waiters_dec(hb1);
87: futex_hb_waiters_inc(hb2);
88: plist_add(&q->list, &hb2->chain);
89: q->lock_ptr = &hb2->lock;
90: /*
91: * hb1 and hb2 belong to the same futex_hash_bucket_private
92: * because if we managed get a reference on hb1 then it can't be
93: * replaced. Therefore we avoid put(hb1)+get(hb2) here.
94: */
95: }
96: q->key = *key2;
97: }
SourceFile:kernel/futex/requeue.c]
|
| 56/2 |
2026/06/02 13:39 |
llm |
expert |
0m
Model:
Tokens:
input: 47860
output: 38
thoughts: 2455
Thoughts: **Analyzing Futex Queue Corruption**
Okay, so I'm digging into this futex queue corruption issue, focusing on how a `struct futex_q` could end up dangling after a thread exits. The initial thought process was: if `requeue_futex` is involved, it should be safe because it holds both `hb1->lock` and `hb2->lock` while moving the `q` between lists and changing `q->lock_ptr`. But then `futex_wait_multiple_setup` came into view, and that threw a wrench into things, but that ultimately was not involved in the observed crash.
The key is that the crash occurs on a thread stack, so it can't be `futex_wait_multiple`. It must be one of the calls that uses a `futex_q` on the stack, like `__futex_wait`, `futex_wait_requeue_pi` or `futex_lock_pi`. The main problem is that a thread *shouldn't* exit if it has a `futex_q` still on its stack because these functions block the thread.
If a thread is killed (SIGKILL), the `futex_do_wait` call should return, and `futex_unqueue` should be called. `futex_unqueue` *must* be called before returning from `__futex_wait`, and it's supposed to remove the `futex_q` from the hash bucket. If `futex_unqueue` fails, it should return 0, which means the futex was already waken up. But if `futex_unqueue` fails to remove the queue, that's bad. I needed to see how that could even happen.
I considered `futex_wake` and how it interacts with the hash buckets, but the logic should be safe; the `hb->lock` is held, ensuring atomic operations.
Then, the possibility that `q->lock_ptr` could point to a *different* lock due to `futex_requeue` came up. If `futex_requeue` changed the `lock_ptr` and also moved the queue to `hb2`, then a `futex_wake` on `hb1` wouldn't see the queue, and a `futex_wake` on `hb2` would block on `hb2->lock`, so that's also safe.
The next thought was around `futex_wait_requeue_pi`, and the different scenarios it presents using the `requeue_state`. I dissected the possible paths depending on `Q_REQUEUE_PI_IGNORE`, `Q_REQUEUE_PI_LOCKED`, and `Q_REQUEUE_PI_DONE` and how they manage the locks and unqueueing. Everything there looked safe as well.
I even checked the failure path in `futex_wait_setup`, but it should be safe since the `futex_q` isn't added to any lists.
This brought me back to `futex_wait_multiple`. My thinking was that the `futex_vector` containing the `futex_q` is allocated with `kmalloc_array`, which is on the heap, not the stack. However, the stack trace indicates a stack address. Therefore, `futex_wait_multiple` can be ruled out.
Therefore, the issue is not related to `futex_wait_multiple`. It must be a direct use of `__futex_wait`, `futex_wait_requeue_pi` or `futex_lock_pi`, where the stack-allocated `futex_q` is involved.
|
| 57/2 |
2026/06/02 13:40 |
tool |
codesearch-definition-source |
0m
Args: map[ContextFile:kernel/futex/pi.c Name:futex_lock_pi]
Results: map[SourceCode: 914: /*
915: * Userspace tried a 0 -> TID atomic transition of the futex value
916: * and failed. The kernel side here does the whole locking operation:
917: * if there are waiters then it will block as a consequence of relying
918: * on rt-mutexes, it does PI, etc. (Due to races the kernel might see
919: * a 0 value of the futex too.).
920: *
921: * Also serves as futex trylock_pi()'ing, and due semantics.
922: */
923: int futex_lock_pi(u32 __user *uaddr, unsigned int flags, ktime_t *time, int trylock)
924: {
925: struct hrtimer_sleeper timeout, *to;
926: struct task_struct *exiting;
927: struct rt_mutex_waiter rt_waiter;
928: struct futex_q q = futex_q_init;
929: DEFINE_WAKE_Q(wake_q);
930: int res, ret;
931:
932: if (!IS_ENABLED(CONFIG_FUTEX_PI))
933: return -ENOSYS;
934:
935: if (refill_pi_state_cache())
936: return -ENOMEM;
937:
938: to = futex_setup_timer(time, &timeout, flags, 0);
939:
940: retry:
941: exiting = NULL;
942: ret = get_futex_key(uaddr, flags, &q.key, FUTEX_WRITE);
943: if (unlikely(ret != 0))
944: goto out;
945:
946: retry_private:
947: if (1) {
948: CLASS(hb, hb)(&q.key);
949:
950: futex_q_lock(&q, hb);
951:
952: ret = futex_lock_pi_atomic(uaddr, hb, &q.key, &q.pi_state, current,
953: &exiting, 0);
954: if (unlikely(ret)) {
955: /*
956: * Atomic work succeeded and we got the lock,
957: * or failed. Either way, we do _not_ block.
958: */
959: switch (ret) {
960: case 1:
961: /* We got the lock. */
962: ret = 0;
963: goto out_unlock_put_key;
964: case -EFAULT:
965: goto uaddr_faulted;
966: case -EBUSY:
967: case -EAGAIN:
968: /*
969: * Two reasons for this:
970: * - EBUSY: Task is exiting and we just wait for the
971: * exit to complete.
972: * - EAGAIN: The user space value changed.
973: */
974: futex_q_unlock(hb);
975: __release(q.lock_ptr);
976: /*
977: * Handle the case where the owner is in the middle of
978: * exiting. Wait for the exit to complete otherwise
979: * this task might loop forever, aka. live lock.
980: */
981: wait_for_owner_exiting(ret, exiting);
982: cond_resched();
983: goto retry;
984: default:
985: goto out_unlock_put_key;
986: }
987: }
988:
989: WARN_ON(!q.pi_state);
990:
991: /*
992: * Only actually queue now that the atomic ops are done:
993: */
994: __futex_queue(&q, hb, current);
995:
996: if (trylock) {
997: ret = rt_mutex_futex_trylock(&q.pi_state->pi_mutex);
998: /* Fixup the trylock return value: */
999: ret = ret ? 0 : -EWOULDBLOCK;
1000: goto no_block;
1001: }
1002:
1003: /*
1004: * Caution; releasing @hb in-scope. The hb->lock is still locked
1005: * while the reference is dropped. The reference can not be dropped
1006: * after the unlock because if a user initiated resize is in progress
1007: * then we might need to wake him. This can not be done after the
1008: * rt_mutex_pre_schedule() invocation. The hb will remain valid because
1009: * the thread, performing resize, will block on hb->lock during
1010: * the requeue.
1011: */
1012: futex_hash_put(no_free_ptr(hb));
1013: /*
1014: * Must be done before we enqueue the waiter, here is unfortunately
1015: * under the hb lock, but that *should* work because it does nothing.
1016: */
1017: rt_mutex_pre_schedule();
1018:
1019: rt_mutex_init_waiter(&rt_waiter);
1020:
1021: /*
1022: * On PREEMPT_RT, when hb->lock becomes an rt_mutex, we must not
1023: * hold it while doing rt_mutex_start_proxy(), because then it will
1024: * include hb->lock in the blocking chain, even through we'll not in
1025: * fact hold it while blocking. This will lead it to report -EDEADLK
1026: * and BUG when futex_unlock_pi() interleaves with this.
1027: *
1028: * Therefore acquire wait_lock while holding hb->lock, but drop the
1029: * latter before calling __rt_mutex_start_proxy_lock(). This
1030: * interleaves with futex_unlock_pi() -- which does a similar lock
1031: * handoff -- such that the latter can observe the futex_q::pi_state
1032: * before __rt_mutex_start_proxy_lock() is done.
1033: */
1034: raw_spin_lock_irq(&q.pi_state->pi_mutex.wait_lock);
1035: spin_unlock(q.lock_ptr);
1036: /*
1037: * __rt_mutex_start_proxy_lock() unconditionally enqueues the @rt_waiter
1038: * such that futex_unlock_pi() is guaranteed to observe the waiter when
1039: * it sees the futex_q::pi_state.
1040: */
1041: ret = __rt_mutex_start_proxy_lock(&q.pi_state->pi_mutex, &rt_waiter, current, &wake_q);
1042: raw_spin_unlock_irq_wake(&q.pi_state->pi_mutex.wait_lock, &wake_q);
1043:
1044: if (ret) {
1045: if (ret == 1)
1046: ret = 0;
1047: goto cleanup;
1048: }
1049:
1050: if (unlikely(to))
1051: hrtimer_sleeper_start_expires(to, HRTIMER_MODE_ABS);
1052:
1053: ret = rt_mutex_wait_proxy_lock(&q.pi_state->pi_mutex, to, &rt_waiter);
1054:
1055: cleanup:
1056: /*
1057: * If we failed to acquire the lock (deadlock/signal/timeout), we must
1058: * unwind the above, however we canont lock hb->lock because
1059: * rt_mutex already has a waiter enqueued and hb->lock can itself try
1060: * and enqueue an rt_waiter through rtlock.
1061: *
1062: * Doing the cleanup without holding hb->lock can cause inconsistent
1063: * state between hb and pi_state, but only in the direction of not
1064: * seeing a waiter that is leaving.
1065: *
1066: * See futex_unlock_pi(), it deals with this inconsistency.
1067: *
1068: * There be dragons here, since we must deal with the inconsistency on
1069: * the way out (here), it is impossible to detect/warn about the race
1070: * the other way around (missing an incoming waiter).
1071: *
1072: * What could possibly go wrong...
1073: */
1074: if (ret && !rt_mutex_cleanup_proxy_lock(&q.pi_state->pi_mutex, &rt_waiter))
1075: ret = 0;
1076:
1077: /*
1078: * Now that the rt_waiter has been dequeued, it is safe to use
1079: * spinlock/rtlock (which might enqueue its own rt_waiter) and fix up
1080: * the
1081: */
1082: futex_q_lockptr_lock(&q);
1083: /*
1084: * Waiter is unqueued.
1085: */
1086: rt_mutex_post_schedule();
1087: no_block:
1088: /*
1089: * Fixup the pi_state owner and possibly acquire the lock if we
1090: * haven't already.
1091: */
1092: res = fixup_pi_owner(uaddr, &q, !ret);
1093: /*
1094: * If fixup_pi_owner() returned an error, propagate that. If it acquired
1095: * the lock, clear our -ETIMEDOUT or -EINTR.
1096: */
1097: if (res)
1098: ret = (res < 0) ? res : 0;
1099:
1100: __release(&hb->lock);
1101: futex_unqueue_pi(&q);
1102: spin_unlock(q.lock_ptr);
1103: if (q.drop_hb_ref) {
1104: CLASS(hb, hb)(&q.key);
1105: /* Additional reference from futex_unlock_pi() */
1106: futex_hash_put(hb);
1107: }
1108: goto out;
1109:
1110: out_unlock_put_key:
1111: futex_q_unlock(hb);
1112: __release(q.lock_ptr);
1113: goto out;
1114:
1115: uaddr_faulted:
1116: futex_q_unlock(hb);
1117: __release(q.lock_ptr);
1118:
1119: ret = fault_in_user_writeable(uaddr);
1120: if (ret)
1121: goto out;
1122:
1123: if (!(flags & FLAGS_SHARED))
1124: goto retry_private;
1125:
1126: goto retry;
1127: }
1128:
1129: out:
1130: if (to) {
1131: hrtimer_cancel(&to->timer);
1132: destroy_hrtimer_on_stack(&to->timer);
1133: }
1134: return ret != -EINTR ? ret : -ERESTARTNOINTR;
1135: }
SourceFile:kernel/futex/pi.c]
|
| 58/2 |
2026/06/02 13:40 |
llm |
expert |
0m
Model:
Tokens:
input: 54561
output: 39
thoughts: 37
Thoughts: Here's my summary, as the thought process unfolds:
**Diving into futex_unqueue_pi**
Okay, so we're in `futex_lock_pi` right now, and the code calls `futex_unqueue_pi(&q)`. Time to dig deeper into that function. I need to understand what `futex_unqueue_pi` is doing. It's obviously involved in priority inheritance, given the "PI" suffix. It's likely related to removing a process from a wait queue on this futex. I'll need to see how it interacts with the queue `q`, what it does with the waiting process, and most importantly, how it ensures the priority inheritance mechanism works correctly when a process is unqueued. Let's see how they've implemented it...
|
| 59/2 |
2026/06/02 13:40 |
tool |
codesearch-definition-source |
0m
Args: map[ContextFile:kernel/futex/core.c Name:futex_unqueue_pi]
Results: map[SourceCode: 984: /*
985: * PI futexes can not be requeued and must remove themselves from the hash
986: * bucket. The hash bucket lock (i.e. lock_ptr) is held.
987: */
988: void futex_unqueue_pi(struct futex_q *q)
989: {
990: /*
991: * If the lock was not acquired (due to timeout or signal) then the
992: * rt_waiter is removed before futex_q is. If this is observed by
993: * an unlocker after dropping the rtmutex wait lock and before
994: * acquiring the hash bucket lock, then the unlocker dequeues the
995: * futex_q from the hash bucket list to guarantee consistent state
996: * vs. userspace. Therefore the dequeue here must be conditional.
997: */
998: if (!plist_node_empty(&q->list))
999: __futex_unqueue(q);
1000:
1001: BUG_ON(!q->pi_state);
1002: put_pi_state(q->pi_state);
1003: q->pi_state = NULL;
1004: }
SourceFile:kernel/futex/core.c]
|
| 60/2 |
2026/06/02 13:40 |
llm |
expert |
0m
Model:
Tokens:
input: 55190
output: 38
thoughts: 126
Thoughts: Okay, here's my breakdown of what's going on, thinking through this code:
**Unpacking the `futex_unqueue_pi` Logic**
Alright, so `futex_unqueue_pi` calls `__futex_unqueue(q)`. That's the core unqueuing function. Interesting. Now, here's where it gets a little subtle... it *doesn't* set `q->lock_ptr` to `NULL` directly. That omission immediately triggers a flag for me. It's often critical to nullify pointers after freeing the memory they point to, so this raises a red flag.
But then, the comment! Ah, the comment provides the key. It highlights a specific race condition: if an unlocker observes that the wait lock has been dropped but *before* the hash bucket lock is acquired, the unlocker itself dequeues the `futex_q`. This is a crucial detail for maintaining consistency with userspace. I need to keep this in mind as I continue digging. The design clearly indicates that `futex_unlock_pi` *can* dequeue the `futex_q` under certain conditions.
So, where does this leave us? Now I need to see how `futex_unlock_pi` handles this situation, let's go check it out.
|
| 61/2 |
2026/06/02 13:40 |
tool |
codesearch-definition-source |
0m
Args: map[ContextFile:kernel/futex/pi.c Name:futex_unlock_pi]
Results: map[SourceCode:1137: /*
1138: * Userspace attempted a TID -> 0 atomic transition, and failed.
1139: * This is the in-kernel slowpath: we look up the PI state (if any),
1140: * and do the rt-mutex unlock.
1141: */
1142: int futex_unlock_pi(u32 __user *uaddr, unsigned int flags)
1143: {
1144: u32 curval, uval, vpid = task_pid_vnr(current);
1145: union futex_key key = FUTEX_KEY_INIT;
1146: struct futex_q *top_waiter;
1147: int ret;
1148:
1149: if (!IS_ENABLED(CONFIG_FUTEX_PI))
1150: return -ENOSYS;
1151:
1152: retry:
1153: if (get_user(uval, uaddr))
1154: return -EFAULT;
1155: /*
1156: * We release only a lock we actually own:
1157: */
1158: if ((uval & FUTEX_TID_MASK) != vpid)
1159: return -EPERM;
1160:
1161: ret = get_futex_key(uaddr, flags, &key, FUTEX_WRITE);
1162: if (ret)
1163: return ret;
1164:
1165: CLASS(hb, hb)(&key);
1166: spin_lock(&hb->lock);
1167: retry_hb:
1168:
1169: /*
1170: * Check waiters first. We do not trust user space values at
1171: * all and we at least want to know if user space fiddled
1172: * with the futex value instead of blindly unlocking.
1173: */
1174: top_waiter = futex_top_waiter(hb, &key);
1175: if (top_waiter) {
1176: struct futex_pi_state *pi_state = top_waiter->pi_state;
1177: struct rt_mutex_waiter *rt_waiter;
1178:
1179: ret = -EINVAL;
1180: if (!pi_state)
1181: goto out_unlock;
1182:
1183: /*
1184: * If current does not own the pi_state then the futex is
1185: * inconsistent and user space fiddled with the futex value.
1186: */
1187: if (pi_state->owner != current)
1188: goto out_unlock;
1189:
1190: /*
1191: * By taking wait_lock while still holding hb->lock, we ensure
1192: * there is no point where we hold neither; and thereby
1193: * wake_futex_pi() must observe any new waiters.
1194: *
1195: * Since the cleanup: case in futex_lock_pi() removes the
1196: * rt_waiter without holding hb->lock, it is possible for
1197: * wake_futex_pi() to not find a waiter while the above does,
1198: * in this case the waiter is on the way out and it can be
1199: * ignored.
1200: *
1201: * In particular; this forces __rt_mutex_start_proxy() to
1202: * complete such that we're guaranteed to observe the
1203: * rt_waiter.
1204: */
1205: raw_spin_lock_irq(&pi_state->pi_mutex.wait_lock);
1206:
1207: /*
1208: * Futex vs rt_mutex waiter state -- if there are no rt_mutex
1209: * waiters even though futex thinks there are, then the waiter
1210: * is leaving. The entry needs to be removed from the list so a
1211: * new futex_lock_pi() is not using this stale PI-state while
1212: * the futex is available in user space again.
1213: * There can be more than one task on its way out so it needs
1214: * to retry.
1215: */
1216: rt_waiter = rt_mutex_top_waiter(&pi_state->pi_mutex);
1217: if (!rt_waiter) {
1218: /*
1219: * Acquire a reference for the leaving waiter to ensure
1220: * valid futex_q::lock_ptr.
1221: */
1222: futex_hash_get(hb);
1223: top_waiter->drop_hb_ref = true;
1224: __futex_unqueue(top_waiter);
1225: raw_spin_unlock_irq(&pi_state->pi_mutex.wait_lock);
1226: goto retry_hb;
1227: }
1228:
1229: get_pi_state(pi_state);
1230: spin_unlock(&hb->lock);
1231:
1232: /* drops pi_state->pi_mutex.wait_lock */
1233: ret = wake_futex_pi(uaddr, uval, pi_state, rt_waiter);
1234:
1235: put_pi_state(pi_state);
1236:
1237: /*
1238: * Success, we're done! No tricky corner cases.
1239: */
1240: if (!ret)
1241: return ret;
1242: /*
1243: * The atomic access to the futex value generated a
1244: * pagefault, so retry the user-access and the wakeup:
1245: */
1246: if (ret == -EFAULT)
1247: goto pi_faulted;
1248: /*
1249: * A unconditional UNLOCK_PI op raced against a waiter
1250: * setting the FUTEX_WAITERS bit. Try again.
1251: */
1252: if (ret == -EAGAIN)
1253: goto pi_retry;
1254: /*
1255: * wake_futex_pi has detected invalid state. Tell user
1256: * space.
1257: */
1258: return ret;
1259: }
1260:
1261: /*
1262: * We have no kernel internal state, i.e. no waiters in the
1263: * kernel. Waiters which are about to queue themselves are stuck
1264: * on hb->lock. So we can safely ignore them. We do neither
1265: * preserve the WAITERS bit not the OWNER_DIED one. We are the
1266: * owner.
1267: */
1268: if ((ret = futex_cmpxchg_value_locked(&curval, uaddr, uval, 0))) {
1269: spin_unlock(&hb->lock);
1270: switch (ret) {
1271: case -EFAULT:
1272: goto pi_faulted;
1273:
1274: case -EAGAIN:
1275: goto pi_retry;
1276:
1277: default:
1278: WARN_ON_ONCE(1);
1279: return ret;
1280: }
1281: }
1282:
1283: /*
1284: * If uval has changed, let user space handle it.
1285: */
1286: ret = (curval == uval) ? 0 : -EAGAIN;
1287:
1288: out_unlock:
1289: spin_unlock(&hb->lock);
1290: return ret;
1291:
1292: pi_retry:
1293: cond_resched();
1294: goto retry;
1295:
1296: pi_faulted:
1297:
1298: ret = fault_in_user_writeable(uaddr);
1299: if (!ret)
1300: goto retry;
1301:
1302: return ret;
1303: }
SourceFile:kernel/futex/pi.c]
|
| 62/2 |
2026/06/02 13:40 |
llm |
expert |
0m
Model:
Tokens:
input: 58044
output: 29
thoughts: 3578
Thoughts: **Delving into the Futex Mystery**
Okay, here's the breakdown of my thinking. The crash in `futex_wake` is the focal point. It seems like we're reading a `futex_q` entry from the hash bucket, but the stack frame is already freed. That indicates the `futex_q` is still somehow in the list *after* the thread should have exited. I've been dissecting the unqueue/queue logic to figure out where things are going wrong.
Initially, I looked at `futex_unlock_pi`. If `rt_waiter` is NULL it calls `__futex_unqueue` and jumps back to retry_hb, *without* setting the `lock_ptr` to `NULL`. The crash could be happening when `futex_wake` iterates over a list concurrently. But the hash bucket lock should prevent concurrency, and `futex_wake` needs to grab it to iterate.
I moved on to `futex_wake_op`. It also iterates and calls `wake()`. However, the hash lock should also protect `futex_wake_op`, so that's not the issue.
I tried to find any place where a `futex_q` gets unqueued *without* holding the bucket lock, but `__futex_unqueue` always requires the `hb->lock` to be held.
Next, I considered that `q->lock_ptr` might be corrupted. However, it's set in `futex_queue` and `requeue_futex`, so it should be valid.
Okay, let's trace the typical paths a futex goes through. If a thread exits, it must have gone through `futex_wait` or `futex_lock_pi`, and both paths should unqueue it. `futex_unqueue` should also prevent this issue if a thread is killed.
I looked at `futex_wait_requeue_pi`, which has multiple return paths, but all of them eventually unqueue. Similarly, `futex_lock_pi` has various branches, but the futex is *always* unqueued. Even `futex_wait_multiple_setup` in error paths will *always* unqueue.
Now the `io_futexv_wait` call looks promising, but this uses io_uring's slab allocator, not the stack so it's not the issue.
The crash address being a vmalloc region is the key! Thread stacks are allocated there. If this is a thread stack, then the `futex_q` *must* be on a thread stack, and the thread must exit with it still queued. Is there a way for a thread to exit while its `futex_q` is still queued?
If a thread is killed by SIGKILL while in `futex_wait`, it will return from `futex_do_wait` and call `futex_unqueue`. It *should* remove it from the list.
The critical realization is that `futex_unqueue` *must* succeed. If `lock_ptr` is NULL, then it should have been unqueued by `__futex_wake_mark`, so the futex should be off the list. So `futex_unqueue` *must* always remove the element. Is there anywhere else `q->lock_ptr` is set to NULL? No.
The bug must be in `futex_wake`. But *how*? If `futex_unqueue` always removes it, then how can `futex_wake` see the freed stack in the list? I am baffled. It should *never* be on the list!
|
| 63/2 |
2026/06/02 13:41 |
tool |
grepper |
0m
Args: map[Expression:lock_ptr = PathPrefix:kernel/futex/]
Results: map[Output:kernel/futex/core.c=200=static void futex_rehash_private(struct futex_private_hash *old,
--
kernel/futex/core.c-227- plist_add(&this->list, &hb_new->chain);
kernel/futex/core.c:228: this->lock_ptr = &hb_new->lock;
kernel/futex/core.c-229- spin_unlock(&hb_new->lock);
--
kernel/futex/core.c=866=void futex_q_lock(struct futex_q *q, struct futex_hash_bucket *hb)
--
kernel/futex/core.c-877-
kernel/futex/core.c:878: q->lock_ptr = &hb->lock;
kernel/futex/core.c-879-
--
kernel/futex/core.c=921=int futex_unqueue(struct futex_q *q)
--
kernel/futex/core.c-934- */
kernel/futex/core.c:935: lock_ptr = READ_ONCE(q->lock_ptr);
kernel/futex/core.c-936- if (lock_ptr != NULL) {
--
kernel/futex/core.c=966=void futex_q_lockptr_lock(struct futex_q *q)
--
kernel/futex/core.c-974-retry:
kernel/futex/core.c:975: lock_ptr = READ_ONCE(q->lock_ptr);
kernel/futex/core.c-976- spin_lock(lock_ptr);
--
kernel/futex/futex.h=163=typedef void (futex_wake_fn)(struct wake_q_head *wake_q, struct futex_q *q);
--
kernel/futex/futex.h-184- * A futex_q has a woken state, just like tasks have TASK_RUNNING.
kernel/futex/futex.h:185: * It is considered woken when plist_node_empty(&q->list) || q->lock_ptr == 0.
kernel/futex/futex.h-186- * The order of wakeup is always to make the first condition true, then
--
kernel/futex/requeue.c=76=void requeue_futex(struct futex_q *q, struct futex_hash_bucket *hb1,
--
kernel/futex/requeue.c-88- plist_add(&q->list, &hb2->chain);
kernel/futex/requeue.c:89: q->lock_ptr = &hb2->lock;
kernel/futex/requeue.c-90- /*
--
kernel/futex/requeue.c=230=void requeue_pi_wake_futex(struct futex_q *q, union futex_key *key,
--
kernel/futex/requeue.c-245- q->drop_hb_ref = true;
kernel/futex/requeue.c:246: q->lock_ptr = &hb->lock;
kernel/futex/requeue.c-247- task = READ_ONCE(q->task);
--
kernel/futex/waitwake.c=110=bool __futex_wake_mark(struct futex_q *q)
--
kernel/futex/waitwake.c-116- /*
kernel/futex/waitwake.c:117: * The waiting task can free the futex_q as soon as q->lock_ptr = NULL
kernel/futex/waitwake.c-118- * is written, without taking any locks. This is possible in the event
]
|
| 64/2 |
2026/06/02 13:41 |
llm |
expert |
0m
Model:
Tokens:
input: 63400
output: 31
thoughts: 51
Thoughts: Here's my thought process, as if I were analyzing this code:
**Diving into Futex Wake-Up Optimization**
Okay, so I'm looking at `__futex_wake_mark` and I see that it's using `smp_store_release(&q->lock_ptr, NULL);`. My immediate reaction is, "Hmm, that's interesting." That's a *release* store on the lock pointer. That immediately triggers a flag, that makes me want to understand the memory ordering implications here, and why a full release store is needed for simply nulling out the lock.
Let's do this right. My next step? I need to understand the full context. I'm going to quickly grep for every instance of `smp_store_release` within the `kernel/futex/` directory. That's where all the futex code lives, so that's the place to start drilling down into the details of the synchronization primitives being used. I need to understand how this `smp_store_release` fits into the broader picture of the futex implementation and, more specifically, in the wake-up path. I need to know why they're using release semantics here, and what memory barriers or other synchronization primitives are involved. Is it to establish a happens-before relationship between the unblocking of the waiters and the release of the lock? Does it avoid a potential reordering of operations? Are we trying to ensure visibility of the updated futex queue state across cores? Gotta follow this trail, before I can make heads or tails of it.
|
| 65/2 |
2026/06/02 13:41 |
tool |
grepper |
0m
Args: map[Expression:smp_store_release PathPrefix:kernel/futex/]
Results: map[Output:kernel/futex/core.c=1547=static void __futex_ref_atomic_begin(struct futex_private_hash *fph)
--
kernel/futex/core.c-1563- atomic_long_set(&mm->futex_atomic, LONG_MAX);
kernel/futex/core.c:1564: smp_store_release(&fph->state, FR_ATOMIC);
kernel/futex/core.c-1565-
--
kernel/futex/waitwake.c=110=bool __futex_wake_mark(struct futex_q *q)
--
kernel/futex/waitwake.c-122- */
kernel/futex/waitwake.c:123: smp_store_release(&q->lock_ptr, NULL);
kernel/futex/waitwake.c-124-
]
|
| 66/2 |
2026/06/02 13:41 |
llm |
expert |
0m
Model:
Tokens:
input: 64050
output: 49
thoughts: 1324
Thoughts: **Analyzing a Potential Race Condition in Futex Wakeup**
Okay, let's break this down systematically. The core issue is a crash when reading `this->wake` within `futex_wake`. Based on my analysis, `__futex_wake_mark` is the only function that sets `q->lock_ptr` to `NULL`, and it always calls `__futex_unqueue(q)` *before* doing so. Consequently, a `q->lock_ptr == NULL` state *must* indicate unqueuing.
But, a thought emerges; could `__futex_unqueue(q)` fail to truly remove the entry from the list? Checking the implementation, specifically the `plist_del` call, confirms it *is* removed. So, how can `futex_wake` possibly see it? Revisiting `futex_wake` and, specifically, the `plist_for_each_entry_safe` macro, is key. It's built on `list_for_each_entry_safe`, which evaluates the next list entry (*n*) before entering the loop body.
Here's the critical observation: `n` is computed *before* the loop body. Therefore, if `this` is unqueued during the loop (e.g., within `this->wake`), it is safe because `n` already holds the subsequent entry. The crash, however, occurs when reading `this->wake`. That means `this` has been freed *before* the access to its `wake` function.
How can `this` be freed before its `wake` member is read? The only likely explanation is that the thread that owns `this` must have exited. Upon exit, the thread would have unqueued `this`. This unqueuing must have involved acquiring the `hb->lock`. However, `futex_wake` itself holds `hb->lock` during its iteration. Therefore, the thread cannot unqueue `this` while `futex_wake` has the lock! This also makes it impossible for `this` to disappear from the list.
The next thought is, can `this` be freed in *any other way*? The bug report indicates a vmalloc'd address, however, it explicitly says it's on a thread stack, so the address space is valid. `futex_q` is also local to the thread, which means, the futex could not be on a thread stack, but passed to another.
Let's rule out preemption. `futex_wake` holds `hb->lock` and disables preemption. Could the `hb->chain` itself be corrupted? If so, it might point to a freed stack location. But how could `hb->chain` be corrupted? The crucial insight: a `futex_q` could be freed *without* being unqueued! This would happen if a thread exits *without* calling `futex_unqueue` after queuing it. Now, it's a matter of verifying all callers of `futex_queue` and `__futex_queue` to confirm this possible code path and confirm that all threads unqueue their futexes.
|
| 67/2 |
2026/06/02 13:41 |
tool |
codesearch-find-references |
0m
Args: map[ContextFile:kernel/futex/core.c IncludeSnippetLines:5 Name:futex_queue SourceTreePrefix:]
Results: map[References:[map[ReferenceKind:calls ReferencingEntityKind:function ReferencingEntityName:futex_wait_multiple_setup SourceFile:kernel/futex/waitwake.c SourceLine:460 SourceSnippet: 455: /*
456: * The bucket lock can't be held while dealing with the
457: * next futex. Queue each futex at this moment so hb can
458: * be unlocked.
459: */
460: futex_queue(q, hb, current);
461: continue;
462: }
463:
464: futex_q_unlock(hb);
465: __release(q->lock_ptr);
] map[ReferenceKind:calls ReferencingEntityKind:function ReferencingEntityName:futex_wait_setup SourceFile:kernel/futex/waitwake.c SourceLine:664 SourceSnippet: 659: * futex_queue() calls spin_unlock() upon completion, both serializing
660: * access to the hash list and forcing another memory barrier.
661: */
662: if (task == current)
663: set_current_state(TASK_INTERRUPTIBLE|TASK_FREEZABLE);
664: futex_queue(q, hb, task);
665: }
666:
667: return ret;
668: }
]] TruncatedOutput:false]
|
| 68/2 |
2026/06/02 13:41 |
llm |
expert |
0m
Model:
Tokens:
input: 66516
output: 39
thoughts: 30
Thoughts: Here's my thought process, as if I were analyzing this code:
**Diving into Futex Wait Setup**
Okay, so I'm tracing through this code, and it seems like `futex_wait_setup` is definitely calling `futex_queue`. That's a key observation. Now, I need to understand *why* it's doing that. Let's dig into the details of `futex_wait_setup` itself. I need to refresh my memory on what this function actually *does*. What's the core functionality? What's it setting up? Let's get the context clear. Need to check the implementation.
|
| 69/2 |
2026/06/02 13:41 |
tool |
codesearch-definition-source |
0m
Args: map[ContextFile:kernel/futex/waitwake.c Name:futex_wait_setup]
Results: map[SourceCode: 574: /**
575: * futex_wait_setup() - Prepare to wait on a futex
576: * @uaddr: the futex userspace address
577: * @val: the expected value
578: * @flags: futex flags (FLAGS_SHARED, etc.)
579: * @q: the associated futex_q
580: * @key2: the second futex_key if used for requeue PI
581: * @task: Task queueing this futex
582: *
583: * Setup the futex_q and locate the hash_bucket. Get the futex value and
584: * compare it with the expected value. Handle atomic faults internally.
585: * Return with the hb lock held on success, and unlocked on failure.
586: *
587: * Return:
588: * - 0 - uaddr contains val and hb has been locked;
589: * - <0 - On error and the hb is unlocked. A possible reason: the uaddr can not
590: * be read, does not contain the expected value or is not properly aligned.
591: */
592: int futex_wait_setup(u32 __user *uaddr, u32 val, unsigned int flags,
593: struct futex_q *q, union futex_key *key2,
594: struct task_struct *task)
595: {
596: u32 uval;
597: int ret;
598:
599: /*
600: * Access the page AFTER the hash-bucket is locked.
601: * Order is important:
602: *
603: * Userspace waiter: val = var; if (cond(val)) futex_wait(&var, val);
604: * Userspace waker: if (cond(var)) { var = new; futex_wake(&var); }
605: *
606: * The basic logical guarantee of a futex is that it blocks ONLY
607: * if cond(var) is known to be true at the time of blocking, for
608: * any cond. If we locked the hash-bucket after testing *uaddr, that
609: * would open a race condition where we could block indefinitely with
610: * cond(var) false, which would violate the guarantee.
611: *
612: * On the other hand, we insert q and release the hash-bucket only
613: * after testing *uaddr. This guarantees that futex_wait() will NOT
614: * absorb a wakeup if *uaddr does not match the desired values
615: * while the syscall executes.
616: */
617: retry:
618: ret = get_futex_key(uaddr, flags, &q->key, FUTEX_READ);
619: if (unlikely(ret != 0))
620: return ret;
621:
622: retry_private:
623: if (1) {
624: CLASS(hb, hb)(&q->key);
625:
626: futex_q_lock(q, hb);
627:
628: ret = futex_get_value_locked(&uval, uaddr);
629:
630: if (ret) {
631: futex_q_unlock(hb);
632: __release(q->lock_ptr);
633:
634: ret = get_user(uval, uaddr);
635: if (ret)
636: return ret;
637:
638: if (!(flags & FLAGS_SHARED))
639: goto retry_private;
640:
641: goto retry;
642: }
643:
644: if (uval != val) {
645: futex_q_unlock(hb);
646: __release(q->lock_ptr);
647: return -EWOULDBLOCK;
648: }
649:
650: if (key2 && futex_match(&q->key, key2)) {
651: futex_q_unlock(hb);
652: __release(q->lock_ptr);
653: return -EINVAL;
654: }
655:
656: /*
657: * The task state is guaranteed to be set before another task can
658: * wake it. set_current_state() is implemented using smp_store_mb() and
659: * futex_queue() calls spin_unlock() upon completion, both serializing
660: * access to the hash list and forcing another memory barrier.
661: */
662: if (task == current)
663: set_current_state(TASK_INTERRUPTIBLE|TASK_FREEZABLE);
664: futex_queue(q, hb, task);
665: }
666:
667: return ret;
668: }
SourceFile:kernel/futex/waitwake.c]
|
| 70/2 |
2026/06/02 13:41 |
llm |
expert |
0m
Model:
Tokens:
input: 68223
output: 37
thoughts: 166
Thoughts: **My Futex-Related Aha! Moment**
Okay, hold on a second. I'm tracing through this `futex_wait_setup` function, and something isn't quite clicking. It calls `futex_queue` with the `futex_hash_bucket` (`hb`)! And if I recall correctly, `futex_queue` is where we enqueue tasks for a futex, right?
*Ah*, here it is: `futex_queue` indeed unlocks `hb`. My goodness!
```c
static inline void futex_queue(struct futex_q *q, struct futex_hash_bucket *hb,
struct task_struct *task)
{
__futex_queue(q, hb, task);
futex_q_unlock(hb);
}
```
Now, the comment in `futex_wait_setup` explicitly says that the function is supposed to: "Return with the hb lock held on success, and unlocked on failure." But... if `futex_queue` *always* unlocks `hb`, as the code clearly shows, then the "success" path of `futex_wait_setup` would also *unconditionally* unlock `hb`, not leaving it held, even though the comment states it should be kept held.
This is worth a closer inspection. I need to revisit `futex_queue` and its surrounding context to ensure I haven't missed something fundamental. My initial reading of this looks like a definite inconsistency... this definitely requires further investigation.
|
| 71/2 |
2026/06/02 13:42 |
tool |
codesearch-definition-source |
0m
Args: map[ContextFile:kernel/futex/futex.h Name:futex_queue]
Results: map[SourceCode: 296: /**
297: * futex_queue() - Enqueue the futex_q on the futex_hash_bucket
298: * @q: The futex_q to enqueue
299: * @hb: The destination hash bucket
300: * @task: Task queueing this futex
301: *
302: * The hb->lock must be held by the caller, and is released here. A call to
303: * futex_queue() is typically paired with exactly one call to futex_unqueue(). The
304: * exceptions involve the PI related operations, which may use futex_unqueue_pi()
305: * or nothing if the unqueue is done as part of the wake process and the unqueue
306: * state is implicit in the state of woken task (see futex_wait_requeue_pi() for
307: * an example).
308: *
309: * Note that @task may be NULL, for async usage of futexes.
310: */
311: static inline void futex_queue(struct futex_q *q, struct futex_hash_bucket *hb,
312: struct task_struct *task)
313: __releases(&hb->lock)
314: __releases(q->lock_ptr)
315: {
316: __futex_queue(q, hb, task);
317: spin_unlock(&hb->lock);
318: __release(q->lock_ptr);
319: }
SourceFile:kernel/futex/futex.h]
|
| 72/2 |
2026/06/02 13:42 |
llm |
expert |
0m
Model:
Tokens:
input: 69221
output: 27
thoughts: 681
Thoughts: **Futex Conundrum**
Alright, let's break this down. It's clicking now. I see that `futex_queue` *unlocks* `hb->lock`! That's a critical detail. So, `futex_wait_setup` returns with `hb` UNLOCKED on success, not held like the comment says. The comment is wrong! Now, let me check `__futex_wait` again to solidify this.
Yep, `futex_wait_setup` queues the futex and unlocks `hb`. Then `futex_do_wait` goes to sleep. And then `futex_unqueue` removes it. So itβs *always* removed! Got it.
But... what happens if `futex_do_wait` gets interrupted? What if `futex_unqueue` is called? The check is there...
```c
if (!futex_unqueue(&q))
return 0;
```
If `futex_unqueue` returns 0, it means `q->lock_ptr == NULL`. That means `__futex_wake_mark` *already* unqueued it! It's no longer in the list! So, `futex_wake` won't even see it. It's completely gone.
Okay, is there *any* other way `q->lock_ptr` could be NULL? What about initialization? Let's check `futex_q_init`.
```c
const struct futex_q futex_q_init = {
/* list gets initialized in futex_queue()*/
.key = FUTEX_KEY_INIT,
.bitset = FUTEX_BITSET_MATCH_ANY,
.requeue_state = ATOMIC_INIT(Q_REQUEUE_PI_NONE),
};
```
Yes, it starts as NULL. But `futex_queue` *sets* it:
```c
void __futex_queue(struct futex_q *q, struct futex_hash_bucket *hb,
struct task_struct *task)
{
...
q->lock_ptr = &hb->lock;
plist_add(&q->list, &hb->chain);
...
}
```
So, it's not NULL when it's in the list. Excellent.
Now, wait... `futex_wake` is iterating. It reads `this = ...` then `next = this->list.next`. Then it calls `this->wake(&wake_q, this)`. What if `this->wake` isn't `futex_wake_mark`? Is there another `wake` function? I better grep for `wake = ` in `kernel/futex/` and see what's out there. This is important.
|
| 73/2 |
2026/06/02 13:42 |
tool |
grepper |
0m
Args: map[Expression:wake = PathPrefix:kernel/futex/]
Results: map[Output:kernel/futex/syscalls.c=220=int futex_parse_waitv(struct futex_vector *futexv,
--
kernel/futex/syscalls.c-247- futexv[i].q = futex_q_init;
kernel/futex/syscalls.c:248: futexv[i].q.wake = wake;
kernel/futex/syscalls.c-249- futexv[i].q.wake_data = wake_data;
]
|
| 74/2 |
2026/06/02 13:42 |
llm |
expert |
0m
Model:
Tokens:
input: 70697
output: 31
thoughts: 35
Thoughts: Okay, here's my take on those thoughts, framed as my own internal monologue:
**Futex Wait State Analysis**
Right, let me just quickly double-check this futex wait setup logic. I'm seeing that `futex_parse_waitv` is responsible for actually setting the `wake` variable. Okay, that makes sense. But, I need to be absolutely sure I understand this flow completely, because futexes are so critical to performance, particularly with the new workloads. Now, what's bugging me is `futex_wait_setup`. I need to ensure Iβm not missing any potential race conditions, edge cases, or optimizations. *Does* `futex_wait_setup` itself contribute to setting that `wake` flag? I need to be certain whether any pre-work there can be missed. I need to be thinking about the entire call graph from the entry point all the way down.
|
| 75/2 |
2026/06/02 13:42 |
tool |
grepper |
0m
Error:
Args: map[Expression:wake = PathPrefix:kernel/futex/waitwake.c]
Results:
|
| 76/2 |
2026/06/02 13:42 |
llm |
expert |
0m
Model:
Tokens:
input: 70974
output: 38
thoughts: 33
Thoughts: Okay, here's my interpretation of those thoughts, framed as my own:
**Digging into Futex Wait Mechanism**
Hmm, something's not quite clicking. I'm trying to trace the wake-up path for a futex wait in this code. Specifically, I'm trying to understand how `q.wake` gets set when dealing with `__futex_wait`. I need to figure out the synchronization path in detail. Time to go back and re-examine the `__futex_wait` function again. I need to make sure I understand the interaction between the wait and the wakeup operations. It's probably something simple that I'm overlooking, but I need to make sure I'm following the whole flow.
|
| 77/2 |
2026/06/02 13:42 |
tool |
codesearch-definition-source |
0m
Args: map[ContextFile:kernel/futex/waitwake.c Name:__futex_wait]
Results: map[SourceCode: 670: int __futex_wait(u32 __user *uaddr, unsigned int flags, u32 val,
671: struct hrtimer_sleeper *to, u32 bitset)
672: {
673: struct futex_q q = futex_q_init;
674: int ret;
675:
676: if (!bitset)
677: return -EINVAL;
678:
679: q.bitset = bitset;
680:
681: retry:
682: /*
683: * Prepare to wait on uaddr. On success, it holds hb->lock and q
684: * is initialized.
685: */
686: ret = futex_wait_setup(uaddr, val, flags, &q, NULL, current);
687: if (ret)
688: return ret;
689:
690: /* futex_queue and wait for wakeup, timeout, or a signal. */
691: futex_do_wait(&q, to);
692:
693: /* If we were woken (and unqueued), we succeeded, whatever. */
694: if (!futex_unqueue(&q))
695: return 0;
696:
697: if (to && !to->task)
698: return -ETIMEDOUT;
699:
700: /*
701: * We expect signal_pending(current), but we might be the
702: * victim of a spurious wakeup as well.
703: */
704: if (!signal_pending(current))
705: goto retry;
706:
707: return -ERESTARTSYS;
708: }
SourceFile:kernel/futex/waitwake.c]
|
| 78/2 |
2026/06/02 13:42 |
llm |
expert |
0m
Model:
Tokens:
input: 71757
output: 39
thoughts: 32
Thoughts: Okay, here's my interpretation of those thoughts, presented as a first-person expert summary:
**The Futex Conundrum**
Alright, hang on a second. I'm looking at this `__futex_wait` code, and something's bugging me. I'm expecting `q.wake` to be set somewhere in this function, but it's not. That's a red flag. Something isn't as it should be.
This suggests that the queue structure 'q' is not being properly initialized at the point where `__futex_wait` is being called. My immediate next step is to examine the `futex_q_init` function. I need to understand *exactly* how that queue structure is set up, what its initial values are, and whether it's actually doing what I think it is. This is the likely culprit, and I need to pinpoint where the state is being incorrectly assigned if, in fact, there is an issue. It seems obvious that the queue is not working as expected, so my investigation immediately starts here. I'm going to follow the initialization logic carefully β I need to determine if it's the root cause of this missed wake up condition.
|
| 79/2 |
2026/06/02 13:42 |
tool |
codesearch-definition-source |
0m
Args: map[ContextFile:kernel/futex/requeue.c Name:futex_q_init]
Results: map[SourceCode: 60: const struct futex_q futex_q_init = {
61: /* list gets initialized in futex_queue()*/
62: .wake = futex_wake_mark,
63: .key = FUTEX_KEY_INIT,
64: .bitset = FUTEX_BITSET_MATCH_ANY,
65: .requeue_state = ATOMIC_INIT(Q_REQUEUE_PI_NONE),
66: };
SourceFile:kernel/futex/requeue.c]
|
| 80/2 |
2026/06/02 13:42 |
llm |
expert |
0m
Model:
Tokens:
input: 72244
output: 42
thoughts: 2039
Thoughts: **Futex Wake Mark and Stack Corruption: A Deep Dive**
Okay, so `futex_q_init` sets `.wake` to `futex_wake_mark`, that's clear. But the core problem seems to be a read of a bad `q->wake` value, and the crash report is indicating a potential stack corruption issue. The crash indicates a read of size 8, which points to reading the `this->wake` pointer.
My thinking: How can the stack frame, where `q` is allocated, become invalid during this sequence? We've got Thread A, the waiter, and Thread B, the waker. Let's trace it. Thread A calls `__futex_wait`, allocates `q` on the stack, queues it in `hb->chain`, and blocks in `futex_do_wait`. Thread B locks `hb->lock` to find `q` in the chain, reads `q->wake`, and calls `q->wake(&wake_q, q)`. This is where `futex_wake_mark` enters the equation, unqueuing `q` and setting `q->lock_ptr` to `NULL`. Thread B continues the iteration, unlocks `hb->lock`, and wakes the queue. Thread A unqueues and returns, and *then* the stack frame is freed.
The crash happens when Thread B *reads* `q->wake`, specifically at step 6! At that time, Thread A should still be blocked in `futex_do_wait` and its stack *should* be valid. Thread A can't be woken up by the kernel until step 12!
But, what if Thread A is woken up by a signal or timeout? If it's a signal, `futex_unqueue` could try to lock the lock pointed to by `q->lock_ptr` which is `&hb->lock`, but `hb->lock` is still held by Thread B. Thread A can't free the stack because it's blocked by the lock. But that's not what's happening. The crash is happening during the read *before* any unlock.
Then, are there other ways the lock pointer could be changed? `requeue_futex` manipulates lock pointers, but it holds both locks, so it's not concurrent with `futex_wake`. That can't be it.
What if Thread A isn't blocked in `futex_do_wait`? In `futex_wait_setup`, `futex_queue` happens before `futex_do_wait`. It's possible that `__futex_wait` is preempted *before* calling `futex_do_wait`, but still, the stack is valid because it hasn't returned.
The breakthrough comes with `futex_wait_multiple_setup`. If `futex_wait_multiple_setup` queues `vs[0]` and then attempts to queue `vs[1]`, but encounters an error, it *unqueues* `vs[0]`, potentially freeing its stack, and then returns. *This could explain an unqueued `futex_q`. The stack is freed, which leads to the memory corruption in the crash.* That also explains the bug.
But, if `futex_unqueue_multiple` fails to unqueue, it can't be. `futex_unqueue` returns 0 if `q->lock_ptr == NULL` which means it has *already* been unqueued and is not on the chain.
What if `futex_unqueue` is called and does *not* lock `hb0->lock`? It reads `q->lock_ptr`, and then tries to lock it. That pointer could be corrupted, and if so the `spin_lock` would crash.
The critical insight now centers on the chain itself. `futex_wake` iterates, reading `this = ...`, then `next = this->list.next`. Then it calls `this->wake()`. If `this` isn't a valid `futex_q` at that point, we've got a problem. The chain could be corrupted if a `futex_q` is freed WITHOUT being unqueued. We also need to check `futex_wait_requeue_pi` but so far, `futex_wait_multiple_setup` seems to be the key.
|
| 81/2 |
2026/06/02 13:43 |
tool |
codesearch-definition-source |
0m
Args: map[ContextFile:kernel/futex/requeue.c Name:futex_wait_requeue_pi]
Results: map[SourceCode: 744: /**
745: * futex_wait_requeue_pi() - Wait on uaddr and take uaddr2
746: * @uaddr: the futex we initially wait on (non-pi)
747: * @flags: futex flags (FLAGS_SHARED, FLAGS_CLOCKRT, etc.), they must be
748: * the same type, no requeueing from private to shared, etc.
749: * @val: the expected value of uaddr
750: * @abs_time: absolute timeout
751: * @bitset: 32 bit wakeup bitset set by userspace, defaults to all
752: * @uaddr2: the pi futex we will take prior to returning to user-space
753: *
754: * The caller will wait on uaddr and will be requeued by futex_requeue() to
755: * uaddr2 which must be PI aware and unique from uaddr. Normal wakeup will wake
756: * on uaddr2 and complete the acquisition of the rt_mutex prior to returning to
757: * userspace. This ensures the rt_mutex maintains an owner when it has waiters;
758: * without one, the pi logic would not know which task to boost/deboost, if
759: * there was a need to.
760: *
761: * We call schedule in futex_wait_queue() when we enqueue and return there
762: * via the following--
763: * 1) wakeup on uaddr2 after an atomic lock acquisition by futex_requeue()
764: * 2) wakeup on uaddr2 after a requeue
765: * 3) signal
766: * 4) timeout
767: *
768: * If 3, cleanup and return -ERESTARTNOINTR.
769: *
770: * If 2, we may then block on trying to take the rt_mutex and return via:
771: * 5) successful lock
772: * 6) signal
773: * 7) timeout
774: * 8) other lock acquisition failure
775: *
776: * If 6, return -EWOULDBLOCK (restarting the syscall would do the same).
777: *
778: * If 4 or 7, we cleanup and return with -ETIMEDOUT.
779: *
780: * Return:
781: * - 0 - On success;
782: * - <0 - On error
783: */
784: int futex_wait_requeue_pi(u32 __user *uaddr, unsigned int flags,
785: u32 val, ktime_t *abs_time, u32 bitset,
786: u32 __user *uaddr2)
787: {
788: struct hrtimer_sleeper timeout, *to;
789: struct rt_mutex_waiter rt_waiter;
790: union futex_key key2 = FUTEX_KEY_INIT;
791: struct futex_q q = futex_q_init;
792: struct rt_mutex_base *pi_mutex;
793: int res, ret;
794:
795: if (!IS_ENABLED(CONFIG_FUTEX_PI))
796: return -ENOSYS;
797:
798: if (uaddr == uaddr2)
799: return -EINVAL;
800:
801: if (!bitset)
802: return -EINVAL;
803:
804: to = futex_setup_timer(abs_time, &timeout, flags,
805: current->timer_slack_ns);
806:
807: /*
808: * The waiter is allocated on our stack, manipulated by the requeue
809: * code while we sleep on uaddr.
810: */
811: rt_mutex_init_waiter(&rt_waiter);
812:
813: ret = get_futex_key(uaddr2, flags, &key2, FUTEX_WRITE);
814: if (unlikely(ret != 0))
815: goto out;
816:
817: q.bitset = bitset;
818: q.rt_waiter = &rt_waiter;
819: q.requeue_pi_key = &key2;
820:
821: /*
822: * Prepare to wait on uaddr. On success, it holds hb->lock and q
823: * is initialized.
824: */
825: ret = futex_wait_setup(uaddr, val, flags, &q, &key2, current);
826: if (ret)
827: goto out;
828:
829: /* Queue the futex_q, drop the hb lock, wait for wakeup. */
830: futex_do_wait(&q, to);
831:
832: switch (futex_requeue_pi_wakeup_sync(&q)) {
833: case Q_REQUEUE_PI_IGNORE:
834: {
835: CLASS(hb, hb)(&q.key);
836: /* The waiter is still on uaddr1 */
837: spin_lock(&hb->lock);
838: ret = handle_early_requeue_pi_wakeup(hb, &q, to);
839: spin_unlock(&hb->lock);
840: }
841: break;
842:
843: case Q_REQUEUE_PI_LOCKED:
844: /* The requeue acquired the lock */
845: if (q.pi_state && (q.pi_state->owner != current)) {
846: futex_q_lockptr_lock(&q);
847: ret = fixup_pi_owner(uaddr2, &q, true);
848: /*
849: * Drop the reference to the pi state which the
850: * requeue_pi() code acquired for us.
851: */
852: put_pi_state(q.pi_state);
853: spin_unlock(q.lock_ptr);
854: /*
855: * Adjust the return value. It's either -EFAULT or
856: * success (1) but the caller expects 0 for success.
857: */
858: ret = ret < 0 ? ret : 0;
859: }
860: break;
861:
862: case Q_REQUEUE_PI_DONE:
863: /* Requeue completed. Current is 'pi_blocked_on' the rtmutex */
864: pi_mutex = &q.pi_state->pi_mutex;
865: ret = rt_mutex_wait_proxy_lock(pi_mutex, to, &rt_waiter);
866:
867: /*
868: * See futex_unlock_pi()'s cleanup: comment.
869: */
870: if (ret && !rt_mutex_cleanup_proxy_lock(pi_mutex, &rt_waiter))
871: ret = 0;
872:
873: futex_q_lockptr_lock(&q);
874: debug_rt_mutex_free_waiter(&rt_waiter);
875: /*
876: * Fixup the pi_state owner and possibly acquire the lock if we
877: * haven't already.
878: */
879: res = fixup_pi_owner(uaddr2, &q, !ret);
880: /*
881: * If fixup_pi_owner() returned an error, propagate that. If it
882: * acquired the lock, clear -ETIMEDOUT or -EINTR.
883: */
884: if (res)
885: ret = (res < 0) ? res : 0;
886:
887: futex_unqueue_pi(&q);
888: spin_unlock(q.lock_ptr);
889:
890: if (ret == -EINTR) {
891: /*
892: * We've already been requeued, but cannot restart
893: * by calling futex_lock_pi() directly. We could
894: * restart this syscall, but it would detect that
895: * the user space "val" changed and return
896: * -EWOULDBLOCK. Save the overhead of the restart
897: * and return -EWOULDBLOCK directly.
898: */
899: ret = -EWOULDBLOCK;
900: }
901: break;
902: default:
903: BUG();
904: }
905: if (q.drop_hb_ref) {
906: CLASS(hb, hb)(&q.key);
907: /* Additional reference from requeue_pi_wake_futex() */
908: futex_hash_put(hb);
909: }
910:
911: out:
912: if (to) {
913: hrtimer_cancel(&to->timer);
914: destroy_hrtimer_on_stack(&to->timer);
915: }
916: return ret;
917: }
SourceFile:kernel/futex/requeue.c]
|
| 82/2 |
2026/06/02 13:43 |
llm |
expert |
0m
Model:
Tokens:
input: 78046
output: 42
thoughts: 212
Thoughts: **Analyzing the PI Futex Requeue Logic**
Okay, hold on a second! Looking at the `Q_REQUEUE_PI_LOCKED` case within this `Q_REQUEUE_PI_LOCKED` block of code, I'm noticing something crucial. Specifically, in this crucial `Q_REQUEUE_PI_LOCKED` scenario where the requeue successfully acquired the lock, I *don't* see a call to `futex_unqueue_pi(&q)`. This raises a red flag. Is the process actually being properly unqueued from the PI futex queue here? That seems like a potential omission. We acquire the lock, potentially fix up the PI owner, put the PI state, and unlock the spinlock. But what about the removal from the queue?
To confirm, I need to check the code that *sets* the `Q_REQUEUE_PI_LOCKED` state. That's the `requeue_pi_wake_futex` function. Understanding how we get into this state is key to determining if the unqueuing is handled elsewhere. It seems like the unqueue is expected to be handled within the `fixup_pi_owner` function. Let's see how that works.
|
| 83/2 |
2026/06/02 13:43 |
tool |
codesearch-definition-source |
0m
Args: map[ContextFile:kernel/futex/requeue.c Name:requeue_pi_wake_futex]
Results: map[SourceCode: 201: /**
202: * requeue_pi_wake_futex() - Wake a task that acquired the lock during requeue
203: * @q: the futex_q
204: * @key: the key of the requeue target futex
205: * @hb: the hash_bucket of the requeue target futex
206: *
207: * During futex_requeue, with requeue_pi=1, it is possible to acquire the
208: * target futex if it is uncontended or via a lock steal.
209: *
210: * 1) Set @q::key to the requeue target futex key so the waiter can detect
211: * the wakeup on the right futex.
212: *
213: * 2) Dequeue @q from the hash bucket.
214: *
215: * 3) Set @q::rt_waiter to NULL so the woken up task can detect atomic lock
216: * acquisition.
217: *
218: * 4) Set the q->lock_ptr to the requeue target hb->lock for the case that
219: * the waiter has to fixup the pi state.
220: *
221: * 5) Complete the requeue state so the waiter can make progress. After
222: * this point the waiter task can return from the syscall immediately in
223: * case that the pi state does not have to be fixed up.
224: *
225: * 6) Wake the waiter task.
226: *
227: * Must be called with both q->lock_ptr and hb->lock held.
228: */
229: static inline
230: void requeue_pi_wake_futex(struct futex_q *q, union futex_key *key,
231: struct futex_hash_bucket *hb)
232: {
233: struct task_struct *task;
234:
235: q->key = *key;
236: __futex_unqueue(q);
237:
238: WARN_ON(!q->rt_waiter);
239: q->rt_waiter = NULL;
240: /*
241: * Acquire a reference for the waiter to ensure valid
242: * futex_q::lock_ptr.
243: */
244: futex_hash_get(hb);
245: q->drop_hb_ref = true;
246: q->lock_ptr = &hb->lock;
247: task = READ_ONCE(q->task);
248:
249: /* Signal locked state to the waiter */
250: futex_requeue_pi_complete(q, 1);
251: wake_up_state(task, TASK_NORMAL);
252: }
SourceFile:kernel/futex/requeue.c]
|
| 84/2 |
2026/06/02 13:43 |
llm |
expert |
1m
Model:
Tokens:
input: 79440
output: 39
thoughts: 8974
Thoughts: **Deep Dive into a Kernel Futex Use-After-Free Bug**
Okay, here's the deal, I've been wrestling with this futex bug, and it's a real head-scratcher. It all starts with `requeue_pi_wake_futex` calling `__futex_unqueue`, so that confirms it *is* unqueued, but it also sets `q->lock_ptr` to `&hb->lock`. This means it's no longer in the list, therefore `futex_wake` shouldnβt see it. The key is in the timing: if `futex_wake` reads `this = ...`, gets preempted, and then `futex_unqueue` runs, we have an issue. `futex_wake` holds `hb->lock`, so `futex_unqueue` *should* block because it needs that same `hb->lock`. Thus, the waiting thread *cannot* free its stack... but it does!
There's no way `futex_wake` *doesn't* hold `hb->lock`. The code shows it explicitly. But what if it's not the same hash bucket? What if `futex_wake` is on `hb1` but `this` was requeued to `hb2`? It *was* removed from `hb1->chain`, so the initial problem should be eliminated! If `futex_wake` is on `hb2`, it takes `hb2->lock`, which should have the same blocking effect! The problem isn't the lock, I see no way for it to release it.
Let's look at what's in `futex_wake`. If it iterates through `hb->chain`, reads `this = ...`, then evaluates `next = this->list.next`, and calls `this->wake(&wake_q, this)`, could `this->wake` not be `futex_wake_mark`? No, `futex_q_init` sets it to `futex_wake_mark`, as does `futex_parse_waitv`. It's ALWAYS `futex_wake_mark`. It would have to be corrupted!
A buffer overflow? If a buffer overflow overwrites `this->wake`, we are in trouble. KASAN is reporting an "unknown-crash" and a read of size 8 at `ffffc900039dfad8`. The memory state shown looks like a stack trace! And the read is of size 8. This is right around `struct futex_q` and seems to be in a thread stack, but something is wrong. The memory shadow is KASAN shadow memory! `f1 f1 f1 f1` is a left redzone, `00 00 00 00` is valid, and then there's this strange `67 18 a8 42`. The memory is corrupted! This would mean the address is wild because it has corrupted shadow memory and it is being read. The shadow memory contains garbage, but why?
Could the stack itself be allocated in a place that has uninitialized shadow memory? If a vmalloc page is freed, its shadow memory is poisoned with `ff`... but there's a memory shadow corruption! This looks like a stack frame and `f1` is redzone, and `00` is valid and `f3` is redzone, but `67 18 a8 42` is... in the shadow memory? It's the stack frame poison!
If `struct futex_q` is randomized, its size might be different, but 128 bytes is exactly 16 shadow bytes! `00 00 00 00 67 18 a8 42 00 00 00 00 00 00 00 00` would be 128 bytes of memory, exactly matching `struct futex_q`. But why are there the strange `67 18 a8 42` shadow bytes in the middle? `00` means valid memory. Generic KASAN and HW-TAGs have different shadow values, which makes this memory corrupted! Could the compiler use ALLOCA redzones? Or VMALLOC invalid? No, it's garbage in the shadow memory. This means there is a wild pointer write!
This means the shadow memory itself is corrupted by an out-of-bounds write. This is a very specific type of corruption! If `0x42a81867` is a pointer, is the KASAN report from a kernel with `CONFIG_KASAN_HW_TAGS`? Not really because HW_TAGS
|
| 85/2 |
2026/06/02 13:44 |
tool |
codesearch-definition-source |
0m
Args: map[ContextFile:kernel/futex/core.c Name:futex_rehash_private]
Results: map[SourceCode: 200: static void futex_rehash_private(struct futex_private_hash *old,
201: struct futex_private_hash *new)
202: {
203: struct futex_hash_bucket *hb_old, *hb_new;
204: unsigned int slots = old->hash_mask + 1;
205: unsigned int i;
206:
207: for (i = 0; i < slots; i++) {
208: struct futex_q *this, *tmp;
209:
210: hb_old = &old->queues[i];
211:
212: spin_lock(&hb_old->lock);
213: plist_for_each_entry_safe(this, tmp, &hb_old->chain, list) {
214:
215: plist_del(&this->list, &hb_old->chain);
216: futex_hb_waiters_dec(hb_old);
217:
218: WARN_ON_ONCE(this->lock_ptr != &hb_old->lock);
219:
220: hb_new = __futex_hash(&this->key, new);
221: futex_hb_waiters_inc(hb_new);
222: /*
223: * The new pointer isn't published yet but an already
224: * moved user can be unqueued due to timeout or signal.
225: */
226: spin_lock_nested(&hb_new->lock, SINGLE_DEPTH_NESTING);
227: plist_add(&this->list, &hb_new->chain);
228: this->lock_ptr = &hb_new->lock;
229: spin_unlock(&hb_new->lock);
230: }
231: spin_unlock(&hb_old->lock);
232: }
233: }
SourceFile:kernel/futex/core.c]
|
| 86/2 |
2026/06/02 13:44 |
llm |
expert |
1m
Model:
Tokens:
input: 90059
output: 40
thoughts: 5074
Thoughts: **Analyzing Futex Concurrency: A Deep Dive**
Okay, so I'm digging into this `futex_rehash_private` function and the potential race conditions it introduces. First, it's moving futex queues from one hash bucket to another. My immediate thought is, "What if `futex_wake` is running at the same time?" `futex_wake` reads `hb` from `CLASS(hb, hb)(&key)`. If it gets the *old* bucket (`hb_old`), there's a lock (`hb_old->lock`). `futex_rehash_private` *holds* that same lock during the move, so that's safe. What about `hb_new`? `futex_wake` locks `hb_new->lock` *before* the queue is added. So, again, it's synchronized.
But what if `futex_wake` gets `hb_old` *after* the move? It won't see the queue. And if it gets `hb_new` before the move, it won't find it there, either. That's fine.
I then started to get concerned about `futex_unqueue` concurrently with `futex_rehash_private`. `futex_unqueue` reads `q->lock_ptr`. It blocks if it reads `&hb_old->lock` - but that's what `futex_rehash_private` *holds*! So, it would block. After it unblocks, `futex_unqueue` checks `lock_ptr != q->lock_ptr` and retries. But `q->lock_ptr` is changed to `&hb_new->lock`, so it is safe.
Then, there's that odd sequence:
```c
spin_lock_nested(&hb_new->lock, SINGLE_DEPTH_NESTING);
plist_add(&this->list, &hb_new->chain);
this->lock_ptr = &hb_new->lock;
spin_unlock(&hb_new->lock);
```
For a moment, the entry is in the new chain, but `lock_ptr` *still* points to the old lock. This intermediate state! If `futex_wake` is on `hb_new`, that is held during the move. So, still no race.
Wait, the potential crash is: `this->wake(&wake_q, this)`. `this->wake` is `futex_wake_mark`, so `__futex_wake_mark` is called, and `__futex_wake_mark` calls `__futex_unqueue`.
`__futex_unqueue` uses `q->lock_ptr` to dereference a hash bucket. This is important! What if `q->lock_ptr` is wrong? That means it will delete from the wrong bucket. But the intermediate state can't be seen so the bucket is correct!
But what else is changing `lock_ptr`? `requeue_futex` holds *both* locks, so no race is possible. `requeue_pi_wake_futex` unqueues first. So, the code looks safe.
So let's consider another potential error, what about an entry on the chain that shouldn't be there. `futex_wake` is iterating over the chain, it reads an entry. It calls `futex_match` and if this fails it just continues to the next entry. If `this` is a `futex_q` that was freed, that's impossible. We established that `futex_unqueue` ALWAYS removes it.
But what if `plist_del` *failed*? `plist_del` should remove it, so this can't happen.
Could the thread stack be freed underneath? The thread calls `futex_wait_setup`, then `futex_do_wait`. It wakes up and calls `futex_unqueue`. But `futex_unqueue` should block, because `futex_wake` holds `hb->lock`!
But what if there is no blocking! How can it not block? The only reason is if `q->lock_ptr` is NULL, which means `__futex_wake_mark` set it to NULL. But `__futex_wake_mark` is called *by* `futex_wake`. So, if that's the case, there's no problem. `futex_wake` moves on. So, there can't be a use-after-free here.
What if `futex_wake` *accesses* `this` after calling `this->wake`?
No. `
|
| 87/2 |
2026/06/02 13:45 |
tool |
codesearch-definition-source |
0m
Error:
Args: map[ContextFile:kernel/futex/waitwake.c Name:futex_unqueue_check]
Results: map[SourceCode: SourceFile:]
|
| 88/2 |
2026/06/02 13:45 |
llm |
expert |
0m
Model:
Tokens:
input: 96219
output: 39
thoughts: 30
Thoughts: Okay, here's my interpretation of those thoughts, tailored for an expert audience, and delivered as a first-person account:
**Navigating the Futex Landscape**
Alright, hold on a second. `futex_unqueue_check`... that doesn't seem right. My internal knowledge of the futex family is pretty extensive, and that particular function name isn't ringing any bells. Weird. Okay, no panic, let's just confirm. Time for a quick sanity check. I'll reach for `grep` and search for the ubiquitous `futex_do_wait`. If `futex_unqueue_check` *is* related, then finding the core wait operation should give me some clues about where that unknown is used or meant to be used. Hopefully this will give me some context, and remind me of the correct name. Time to go digging...
|
| 89/2 |
2026/06/02 13:45 |
tool |
codesearch-definition-source |
0m
Args: map[ContextFile:kernel/futex/waitwake.c Name:futex_do_wait]
Results: map[SourceCode: 336: /**
337: * futex_do_wait() - wait for wakeup, timeout, or signal
338: * @q: the futex_q to queue up on
339: * @timeout: the prepared hrtimer_sleeper, or null for no timeout
340: */
341: void futex_do_wait(struct futex_q *q, struct hrtimer_sleeper *timeout)
342: {
343: /* Arm the timer */
344: if (timeout)
345: hrtimer_sleeper_start_expires(timeout, HRTIMER_MODE_ABS);
346:
347: /*
348: * If we have been removed from the hash list, then another task
349: * has tried to wake us, and we can skip the call to schedule().
350: */
351: if (likely(!plist_node_empty(&q->list))) {
352: /*
353: * If the timer has already expired, current will already be
354: * flagged for rescheduling. Only call schedule if there
355: * is no timeout, or if it has yet to expire.
356: */
357: if (!timeout || timeout->task)
358: schedule();
359: }
360: __set_current_state(TASK_RUNNING);
361: }
SourceFile:kernel/futex/waitwake.c]
|
| 90/2 |
2026/06/02 13:45 |
llm |
expert |
0m
Model:
Tokens:
input: 96902
output: 66
thoughts: 3957
Thoughts: **Analyzing a Kernel Futex UAF Vulnerability**
Okay, so I'm digging into this futex crash. The core of it seems to be a use-after-free, where `futex_wake` is accessing a freed stack frame. Let's break this down. First, `futex_do_wait` calls `schedule()`, then `__futex_wait` calls `futex_unqueue`. Looks like the thread always gets unqueued before returning. The interesting question is *how* it gets unqueued.
My initial thought was that a race condition might let the wake-up happen *after* the unqueue, but there are multiple paths through the relevant functions. No matter the path (`futex_wait_setup`, `futex_wait_requeue_pi`, `futex_lock_pi`), it *always* calls `futex_unqueue` before the thread exits, at least in the happy path.
The crux seems to be that `futex_wake` is reading the `futex_q` in a linked list, but the thread that was waiting *somehow* manages to unqueue itself and exit. And, the `futex_wake` is still seeing the struct on the list after all that?
The common case calls `spin_lock(&hb->lock)` and then `__futex_unqueue`, with the lock held by the `futex_wake`. So, *before* the thread unlocks, the list should be safe. However, the code calls `futex_wake_mark` that does `smp_store_release(&q->lock_ptr, NULL)` *and* calls `__futex_unqueue`. So, if the lock pointer is null after that, the wake path would be fine, but the crash is after the thread is waked up, which means `q->lock_ptr` is not null.
I keep getting back to the crucial line: "If `futex_wake` holds `hb->lock`, then `futex_unqueue` CANNOT complete!". But the `futex_unqueue` code can skip the spinlock if `q->lock_ptr` is `NULL`. When is `q->lock_ptr` `NULL`? It only gets set that way in `__futex_wake_mark`, *after* the futex has been removed from the list.
Now I realize that, the only way for the stack to be accessed, *after* the thread unqueues, is if `q->lock_ptr` is `NULL`. `futex_unqueue` needs to see the NULL value, for the thread to be able to exit! But the crashing line is where the wake routine gets the wake function of the `futex_q`. This means the `futex_q` is still accessible, but the thread exited! The only legitimate way for this to occur is if the `futex_wake` code *sees* the entry *before* it gets marked, but the thread unqueues *and* exists *after* the critical code in the `futex_wake`.
But the crash is on the function call: `this->wake(&wake_q, this)` so `this` has to be valid!
If the thread is killed by SIGKILL, it still calls `futex_unqueue`. If it's a kernel thread calling `kthread_stop`, or an `io_uring` worker, it still calls `futex_unqueue`! A stack corruption where the lock pointer gets a NULL value *could* explain a UAF.
Then, there is the edge case: `futex_wait_multiple_setup` could fail, but it's not a stack frame.
So, the theory: memory corruption on `q->lock_ptr`, or possibly a race, leads to a use-after-free, where `futex_wake` sees the futex node still in the list, then the thread that was waiting unqueues and *immediately* exits. The crash is reading the freed stack frame, specifically `this->wake`. That seems very likely to me.
Okay, let's nail this down.
This is a local privilege escalation/memory corruption. It's definitely exploitable by an unprivileged user in a user namespace. It causes a denial of service. The analysis is done, and it's time to mark it accordingly.
|
| 91/2 |
2026/06/02 13:46 |
tool |
set-results |
0m
Args: map[DenialOfService:true Exploitable:true FilesystemTrigger:false NetworkTrigger:false PeripheralTrigger:false RemoteTrigger:false Unprivileged:true UserNamespace:true VMGuestTrigger:false VMHostTrigger:false]
Results: map[DenialOfService:true Exploitable:true FilesystemTrigger:false NetworkTrigger:false PeripheralTrigger:false RemoteTrigger:false Unprivileged:true UserNamespace:true VMGuestTrigger:false VMHostTrigger:false]
|
| 92/2 |
2026/06/02 13:46 |
llm |
expert |
0m
Model:
Tokens:
input: 101933
output: 728
thoughts: 331
Thoughts: **Final Reasoning on the Futex Bug**
Okay, here's the breakdown, the results are in, and now it's time to provide the definitive reasoning. The bug, as I understand it, is a nasty KASAN unknown-crash, and, from what I've seen, it seems to manifest as a use-after-free or, potentially, an out-of-bounds read in the `futex_wake` function. The key here is the fact that the crash occurs when `futex_wake` is trying to read `this->wake` from a `struct futex_q` structure. This `futex_q` was allocated on a thread's stack.
The real problem is that the thread's stack, where that `futex_q` was, has already been freed. That means the thread that originally called `futex_wait`, which would have allocated the `futex_q` on its stack, has already exited. However, somehow, `futex_wake` is still accessing that `futex_q` from the hash bucket list. This shouldn't happen. A thread, as far as I'm aware, shouldn't be able to exit without unqueuing its `futex_q`, and the `futex_unqueue` function *should* synchronize with `futex_wake` using the hash bucket spinlock (`hb->lock`).
The fact that `futex_wake` is accessing a freed stack frame tells me something has gone very wrong. Either the synchronization was bypassed somehow β perhaps a corrupted `q->lock_ptr` allowed a waiting thread to return and exit prematurely, which is a major issue β or there's a race condition lurking in the futex requeue/rehash logic that's leaving a dangling pointer in the hash bucket.
Either way, this is a memory corruption issue, a use-after-free on a thread stack, and that's bad news. It can lead to kernel crashes, which, as we know, translates to Denial of Service. In a worst-case scenario, it could also potentially open the door to privilege escalation. Given that the `futex` system call is readily available to unprivileged users and within user namespaces, this bug is accessible to just about anyone. It's not dependent on any specific hardware, network configuration, or filesystem triggers, making it a very serious vulnerability.
|
| 93/1 |
2026/06/02 13:46 |
action |
format-explanation |
0m
Results: map[Explanation:Based on the analysis of the bug report and the kernel source code, here is the
assessment of the vulnerability:
**Bug Analysis:**
The bug is a KASAN report indicating an invalid read of size 8 in `futex_wake()`
at `kernel/futex/waitwake.c:191`. The read corresponds to the evaluation of the
function pointer `this->wake` (which is typically `futex_wake_mark`) from a
`struct futex_q` object.
The KASAN report explicitly states that the buggy address belongs to a vmalloc
virtual mapping that was allocated as a thread stack (`alloc_thread_stack_node`)
and has since been freed (`__free_frozen_pages`, `__slab_free`). This means we
are looking at a **Use-After-Free (UAF)** on a thread stack.
In the normal lifecycle of a futex wait:
1. A thread allocates a `struct futex_q` on its stack (e.g., in `__futex_wait`).
2. It queues the `futex_q` into a futex hash bucket (`hb->chain`) and sets
`q->lock_ptr = &hb->lock`.
3. It blocks in `futex_do_wait()`.
4. When woken up (or upon timeout/signal), it calls `futex_unqueue()`, which
acquires `hb->lock`, removes the `futex_q` from the list, and allows the thread
to safely return and eventually exit (freeing its stack).
For `futex_wake()` to access a `futex_q` whose underlying stack has already been
freed, the waiting thread must have managed to return from the syscall and exit
*while* its `futex_q` was still linked in the hash bucket being iterated by
`futex_wake()`. Since `futex_wake()` holds `hb->lock` while iterating,
`futex_unqueue()` should have blocked the waiting thread from exiting. The fact
that it didn't implies a severe synchronization failureβlikely caused by memory
corruption (e.g., `q->lock_ptr` being overwritten, allowing `futex_unqueue` to
take the wrong lock or return prematurely) or a complex race condition in the
futex requeue/rehash logic leaving a dangling pointer in the list.
**Security Impact:**
* **Exploitable:** Yes. Use-After-Free vulnerabilities on thread stacks are
highly exploitable. An attacker can spray the freed vmalloc space with
controlled data, overwriting the `this->wake` function pointer. When
`futex_wake()` subsequently calls `this->wake(&wake_q, this)`, it will result in
arbitrary control-flow hijacking (RIP control), leading to local privilege
escalation (LPE).
* **Denial of Service:** Yes. Triggering the bug causes a kernel crash/panic,
leading to a denial of service.
* **Accessible From Unprivileged Processes:** Yes. The `futex` system call is a
fundamental synchronization primitive available to all unprivileged user-space
processes without any capability requirements.
* **Accessible From User Namespaces:** Yes. The bug can be triggered from within
a restricted user namespace.
* **Other Triggers:** The bug is localized to the core kernel futex subsystem
and does not require specific VM guest/host interactions, network packets,
peripheral devices, or malicious filesystems to trigger.]
|