Merge branch 'core-urgent-for-linus' of git://git.kernel.org/pub/scm/linux/kernel/git/tip/tip
Pull core futex/rtmutex fixes from Thomas Gleixner: "Three fixlets for long standing issues in the futex/rtmutex code unearthed by Dave Jones syscall fuzzer: - Add missing early deadlock detection checks in the futex code - Prevent user space from attaching a futex to kernel threads - Make the deadlock detector of rtmutex work again Looks large, but is more comments than code change" * 'core-urgent-for-linus' of git://git.kernel.org/pub/scm/linux/kernel/git/tip/tip: rtmutex: Fix deadlock detector for real futex: Prevent attaching to kernel threads futex: Add another early deadlock detection check
This commit is contained in:
commit
a4bf79eb6a
|
@ -745,7 +745,8 @@ void exit_pi_state_list(struct task_struct *curr)
|
||||||
|
|
||||||
static int
|
static int
|
||||||
lookup_pi_state(u32 uval, struct futex_hash_bucket *hb,
|
lookup_pi_state(u32 uval, struct futex_hash_bucket *hb,
|
||||||
union futex_key *key, struct futex_pi_state **ps)
|
union futex_key *key, struct futex_pi_state **ps,
|
||||||
|
struct task_struct *task)
|
||||||
{
|
{
|
||||||
struct futex_pi_state *pi_state = NULL;
|
struct futex_pi_state *pi_state = NULL;
|
||||||
struct futex_q *this, *next;
|
struct futex_q *this, *next;
|
||||||
|
@ -786,6 +787,16 @@ lookup_pi_state(u32 uval, struct futex_hash_bucket *hb,
|
||||||
return -EINVAL;
|
return -EINVAL;
|
||||||
}
|
}
|
||||||
|
|
||||||
|
/*
|
||||||
|
* Protect against a corrupted uval. If uval
|
||||||
|
* is 0x80000000 then pid is 0 and the waiter
|
||||||
|
* bit is set. So the deadlock check in the
|
||||||
|
* calling code has failed and we did not fall
|
||||||
|
* into the check above due to !pid.
|
||||||
|
*/
|
||||||
|
if (task && pi_state->owner == task)
|
||||||
|
return -EDEADLK;
|
||||||
|
|
||||||
atomic_inc(&pi_state->refcount);
|
atomic_inc(&pi_state->refcount);
|
||||||
*ps = pi_state;
|
*ps = pi_state;
|
||||||
|
|
||||||
|
@ -803,6 +814,11 @@ lookup_pi_state(u32 uval, struct futex_hash_bucket *hb,
|
||||||
if (!p)
|
if (!p)
|
||||||
return -ESRCH;
|
return -ESRCH;
|
||||||
|
|
||||||
|
if (!p->mm) {
|
||||||
|
put_task_struct(p);
|
||||||
|
return -EPERM;
|
||||||
|
}
|
||||||
|
|
||||||
/*
|
/*
|
||||||
* We need to look at the task state flags to figure out,
|
* We need to look at the task state flags to figure out,
|
||||||
* whether the task is exiting. To protect against the do_exit
|
* whether the task is exiting. To protect against the do_exit
|
||||||
|
@ -935,7 +951,7 @@ retry:
|
||||||
* We dont have the lock. Look up the PI state (or create it if
|
* We dont have the lock. Look up the PI state (or create it if
|
||||||
* we are the first waiter):
|
* we are the first waiter):
|
||||||
*/
|
*/
|
||||||
ret = lookup_pi_state(uval, hb, key, ps);
|
ret = lookup_pi_state(uval, hb, key, ps, task);
|
||||||
|
|
||||||
if (unlikely(ret)) {
|
if (unlikely(ret)) {
|
||||||
switch (ret) {
|
switch (ret) {
|
||||||
|
@ -1347,7 +1363,7 @@ void requeue_pi_wake_futex(struct futex_q *q, union futex_key *key,
|
||||||
*
|
*
|
||||||
* Return:
|
* Return:
|
||||||
* 0 - failed to acquire the lock atomically;
|
* 0 - failed to acquire the lock atomically;
|
||||||
* 1 - acquired the lock;
|
* >0 - acquired the lock, return value is vpid of the top_waiter
|
||||||
* <0 - error
|
* <0 - error
|
||||||
*/
|
*/
|
||||||
static int futex_proxy_trylock_atomic(u32 __user *pifutex,
|
static int futex_proxy_trylock_atomic(u32 __user *pifutex,
|
||||||
|
@ -1358,7 +1374,7 @@ static int futex_proxy_trylock_atomic(u32 __user *pifutex,
|
||||||
{
|
{
|
||||||
struct futex_q *top_waiter = NULL;
|
struct futex_q *top_waiter = NULL;
|
||||||
u32 curval;
|
u32 curval;
|
||||||
int ret;
|
int ret, vpid;
|
||||||
|
|
||||||
if (get_futex_value_locked(&curval, pifutex))
|
if (get_futex_value_locked(&curval, pifutex))
|
||||||
return -EFAULT;
|
return -EFAULT;
|
||||||
|
@ -1386,11 +1402,13 @@ static int futex_proxy_trylock_atomic(u32 __user *pifutex,
|
||||||
* 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
|
||||||
* in ps in contended cases.
|
* in ps in contended cases.
|
||||||
*/
|
*/
|
||||||
|
vpid = task_pid_vnr(top_waiter->task);
|
||||||
ret = futex_lock_pi_atomic(pifutex, hb2, key2, ps, top_waiter->task,
|
ret = futex_lock_pi_atomic(pifutex, hb2, key2, ps, top_waiter->task,
|
||||||
set_waiters);
|
set_waiters);
|
||||||
if (ret == 1)
|
if (ret == 1) {
|
||||||
requeue_pi_wake_futex(top_waiter, key2, hb2);
|
requeue_pi_wake_futex(top_waiter, key2, hb2);
|
||||||
|
return vpid;
|
||||||
|
}
|
||||||
return ret;
|
return ret;
|
||||||
}
|
}
|
||||||
|
|
||||||
|
@ -1421,7 +1439,6 @@ static int futex_requeue(u32 __user *uaddr1, unsigned int flags,
|
||||||
struct futex_pi_state *pi_state = NULL;
|
struct futex_pi_state *pi_state = NULL;
|
||||||
struct futex_hash_bucket *hb1, *hb2;
|
struct futex_hash_bucket *hb1, *hb2;
|
||||||
struct futex_q *this, *next;
|
struct futex_q *this, *next;
|
||||||
u32 curval2;
|
|
||||||
|
|
||||||
if (requeue_pi) {
|
if (requeue_pi) {
|
||||||
/*
|
/*
|
||||||
|
@ -1509,16 +1526,25 @@ retry_private:
|
||||||
* At this point the top_waiter has either taken uaddr2 or is
|
* At this point the top_waiter has either taken uaddr2 or is
|
||||||
* waiting on it. If the former, then the pi_state will not
|
* waiting on it. If the former, then the pi_state will not
|
||||||
* exist yet, look it up one more time to ensure we have a
|
* exist yet, look it up one more time to ensure we have a
|
||||||
* reference to it.
|
* reference to it. If the lock was taken, ret contains the
|
||||||
|
* vpid of the top waiter task.
|
||||||
*/
|
*/
|
||||||
if (ret == 1) {
|
if (ret > 0) {
|
||||||
WARN_ON(pi_state);
|
WARN_ON(pi_state);
|
||||||
drop_count++;
|
drop_count++;
|
||||||
task_count++;
|
task_count++;
|
||||||
ret = get_futex_value_locked(&curval2, uaddr2);
|
/*
|
||||||
if (!ret)
|
* If we acquired the lock, then the user
|
||||||
ret = lookup_pi_state(curval2, hb2, &key2,
|
* space value of uaddr2 should be vpid. It
|
||||||
&pi_state);
|
* cannot be changed by the top waiter as it
|
||||||
|
* is blocked on hb2 lock if it tries to do
|
||||||
|
* so. If something fiddled with it behind our
|
||||||
|
* back the pi state lookup might unearth
|
||||||
|
* it. So we rather use the known value than
|
||||||
|
* rereading and handing potential crap to
|
||||||
|
* lookup_pi_state.
|
||||||
|
*/
|
||||||
|
ret = lookup_pi_state(ret, hb2, &key2, &pi_state, NULL);
|
||||||
}
|
}
|
||||||
|
|
||||||
switch (ret) {
|
switch (ret) {
|
||||||
|
|
|
@ -343,9 +343,16 @@ static int rt_mutex_adjust_prio_chain(struct task_struct *task,
|
||||||
* top_waiter can be NULL, when we are in the deboosting
|
* top_waiter can be NULL, when we are in the deboosting
|
||||||
* mode!
|
* mode!
|
||||||
*/
|
*/
|
||||||
if (top_waiter && (!task_has_pi_waiters(task) ||
|
if (top_waiter) {
|
||||||
top_waiter != task_top_pi_waiter(task)))
|
if (!task_has_pi_waiters(task))
|
||||||
goto out_unlock_pi;
|
goto out_unlock_pi;
|
||||||
|
/*
|
||||||
|
* If deadlock detection is off, we stop here if we
|
||||||
|
* are not the top pi waiter of the task.
|
||||||
|
*/
|
||||||
|
if (!detect_deadlock && top_waiter != task_top_pi_waiter(task))
|
||||||
|
goto out_unlock_pi;
|
||||||
|
}
|
||||||
|
|
||||||
/*
|
/*
|
||||||
* When deadlock detection is off then we check, if further
|
* When deadlock detection is off then we check, if further
|
||||||
|
@ -361,7 +368,12 @@ static int rt_mutex_adjust_prio_chain(struct task_struct *task,
|
||||||
goto retry;
|
goto retry;
|
||||||
}
|
}
|
||||||
|
|
||||||
/* Deadlock detection */
|
/*
|
||||||
|
* Deadlock detection. If the lock is the same as the original
|
||||||
|
* lock which caused us to walk the lock chain or if the
|
||||||
|
* current lock is owned by the task which initiated the chain
|
||||||
|
* walk, we detected a deadlock.
|
||||||
|
*/
|
||||||
if (lock == orig_lock || rt_mutex_owner(lock) == top_task) {
|
if (lock == orig_lock || rt_mutex_owner(lock) == top_task) {
|
||||||
debug_rt_mutex_deadlock(deadlock_detect, orig_waiter, lock);
|
debug_rt_mutex_deadlock(deadlock_detect, orig_waiter, lock);
|
||||||
raw_spin_unlock(&lock->wait_lock);
|
raw_spin_unlock(&lock->wait_lock);
|
||||||
|
@ -527,6 +539,18 @@ static int task_blocks_on_rt_mutex(struct rt_mutex *lock,
|
||||||
unsigned long flags;
|
unsigned long flags;
|
||||||
int chain_walk = 0, res;
|
int chain_walk = 0, res;
|
||||||
|
|
||||||
|
/*
|
||||||
|
* Early deadlock detection. We really don't want the task to
|
||||||
|
* enqueue on itself just to untangle the mess later. It's not
|
||||||
|
* only an optimization. We drop the locks, so another waiter
|
||||||
|
* can come in before the chain walk detects the deadlock. So
|
||||||
|
* the other will detect the deadlock and return -EDEADLOCK,
|
||||||
|
* which is wrong, as the other waiter is not in a deadlock
|
||||||
|
* situation.
|
||||||
|
*/
|
||||||
|
if (detect_deadlock && owner == task)
|
||||||
|
return -EDEADLK;
|
||||||
|
|
||||||
raw_spin_lock_irqsave(&task->pi_lock, flags);
|
raw_spin_lock_irqsave(&task->pi_lock, flags);
|
||||||
__rt_mutex_adjust_prio(task);
|
__rt_mutex_adjust_prio(task);
|
||||||
waiter->task = task;
|
waiter->task = task;
|
||||||
|
|
Loading…
Reference in New Issue