Commit 84bc4af5 authored by Darren Hart's avatar Darren Hart Committed by Thomas Gleixner

futex: Detect mismatched requeue targets

There is currently no check to ensure that userspace uses the same
futex requeue target (uaddr2) in futex_requeue() that the waiter used
in futex_wait_requeue_pi().  A mismatch here could very unexpected
results as the waiter assumes it either wakes on uaddr1 or uaddr2. We
could detect this on wakeup in the waiter, but the cleanup is more
intense after the improper requeue has occured.

This patch stores the waiter's expected requeue target in a new
requeue_pi_key pointer in the futex_q which futex_requeue() checks
prior to attempting to do a proxy lock acquistion or a requeue when
requeue_pi=1. If they don't match, return -EINVAL from futex_requeue,
aborting the requeue of any remaining waiters.
Signed-off-by: default avatarDarren Hart <dvhltc@us.ibm.com>
Cc: Peter Zijlstra <peterz@infradead.org>
Cc: Eric Dumazet <eric.dumazet@gmail.com>
Cc: John Kacur <jkacur@redhat.com>
Cc: Dinakar Guniguntala <dino@in.ibm.com>
Cc: John Stultz <johnstul@us.ibm.com>
LKML-Reference: <20090814003650.14634.63916.stgit@Aeon>
Signed-off-by: default avatarThomas Gleixner <tglx@linutronix.de>
parent cc6db4e6
...@@ -115,6 +115,9 @@ struct futex_q { ...@@ -115,6 +115,9 @@ struct futex_q {
/* rt_waiter storage for requeue_pi: */ /* rt_waiter storage for requeue_pi: */
struct rt_mutex_waiter *rt_waiter; struct rt_mutex_waiter *rt_waiter;
/* The expected requeue pi target futex key: */
union futex_key *requeue_pi_key;
/* Bitset for the optional bitmasked wakeup */ /* Bitset for the optional bitmasked wakeup */
u32 bitset; u32 bitset;
}; };
...@@ -1080,6 +1083,10 @@ static int futex_proxy_trylock_atomic(u32 __user *pifutex, ...@@ -1080,6 +1083,10 @@ static int futex_proxy_trylock_atomic(u32 __user *pifutex,
if (!top_waiter) if (!top_waiter)
return 0; return 0;
/* Ensure we requeue to the expected futex. */
if (!match_futex(top_waiter->requeue_pi_key, key2))
return -EINVAL;
/* /*
* Try to take the lock for top_waiter. Set the FUTEX_WAITERS bit in * Try to take the lock for top_waiter. Set the FUTEX_WAITERS bit in
* the contended case or if set_waiters is 1. The pi_state is returned * the contended case or if set_waiters is 1. The pi_state is returned
...@@ -1260,6 +1267,12 @@ static int futex_requeue(u32 __user *uaddr1, int fshared, u32 __user *uaddr2, ...@@ -1260,6 +1267,12 @@ static int futex_requeue(u32 __user *uaddr1, int fshared, u32 __user *uaddr2,
continue; continue;
} }
/* Ensure we requeue to the expected futex for requeue_pi. */
if (requeue_pi && !match_futex(this->requeue_pi_key, &key2)) {
ret = -EINVAL;
break;
}
/* /*
* Requeue nr_requeue waiters and possibly one more in the case * Requeue nr_requeue waiters and possibly one more in the case
* of requeue_pi if we couldn't acquire the lock atomically. * of requeue_pi if we couldn't acquire the lock atomically.
...@@ -1735,6 +1748,7 @@ static int futex_wait(u32 __user *uaddr, int fshared, ...@@ -1735,6 +1748,7 @@ static int futex_wait(u32 __user *uaddr, int fshared,
q.pi_state = NULL; q.pi_state = NULL;
q.bitset = bitset; q.bitset = bitset;
q.rt_waiter = NULL; q.rt_waiter = NULL;
q.requeue_pi_key = NULL;
if (abs_time) { if (abs_time) {
to = &timeout; to = &timeout;
...@@ -1842,6 +1856,7 @@ static int futex_lock_pi(u32 __user *uaddr, int fshared, ...@@ -1842,6 +1856,7 @@ static int futex_lock_pi(u32 __user *uaddr, int fshared,
q.pi_state = NULL; q.pi_state = NULL;
q.rt_waiter = NULL; q.rt_waiter = NULL;
q.requeue_pi_key = NULL;
retry: retry:
q.key = FUTEX_KEY_INIT; q.key = FUTEX_KEY_INIT;
ret = get_futex_key(uaddr, fshared, &q.key, VERIFY_WRITE); ret = get_futex_key(uaddr, fshared, &q.key, VERIFY_WRITE);
...@@ -2153,15 +2168,16 @@ static int futex_wait_requeue_pi(u32 __user *uaddr, int fshared, ...@@ -2153,15 +2168,16 @@ static int futex_wait_requeue_pi(u32 __user *uaddr, int fshared,
debug_rt_mutex_init_waiter(&rt_waiter); debug_rt_mutex_init_waiter(&rt_waiter);
rt_waiter.task = NULL; rt_waiter.task = NULL;
q.pi_state = NULL;
q.bitset = bitset;
q.rt_waiter = &rt_waiter;
key2 = FUTEX_KEY_INIT; key2 = FUTEX_KEY_INIT;
ret = get_futex_key(uaddr2, fshared, &key2, VERIFY_WRITE); ret = get_futex_key(uaddr2, fshared, &key2, VERIFY_WRITE);
if (unlikely(ret != 0)) if (unlikely(ret != 0))
goto out; goto out;
q.pi_state = NULL;
q.bitset = bitset;
q.rt_waiter = &rt_waiter;
q.requeue_pi_key = &key2;
/* Prepare to wait on uaddr. */ /* Prepare to wait on uaddr. */
ret = futex_wait_setup(uaddr, val, fshared, &q, &hb); ret = futex_wait_setup(uaddr, val, fshared, &q, &hb);
if (ret) if (ret)
......
Markdown is supported
0%
or
You are about to add 0 people to the discussion. Proceed with caution.
Finish editing this message first!
Please register or to comment