All of lore.kernel.org
 help / color / mirror / Atom feed
From: Sebastian Andrzej Siewior <bigeasy@linutronix.de>
To: Yao Kai <yaokai34@huawei.com>
Cc: linux-kernel@vger.kernel.org, tglx@kernel.org, mingo@redhat.com,
	peterz@infradead.org, dvhart@infradead.org, dave@stgolabs.net,
	andrealmeid@igalia.com, liuyongqiang13@huawei.com
Subject: Re: [PATCH 2/2] futex/requeue: Prevent rcuwait use-after-free during requeue PI
Date: Mon, 20 Jul 2026 16:55:39 +0200	[thread overview]
Message-ID: <20260720145539.nNAar9Bm@linutronix.de> (raw)
In-Reply-To: <235279a2-e6e4-4b6a-b76b-8a972a3e2af8@huawei.com>

On 2026-07-20 10:50:55 [+0800], Yao Kai wrote:
> 
> 
> On 7/17/2026 5:38 PM, Sebastian Andrzej Siewior wrote:
> > On 2026-07-17 16:49:22 [+0800], Yao Kai wrote:
> > > On PREEMPT_RT, FUTEX_CMP_REQUEUE_PI can trigger a KASAN report:
> > > 
> > >    BUG: KASAN: slab-out-of-bounds in _raw_spin_lock_irqsave+0x76/0xe0
> > >    Call Trace:
> > >     _raw_spin_lock_irqsave+0x76/0xe0
> > >     try_to_wake_up+0xab/0x1540
> > >     rcuwait_wake_up+0x39/0x60
> > >     futex_requeue+0x18c3/0x1e10
> > > 
> > > 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()
> > > 
> > >          * timeout/signal wakes waiter *
> > > 
> > >    futex_requeue_pi_wakeup_sync()
> > >      IN_PROGRESS -> WAIT
> > >      rcuwait_wait_event()
> > >                                          requeue_pi_wake_futex()
> > >                                            task = READ_ONCE(q->task)
> > >                                            futex_requeue_pi_complete()
> > >                                              WAIT -> LOCKED
> > >      return LOCKED
> > >    return
> > > // q lifetime ends
> > >                                              rcuwait_wake_up()
> > > 
> > > futex_requeue_pi_complete() publishes LOCKED before calling
> > > rcuwait_wake_up(). Once the waiter observes LOCKED, it can return from
> > > futex_wait_requeue_pi() and let q go out of scope before rcuwait_wake_up()
> > > reads q->requeue_wait.task and passes the stale pointer to
> > > try_to_wake_up().
> > > 
> > > Skip rcuwait_wake_up() for Q_REQUEUE_PI_LOCKED. requeue_pi_wake_futex()
> > > already saves q->task before publishing LOCKED and wakes the saved task
> > > afterward.
> > > 
> > > Fixes: 07d91ef510fb1 ("futex: Prevent requeue_pi() lock nesting issue on RT")
> > > Cc: stable@vger.kernel.org
> > > Signed-off-by: Yao Kai <yaokai34@huawei.com>
> > > ---
> > >   kernel/futex/requeue.c | 9 +++++++--
> > >   1 file changed, 7 insertions(+), 2 deletions(-)
> > > 
> > > diff --git a/kernel/futex/requeue.c b/kernel/futex/requeue.c
> > > index abc652b5b2dd..59e587775d9b 100644
> > > --- a/kernel/futex/requeue.c
> > > +++ b/kernel/futex/requeue.c
> > > @@ -155,8 +155,13 @@ 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))
> > > +	/*
> > > +	 * If the waiter interleaved with the requeue, let it know. For LOCKED,
> > > +	 * q may already be invalid, so requeue_pi_wake_futex() wakes the saved
> > > +	 * task instead.
> > > +	 */
> > > +	if (unlikely(old == Q_REQUEUE_PI_WAIT) &&
> > > +	    new != Q_REQUEUE_PI_LOCKED)
> > >   		rcuwait_wake_up(&q->requeue_wait);
> > 
> > Your whole assumption is based on the requeue_state in
> > futex_requeue_pi_wakeup_sync() changes from Q_REQUEUE_PI_IN_PROGRESS to
> > Q_REQUEUE_PI_WAIT and the rcuwait_wait_event() does not wait because the
> > condition becomes true before that happens. So the rcuwait_wake_up()
> > could access q.requeue_wait which is allocated on behalf of the waiter
> > which is gone. Certainly possible. But if we skip the wait in thise case
> > we probably miss the 99% cases where the waiter did wait, no?
> > 
> > This looks similar to commit b549113738e8c ("futex: Prevent
> > use-after-free during requeue-PI").
> > 
> > >   #endif
> > >   }
> > 
> > Sebastian
> 
> No wakeup is missed. Q_REQUEUE_PI_LOCKED is only published by
> requeue_pi_wake_futex(), which saves q->task before publishing the state
> and calls wake_up_state(task, TASK_NORMAL) afterwards. The other
> futex_requeue_pi_complete() callers produce DONE or an error state, and
> the rcuwait wakeup is retained for those paths.

 T1                                         T2
