linux-kernel.vger.kernel.org archive mirror
 help / color / mirror / Atom feed
* [PATCH v3 0/2] futex: Address two futex-requeue-pi issues
@ 2026-08-24 12:55 Sebastian Andrzej Siewior
  2026-08-24 12:55 ` [PATCH v3 1/2] futex: Add missing rt_mutex_.*_schedule() around rt_mutex_wait_proxy_lock() Sebastian Andrzej Siewior
  2026-08-24 12:55 ` [PATCH v3 2/2] futex: Prevent rcuwait use-after-free during requeue PI Sebastian Andrzej Siewior
  0 siblings, 2 replies; 5+ messages in thread
From: Sebastian Andrzej Siewior @ 2026-08-24 12:55 UTC (permalink / raw)
  To: linux-kernel
  Cc: André Almeida, Darren Hart, Davidlohr Bueso, Ingo Molnar,
	Peter Zijlstra, Thomas Gleixner, Borislav Petkov, Yao Kai,
	Sebastian Andrzej Siewior

This is an update to Yao Kai initial series. I polished the commit
description and bit and update the comment in #2.

#1 Was simply missed in the initial commit and never noticed.
#2 Requires extreme precise timing to reproduce.

v2…v3: https://lore.kernel.org/20260722085140.1949077-1-yaokai34@huawei.com
 - Update commit message for both patches.
 - Drop the comment from #1. The whole thing has nothing to do with
   skipped schedule(). The only problem is that that rt_mutex_schedule()
   requires a rt_mutex_.*_schedule() invocation before rtmutex is about
   to be acquired. In case it went unnoticed for so long because that
   rt_mutex is usually not contended.
 - Update the comment in #2 to describe the race and why the wake is
   skipped.

v1…v2: (Yao Kai) https://lore.kernel.org/20260722085140.1949077-1-yaokai34@huawei.com
 - Replace the scheduler helper split in patch 1 with
   rt_mutex_pre_schedule()/rt_mutex_post_schedule() directly around
   rt_mutex_wait_proxy_lock().
 - Expand patch 2's comment and changelog to explain why the saved-task
   wakeup covers rcuwait without losing a wakeup.

Yao Kai (2):
  futex: Add missing rt_mutex_.*_schedule() around
    rt_mutex_wait_proxy_lock()
  futex: Prevent rcuwait use-after-free during requeue PI

 kernel/futex/requeue.c | 16 ++++++++++++++--
 1 file changed, 14 insertions(+), 2 deletions(-)

Sebastian

^ permalink raw reply	[flat|nested] 5+ messages in thread

* [PATCH v3 1/2] futex: Add missing rt_mutex_.*_schedule() around rt_mutex_wait_proxy_lock()
  2026-08-24 12:55 [PATCH v3 0/2] futex: Address two futex-requeue-pi issues Sebastian Andrzej Siewior
