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