Re: [PATCH RFC v3] futex: Fix might_sleep() warning in futex_pivot_pending()
Yao Kai <[email protected]>
| Newsgroups | dev.linux.lists.syzbot |
|---|---|
| Message-ID | <[email protected]> |
On 8/13/2026 12:08 PM, syzbot wrote: > A recent change modified futex_pivot_pending() to acquire a mutex to fix a > race condition. However, futex_pivot_pending() is evaluated as a condition > inside wait_var_event() in futex_hash_allocate(). Since wait_var_event() > sets the task state to TASK_UNINTERRUPTIBLE before evaluating the > condition, calling a blocking operation like mutex_lock() is invalid and > triggers a might_sleep() warning: > > do not call blocking ops when !TASK_RUNNING; state=2 set at > [<ffffffff819e8c8d>] prepare_to_wait_event+0x3dd/0x480 > kernel/sched/wait.c:317 > WARNING: kernel/sched/core.c:9124 at __might_sleep+0x92/0xf0 > kernel/sched/core.c:9120 > Call Trace: > <TASK> > __mutex_lock_common kernel/locking/mutex.c:623 [inline] > __mutex_lock+0x118/0x1550 kernel/locking/mutex.c:821 > class_mutex_constructor include/linux/mutex.h:253 [inline] > futex_pivot_pending kernel/futex/core.c:1789 [inline] > futex_hash_allocate+0x7fb/0xf00 kernel/futex/core.c:1872 > __do_sys_prctl kernel/sys.c:2885 [inline] > __se_sys_prctl+0x78c/0x1910 kernel/sys.c:2534 > > Fix this by reverting futex_pivot_pending() to a lockless implementation > using RCU and memory barriers, which is the idiomatic way to handle > conditions in wait_event loops. By reading the hash pointer first, > executing an smp_rmb() memory barrier, and then reading hash_new, we > leverage the Message Passing (MP) pattern to guarantee correctness without > blocking. This pairs with the rcu_assign_pointer() release barrier in > __futex_pivot_hash(). If the reader sees the new hash, it is guaranteed to > see the cleared hash_new and correctly return true. If the reader sees the > old hash, it will check futex_ref_is_dead(old), which will return true if > the writer has already completed the pivot. The old hash memory is > guaranteed to remain valid for the duration of the check in > futex_ref_is_dead() because futex_pivot_pending() executes within an RCU > read-side critical section and the old hash is freed using kvfree_rcu(). > > Cc: [email protected] > > Fixes: 8e7ff730dd96 ("futex: Fix race in futex_pivot_pending() during private hash resize") > Assisted-by: Gemini:gemini-3.6-flash Gemini:gemini-3.1-pro-preview syzbot > Reported-by: [email protected] > Closes: https://syzkaller.appspot.com/bug?extid=350a93852ac854927f45 > Link: https://syzkaller.appspot.com/ai_job?id=89be9910-7060-43cd-8969-b14a2e87a76a > To: <[email protected]> > To: "Ingo Molnar" <[email protected]> > To: "Thomas Gleixner" <[email protected]> > To: "Yao Kai" <[email protected]> > Cc: =?utf-8?q?Andr=C3=A9_Almeida?= <[email protected]> > Cc: "Davidlohr Bueso" <[email protected]> > Cc: "Darren Hart" <[email protected]> > Cc: "Peter Zijlstra" <[email protected]> > > --- > v3: > - Dropped paragraph discussing busy-loop behavior when mutex is contended. > - Added Cc: [email protected] tag. > > v2: > - Use WRITE_ONCE() for stores to hash_new to complement READ_ONCE() in futex_pivot_pending(). > https://lore.kernel.org/all/[email protected]/T/ > > v1: > https://lore.kernel.org/all/[email protected]/T/ > --- > diff --git a/kernel/futex/core.c b/kernel/futex/core.c > index 128c5752f..e84be5410 100644 > --- a/kernel/futex/core.c > +++ b/kernel/futex/core.c > @@ -202,7 +202,7 @@ static bool __futex_pivot_hash(struct mm_struct *mm, struct futex_private_hash * > fph = rcu_dereference_protected(mmph->hash, lockdep_is_held(&mmph->lock)); > if (fph) { > if (!futex_ref_is_dead(fph)) { > - mmph->hash_new = new; > + WRITE_ONCE(mmph->hash_new, new); > return false; > } > > @@ -224,7 +224,7 @@ static void futex_pivot_hash(struct mm_struct *mm) > > fph = mm->futex.phash.hash_new; > if (fph) { > - mm->futex.phash.hash_new = NULL; > + WRITE_ONCE(mm->futex.phash.hash_new, NULL); > __futex_pivot_hash(mm, fph); > } > } > @@ -1786,12 +1786,18 @@ static bool futex_pivot_pending(struct mm_struct *mm) > struct futex_mm_phash *mmph = &mm->futex.phash; > struct futex_private_hash *fph; > > - guard(mutex)(&mmph->lock); > + guard(rcu)(); > > - if (!mmph->hash_new) > + fph = rcu_dereference(mmph->hash); > + /* > + * Ensure that if we see the new hash, we will also see the cleared > + * hash_new pointer. Pairs with rcu_assign_pointer() in > + * __futex_pivot_hash(). > + */ > + smp_rmb(); > + if (!READ_ONCE(mmph->hash_new)) > return true; > > - fph = rcu_dereference_raw(mmph->hash); > return futex_ref_is_dead(fph); > } > > @@ -1879,7 +1885,7 @@ static int futex_hash_allocate(unsigned int hash_slots, unsigned int flags) > cur = rcu_dereference_protected(mm->futex.phash.hash, > lockdep_is_held(&mm->futex.phash.lock)); > new = mm->futex.phash.hash_new; > - mm->futex.phash.hash_new = NULL; > + WRITE_ONCE(mm->futex.phash.hash_new, NULL); > > if (fph) { > if (cur && !cur->hash_mask) { > @@ -1889,7 +1895,7 @@ static int futex_hash_allocate(unsigned int hash_slots, unsigned int flags) > * the second one returns here. > */ > free = fph; > - mm->futex.phash.hash_new = new; > + WRITE_ONCE(mm->futex.phash.hash_new, new); > return -EBUSY; > } > if (cur && !new) { > > > base-commit: db2ddb87143519e20a95aa36c60b36107b736a58 Thanks. The code and commit message now look good. One formatting issue remains: Cc: [email protected] is separated from the rest of the trailer block by a blank line, so it is not recognized by git interpret-trailers. Please move it into the contiguous trailer block, for example: Fixes: 8e7ff730dd96 ("futex: Fix race in futex_pivot_pending() during private hash resize") Cc: [email protected] Assisted-by: ... Please send a v4 with this fixed.