@ 2026-08-24 12:55 ` Sebastian Andrzej Siewior
  2026-08-24 13:23   ` Peter Zijlstra
  2026-08-24 12:55 ` [PATCH v3 2/2] futex: Prevent rcuwait use-after-free during requeue PI Sebastian Andrzej Siewior
  1 sibling, 1 reply; 5+ messages in thread
From: Sebastian Andrzej Siewior @ 2026-08-24 12:55 UTC (permalink / raw)
  To: linux-kernel
  Cc: André Almeida, Darren Hart, Davidlohr Bueso, Ingo Molnar,
	Peter Zijlstra, Thomas Gleixner, Borislav Petkov, Yao Kai,
	Sebastian Andrzej Siewior

From: Yao Kai <yaokai34@huawei.com>

A waiter requeued onto a PI futex can reach rt_mutex_wait_proxy_lock()
without rtmutex schedule preparation, triggering the lockdep_assert() in
rt_mutex_schedule().
The lack of it, can be seen with requeue PI, multiple waiters and
requeing multiple tasks, the subsequent requeued task can be requeued in
the state Q_REQUEUE_PI_DONE:

        waiter                          requeue task
        ------                          ------------
futex_wait_requeue_pi()
  futex_wait_setup()
    futex_queue(&q)
                                        futex_requeue()
                                          futex_proxy_trylock_atomic()
                                          futex_requeue_pi_prepare()
                                           Q_REQUEUE_PI_NONE->Q_REQUEUE_PI_IN_PROGRESS

                                          rt_mutex_start_proxy_lock() (ret = 0)
                                          requeue_futex()
  futex_do_wait()
                                          futex_requeue_pi_complete()
                                            Q_REQUEUE_PI_IN_PROGRESS -> Q_REQUEUE_PI_DONE
  futex_requeue_pi_wakeup_sync()
  rt_mutex_wait_proxy_lock()
    rt_mutex_schedule() (on contention)

In the Q_REQUEUE_PI_DONE case the waiter will acquire the pi_mutex.
Should the lock be contended, the waiter will invoke rt_mutex_schedule()
without invoking rt_mutex_.*_schedule() before/ after scheduling.

Invoke rt_mutex_pre_schedule() and rt_mutex_post_schedule() directly around
rt_mutex_wait_proxy_lock().

[bigeasy: Redid parts of the changelog, dropped the comment misleading]

Fixes: d14f9e930b90 ("locking/rtmutex: Use rt_mutex specific scheduler helpers")
Suggested-by: Sebastian Andrzej Siewior <bigeasy@linutronix.de>
Signed-off-by: Yao Kai <yaokai34@huawei.com>
Signed-off-by: Sebastian Andrzej Siewior <bigeasy@linutronix.de>
Reviewed-by: Sebastian Andrzej Siewior <bigeasy@linutronix.de>
---
 kernel/futex/requeue.c | 4 ++++
 1 file changed, 4 insertions(+)

diff --git a/kernel/futex/requeue.c b/kernel/futex/requeue.c
index 79823ad136830..d8c9e7d218695 100644
--- a/kernel/futex/requeue.c
+++ b/kernel/futex/requeue.c
@@ -1,6 +1,7 @@
 // SPDX-License-Identifier: GPL-2.0-or-later
 
 #include <linux/plist.h>
+#include <linux/sched/rt.h>
 #include <linux/sched/signal.h>
 
 #include "futex.h"
@@ -865,7 +866,10 @@ int futex_wait_requeue_pi(u32 __user *uaddr, unsigned int flags,
 	case Q_REQUEUE_PI_DONE:
 		/* Requeue completed. Current is 'pi_blocked_on' the rtmutex */
 		pi_mutex = &q.pi_state->pi_mutex;
+
+		rt_mutex_pre_schedule();
 		ret = rt_mutex_wait_proxy_lock(pi_mutex, to, &rt_waiter);
+		rt_mutex_post_schedule();
 
 		/*
 		 * See futex_unlock_pi()'s cleanup: comment.
-- 
2.55.0


^ permalink raw reply related	[flat|nested] 5+ messages in thread

* [PATCH v3 2/2] futex: Prevent rcuwait use-after-free during requeue PI
  2026-08-24 12:55 [PATCH v3 0/2] futex: Address two futex-requeue-pi issues Sebastian Andrzej Siewior
  2026-08-24 12:55 ` [PATCH v3 1/2] futex: Add missing rt_mutex_.*_schedule() around rt_mutex_wait_proxy_lock() Sebastian Andrzej Siewior
