All of lore.kernel.org
 help / color / mirror / Atom feed
* [PATCH] kernel/futex: handle the case where we got a "late" waiter
@ 2016-04-15 12:35 Sebastian Andrzej Siewior
  2016-04-19 22:27 ` Davidlohr Bueso
  2016-04-20 11:51 ` [tip:locking/urgent] futex: Handle unlock_pi race gracefully tip-bot for Sebastian Andrzej Siewior
  0 siblings, 2 replies; 5+ messages in thread
From: Sebastian Andrzej Siewior @ 2016-04-15 12:35 UTC (permalink / raw)
  To: Thomas Gleixner; +Cc: Davidlohr Bueso, linux-kernel, Sebastian Andrzej Siewior

futex_unlock_pi() gets uval before taking the hb lock. Now imagine
someone in futex_lock_pi() took the lock. While futex_unlock_pi() waits
for the hb lock, the LOCK_PI sets FUTEX_WAITERS and drops the lock.
Now, futex_unlock_pi() figures out that there is waiter and invokes
wake_futex_pi() with the old uval which does not yet have FUTEX_WAITERS
set. This flaw lets cmpxchg_futex_value_locked() fail and return -EINVAL.

To address this race we could either use get_futex_value_locked() after
we obtained the lock but then we would need to handle the EFAULT case
(which I think means get_user() without the lock).
To avoid this I let wake_futex_pi() detect this and return -EAGAIN so it
can read the new value and try again.

As bad as this may sound: FUTEX_OWNER_DIED is not set while the owner is
unlocking the lock. FUTEX_WAITERS gets set by another task *but* this
means that the owner invoked the futex() syscall rather than doing a
cmpxchg() in userland (and libc does not do that).

Fixes: ccf9e6a80d9e ("futex: Make unlock_pi more robust")
Signed-off-by: Sebastian Andrzej Siewior <bigeasy@linutronix.de>
---
 kernel/futex.c | 16 ++++++++++++++--
 1 file changed, 14 insertions(+), 2 deletions(-)

diff --git a/kernel/futex.c b/kernel/futex.c
index 0d557e13823a..2911f54a6dde 100644
--- a/kernel/futex.c
+++ b/kernel/futex.c
@@ -1496,8 +1496,12 @@ static int wake_futex_pi(u32 __user *uaddr, u32 uval, struct futex_q *this,
 
 	if (cmpxchg_futex_value_locked(&curval, uaddr, uval, newval))
 		ret = -EFAULT;
-	else if (curval != uval)
-		ret = -EINVAL;
+	else if (curval != uval) {
+		if ((FUTEX_TID_MASK & curval) == uval)
+			ret = -EAGAIN;
+		else
+			ret = -EINVAL;
+	}
 	if (ret) {
 		raw_spin_unlock_irq(&pi_state->pi_mutex.wait_lock);
 		return ret;
@@ -2852,6 +2856,12 @@ static int futex_unlock_pi(u32 __user *uaddr, unsigned int flags)
 		if (ret == -EFAULT)
 			goto pi_faulted;
 		/*
+		 * Between get_user() and obtaining the hb->lock the uval
+		 * gained a flag. Retry with the new value.
+		 */
+		if (ret == -EAGAIN)
+			goto pi_faulted;
+		/*
 		 * wake_futex_pi has detected invalid state. Tell user
 		 * space.
 		 */
@@ -2883,6 +2893,8 @@ static int futex_unlock_pi(u32 __user *uaddr, unsigned int flags)
 	spin_unlock(&hb->lock);
 	put_futex_key(&key);
 
+	if (ret == -EAGAIN)
+		goto retry;
 	ret = fault_in_user_writeable(uaddr);
 	if (!ret)
 		goto retry;
-- 
2.8.0.rc3

^ permalink raw reply related	[flat|nested] 5+ messages in thread

end of thread, other threads:[~2016-04-20 11:52 UTC | newest]

Thread overview: 5+ messages (download: mbox.gz follow: Atom feed
-- links below jump to the message on this page --
2016-04-15 12:35 [PATCH] kernel/futex: handle the case where we got a "late" waiter Sebastian Andrzej Siewior
2016-04-19 22:27 ` Davidlohr Bueso
2016-04-20  7:09   ` Sebastian Andrzej Siewior
2016-04-20  7:36   ` Thomas Gleixner
2016-04-20 11:51 ` [tip:locking/urgent] futex: Handle unlock_pi race gracefully tip-bot for Sebastian Andrzej Siewior

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.