Messages in this thread Patch in this message | | | From | Kamal Mostafa <> | Subject | [PATCH 3.19.y-ckt 46/66] futex: Handle unlock_pi race gracefully | Date | Tue, 26 Apr 2016 13:24:37 -0700 |
| |
3.19.8-ckt20 -stable review patch. If anyone has any objections, please let me know.
---8<------------------------------------------------------------
From: Sebastian Andrzej Siewior <bigeasy@linutronix.de>
commit 89e9e66ba1b3bde9d8ea90566c2aee20697ad681 upstream.
If userspace calls UNLOCK_PI unconditionally without trying the TID -> 0 transition in user space first then the user space value might not have the waiters bit set. This opens the following race:
CPU0 CPU1 uval = get_user(futex) lock(hb) lock(hb) futex |= FUTEX_WAITERS .... unlock(hb)
cmpxchg(futex, uval, newval)
So the cmpxchg fails and returns -EINVAL to user space, which is wrong because the futex value is valid.
To handle this (yes, yet another) corner case gracefully, check for a flag change and retry.
[ tglx: Massaged changelog and slightly reworked implementation ]
Fixes: ccf9e6a80d9e ("futex: Make unlock_pi more robust") Signed-off-by: Sebastian Andrzej Siewior <bigeasy@linutronix.de> Cc: Davidlohr Bueso <dave@stgolabs.net> Cc: Darren Hart <dvhart@linux.intel.com> Cc: Peter Zijlstra <peterz@infradead.org> Link: http://lkml.kernel.org/r/1460723739-5195-1-git-send-email-bigeasy@linutronix.de Signed-off-by: Thomas Gleixner <tglx@linutronix.de> [ kamal: backport to 3.19-stable: context ] Signed-off-by: Kamal Mostafa <kamal@canonical.com> --- kernel/futex.c | 25 ++++++++++++++++++++++--- 1 file changed, 22 insertions(+), 3 deletions(-)
diff --git a/kernel/futex.c b/kernel/futex.c index 1c43013..9779993 100644 --- a/kernel/futex.c +++ b/kernel/futex.c @@ -1157,10 +1157,20 @@ static int wake_futex_pi(u32 __user *uaddr, u32 uval, struct futex_q *this) */ newval = FUTEX_WAITERS | task_pid_vnr(new_owner); - if (cmpxchg_futex_value_locked(&curval, uaddr, uval, newval)) + if (cmpxchg_futex_value_locked(&curval, uaddr, uval, newval)) { ret = -EFAULT; - else if (curval != uval) - ret = -EINVAL; + } else if (curval != uval) { + /* + * If a unconditional UNLOCK_PI operation (user space did not + * try the TID->0 transition) raced with a waiter setting the + * FUTEX_WAITERS flag between get_user() and locking the hash + * bucket lock, retry the operation. + */ + if ((FUTEX_TID_MASK & curval) == uval) + ret = -EAGAIN; + else + ret = -EINVAL; + } if (ret) { raw_spin_unlock(&pi_state->pi_mutex.wait_lock); return ret; @@ -2419,6 +2429,15 @@ retry: */ if (ret == -EFAULT) goto pi_faulted; + /* + * A unconditional UNLOCK_PI op raced against a waiter + * setting the FUTEX_WAITERS bit. Try again. + */ + if (ret == -EAGAIN) { + spin_unlock(&hb->lock); + put_futex_key(&key); + goto retry; + } goto out_unlock; } -- 2.7.4
| |