]> git.ipfire.org Git - thirdparty/kernel/linux.git/commitdiff
futex: Clear stale exiting pointer in futex_lock_pi() retry path
authorDavidlohr Bueso <dave@stgolabs.net>
Thu, 26 Mar 2026 00:17:59 +0000 (17:17 -0700)
committerThomas Gleixner <tglx@kernel.org>
Sat, 28 Mar 2026 12:54:02 +0000 (13:54 +0100)
Fuzzying/stressing futexes triggered:

    WARNING: kernel/futex/core.c:825 at wait_for_owner_exiting+0x7a/0x80, CPU#11: futex_lock_pi_s/524

When futex_lock_pi_atomic() sees the owner is exiting, it returns -EBUSY
and stores a refcounted task pointer in 'exiting'.

After wait_for_owner_exiting() consumes that reference, the local pointer
is never reset to nil. Upon a retry, if futex_lock_pi_atomic() returns a
different error, the bogus pointer is passed to wait_for_owner_exiting().

  CPU0      CPU1        CPU2
  futex_lock_pi(uaddr)
  // acquires the PI futex
  exit()
    futex_cleanup_begin()
      futex_state = EXITING;
     futex_lock_pi(uaddr)
       futex_lock_pi_atomic()
 attach_to_pi_owner()
   // observes EXITING
   *exiting = owner;  // takes ref
   return -EBUSY
       wait_for_owner_exiting(-EBUSY, owner)
 put_task_struct();   // drops ref
       // exiting still points to owner
       goto retry;
       futex_lock_pi_atomic()
 lock_pi_update_atomic()
   cmpxchg(uaddr)
*uaddr ^= WAITERS // whatever
   // value changed
 return -EAGAIN;
       wait_for_owner_exiting(-EAGAIN, exiting) // stale
 WARN_ON_ONCE(exiting)

Fix this by resetting upon retry, essentially aligning it with requeue_pi.

Fixes: 3ef240eaff36 ("futex: Prevent exit livelock")
Signed-off-by: Davidlohr Bueso <dave@stgolabs.net>
Signed-off-by: Thomas Gleixner <tglx@kernel.org>
Cc: stable@vger.kernel.org
Link: https://patch.msgid.link/20260326001759.4129680-1-dave@stgolabs.net
kernel/futex/pi.c

index bc1f7e83a37e12252d9dd9256921b9dc478404bf..7808068fa59eb9efa9b8a4e62af31ef55771ba66 100644 (file)
@@ -918,7 +918,7 @@ int fixup_pi_owner(u32 __user *uaddr, struct futex_q *q, int locked)
 int futex_lock_pi(u32 __user *uaddr, unsigned int flags, ktime_t *time, int trylock)
 {
        struct hrtimer_sleeper timeout, *to;
-       struct task_struct *exiting = NULL;
+       struct task_struct *exiting;
        struct rt_mutex_waiter rt_waiter;
        struct futex_q q = futex_q_init;
        DEFINE_WAKE_Q(wake_q);
@@ -933,6 +933,7 @@ int futex_lock_pi(u32 __user *uaddr, unsigned int flags, ktime_t *time, int tryl
        to = futex_setup_timer(time, &timeout, flags, 0);
 
 retry:
+       exiting = NULL;
        ret = get_futex_key(uaddr, flags, &q.key, FUTEX_WRITE);
        if (unlikely(ret != 0))
                goto out;