Skip to content

Commit 0d78592

Browse files
committed
Merge tag 'locking-urgent-2026-08-22' of git://git.kernel.org/pub/scm/linux/kernel/git/tip/tip
Pull futex fixes from Ingo Molnar: - Enforce that the private futex owner shares the mm when attaching (Kyle Zeng, Thomas Gleixner) - Fix race on the initial mm->futex.phash.ref allocation (Hyunwoo Kim) - Fix might_sleep() warning in futex_pivot_pending() (Peter Zijlstra) * tag 'locking-urgent-2026-08-22' of git://git.kernel.org/pub/scm/linux/kernel/git/tip/tip: futex: Fix might_sleep() warning in futex_pivot_pending() futex: Fix race on the initial mm->futex.phash.ref allocation futex: Clean up the redundant exit/exec functions futex/pi: Plug private futex exec() race futex: Sanitize and document task_struct::futex::state transitions futex/pi: Reject cross-mm private futex owners
2 parents d4fd160 + d8aa5dd commit 0d78592

11 files changed

Lines changed: 214 additions & 81 deletions

File tree

‎fs/exec.c‎

Lines changed: 6 additions & 3 deletions
Original file line numberDiff line numberDiff line change
@@ -30,6 +30,7 @@
3030
#include <linux/mm.h>
3131
#include <linux/stat.h>
3232
#include <linux/fcntl.h>
33+
#include <linux/futex.h>
3334
#include <linux/swap.h>
3435
#include <linux/string.h>
3536
#include <linux/init.h>
@@ -854,7 +855,8 @@ static int exec_mmap(struct linux_binprm *bprm)
854855
/* Notify parent that we're no longer interested in the old VM */
855856
tsk = current;
856857
old_mm = current->mm;
857-
exec_mm_release(tsk, old_mm);
858+
/* Clean up futexes and release the mm */
859+
mm_exit_exec_release(tsk, old_mm);
858860

859861
ret = down_write_killable(&tsk->signal->exec_update_lock);
860862
if (ret)
@@ -902,9 +904,10 @@ static int exec_mmap(struct linux_binprm *bprm)
902904
BUG_ON(active_mm != old_mm);
903905
/* Defer teardown to setup_new_exec(), outside the exec locks. */
904906
bprm->old_mm = old_mm;
905-
return 0;
907+
} else {
908+
mmdrop_lazy_tlb(active_mm);
906909
}
907-
mmdrop_lazy_tlb(active_mm);
910+
futex_exec_done(tsk);
908911
return 0;
909912
}
910913

‎include/linux/futex.h‎

Lines changed: 4 additions & 4 deletions
Original file line numberDiff line numberDiff line change
@@ -71,8 +71,8 @@ static inline void futex_init_task(struct task_struct *tsk)
7171
}
7272

7373
void futex_exit_recursive(struct task_struct *tsk);
74-
void futex_exit_release(struct task_struct *tsk);
75-
void futex_exec_release(struct task_struct *tsk);
74+
void futex_exit_exec_release(struct task_struct *tsk);
75+
void futex_exec_done(struct task_struct *tsk);
7676

