* [PATCH v4 0/2] futex: Address two futex-requeue-pi issues @ 2026-09-01 13:54 Sebastian Andrzej Siewior 2026-09-01 13:54 ` [PATCH v4 1/2] futex: Provide rt_mutex_.*_schedule() equivalents for futex scheduling Sebastian Andrzej Siewior 2026-09-01 13:54 ` [PATCH v4 2/2] futex: Prevent rcuwait use-after-free during requeue PI Sebastian Andrzej Siewior 0 siblings, 2 replies; 6+ messages in thread From: Sebastian Andrzej Siewior @ 2026-09-01 13:54 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 replaced #1 and updated the commit a bit in #2. #1 Was simply missed in the initial commit and never noticed. #2 Requires extreme precise timing to reproduce. v3…4: https://lore.kernel.org/all/20260824125544.2353006-1-bigeasy@linutronix.de/ - PeterZ pointed out that doing sched_submit_work() while waiter is enqueued looks a bit wrong. tglx's enthusiasm was also missing. Rewrote that bit to only trigger the assert. 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. Sebastian Andrzej Siewior (1): futex: Provide rt_mutex_.*_schedule() equivalents for futex scheduling Yao Kai (1): futex: Prevent rcuwait use-after-free during requeue PI include/linux/sched/rt.h | 2 ++ kernel/futex/pi.c | 16 +++------------- kernel/futex/requeue.c | 12 ++++++++++-- kernel/locking/rtmutex_api.c | 2 ++ kernel/sched/core.c | 16 ++++++++++++++++ 5 files changed, 33 insertions(+), 15 deletions(-) -- 2.55.0 ^ permalink raw reply [flat|nested] 6+ messages in thread
* [PATCH v4 1/2] futex: Provide rt_mutex_.*_schedule() equivalents for futex scheduling 2026-09-01 13:54 [PATCH v4 0/2] futex: Address two futex-requeue-pi issues Sebastian Andrzej Siewior @ 2026-09-01 13:54 ` Sebastian Andrzej Siewior 2026-09-01 13:56 ` Sebastian Andrzej Siewior 2026-09-04 6:15 ` [tip: locking/urgent] " tip-bot2 for Sebastian Andrzej Siewior 2026-09-01 13:54 ` [PATCH v4 2/2] futex: Prevent rcuwait use-after-free during requeue PI Sebastian Andrzej Siewior 1 sibling, 2 replies; 6+ messages in thread From: Sebastian Andrzej Siewior @ 2026-09-01 13:54 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 There is rt_mutex_{pre|post}_schedule() around rt_mutex_wait_proxy_lock() to ensure that sched_submit_work()/ sched_update_worker() is invoked before we schedule out and block on rt_mutex while waiting for it become available. The reason is that blocking on rt_mutex assigns a pi_waiter for the PI chain and sched_submit_work() will also assign a pi_waiter if it blocks on lock but a this point we already have a waiter assigned. We can't skip sched_submit_work() entirely because I/O relies on the fact that I/O queue is flushed while it blocks on a sleeping lock. Therefore sched_submit_work() is moved before we block on the lock. Sleeping lock in this context means mutex or rw_semaphore not spinlock_t on PREEMPT_RT. Because the mutex abstraction on PREEMPT_RT uses the same abstraction as the futex proxy lock, the futex code ended up using rt_mutex_{pre|post}_schedule(), too. Using it is/ was just to keep the task_struct::sched_rt_mutex assertion happy. Futex proxy lock is used only in the syscall context of a task. At this point it never got any I/O that needs to be flushed and it can't be a workqueue that needs to notify that it will be scheduled out. Therefore sched_submit_work() does nothing here. By mistake futex_wait_requeue_pi() -> rt_mutex_wait_proxy_lock() did not get the rt_mutex_{pre|post}_schedule() annotation. This was not noticed because in this callchain the lock is (usually) not contended and so rt_mutex_slowlock_block() does not schedule, triggering the assert. Adding rt_mutex_pre_schedule() here looks wrong (as noted by PeterZ) because at this point there is a pi_waiter recorded and invoking sched_submit_work() with a possible lock contention would be wrong. Add rt_mutex_futex_{pre|post}_schedule() which toggles the sched_rt_mutex assert and does not involve sched_submit_work(). Add asserts here to ensure that sched_submit_work() would do nothing. Use it only in futex proxy lock case which is rt_mutex_wait_proxy_lock(). Remove it from futex_lock_pi(). Fixes: d14f9e930b90 ("locking/rtmutex: Use rt_mutex specific scheduler helpers") Reported-by: Yao Kai <yaokai34@huawei.com> Closes: https://lore.kernel.org/all/20260717084922.4153317-2-yaokai34@huawei.com Signed-off-by: Sebastian Andrzej Siewior <bigeasy@linutronix.de> --- include/linux/sched/rt.h | 2 ++ kernel/futex/pi.c | 16 +++------------- kernel/locking/rtmutex_api.c | 2 ++ kernel/sched/core.c | 16 ++++++++++++++++ 4 files changed, 23 insertions(+), 13 deletions(-) diff --git a/include/linux/sched/rt.h b/include/linux/sched/rt.h index 4e3338103654c..922935cc33833 100644 --- a/include/linux/sched/rt.h +++ b/include/linux/sched/rt.h @@ -52,8 +52,10 @@ static inline bool rt_or_dl_task_policy(struct task_struct *tsk) #ifdef CONFIG_RT_MUTEXES extern void rt_mutex_pre_schedule(void); +extern void rt_mutex_futex_pre_schedule(void); extern void rt_mutex_schedule(void); extern void rt_mutex_post_schedule(void); +extern void rt_mutex_futex_post_schedule(void); /* * Must hold either p->pi_lock or task_rq(p)->lock. diff --git a/kernel/futex/pi.c b/kernel/futex/pi.c index 88788e584ec8a..98f1b962e59a0 100644 --- a/kernel/futex/pi.c +++ b/kernel/futex/pi.c @@ -1070,17 +1070,11 @@ int futex_lock_pi(u32 __user *uaddr, unsigned int flags, ktime_t *time, int tryl * Caution; releasing @hb in-scope. The hb->lock is still locked * while the reference is dropped. The reference can not be dropped * after the unlock because if a user initiated resize is in progress - * then we might need to wake him. This can not be done after the - * rt_mutex_pre_schedule() invocation. The hb will remain valid because - * the thread, performing resize, will block on hb->lock during - * the requeue. + * then we might need to wake him. The hb will remain valid + * because the thread, performing resize, will block on + * hb->lock during the requeue. */ futex_private_hash_put(no_free_ptr(hbr.fph)); - /* - * Must be done before we enqueue the waiter, here is unfortunately - * under the hb lock, but that *should* work because it does nothing. - */ - rt_mutex_pre_schedule(); rt_mutex_init_waiter(&rt_waiter); @@ -1146,10 +1140,6 @@ int futex_lock_pi(u32 __user *uaddr, unsigned int flags, ktime_t *time, int tryl * the */ futex_q_lockptr_lock(&q); - /* - * Waiter is unqueued. - */ - rt_mutex_post_schedule(); no_block: /* * Fixup the pi_state owner and possibly acquire the lock if we diff --git a/kernel/locking/rtmutex_api.c b/kernel/locking/rtmutex_api.c index 5d48d64725b12..eb18b094473c9 100644 --- a/kernel/locking/rtmutex_api.c +++ b/kernel/locking/rtmutex_api.c @@ -423,6 +423,7 @@ int __sched rt_mutex_wait_proxy_lock(struct rt_mutex_base *lock, { int ret; + rt_mutex_futex_pre_schedule(); raw_spin_lock_irq(&lock->wait_lock); /* sleep on the mutex */ set_current_state(TASK_INTERRUPTIBLE); @@ -433,6 +434,7 @@ int __sched rt_mutex_wait_proxy_lock(struct rt_mutex_base *lock, */ fixup_rt_mutex_waiters(lock, true); raw_spin_unlock_irq(&lock->wait_lock); + rt_mutex_futex_post_schedule(); return ret; } diff --git a/kernel/sched/core.c b/kernel/sched/core.c index f78275192036b..449ccd871be81 100644 --- a/kernel/sched/core.c +++ b/kernel/sched/core.c @@ -7637,6 +7637,17 @@ void rt_mutex_pre_schedule(void) sched_submit_work(current); } +/* + * Used within the futex syscall context, skips sched_submit_work() because none + * its work will be done. Asserts ensure that it is indeed the case. + */ +void rt_mutex_futex_pre_schedule(void) +{ + lockdep_assert(!(current->flags & (PF_WQ_WORKER | PF_IO_WORKER))); + lockdep_assert(!current->plug); + lockdep_assert(!fetch_and_set(current->sched_rt_mutex, 1)); +} + void rt_mutex_schedule(void) { lockdep_assert(current->sched_rt_mutex); @@ -7649,6 +7660,11 @@ void rt_mutex_post_schedule(void) lockdep_assert(fetch_and_set(current->sched_rt_mutex, 0)); } +void rt_mutex_futex_post_schedule(void) +{ + lockdep_assert(fetch_and_set(current->sched_rt_mutex, 0)); +} + /* * rt_mutex_setprio - set the current priority of a task * @p: task to boost -- 2.55.0 ^ permalink raw reply [flat|nested] 6+ messages in thread
* Re: [PATCH v4 1/2] futex: Provide rt_mutex_.*_schedule() equivalents for futex scheduling 2026-09-01 13:54 ` [PATCH v4 1/2] futex: Provide rt_mutex_.*_schedule() equivalents for futex scheduling Sebastian Andrzej Siewior @ 2026-09-01 13:56 ` Sebastian Andrzej Siewior 2026-09-04 6:15 ` [tip: locking/urgent] " tip-bot2 for Sebastian Andrzej Siewior 1 sibling, 0 replies; 6+ messages in thread From: Sebastian Andrzej Siewior @ 2026-09-01 13:56 UTC (permalink / raw) To: linux-kernel Cc: André Almeida, Darren Hart, Davidlohr Bueso, Ingo Molnar, Peter Zijlstra, Thomas Gleixner, Borislav Petkov, Yao Kai On 2026-09-01 15:54:51 [+0200], To linux-kernel@vger.kernel.org wrote: > By mistake futex_wait_requeue_pi() -> rt_mutex_wait_proxy_lock() did not > get the rt_mutex_{pre|post}_schedule() annotation. This was not noticed > because in this callchain the lock is (usually) not contended and so > rt_mutex_slowlock_block() does not schedule, triggering the assert. The following makes it trigger more reliably. diff --git a/kernel/locking/rtmutex.c b/kernel/locking/rtmutex.c index 4728631ae7194..15037c35c684f 100644 --- a/kernel/locking/rtmutex.c +++ b/kernel/locking/rtmutex.c @@ -1633,13 +1633,14 @@ static int __sched rt_mutex_slowlock_block(struct rt_mutex_base *lock, struct rt_mutex *rtm = container_of(lock, struct rt_mutex, rtmutex); struct task_struct *owner; int ret = 0; + int first_loop = 1; __assume_ctx_lock(&rtm->rtmutex.wait_lock); lockevent_inc(rtmutex_slow_block); for (;;) { /* Try to acquire the lock: */ - if (try_to_take_rt_mutex(lock, current, waiter)) { + if (!first_loop && try_to_take_rt_mutex(lock, current, waiter)) { lockevent_inc(rtmutex_slow_acq3); break; } @@ -1665,7 +1666,11 @@ static int __sched rt_mutex_slowlock_block(struct rt_mutex_base *lock, owner = NULL; raw_spin_unlock_irq_wake(&lock->wait_lock, wake_q); - if (!owner || !rtmutex_spin_on_owner(lock, waiter, owner)) { + if (first_loop || !owner || !rtmutex_spin_on_owner(lock, waiter, owner)) { + if (first_loop) { + first_loop = 0; + __set_current_state(TASK_RUNNING); + } lockevent_inc(rtmutex_slow_sleep); rt_mutex_schedule(); } Sebastian ^ permalink raw reply [flat|nested] 6+ messages in thread
* [tip: locking/urgent] futex: Provide rt_mutex_.*_schedule() equivalents for futex scheduling 2026-09-01 13:54 ` [PATCH v4 1/2] futex: Provide rt_mutex_.*_schedule() equivalents for futex scheduling Sebastian Andrzej Siewior 2026-09-01 13:56 ` Sebastian Andrzej Siewior @ 2026-09-04 6:15 ` tip-bot2 for Sebastian Andrzej Siewior 1 sibling, 0 replies; 6+ messages in thread From: tip-bot2 for Sebastian Andrzej Siewior @ 2026-09-04 6:15 UTC (permalink / raw) To: linux-tip-commits Cc: Yao Kai, Sebastian Andrzej Siewior, Thomas Gleixner, stable, x86, linux-kernel The following commit has been merged into the locking/urgent branch of tip: Commit-ID: 912edebe8501a36c6bedcef03bd238ab90a7e060 Gitweb: https://git.kernel.org/tip/912edebe8501a36c6bedcef03bd238ab90a7e060 Author: Sebastian Andrzej Siewior <bigeasy@linutronix.de> AuthorDate: Tue, 01 Sep 2026 15:54:51 +02:00 Committer: Thomas Gleixner <tglx@kernel.org> CommitterDate: Fri, 04 Sep 2026 08:14:15 +02:00 futex: Provide rt_mutex_.*_schedule() equivalents for futex scheduling There is rt_mutex_{pre|post}_schedule() around rt_mutex_wait_proxy_lock() to ensure that sched_submit_work()/ sched_update_worker() is invoked before we schedule out and block on rt_mutex while waiting for it become available. The reason is that blocking on rt_mutex assigns a pi_waiter for the PI chain and sched_submit_work() will also assign a pi_waiter if it blocks on lock but a this point we already have a waiter assigned. We can't skip sched_submit_work() entirely because I/O relies on the fact that I/O queue is flushed while it blocks on a sleeping lock. Therefore sched_submit_work() is moved before we block on the lock. Sleeping lock in this context means mutex or rw_semaphore not spinlock_t on PREEMPT_RT. Because the mutex abstraction on PREEMPT_RT uses the same abstraction as the futex proxy lock, the futex code ended up using rt_mutex_{pre|post}_schedule(), too. Using it is/ was just to keep the task_struct::sched_rt_mutex assertion happy. Futex proxy lock is used only in the syscall context of a task. At this point it never got any I/O that needs to be flushed and it can't be a workqueue that needs to notify that it will be scheduled out. Therefore sched_submit_work() does nothing here. By mistake futex_wait_requeue_pi() -> rt_mutex_wait_proxy_lock() did not get the rt_mutex_{pre|post}_schedule() annotation. This was not noticed because in this callchain the lock is (usually) not contended and so rt_mutex_slowlock_block() does not schedule, triggering the assert. Adding rt_mutex_pre_schedule() here looks wrong (as noted by PeterZ) because at this point there is a pi_waiter recorded and invoking sched_submit_work() with a possible lock contention would be wrong. Add rt_mutex_futex_{pre|post}_schedule() which toggles the sched_rt_mutex assert and does not involve sched_submit_work(). Add asserts here to ensure that sched_submit_work() would do nothing. Use it only in futex proxy lock case which is rt_mutex_wait_proxy_lock(). Remove it from futex_lock_pi(). Fixes: d14f9e930b90 ("locking/rtmutex: Use rt_mutex specific scheduler helpers") Reported-by: Yao Kai <yaokai34@huawei.com> Signed-off-by: Sebastian Andrzej Siewior <bigeasy@linutronix.de> Signed-off-by: Thomas Gleixner <tglx@kernel.org> Cc: stable@vger.kernel.org Link: https://patch.msgid.link/20260901135453.3121948-2-bigeasy@linutronix.de Closes: https://lore.kernel.org/all/20260717084922.4153317-2-yaokai34@huawei.com --- include/linux/sched/rt.h | 2 ++ kernel/futex/pi.c | 16 +++------------- kernel/locking/rtmutex_api.c | 2 ++ kernel/sched/core.c | 16 ++++++++++++++++ 4 files changed, 23 insertions(+), 13 deletions(-) diff --git a/include/linux/sched/rt.h b/include/linux/sched/rt.h index 4e33381..922935c 100644 --- a/include/linux/sched/rt.h +++ b/include/linux/sched/rt.h @@ -52,8 +52,10 @@ static inline bool rt_or_dl_task_policy(struct task_struct *tsk) #ifdef CONFIG_RT_MUTEXES extern void rt_mutex_pre_schedule(void); +extern void rt_mutex_futex_pre_schedule(void); extern void rt_mutex_schedule(void); extern void rt_mutex_post_schedule(void); +extern void rt_mutex_futex_post_schedule(void); /* * Must hold either p->pi_lock or task_rq(p)->lock. diff --git a/kernel/futex/pi.c b/kernel/futex/pi.c index 88788e5..98f1b96 100644 --- a/kernel/futex/pi.c +++ b/kernel/futex/pi.c @@ -1070,17 +1070,11 @@ retry_private: * Caution; releasing @hb in-scope. The hb->lock is still locked * while the reference is dropped. The reference can not be dropped * after the unlock because if a user initiated resize is in progress - * then we might need to wake him. This can not be done after the - * rt_mutex_pre_schedule() invocation. The hb will remain valid because - * the thread, performing resize, will block on hb->lock during - * the requeue. + * then we might need to wake him. The hb will remain valid + * because the thread, performing resize, will block on + * hb->lock during the requeue. */ futex_private_hash_put(no_free_ptr(hbr.fph)); - /* - * Must be done before we enqueue the waiter, here is unfortunately - * under the hb lock, but that *should* work because it does nothing. - */ - rt_mutex_pre_schedule(); rt_mutex_init_waiter(&rt_waiter); @@ -1146,10 +1140,6 @@ cleanup: * the */ futex_q_lockptr_lock(&q); - /* - * Waiter is unqueued. - */ - rt_mutex_post_schedule(); no_block: /* * Fixup the pi_state owner and possibly acquire the lock if we diff --git a/kernel/locking/rtmutex_api.c b/kernel/locking/rtmutex_api.c index 5d48d64..eb18b09 100644 --- a/kernel/locking/rtmutex_api.c +++ b/kernel/locking/rtmutex_api.c @@ -423,6 +423,7 @@ int __sched rt_mutex_wait_proxy_lock(struct rt_mutex_base *lock, { int ret; + rt_mutex_futex_pre_schedule(); raw_spin_lock_irq(&lock->wait_lock); /* sleep on the mutex */ set_current_state(TASK_INTERRUPTIBLE); @@ -433,6 +434,7 @@ int __sched rt_mutex_wait_proxy_lock(struct rt_mutex_base *lock, */ fixup_rt_mutex_waiters(lock, true); raw_spin_unlock_irq(&lock->wait_lock); + rt_mutex_futex_post_schedule(); return ret; } diff --git a/kernel/sched/core.c b/kernel/sched/core.c index f782751..449ccd8 100644 --- a/kernel/sched/core.c +++ b/kernel/sched/core.c @@ -7637,6 +7637,17 @@ void rt_mutex_pre_schedule(void) sched_submit_work(current); } +/* + * Used within the futex syscall context, skips sched_submit_work() because none + * its work will be done. Asserts ensure that it is indeed the case. + */ +void rt_mutex_futex_pre_schedule(void) +{ + lockdep_assert(!(current->flags & (PF_WQ_WORKER | PF_IO_WORKER))); + lockdep_assert(!current->plug); + lockdep_assert(!fetch_and_set(current->sched_rt_mutex, 1)); +} + void rt_mutex_schedule(void) { lockdep_assert(current->sched_rt_mutex); @@ -7649,6 +7660,11 @@ void rt_mutex_post_schedule(void) lockdep_assert(fetch_and_set(current->sched_rt_mutex, 0)); } +void rt_mutex_futex_post_schedule(void) +{ + lockdep_assert(fetch_and_set(current->sched_rt_mutex, 0)); +} + /* * rt_mutex_setprio - set the current priority of a task * @p: task to boost ^ permalink raw reply [flat|nested] 6+ messages in thread
* [PATCH v4 2/2] futex: Prevent rcuwait use-after-free during requeue PI 2026-09-01 13:54 [PATCH v4 0/2] futex: Address two futex-requeue-pi issues Sebastian Andrzej Siewior 2026-09-01 13:54 ` [PATCH v4 1/2] futex: Provide rt_mutex_.*_schedule() equivalents for futex scheduling Sebastian Andrzej Siewior @ 2026-09-01 13:54 ` Sebastian Andrzej Siewior 2026-09-04 6:15 ` [tip: locking/urgent] " tip-bot2 for Yao Kai 1 sibling, 1 reply; 6+ messages in thread From: Sebastian Andrzej Siewior @ 2026-09-01 13:54 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 79823ad136830..b3f4a4bccb126 100644 --- a/kernel/futex/requeue.c +++ b/kernel/futex/requeue.c @@ -154,8 +154,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 [flat|nested] 6+ messages in thread
* [tip: locking/urgent] futex: Prevent rcuwait use-after-free during requeue PI 2026-09-01 13:54 ` [PATCH v4 2/2] futex: Prevent rcuwait use-after-free during requeue PI Sebastian Andrzej Siewior @ 2026-09-04 6:15 ` tip-bot2 for Yao Kai 0 siblings, 0 replies; 6+ messages in thread From: tip-bot2 for Yao Kai @ 2026-09-04 6:15 UTC (permalink / raw) To: linux-tip-commits Cc: Yao Kai, Sebastian Andrzej Siewior, Thomas Gleixner, stable, x86, linux-kernel The following commit has been merged into the locking/urgent branch of tip: Commit-ID: a3b8d46fe401cba3a5c46dea610e6eb3dc15370e Gitweb: https://git.kernel.org/tip/a3b8d46fe401cba3a5c46dea610e6eb3dc15370e Author: Yao Kai <yaokai34@huawei.com> AuthorDate: Tue, 01 Sep 2026 15:54:52 +02:00 Committer: Thomas Gleixner <tglx@kernel.org> CommitterDate: Fri, 04 Sep 2026 08:14:15 +02:00 futex: Prevent rcuwait use-after-free during requeue PI 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> Signed-off-by: Thomas Gleixner <tglx@kernel.org> Reviewed-by: Sebastian Andrzej Siewior <bigeasy@linutronix.de> Cc: stable@vger.kernel.org Link: https://patch.msgid.link/20260901135453.3121948-3-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 79823ad..b3f4a4b 100644 --- a/kernel/futex/requeue.c +++ b/kernel/futex/requeue.c @@ -154,8 +154,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 } ^ permalink raw reply [flat|nested] 6+ messages in thread
end of thread, other threads:[~2026-09-04 6:15 UTC | newest] Thread overview: 6+ messages (download: mbox.gz / follow: Atom feed) -- links below jump to the message on this page -- 2026-09-01 13:54 [PATCH v4 0/2] futex: Address two futex-requeue-pi issues Sebastian Andrzej Siewior 2026-09-01 13:54 ` [PATCH v4 1/2] futex: Provide rt_mutex_.*_schedule() equivalents for futex scheduling Sebastian Andrzej Siewior 2026-09-01 13:56 ` Sebastian Andrzej Siewior 2026-09-04 6:15 ` [tip: locking/urgent] " tip-bot2 for Sebastian Andrzej Siewior 2026-09-01 13:54 ` [PATCH v4 2/2] futex: Prevent rcuwait use-after-free during requeue PI Sebastian Andrzej Siewior 2026-09-04 6:15 ` [tip: locking/urgent] " tip-bot2 for Yao Kai
This is a public inbox, see mirroring instructions for how to clone and mirror all data and code used for this inbox
all inboxes | Powered by JetHome®