Skip to content

Commit

Permalink
sched: add new API sched_setscheduler_nocheck: add a flag to control …
Browse files Browse the repository at this point in the history
…access checks

Hidehiro Kawai noticed that sched_setscheduler() can fail in
stop_machine: it calls sched_setscheduler() from insmod, which can
have CAP_SYS_MODULE without CAP_SYS_NICE.

Two cases could have failed, so are changed to sched_setscheduler_nocheck:
  kernel/softirq.c:cpu_callback()
	- CPU hotplug callback
  kernel/stop_machine.c:__stop_machine_run()
	- Called from various places, including modprobe()

Signed-off-by: Rusty Russell <[email protected]>
Cc: Jeremy Fitzhardinge <[email protected]>
Cc: Hidehiro Kawai <[email protected]>
Cc: Andrew Morton <[email protected]>
Cc: [email protected]
Cc: sugita <[email protected]>
Cc: Satoshi OSHIMA <[email protected]>
Signed-off-by: Ingo Molnar <[email protected]>
  • Loading branch information
rustyrussell authored and Ingo Molnar committed Jun 23, 2008
1 parent 481c534 commit 961ccdd
Show file tree
Hide file tree
Showing 4 changed files with 40 additions and 14 deletions.
2 changes: 2 additions & 0 deletions include/linux/sched.h
Original file line number Diff line number Diff line change
Expand Up @@ -1655,6 +1655,8 @@ extern int can_nice(const struct task_struct *p, const int nice);
extern int task_curr(const struct task_struct *p);
extern int idle_cpu(int cpu);
extern int sched_setscheduler(struct task_struct *, int, struct sched_param *);
extern int sched_setscheduler_nocheck(struct task_struct *, int,
struct sched_param *);
extern struct task_struct *idle_task(int cpu);
extern struct task_struct *curr_task(int cpu);
extern void set_curr_task(int cpu, struct task_struct *p);
Expand Down
48 changes: 36 additions & 12 deletions kernel/sched.c
Original file line number Diff line number Diff line change
Expand Up @@ -4746,16 +4746,8 @@ __setscheduler(struct rq *rq, struct task_struct *p, int policy, int prio)
set_load_weight(p);
}

/**
* sched_setscheduler - change the scheduling policy and/or RT priority of a thread.
* @p: the task in question.
* @policy: new policy.
* @param: structure containing the new RT priority.
*
* NOTE that the task may be already dead.
*/
int sched_setscheduler(struct task_struct *p, int policy,
struct sched_param *param)
static int __sched_setscheduler(struct task_struct *p, int policy,
struct sched_param *param, bool user)
{
int retval, oldprio, oldpolicy = -1, on_rq, running;
unsigned long flags;
Expand Down Expand Up @@ -4787,7 +4779,7 @@ int sched_setscheduler(struct task_struct *p, int policy,
/*
* Allow unprivileged RT tasks to decrease priority:
*/
if (!capable(CAP_SYS_NICE)) {
if (user && !capable(CAP_SYS_NICE)) {
if (rt_policy(policy)) {
unsigned long rlim_rtprio;

Expand Down Expand Up @@ -4823,7 +4815,8 @@ int sched_setscheduler(struct task_struct *p, int policy,
* Do not allow realtime tasks into groups that have no runtime
* assigned.
*/
if (rt_policy(policy) && task_group(p)->rt_bandwidth.rt_runtime == 0)
if (user
&& rt_policy(policy) && task_group(p)->rt_bandwidth.rt_runtime == 0)
return -EPERM;
#endif

Expand Down Expand Up @@ -4872,8 +4865,39 @@ int sched_setscheduler(struct task_struct *p, int policy,

return 0;
}

/**
* sched_setscheduler - change the scheduling policy and/or RT priority of a thread.
* @p: the task in question.
* @policy: new policy.
* @param: structure containing the new RT priority.
*
* NOTE that the task may be already dead.
*/
int sched_setscheduler(struct task_struct *p, int policy,
struct sched_param *param)
{
return __sched_setscheduler(p, policy, param, true);
}
EXPORT_SYMBOL_GPL(sched_setscheduler);

/**
* sched_setscheduler_nocheck - change the scheduling policy and/or RT priority of a thread from kernelspace.
* @p: the task in question.
* @policy: new policy.
* @param: structure containing the new RT priority.
*
* Just like sched_setscheduler, only don't bother checking if the
* current context has permission. For example, this is needed in
* stop_machine(): we create temporary high priority worker threads,
* but our caller might not have that capability.
*/
int sched_setscheduler_nocheck(struct task_struct *p, int policy,
struct sched_param *param)
{
return __sched_setscheduler(p, policy, param, false);
}

static int
do_sched_setscheduler(pid_t pid, int policy, struct sched_param __user *param)
{
Expand Down
2 changes: 1 addition & 1 deletion kernel/softirq.c
Original file line number Diff line number Diff line change
Expand Up @@ -645,7 +645,7 @@ static int __cpuinit cpu_callback(struct notifier_block *nfb,

p = per_cpu(ksoftirqd, hotcpu);
per_cpu(ksoftirqd, hotcpu) = NULL;
sched_setscheduler(p, SCHED_FIFO, &param);
sched_setscheduler_nocheck(p, SCHED_FIFO, &param);
kthread_stop(p);
takeover_tasklets(hotcpu);
break;
Expand Down
2 changes: 1 addition & 1 deletion kernel/stop_machine.c
Original file line number Diff line number Diff line change
Expand Up @@ -187,7 +187,7 @@ struct task_struct *__stop_machine_run(int (*fn)(void *), void *data,
struct sched_param param = { .sched_priority = MAX_RT_PRIO-1 };

/* One high-prio thread per cpu. We'll do this one. */
sched_setscheduler(p, SCHED_FIFO, &param);
sched_setscheduler_nocheck(p, SCHED_FIFO, &param);
kthread_bind(p, cpu);
wake_up_process(p);
wait_for_completion(&smdata.done);
Expand Down

0 comments on commit 961ccdd

Please sign in to comment.