7777
long do_futex(u32 __user *uaddr, int op, u32 val, ktime_t *timeout,
7878
u32 __user *uaddr2, u32 val2, u32 val3);
@@ -89,8 +89,8 @@ static inline int futex_hash_free(struct mm_struct *mm) { return 0; }
8989
#else /* CONFIG_FUTEX */
9090
static inline void futex_init_task(struct task_struct *tsk) { }
9191
static inline void futex_exit_recursive(struct task_struct *tsk) { }
92-
static inline void futex_exit_release(struct task_struct *tsk) { }
93-
static inline void futex_exec_release(struct task_struct *tsk) { }
92+
static inline void futex_exit_exec_release(struct task_struct *tsk) { }
93+
static inline void futex_exec_done(struct task_struct *tsk) { }
9494
static inline long do_futex(u32 __user *uaddr, int op, u32 val, ktime_t *timeout,
9595
u32 __user *uaddr2, u32 val2, u32 val3)
9696
{

‎include/linux/sched/mm.h‎

Lines changed: 6 additions & 4 deletions
Original file line numberDiff line numberDiff line change
@@ -155,10 +155,12 @@ extern struct mm_struct *get_task_mm(struct task_struct *task);
155155
* succeeds.
156156
*/
157157
extern struct mm_struct *mm_access(struct task_struct *task, unsigned int mode);
158-
/* Remove the current tasks stale references to the old mm_struct on exit() */
159-
extern void exit_mm_release(struct task_struct *, struct mm_struct *);
160-
/* Remove the current tasks stale references to the old mm_struct on exec() */
161-
extern void exec_mm_release(struct task_struct *, struct mm_struct *);
158+
159+
/*
160+
* Remove the current tasks stale references to the old mm_struct on exit() and
161+
* exec(). Cleans up futexes as well.
162+
*/
163+
extern void mm_exit_exec_release(struct task_struct *, struct mm_struct *);
162164

163165
#ifdef CONFIG_MEMCG
164166
extern void mm_update_next_owner(struct mm_struct *mm);

‎include/linux/wait.h‎

Lines changed: 1 addition & 0 deletions
Original file line numberDiff line numberDiff line change
@@ -1228,6 +1228,7 @@ long prepare_to_wait_event(struct wait_queue_head *wq_head, struct wait_queue_en
12281228
void finish_wait(struct wait_queue_head *wq_head, struct wait_queue_entry *wq_entry);
12291229
long wait_woken(struct wait_queue_entry *wq_entry, unsigned mode, long timeout);
12301230
int woken_wake_function(struct wait_queue_entry *wq_entry, unsigned mode, int sync, void *key);
1231+
int woken_wake_bit_function(struct wait_queue_entry *wq_entry, unsigned mode, int sync, void *key);
12311232
int autoremove_wake_function(struct wait_queue_entry *wq_entry, unsigned mode, int sync, void *key);
12321233

12331234
#define DEFINE_WAIT_FUNC(name, function) \

‎include/linux/wait_bit.h‎

Lines changed: 1 addition & 0 deletions
Original file line numberDiff line numberDiff line change
@@ -32,6 +32,7 @@ int out_of_line_wait_on_bit_timeout(unsigned long *word, int, wait_bit_action_f
3232
int out_of_line_wait_on_bit_lock(unsigned long *word, int, wait_bit_action_f *action, unsigned int mode);
3333
struct wait_queue_head *bit_waitqueue(unsigned long *word, int bit);
3434
extern void __init wait_bit_init(void);
35+
extern struct wait_bit_key *__var_wake_key(struct wait_queue_entry *wq_entry, void *arg);
3536

3637
int wake_bit_function(struct wait_queue_entry *wq_entry, unsigned mode, int sync, void *key);
3738

‎kernel/exit.c‎

Lines changed: 1 addition & 1 deletion
Original file line numberDiff line numberDiff line change
@@ -581,7 +581,7 @@ static void exit_mm(void)
581581
{
582582
struct mm_struct *mm = current->mm;
583583

584-
exit_mm_release(current, mm);
584+
mm_exit_exec_release(current, mm);
585585
if (!mm)
586586
return;
587587

‎kernel/fork.c‎

Lines changed: 2 additions & 8 deletions
Original file line numberDiff line numberDiff line change
@@ -1508,15 +1508,9 @@ static void mm_release(struct task_struct *tsk, struct mm_struct *mm)
15081508
complete_vfork_done(tsk);
15091509
}
15101510

1511-
void exit_mm_release(struct task_struct *tsk, struct mm_struct *mm)
1511+
void mm_exit_exec_release(struct task_struct *tsk, struct mm_struct *mm)
15121512
{
1513-
futex_exit_release(tsk);
1514-
mm_release(tsk, mm);
1515-
}
1516-
1517-
void exec_mm_release(struct task_struct *tsk, struct mm_struct *mm)
1518-
{
1519-
futex_exec_release(tsk);
1513+
futex_exit_exec_release(tsk);
15201514
mm_release(tsk, mm);
15211515
}
15221516

‎kernel/futex/core.c‎

Lines changed: 74 additions & 30 deletions
Original file line numberDiff line numberDiff line change
@@ -46,6 +46,7 @@
4646
#include <linux/slab.h>
4747
#include <linux/vmalloc.h>
4848
#include <linux/kmemleak.h>
49+
#include <linux/wait_bit.h>
4950

5051
#include <vdso/futex.h>
5152

@@ -1527,44 +1528,59 @@ static void futex_cleanup_begin(struct task_struct *tsk)
15271528
raw_spin_unlock_irq(&tsk->pi_lock);
15281529
}
15291530

1530-
static void futex_cleanup_end(struct task_struct *tsk, int state)
1531+
static void futex_cleanup_end(struct task_struct *tsk)
15311532
__releases(&tsk->futex.exit_mutex)
15321533
{
1533-
/*
1534-
* Lockless store. The only side effect is that an observer might
1535-
* take another loop until it becomes visible.
1536-
*/
1537-
tsk->futex.state = state;
1534+
scoped_guard(raw_spinlock_irq, &tsk->pi_lock)
1535+
tsk->futex.state = FUTEX_STATE_DEAD;
1536+
15381537
/*
15391538
* Drop the exit protection. This unblocks waiters which observed
15401539
* FUTEX_STATE_EXITING to reevaluate the state.
15411540
*/
15421541
mutex_unlock(&tsk->futex.exit_mutex);
15431542
}
15441543

1545-
void futex_exec_release(struct task_struct *tsk)
1544+
/*
1545+
* Invoked from mm_exit_exec_release() to cleanup the robust lists and pi state
1546+
* of the outgoing task.
1547+
*
1548+
* exec() makes it interesting for futexes because the TID of the task stays the
1549+
* same, but from a futex perspective the task has to be treated like an exiting
1550+
* task. This is especially important for the sanity check for private futexes
1551+
* in attach_to_pi_owner() which compares the owner's mm with the waiter's mm.
1552+
*
1553+
* That check would give the wrong answer if futex_cleanup_end() would
1554+
* set the state to FUTEX_STATE_OK as long as the task still has the old
1555+
* mm.
1556+
*
1557+
* After the task has switched to the new mm it sets it to
1558+
* FUTEX_STATE_OK again in futex_exec_done().
1559+
*/
1560+
void futex_exit_exec_release(struct task_struct *tsk)
15461561
{
1547-
/*
1548-
* The state handling is done for consistency, but in the case of
1549-
* exec() there is no way to prevent further damage as the PID stays
1550-
* the same. But for the unlikely and arguably buggy case that a
1551-
* futex is held on exec(), this provides at least as much state
1552-
* consistency protection which is possible.
1553-
*/
15541562
futex_cleanup_begin(tsk);
15551563
futex_cleanup(tsk);
1556-
/*
1557-
* Reset the state to FUTEX_STATE_OK. The task is alive and about
1558-
* exec a new binary.
1559-
*/
1560-
futex_cleanup_end(tsk, FUTEX_STATE_OK);
1564+
futex_cleanup_end(tsk);
15611565
}
15621566

1563-
void futex_exit_release(struct task_struct *tsk)
1567+
/*
1568+
* exec() has switched to the new mm. Futex operations are safe again.
1569+
*/
1570+
void futex_exec_done(struct task_struct *tsk)
15641571
{
1565-
futex_cleanup_begin(tsk);
1566-
futex_cleanup(tsk);
1567-
futex_cleanup_end(tsk, FUTEX_STATE_DEAD);
1572+
/*
1573+
* This store does not have to take tsk::futex::exit_mutex because the
1574+
* phase where waiters block on it during state FUTEX_STATE_EXITING has
1575+
* been finished when futex_cleanup_end() set the state to
1576+
* FUTEX_STATE_DEAD.
1577+
*
1578+
* This transitions back from FUTEX_STATE_DEAD to FUTEX_STATE_OK. The
1579+
* ordering guarantee required here is that the previous store to
1580+
* tsk::mm in the calling code cannot be reordered against this store.
1581+
*/
1582+
guard(raw_spinlock_irq)(&tsk->pi_lock);
1583+
tsk->futex.state = FUTEX_STATE_OK;
15681584
}
15691585

15701586
static void futex_hash_bucket_init(struct futex_hash_bucket *fhb)
@@ -1844,14 +1860,18 @@ static int futex_hash_allocate(unsigned int hash_slots, unsigned int flags)
18441860
}
18451861

18461862
if (!mm->futex.phash.ref) {
1863+
unsigned int __percpu *ref = alloc_percpu(unsigned int);
1864+
1865+
if (!ref)
1866+
return -ENOMEM;
1867+
18471868
/*
1848-
* This will always be allocated by the first thread and
1849-
* therefore requires no locking.
1869+
* Tasks sharing the mm can run this concurrently, so take the
1870+
* initial reference before publishing the counter.
18501871
*/
1851-
mm->futex.phash.ref = alloc_percpu(unsigned int);
1852-
if (!mm->futex.phash.ref)
1853-
return -ENOMEM;
1854-
this_cpu_inc(*mm->futex.phash.ref); /* 0 -> 1 */
1872+
this_cpu_inc(*ref); /* 0 -> 1 */
1873+
if (cmpxchg(&mm->futex.phash.ref, NULL, ref))
1874+
free_percpu(ref);
18551875
}
18561876

18571877
fph = kvzalloc(struct_size(fph, queues, hash_slots),
@@ -1867,11 +1887,35 @@ static int futex_hash_allocate(unsigned int hash_slots, unsigned int flags)
18671887
futex_hash_bucket_init(&fph->queues[i]);
18681888

18691889
if (custom) {
1890+
struct wait_bit_queue_entry __wbq_entry;
1891+
struct wait_queue_head *__wq_head;
1892+
18701893
/*
18711894
* Only let prctl() wait / retry; don't unduly delay clone().
18721895
*/
18731896
again:
1874-
wait_var_event(mm, futex_pivot_pending(mm));
1897+
__wq_head = __var_waitqueue(mm);
1898+
init_wait_var_entry(&__wbq_entry, mm, 0);
1899+
__wbq_entry.wq_entry.func = woken_wake_bit_function;
1900+
add_wait_queue(__wq_head, &__wbq_entry.wq_entry);
1901+
1902+
/*
1903+
* add_wait_queue() futex_ref_put()
1904+
* MB (this) MB (implied)
1905+
* futex_pivot_pending() wake_up_var()
1906+
* waitqueue_active()
1907+
*
1908+
* Notably, it must not be possible to see
1909+
* !futex_pivot_pending() && !waitqueue_active().
1910+
*/
1911+
smp_mb();
1912+
1913+
while (!futex_pivot_pending(mm) &&
1914+
wait_woken(&__wbq_entry.wq_entry, TASK_UNINTERRUPTIBLE,
1915+
MAX_SCHEDULE_TIMEOUT))
1916+
/* empty */;
1917+
1918+
remove_wait_queue(__wq_head, &__wbq_entry.wq_entry);
18751919
}
18761920

18771921
scoped_guard(mutex, &mm->futex.phash.lock) {

0 commit comments

Comments
 (0)