@ 2026-08-24 12:55 ` Sebastian Andrzej Siewior
  1 sibling, 0 replies; 5+ messages in thread
From: Sebastian Andrzej Siewior @ 2026-08-24 12:55 UTC (permalink / raw)
  To: linux-kernel
  Cc: André Almeida, Darren Hart, Davidlohr Bueso, Ingo Molnar,
	Peter Zijlstra, Thomas Gleixner, Borislav Petkov, Yao Kai,
	Sebastian Andrzej Siewior

From: Yao Kai <yaokai34@huawei.com>

On PREEMPT_RT, FUTEX_CMP_REQUEUE_PI can trigger a KASAN report
(slab-out-of-bounds) in futex_requeue_pi_complete() invocation of
rcuwait_wake_up().

The futex_q used by futex_wait_requeue_pi() is allocated on the waiter's
stack. An early wakeup can race with a PI requeue as follows:

        waiter                          requeue task
        ------                          ------------
futex_wait_requeue_pi()
  futex_do_wait()
    schedule()
                                       futex_requeue
                                         futex_proxy_trylock_atomic()
                                           futex_requeue_pi_prepare()
                                            Q_REQUEUE_PI_NONE -> Q_REQUEUE_PI_IN_PROGRESS
* timeout/ signal wakes waiter *
  futex_requeue_pi_wakeup_sync()
   Q_REQUEUE_PI_IN_PROGRESS -> Q_REQUEUE_PI_WAIT
                                           requeue_pi_wake_futex
                                             futex_requeue_pi_complete()
                                               cmpxchg Q_REQUEUE_PI_WAIT -> Q_REQUEUE_PI_LOCKED
    rcuwait_wait_event()
      if (atomic_read(&q->requeue_state) != Q_REQUEUE_PI_WAIT)
       break /* no schedule() */

 /* q.pi_state->owner == current */
 futex_private_hash_put()
 /* return from syscall */
                                              rcuwait_wake_up(&q->requeue_wait)
                                                /* q is gone */

futex_requeue_pi_complete() publishes Q_REQUEUE_PI_LOCKED before
calling rcuwait_wake_up(). The waiter observes this state in
rcuwait_wait_event() before invoking schedule() in rcuwait_wait_event().
Here, the waiter is free leave the syscall before requeue task can
complete the wake.

To address this race skip rcuwait_wake_up() in the Q_REQUEUE_PI_LOCKED
case.
This state is only published by requeue_pi_wake_futex(), which saves
q->task before futex_requeue_pi_complete() and wakes the waiter via
wake_up_state().

This wake is intended to wake the waiter from its futex_do_wait() sleep.
If the waiter is still sleeping there, it can not get into the
Q_REQUEUE_PI_WAIT state (and require this removed wake).
Should the waiter be woken up from futex_do_wait() by other means (as in
this example) and sleep in futex_requeue_pi_wakeup_sync() then the
wake_up_state() from requeue_pi_wake_futex() will wake it, too.
Should the waiter task terminate before wake_up_state() had a chance to
wake the task then the task pointer does not become invalid because the
futex_hash_bucket::lock is held and the task pointer is RCU protected.

[bigeasy: Updated comment and commit message]

Fixes: 07d91ef510fb1 ("futex: Prevent requeue_pi() lock nesting issue on RT")
Signed-off-by: Yao Kai <yaokai34@huawei.com>
Signed-off-by: Sebastian Andrzej Siewior <bigeasy@linutronix.de>
Reviewed-by: Sebastian Andrzej Siewior <bigeasy@linutronix.de>
---
 kernel/futex/requeue.c | 12 ++++++++++--
 1 file changed, 10 insertions(+), 2 deletions(-)

diff --git a/kernel/futex/requeue.c b/kernel/futex/requeue.c
index d8c9e7d218695..6e98d5189f90a 100644
--- a/kernel/futex/requeue.c
+++ b/kernel/futex/requeue.c
@@ -155,8 +155,16 @@ static inline void futex_requeue_pi_complete(struct futex_q *q, int locked)
 	} while (!atomic_try_cmpxchg(&q->requeue_state, &old, new));
 
 #ifdef CONFIG_PREEMPT_RT
-	/* If the waiter interleaved with the requeue let it know */
-	if (unlikely(old == Q_REQUEUE_PI_WAIT))
+	/*
+	 * The waiter in futex_requeue_pi_wakeup_sync() can interleave with the
+	 * wake below: It will assign Q_REQUEUE_PI_IN_PROGRESS and here it will
+	 * be updated to Q_REQUEUE_PI_LOCKED (locked = 1). The rcuwait_wait_event()
+	 * will already read Q_REQUEUE_PI_LOCKED and skip the schedule() invocation,
+	 * leading to an access of futex_q::requeue_wait after the waiter returned.
+	 * In this case only we skip the wake here and rely on following wake in
+	 * requeue_pi_wake_futex() to perform the wake if needed.
+	 */
+	if (unlikely(old == Q_REQUEUE_PI_WAIT) && new != Q_REQUEUE_PI_LOCKED)
 		rcuwait_wake_up(&q->requeue_wait);
 #endif
 }
-- 
2.55.0


^ permalink raw reply related	[flat|nested] 5+ messages in thread

* Re: [PATCH v3 1/2] futex: Add missing rt_mutex_.*_schedule() around rt_mutex_wait_proxy_lock()
  2026-08-24 12:55 ` [PATCH v3 1/2] futex: Add missing rt_mutex_.*_schedule() around rt_mutex_wait_proxy_lock() Sebastian Andrzej Siewior
