Re: [PATCH RFC v2] 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 11:15 AM, syzbot wrote:
> A recent change in commit 8e7ff730dd96 ("futex: Fix race in
> futex_pivot_pending() during private hash resize") 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
> 
> Furthermore, if the mutex is contended, mutex_lock() will block. When it
> acquires the lock and returns, the task state will be reset to
> TASK_RUNNING. This causes the subsequent schedule() in the wait loop to
> return immediately, leading to a busy loop that consumes 100% CPU until the
> condition is met.
> 
> 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().
> 
> 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=d25bd376-be2e-491a-a0a7-ef39dc13a9a5
> 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]>
> 
> ---
> v2:
> - Use WRITE_ONCE() for stores to hash_new to complement READ_ONCE() in futex_pivot_pending().
> 
> 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 WRITE_ONCE() changes address my comment and the code looks
correct.

One commit-message correction remains: mutex_lock() may restore
TASK_RUNNING when it takes the slow path, causing the immediately
following schedule() not to sleep. However, the next wait-loop iteration
sets TASK_UNINTERRUPTIBLE again, so this does not necessarily result in a
100% CPU busy loop until the condition is met. Please drop or reword that
paragraph.

Please also add:

Cc: [email protected]

since the commit being fixed is marked for stable.

Please send a v3 with these changes.
lmpx.com only provides a reader for public news (NNTP) servers. It is not affiliated with the servers or forums shown here and is not responsible for the content of articles, which is written by their respective authors.