futex_requeue_pi_wakeup_sync()
  old = Q_REQUEUE_PI_IN_PROGRESS
  new = Q_REQUEUE_PI_WAIT
  cmpxchg()
  if (old == Q_REQUEUE_PI_IN_PROGRESS)
                                           futex_requeue_pi_complete
					     old = Q_REQUEUE_PI_WAIT
					     new = Q_REQUEUE_PI_LOCKED
					     cmpxchg()
   rcuwait_wait_event()
  <leave>
                                             rcuwait_wake_up(&q->requeue_wait);

So this your case then T2 updates the state before T1 enters sleep
because the condition was true before that. So you patch makes sense.
However given the more common case:

 T1                                         T2
futex_requeue_pi_wakeup_sync()
  old = Q_REQUEUE_PI_IN_PROGRESS
  new = Q_REQUEUE_PI_WAIT
  cmpxchg()
                                           futex_requeue_pi_complete
					     old = Q_REQUEUE_PI_WAIT
					     new = Q_REQUEUE_PI_LOCKED
					     cmpxchg()
  if (old == Q_REQUEUE_PI_IN_PROGRESS)
   rcuwait_wait_event()
                                             rcuwait_wake_up(&q->requeue_wait);
  <leave>

You will miss to wake T1 if T2 skips the wake, as suggested. Or do I
miss something?

> Thanks,
> Yao

Sebastian

  reply	other threads:[~2026-07-20 14:55 UTC|newest]

Thread overview: 15+ messages / expand[flat|nested]  mbox.gz  Atom feed  top
2026-07-17  8:49 [PATCH 0/2] futex/requeue: Fix requeue PI races Yao Kai
2026-07-17  8:49 ` [PATCH 1/2] futex/requeue: Fix rtmutex schedule preparation for requeue PI Yao Kai
2026-07-17  8:55   ` Sebastian Andrzej Siewior
2026-07-20  2:40     ` Yao Kai
2026-07-20 14:58       ` Sebastian Andrzej Siewior
2026-07-21  1:51         ` Yao Kai
2026-07-21  7:23           ` Sebastian Andrzej Siewior
2026-07-21  9:02             ` Yao Kai
2026-07-21  9:45               ` Sebastian Andrzej Siewior
2026-07-17  8:49 ` [PATCH 2/2] futex/requeue: Prevent rcuwait use-after-free during " Yao Kai
2026-07-17  9:38   ` Sebastian Andrzej Siewior
2026-07-20  2:50     ` Yao Kai
2026-07-20 14:55       ` Sebastian Andrzej Siewior [this message]
2026-07-21  2:19         ` Yao Kai
2026-07-21  7:19           ` Sebastian Andrzej Siewior

Reply instructions:

You may reply publicly to this message via plain-text email
using any one of the following methods:

* Save the following mbox file, import it into your mail client,
  and reply-to-all from there: mbox

  Avoid top-posting and favor interleaved quoting:
  https://en.wikipedia.org/wiki/Posting_style#Interleaved_style

* Reply using the --to, --cc, and --in-reply-to
  switches of git-send-email(1):

  git send-email \
    --in-reply-to=20260720145539.nNAar9Bm@linutronix.de \
    --to=bigeasy@linutronix.de \
    --cc=andrealmeid@igalia.com \
    --cc=dave@stgolabs.net \
    --cc=dvhart@infradead.org \
    --cc=linux-kernel@vger.kernel.org \
    --cc=liuyongqiang13@huawei.com \
    --cc=mingo@redhat.com \
    --cc=peterz@infradead.org \
    --cc=tglx@kernel.org \
    --cc=yaokai34@huawei.com \
    /path/to/YOUR_REPLY

  https://kernel.org/pub/software/scm/git/docs/git-send-email.html

* If your mail client supports setting the In-Reply-To header
  via mailto: links, try the mailto: link
Be sure your reply has a Subject: header at the top and a blank line before the message body.
This is an external index of several public inboxes,
see mirroring instructions on how to clone and mirror
all data and code used by this external index.