@ 2026-08-24 13:23   ` Peter Zijlstra
  2026-08-24 14:09     ` Sebastian Andrzej Siewior
  0 siblings, 1 reply; 5+ messages in thread
From: Peter Zijlstra @ 2026-08-24 13:23 UTC (permalink / raw)
  To: Sebastian Andrzej Siewior
  Cc: linux-kernel, André Almeida, Darren Hart, Davidlohr Bueso,
	Ingo Molnar, Thomas Gleixner, Borislav Petkov, Yao Kai

On Mon, Aug 24, 2026 at 02:55:42PM +0200, Sebastian Andrzej Siewior wrote:
> From: Yao Kai <yaokai34@huawei.com>
> 
> A waiter requeued onto a PI futex can reach rt_mutex_wait_proxy_lock()
> without rtmutex schedule preparation, triggering the lockdep_assert() in
> rt_mutex_schedule().
> The lack of it, can be seen with requeue PI, multiple waiters and
> requeing multiple tasks, the subsequent requeued task can be requeued in
> the state Q_REQUEUE_PI_DONE:
> 
>         waiter                          requeue task
>         ------                          ------------
> futex_wait_requeue_pi()
>   futex_wait_setup()
>     futex_queue(&q)
>                                         futex_requeue()
>                                           futex_proxy_trylock_atomic()
>                                           futex_requeue_pi_prepare()
>                                            Q_REQUEUE_PI_NONE->Q_REQUEUE_PI_IN_PROGRESS
> 
>                                           rt_mutex_start_proxy_lock() (ret = 0)
>                                           requeue_futex()
>   futex_do_wait()
>                                           futex_requeue_pi_complete()
>                                             Q_REQUEUE_PI_IN_PROGRESS -> Q_REQUEUE_PI_DONE
>   futex_requeue_pi_wakeup_sync()
>   rt_mutex_wait_proxy_lock()
>     rt_mutex_schedule() (on contention)
> 
> In the Q_REQUEUE_PI_DONE case the waiter will acquire the pi_mutex.
> Should the lock be contended, the waiter will invoke rt_mutex_schedule()
> without invoking rt_mutex_.*_schedule() before/ after scheduling.
> 
> Invoke rt_mutex_pre_schedule() and rt_mutex_post_schedule() directly around
> rt_mutex_wait_proxy_lock().

Still doesn't explain why this is makes sense. Perhaps something like
the below comment to demystify the code?

