diff options
| author | Linus Torvalds <torvalds@linux-foundation.org> | 2026-06-15 14:21:14 +0530 |
|---|---|---|
| committer | Linus Torvalds <torvalds@linux-foundation.org> | 2026-06-15 14:21:14 +0530 |
| commit | 764e77d868a5b932c709e20ddb5993f9111a841c (patch) | |
| tree | e3285615d0abbafba6ca3559d6fbd20bf31c4589 /Documentation | |
| parent | 186d3c4e92242351afc24d9784f31cb4cd08a4b7 (diff) | |
| parent | 4f070ccb4dc4692e3b6757819fb80655f58b4f12 (diff) | |
| download | linux-next-764e77d868a5b932c709e20ddb5993f9111a841c.tar.gz linux-next-764e77d868a5b932c709e20ddb5993f9111a841c.zip | |
Merge tag 'locking-core-2026-06-14' of gitolite.kernel.org:pub/scm/linux/kernel/git/tip/tip
Pull locking updates from Ingo Molnar:
"Futex updates:
- Optimize futex hash bucket access patterns (Peter Zijlstra)
- Large series to address the robust futex unlock race for real, by
Thomas Gleixner:
"The robust futex unlock mechanism is racy in respect to the
clearing of the robust_list_head::list_op_pending pointer because
unlock and clearing the pointer are not atomic.
The race window is between the unlock and clearing the pending op
pointer. If the task is forced to exit in this window, exit will
access a potentially invalid pending op pointer when cleaning up
the robust list.
That happens if another task manages to unmap the object
containing the lock before the cleanup, which results in an UAF.
In the worst case this UAF can lead to memory corruption when
unrelated content has been mapped to the same address by the time
the access happens.
User space can't solve this problem without help from the kernel.
This series provides the kernel side infrastructure to help it
along:
1) Combined unlock, pointer clearing, wake-up for the
contended case
2) VDSO based unlock and pointer clearing helpers with a
fix-up function in the kernel when user space was interrupted
within the critical section.
... with help by André Almeida:
- Add a note about robust list race condition (André Almeida)
- Add self-tests for robust release operations (André Almeida)
Context analysis updates:
- Implement context analysis for 'struct rt_mutex'. (Bart Van Assche)
- Bump required Clang version to 23 (Marco Elver)
Guard infrastructure updates:
- Series to remove NULL check from unconditional guards (Dmitry
Ilvokhin)
Lockdep updates:
- Restore self-test migrate_disable() and sched_rt_mutex state on
PREEMPT_RT (Karl Mehltretter)
Membarriers updates:
- Use per-CPU mutexes for targeted commands (Aniket Gattani)
- Modernize membarrier_global_expedited with cleanup guards (Aniket
Gattani)
- Add rseq stress test for CFS throttle interactions (Aniket Gattani)
percpu-rwsems updates:
- Extract __percpu_up_read() to optimize inlining overhead (Dmitry
Ilvokhin)
Seqlocks updates:
- Allow UBSAN_ALIGNMENT to fail optimizing (Heiko Carstens)
Lock tracing:
- Add contended_release tracepoint to sleepable locks such as
mutexes, percpu-rwsems, rtmutexes, rwsems and semaphores (Dmitry
Ilvokhin)
MAINTAINERS updates:
- MAINTAINERS: Add RUST [SYNC] entry (Boqun Feng)
Misc updates and fixes by Randy Dunlap, YE WEI-HONG, Fabricio Parra,
Dmitry Ilvokhin and Peter Zijlstra"
* tag 'locking-core-2026-06-14' of gitolite.kernel.org:pub/scm/linux/kernel/git/tip/tip: (36 commits)
locking: Add contended_release tracepoint to sleepable locks
locking/percpu-rwsem: Extract __percpu_up_read()
tracing/lock: Remove unnecessary linux/sched.h include
futex: Optimize futex hash bucket access patterns
rust: sync: completion: Mark inline complete_all and wait_for_completion
MAINTAINERS: Add RUST [SYNC] entry
cleanup: Specify nonnull argument index
selftests: futex: Add tests for robust release operations
Documentation: futex: Add a note about robust list race condition
x86/vdso: Implement __vdso_futex_robust_try_unlock()
x86/vdso: Prepare for robust futex unlock support
futex: Provide infrastructure to plug the non contended robust futex unlock race
futex: Add robust futex unlock IP range
futex: Add support for unlocking robust futexes
futex: Cleanup UAPI defines
x86: Select ARCH_MEMORY_ORDER_TSO
uaccess: Provide unsafe_atomic_store_release_user()
futex: Provide UABI defines for robust list entry modifiers
futex: Move futex related mm_struct data into a struct
futex: Make futex_mm_init() void
...
Diffstat (limited to 'Documentation')
| -rw-r--r-- | Documentation/dev-tools/context-analysis.rst | 2 | ||||
| -rw-r--r-- | Documentation/locking/robust-futex-ABI.rst | 44 | ||||
| -rw-r--r-- | Documentation/locking/robust-futexes.rst | 8 |
3 files changed, 49 insertions, 5 deletions
diff --git a/Documentation/dev-tools/context-analysis.rst b/Documentation/dev-tools/context-analysis.rst index 54d9ee28de98..8e71e1e75b5b 100644 --- a/Documentation/dev-tools/context-analysis.rst +++ b/Documentation/dev-tools/context-analysis.rst @@ -17,7 +17,7 @@ features. To enable for Clang, configure the kernel with:: CONFIG_WARN_CONTEXT_ANALYSIS=y -The feature requires Clang 22 or later. +The feature requires Clang 23 or later. The analysis is *opt-in by default*, and requires declaring which modules and subsystems should be analyzed in the respective `Makefile`:: diff --git a/Documentation/locking/robust-futex-ABI.rst b/Documentation/locking/robust-futex-ABI.rst index f24904f1c16f..5e6a0665b8ba 100644 --- a/Documentation/locking/robust-futex-ABI.rst +++ b/Documentation/locking/robust-futex-ABI.rst @@ -153,6 +153,9 @@ On removal: 3) release the futex lock, and 4) clear the 'lock_op_pending' word. +Please note that the removal of a robust futex purely in userspace is +racy. Refer to the next chapter to learn more and how to avoid this. + On exit, the kernel will consider the address stored in 'list_op_pending' and the address of each 'lock word' found by walking the list starting at 'head'. For each such address, if the bottom 30 @@ -182,3 +185,44 @@ any point: When the kernel sees a list entry whose 'lock word' doesn't have the current threads TID in the lower 30 bits, it does nothing with that entry, and goes on to the next entry. + +Robust release is racy +---------------------- + +The removal of a robust futex from the list is racy when doing it solely in +userspace. Quoting Thomas Gleixner for the explanation: + + The robust futex unlock mechanism is racy in respect to the clearing of the + robust_list_head::list_op_pending pointer because unlock and clearing the + pointer are not atomic. The race window is between the unlock and clearing + the pending op pointer. If the task is forced to exit in this window, exit + will access a potentially invalid pending op pointer when cleaning up the + robust list. That happens if another task manages to unmap the object + containing the lock before the cleanup, which results in an UAF. In the + worst case this UAF can lead to memory corruption when unrelated content + has been mapped to the same address by the time the access happens. + +A full in-depth analysis can be read at +https://lore.kernel.org/lkml/20260316162316.356674433@kernel.org/ + +To overcome that, the kernel needs to participate in the lock release operation. +This ensures that the release happens "atomically" with regard to releasing +the lock and removing the address from ``list_op_pending``. If the release is +interrupted by a signal, the kernel will also verify if it interrupted the +release operation. + +For the contended unlock case, where other threads are waiting for the lock +release, there's the ``FUTEX_ROBUST_UNLOCK`` operation feature flag for the +``futex()`` system call, which must be used with one of the following +operations: ``FUTEX_WAKE``, ``FUTEX_WAKE_BITSET`` or ``FUTEX_UNLOCK_PI``. +The kernel will release the lock (set the futex word to zero), clean the +``list_op_pending`` field. Then, it will proceed with the normal wake path. + +For the non-contended path, there's still a race between checking the futex word +and clearing the ``list_op_pending`` field. To solve this without the need of a +complete system call, userspace should call the virtual syscall +``__vdso_futex_robust_listXX_try_unlock()`` (where XX is either 32 or 64, +depending on the size of the pointer). If the vDSO call succeeds, it means that +it released the lock and cleared ``list_op_pending``. If it fails, that means +that there are waiters for this lock and a call to ``futex()`` syscall with +``FUTEX_ROBUST_UNLOCK`` is needed. diff --git a/Documentation/locking/robust-futexes.rst b/Documentation/locking/robust-futexes.rst index 6361fb01c9c1..1423f53ea2f4 100644 --- a/Documentation/locking/robust-futexes.rst +++ b/Documentation/locking/robust-futexes.rst @@ -94,7 +94,7 @@ time, the kernel checks this user-space list: are there any robust futex locks to be cleaned up? In the common case, at do_exit() time, there is no list registered, so -the cost of robust futexes is just a simple current->robust_list != NULL +the cost of robust futexes is just a current->futex.robust_list != NULL comparison. If the thread has registered a list, then normally the list is empty. If the thread/process crashed or terminated in some incorrect way then the list might be non-empty: in this case the kernel carefully @@ -178,9 +178,9 @@ one to query the registered list pointer:: size_t __user *len_ptr); List registration is very fast: the pointer is simply stored in -current->robust_list. [Note that in the future, if robust futexes become -widespread, we could extend sys_clone() to register a robust-list head -for new threads, without the need of another syscall.] +current->futex.robust_list. [Note that in the future, if robust futexes +become widespread, we could extend sys_clone() to register a robust-list +head for new threads, without the need of another syscall.] So there is virtually zero overhead for tasks not using robust futexes, and even for robust futex users, there is only one extra syscall per |
