* [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).