> [bigeasy: Redid parts of the changelog, dropped the comment misleading]
> 
> Fixes: d14f9e930b90 ("locking/rtmutex: Use rt_mutex specific scheduler helpers")
> Suggested-by: Sebastian Andrzej Siewior <bigeasy@linutronix.de>
> Signed-off-by: Yao Kai <yaokai34@huawei.com>
> Signed-off-by: Sebastian Andrzej Siewior <bigeasy@linutronix.de>
> Reviewed-by: Sebastian Andrzej Siewior <bigeasy@linutronix.de>
> ---
>  kernel/futex/requeue.c | 4 ++++
>  1 file changed, 4 insertions(+)
> 
> diff --git a/kernel/futex/requeue.c b/kernel/futex/requeue.c
> index 79823ad136830..d8c9e7d218695 100644
> --- a/kernel/futex/requeue.c
> +++ b/kernel/futex/requeue.c
> @@ -1,6 +1,7 @@
>  // SPDX-License-Identifier: GPL-2.0-or-later
>  
>  #include <linux/plist.h>
> +#include <linux/sched/rt.h>
>  #include <linux/sched/signal.h>
>  
>  #include "futex.h"
> @@ -865,7 +866,10 @@ int futex_wait_requeue_pi(u32 __user *uaddr, unsigned int flags,
>  	case Q_REQUEUE_PI_DONE:
>  		/* Requeue completed. Current is 'pi_blocked_on' the rtmutex */
>  		pi_mutex = &q.pi_state->pi_mutex;
> +

		/*
		 * Since current is doing the requeue, it cannot also be
		 * a waiter on the same futex.
		 */

> +		rt_mutex_pre_schedule();
>  		ret = rt_mutex_wait_proxy_lock(pi_mutex, to, &rt_waiter);
> +		rt_mutex_post_schedule();
>  
>  		/*
>  		 * See futex_unlock_pi()'s cleanup: comment.
> -- 
> 2.55.0
> 

^ permalink raw reply	[flat|nested] 5+ messages in thread

* Re: [PATCH v3 1/2] futex: Add missing rt_mutex_.*_schedule() around rt_mutex_wait_proxy_lock()
  2026-08-24 13:23   ` Peter Zijlstra
@ 2026-08-24 14:09     ` Sebastian Andrzej Siewior
  0 siblings, 0 replies; 5+ messages in thread
From: Sebastian Andrzej Siewior @ 2026-08-24 14:09 UTC (permalink / raw)
  To: Peter Zijlstra
  Cc: linux-kernel, André Almeida, Darren Hart, Davidlohr Bueso,
	Ingo Molnar, Thomas Gleixner, Borislav Petkov, Yao Kai

On 2026-08-24 15:23:34 [+0200], Peter Zijlstra wrote:
> Still doesn't explain why this is makes sense. Perhaps something like
> the below comment to demystify the code?

I'm sorry. Could you please peek at 20260824140809.eJxsIRHn@linutronix.de ?

Sebastian

^ permalink raw reply	[flat|nested] 5+ messages in thread

end of thread, other threads:[~2026-08-24 14:09 UTC | newest]

Thread overview: 5+ messages (download: mbox.gz follow: Atom feed
-- links below jump to the message on this page --
2026-08-24 12:55 [PATCH v3 0/2] futex: Address two futex-requeue-pi issues Sebastian Andrzej Siewior
2026-08-24 12:55 ` [PATCH v3 1/2] futex: Add missing rt_mutex_.*_schedule() around rt_mutex_wait_proxy_lock() Sebastian Andrzej Siewior
2026-08-24 13:23   ` Peter Zijlstra
2026-08-24 14:09     ` Sebastian Andrzej Siewior
2026-08-24 12:55 ` [PATCH v3 2/2] futex: Prevent rcuwait use-after-free during requeue PI Sebastian Andrzej Siewior

This is a public inbox, see mirroring instructions
for how to clone and mirror all data and code used for this inbox;
as well as URLs for NNTP newsgroup(s).