From nobody Mon Sep 28 08:46:34 2026 Received: from galois.linutronix.de (Galois.linutronix.de [193.142.43.55]) (using TLSv1.2 with cipher ECDHE-RSA-AES256-GCM-SHA384 (256/256 bits)) (No client certificate requested) by smtp.subspace.kernel.org (Postfix) with ESMTPS id ADE8B41E6D4 for ; Mon, 24 Aug 2026 12:55:49 +0000 (UTC) Authentication-Results: smtp.subspace.kernel.org; arc=none smtp.client-ip=193.142.43.55 ARC-Seal: i=1; a=rsa-sha256; d=subspace.kernel.org; s=arc-20240116; t=1787576151; cv=none; b=Y9Lad8n+3nILudPRqWfJwffHxdq/bkC9nQ+eivFfMbo4V7Nit/3rjL/peatcN+td3m4kvYsK9w3ZgOMQKnCZSa4jeFjiln4cEExUjxN65fXX2qnsGPLREtCGO5ACCqWdeUzeozIyOhvMzHhCL0K+PKOO74114Ppuk+Q3CGL1keI= ARC-Message-Signature: i=1; a=rsa-sha256; d=subspace.kernel.org; s=arc-20240116; t=1787576151; c=relaxed/simple; bh=XCxO6KF9qPFJHZJxZ/QsRmgqkZHWnV0CRv3pJWhYoUA=; h=From:To:Cc:Subject:Date:Message-ID:In-Reply-To:References: MIME-Version; b=oFozNxE59AtVp5ZTbqo+KaGclCBklX7/DhN9t4zQSBPhYdL33JhGK2qZEs8LIHgIuArEyc4GNbLUPx26o44GNUoKfqaIq9h4YFb/jVj1Uoajo9uQ5ruLDUy8QxnK23yBzx56Fzse1etDvKIjCiCZvcJfSXeZZuKgDoY6hC00ZRY= ARC-Authentication-Results: i=1; smtp.subspace.kernel.org; dmarc=pass (p=none dis=none) header.from=linutronix.de; spf=pass smtp.mailfrom=linutronix.de; dkim=pass (2048-bit key) header.d=linutronix.de header.i=@linutronix.de header.b=c/81aguX; dkim=permerror (0-bit key) header.d=linutronix.de header.i=@linutronix.de header.b=Cw/ACvPT; arc=none smtp.client-ip=193.142.43.55 Authentication-Results: smtp.subspace.kernel.org; dmarc=pass (p=none dis=none) header.from=linutronix.de Authentication-Results: smtp.subspace.kernel.org; spf=pass smtp.mailfrom=linutronix.de Authentication-Results: smtp.subspace.kernel.org; dkim=pass (2048-bit key) header.d=linutronix.de header.i=@linutronix.de header.b="c/81aguX"; dkim=permerror (0-bit key) header.d=linutronix.de header.i=@linutronix.de header.b="Cw/ACvPT" From: Sebastian Andrzej Siewior DKIM-Signature: v=1; a=rsa-sha256; c=relaxed/relaxed; d=linutronix.de; s=2020; t=1787576147; h=from:from:reply-to:subject:subject:date:date:message-id:message-id: to:to:cc:cc:mime-version:mime-version: content-transfer-encoding:content-transfer-encoding: in-reply-to:in-reply-to:references:references; bh=bmBKZ13PvZOCKwpfUkMMzUCv+CKama+5K4cYEVWPUB4=; b=c/81aguXMHyqx59D9NA060uTa//CsOX/2yfo9ZAe2JlWL9RCPcP33iAa2TZaWNcWfTUpNb DLQ/1vHXtS74eGOSpZTUPV5vGG+FqrU1spp/q9XB6SsWiV6f0u9+7yo6og4f8pEgWZbILV gRwzdPD9ZbA74ZMwhRN7wkVzsQG2tcIu04wlb+lZXI4GzPQ9/J1sWKbh1sDDqRaxLj61Gi o9rrRwogxBK1RCQg2w7/+FTsStuKflcRaaxYObWImWaknRPjeV62BXuLAEgoX8Zo3va4Gu 9iY+HLGAUxgTfJJWbOvX/KAf3TNrRXI1o4yVGcci0y28dQnYv1BU/zf71RqSvw== DKIM-Signature: v=1; a=ed25519-sha256; c=relaxed/relaxed; d=linutronix.de; s=2020e; t=1787576147; h=from:from:reply-to:subject:subject:date:date:message-id:message-id: to:to:cc:cc:mime-version:mime-version: content-transfer-encoding:content-transfer-encoding: in-reply-to:in-reply-to:references:references; bh=bmBKZ13PvZOCKwpfUkMMzUCv+CKama+5K4cYEVWPUB4=; b=Cw/ACvPTKgyyeN4Amu+qbdcAiCswGdu9jMXef3JALsYiR7OxYFHitHwdz0hORcw+wtsobG ojSdhz1fkU3weoBw== To: linux-kernel@vger.kernel.org Cc: =?UTF-8?q?Andr=C3=A9=20Almeida?= , Darren Hart , Davidlohr Bueso , Ingo Molnar , Peter Zijlstra , Thomas Gleixner , Borislav Petkov , Yao Kai , Sebastian Andrzej Siewior Subject: [PATCH v3 1/2] futex: Add missing rt_mutex_.*_schedule() around rt_mutex_wait_proxy_lock() Date: Mon, 24 Aug 2026 14:55:42 +0200 Message-ID: <20260824125544.2353006-2-bigeasy@linutronix.de> In-Reply-To: <20260824125544.2353006-1-bigeasy@linutronix.de> References: <20260824125544.2353006-1-bigeasy@linutronix.de> Precedence: bulk X-Mailing-List: linux-kernel@vger.kernel.org List-Id: List-Subscribe: List-Unsubscribe: MIME-Version: 1.0 Content-Transfer-Encoding: quoted-printable Content-Type: text/plain; charset="utf-8" From: Yao Kai 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 = =3D 0) requeue_futex() futex_do_wait() futex_requeue_pi_complete() Q_REQUEUE_PI_IN_PROGRESS -> Q_R= EQUEUE_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 help= ers") Suggested-by: Sebastian Andrzej Siewior Signed-off-by: Yao Kai Signed-off-by: Sebastian Andrzej Siewior Reviewed-by: Sebastian Andrzej Siewior --- 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 =20 #include +#include #include =20 #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 =3D &q.pi_state->pi_mutex; + + rt_mutex_pre_schedule(); ret =3D rt_mutex_wait_proxy_lock(pi_mutex, to, &rt_waiter); + rt_mutex_post_schedule(); =20 /* * See futex_unlock_pi()'s cleanup: comment. --=20 2.55.0 From nobody Mon Sep 28 08:46:34 2026 Received: from galois.linutronix.de (Galois.linutronix.de [193.142.43.55]) (using TLSv1.2 with cipher ECDHE-RSA-AES256-GCM-SHA384 (256/256 bits)) (No client certificate requested) by smtp.subspace.kernel.org (Postfix) with ESMTPS id 2F58441D643 for ; Mon, 24 Aug 2026 12:55:50 +0000 (UTC) Authentication-Results: smtp.subspace.kernel.org; arc=none smtp.client-ip=193.142.43.55 ARC-Seal: i=1; a=rsa-sha256; d=subspace.kernel.org; s=arc-20240116; t=1787576151; cv=none; b=rL3Hplyp8p7VVM/vcG8TBykGkuVhrIKN3X2kAl9CYll0eqVa3X+Dr8zXcD5VSQSyqrAHcQDV5ZyZnSSHadYDlwUzhN3/Ew9Of/mH5+dBdFjS4bjWroayEt8iy76QacQWhzPJ8A2vvi0P8xU/rS/ZHEuoXVROaIvaYJyzjLf7tl0= ARC-Message-Signature: i=1; a=rsa-sha256; d=subspace.kernel.org; s=arc-20240116; t=1787576151; c=relaxed/simple; bh=paTJTuIF1vDm2921NHZKE4MDPpXfGKUS/JUYB3vBgv0=; h=From:To:Cc:Subject:Date:Message-ID:In-Reply-To:References: MIME-Version; b=WF2XDlrYyavxNr64lPScLvtslcyt22UG5rFeaW42ve2Fq4I4Lx34xtBzOpAgUgh6Uu8I/IqZzLrhQu2SVQ6oLQSZbPYI5SX9wtcWDdB/zbYCHUiiQT1emjW5S0vmfvVbK/Uru9ppudmFIg/O+ojeTBq2EVlAQbtMh+l5Z1xceMM= ARC-Authentication-Results: i=1; smtp.subspace.kernel.org; dmarc=pass (p=none dis=none) header.from=linutronix.de; spf=pass smtp.mailfrom=linutronix.de; dkim=pass (2048-bit key) header.d=linutronix.de header.i=@linutronix.de header.b=2Qpex051; dkim=permerror (0-bit key) header.d=linutronix.de header.i=@linutronix.de header.b=1fpKSvP2; arc=none smtp.client-ip=193.142.43.55 Authentication-Results: smtp.subspace.kernel.org; dmarc=pass (p=none dis=none) header.from=linutronix.de Authentication-Results: smtp.subspace.kernel.org; spf=pass smtp.mailfrom=linutronix.de Authentication-Results: smtp.subspace.kernel.org; dkim=pass (2048-bit key) header.d=linutronix.de header.i=@linutronix.de header.b="2Qpex051"; dkim=permerror (0-bit key) header.d=linutronix.de header.i=@linutronix.de header.b="1fpKSvP2" From: Sebastian Andrzej Siewior DKIM-Signature: v=1; a=rsa-sha256; c=relaxed/relaxed; d=linutronix.de; s=2020; t=1787576148; h=from:from:reply-to:subject:subject:date:date:message-id:message-id: to:to:cc:cc:mime-version:mime-version: content-transfer-encoding:content-transfer-encoding: in-reply-to:in-reply-to:references:references; bh=5j5n7+RiC5y8ybrbR+wRzqB3/bOYxj+kk02S61tdk+k=; b=2Qpex051mRstqdEbUJun2RRHG1+uuIq+KyTNUc4z9joh9Lra1GjqQEVQQF2tezCyha7p8j M+Vm2WIFwFLAQJ5Qsz5Et9mRQbw+x41SNxxOlzzMDb1LEQ4lupUpmWK+yiXx8sqDumhc1l zl8REaeMyhElBroy2Kcg9CENoi0w8L2nG6vamWNC+lOjmjv4wL4rmd9uo7HmLMYuyb3N28 CvXqG9SqC1fUzvttk21CPaRBdpYhaKzxxOmgQDof3bJstZ49qCiKOrc5VBW5q31zGq5+Tf ErZShSZHnIaa+2n8XiyARWARxlHRpQyQbltfVEqgbY2DS69JWDepGgni3XnRdQ== DKIM-Signature: v=1; a=ed25519-sha256; c=relaxed/relaxed; d=linutronix.de; s=2020e; t=1787576148; h=from:from:reply-to:subject:subject:date:date:message-id:message-id: to:to:cc:cc:mime-version:mime-version: content-transfer-encoding:content-transfer-encoding: in-reply-to:in-reply-to:references:references; bh=5j5n7+RiC5y8ybrbR+wRzqB3/bOYxj+kk02S61tdk+k=; b=1fpKSvP23X+aWcuH7E3zjCNESFDKuoB9FGltTrwvLNb/UK28kHGJqINLfaLR8jRJt4sWRl 0uJ/d4Pxcpm/OSDA== To: linux-kernel@vger.kernel.org Cc: =?UTF-8?q?Andr=C3=A9=20Almeida?= , Darren Hart , Davidlohr Bueso , Ingo Molnar , Peter Zijlstra , Thomas Gleixner , Borislav Petkov , Yao Kai , Sebastian Andrzej Siewior Subject: [PATCH v3 2/2] futex: Prevent rcuwait use-after-free during requeue PI Date: Mon, 24 Aug 2026 14:55:43 +0200 Message-ID: <20260824125544.2353006-3-bigeasy@linutronix.de> In-Reply-To: <20260824125544.2353006-1-bigeasy@linutronix.de> References: <20260824125544.2353006-1-bigeasy@linutronix.de> Precedence: bulk X-Mailing-List: linux-kernel@vger.kernel.org List-Id: List-Subscribe: List-Unsubscribe: MIME-Version: 1.0 Content-Transfer-Encoding: quoted-printable Content-Type: text/plain; charset="utf-8" From: Yao Kai 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) !=3D Q_REQUEUE_PI_WAIT) break /* no schedule() */ /* q.pi_state->owner =3D=3D current */ futex_private_hash_put() /* return from syscall */ rcuwait_wake_up(&q->requeue_w= ait) /* 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 Signed-off-by: Sebastian Andrzej Siewior Reviewed-by: Sebastian Andrzej Siewior --- 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 fu= tex_q *q, int locked) } while (!atomic_try_cmpxchg(&q->requeue_state, &old, new)); =20 #ifdef CONFIG_PREEMPT_RT - /* If the waiter interleaved with the requeue let it know */ - if (unlikely(old =3D=3D 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 =3D 1). The rcuwait_wait_eve= nt() + * will already read Q_REQUEUE_PI_LOCKED and skip the schedule() invocati= on, + * leading to an access of futex_q::requeue_wait after the waiter returne= d. + * 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 =3D=3D Q_REQUEUE_PI_WAIT) && new !=3D Q_REQUEUE_PI_LOCKE= D) rcuwait_wake_up(&q->requeue_wait); #endif } --=20 2.55.0