Commit 67a2dad3 authored by Chris Wright's avatar Chris Wright Committed by Linus Torvalds

[PATCH] delay rq_lock acquisition in setscheduler

Doing access control checks with rq_lock held can cause deadlock when
audit messages are created (via printk or audit infrastructure) which
trigger a wakeup and deadlock, as noted by both SELinux and SubDomain
folks.  This patch will let the security checks happen w/out lock held,
then re-sample the p->policy in case it was raced. 

Originally from John Johansen <johansen@immunix.com>, reworked by me.
AFAIK, this version drew no objections from Ingo or Andrea. 

From: John Johansen <johansen@immunix.com>
Acked-by: default avatarIngo Molnar <mingo@elte.hu>
Signed-off-by: default avatarChris Wright <chrisw@osdl.org>
Signed-off-by: default avatarLinus Torvalds <torvalds@osdl.org>
parent 69fbc509
...@@ -3038,7 +3038,7 @@ static int setscheduler(pid_t pid, int policy, struct sched_param __user *param) ...@@ -3038,7 +3038,7 @@ static int setscheduler(pid_t pid, int policy, struct sched_param __user *param)
{ {
struct sched_param lp; struct sched_param lp;
int retval = -EINVAL; int retval = -EINVAL;
int oldprio; int oldprio, oldpolicy = -1;
prio_array_t *array; prio_array_t *array;
unsigned long flags; unsigned long flags;
runqueue_t *rq; runqueue_t *rq;
...@@ -3060,23 +3060,17 @@ static int setscheduler(pid_t pid, int policy, struct sched_param __user *param) ...@@ -3060,23 +3060,17 @@ static int setscheduler(pid_t pid, int policy, struct sched_param __user *param)
retval = -ESRCH; retval = -ESRCH;
if (!p) if (!p)
goto out_unlock_tasklist; goto out_unlock;
recheck:
/* /* double check policy once rq lock held */
* To be able to change p->policy safely, the apropriate
* runqueue lock must be held.
*/
rq = task_rq_lock(p, &flags);
if (policy < 0) if (policy < 0)
policy = p->policy; policy = oldpolicy = p->policy;
else { else {
retval = -EINVAL; retval = -EINVAL;
if (policy != SCHED_FIFO && policy != SCHED_RR && if (policy != SCHED_FIFO && policy != SCHED_RR &&
policy != SCHED_NORMAL) policy != SCHED_NORMAL)
goto out_unlock; goto out_unlock;
} }
/* /*
* Valid priorities for SCHED_FIFO and SCHED_RR are * Valid priorities for SCHED_FIFO and SCHED_RR are
* 1..MAX_USER_RT_PRIO-1, valid priority for SCHED_NORMAL is 0. * 1..MAX_USER_RT_PRIO-1, valid priority for SCHED_NORMAL is 0.
...@@ -3098,7 +3092,17 @@ static int setscheduler(pid_t pid, int policy, struct sched_param __user *param) ...@@ -3098,7 +3092,17 @@ static int setscheduler(pid_t pid, int policy, struct sched_param __user *param)
retval = security_task_setscheduler(p, policy, &lp); retval = security_task_setscheduler(p, policy, &lp);
if (retval) if (retval)
goto out_unlock; goto out_unlock;
/*
* To be able to change p->policy safely, the apropriate
* runqueue lock must be held.
*/
rq = task_rq_lock(p, &flags);
/* recheck policy now with rq lock held */
if (unlikely(oldpolicy != -1 && oldpolicy != p->policy)) {
policy = oldpolicy = -1;
task_rq_unlock(rq, &flags);
goto recheck;
}
array = p->array; array = p->array;
if (array) if (array)
deactivate_task(p, task_rq(p)); deactivate_task(p, task_rq(p));
...@@ -3118,12 +3122,9 @@ static int setscheduler(pid_t pid, int policy, struct sched_param __user *param) ...@@ -3118,12 +3122,9 @@ static int setscheduler(pid_t pid, int policy, struct sched_param __user *param)
} else if (TASK_PREEMPTS_CURR(p, rq)) } else if (TASK_PREEMPTS_CURR(p, rq))
resched_task(rq->curr); resched_task(rq->curr);
} }
out_unlock:
task_rq_unlock(rq, &flags); task_rq_unlock(rq, &flags);
out_unlock_tasklist: out_unlock:
read_unlock_irq(&tasklist_lock); read_unlock_irq(&tasklist_lock);
out_nounlock: out_nounlock:
return retval; return retval;
} }
......
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