Donate to e Foundation | Murena handsets with /e/OS | Own a part of Murena! Learn more

Commit 54e99124 authored by Dhaval Giani's avatar Dhaval Giani Committed by Ingo Molnar
Browse files

sched: don't allow setuid to succeed if the user does not have rt bandwidth



Impact: fix hung task with certain (non-default) rt-limit settings

Corey Hickey reported that on using setuid to change the uid of a
rt process, the process would be unkillable and not be running.
This is because there was no rt runtime for that user group. Add
in a check to see if a user can attach an rt task to its task group.
On failure, return EINVAL, which is also returned in
CONFIG_CGROUP_SCHED.

Reported-by: default avatarCorey Hickey <bugfood-ml@fatooh.org>
Signed-off-by: default avatarDhaval Giani <dhaval@linux.vnet.ibm.com>
Acked-by: default avatarPeter Zijlstra <a.p.zijlstra@chello.nl>
Signed-off-by: default avatarIngo Molnar <mingo@elte.hu>
parent cac64d00
Loading
Loading
Loading
Loading
+4 −0
Original line number Diff line number Diff line
@@ -2291,9 +2291,13 @@ extern long sched_group_rt_runtime(struct task_group *tg);
extern int sched_group_set_rt_period(struct task_group *tg,
				      long rt_period_us);
extern long sched_group_rt_period(struct task_group *tg);
extern int sched_rt_can_attach(struct task_group *tg, struct task_struct *tsk);
#endif
#endif

extern int task_can_switch_user(struct user_struct *up,
					struct task_struct *tsk);

#ifdef CONFIG_TASK_XACCT
static inline void add_rchar(struct task_struct *tsk, ssize_t amt)
{
+11 −2
Original line number Diff line number Diff line
@@ -9224,6 +9224,16 @@ static int sched_rt_global_constraints(void)

	return ret;
}

int sched_rt_can_attach(struct task_group *tg, struct task_struct *tsk)
{
	/* Don't accept realtime tasks when there is no way for them to run */
	if (rt_task(tsk) && tg->rt_bandwidth.rt_runtime == 0)
		return 0;

	return 1;
}

#else /* !CONFIG_RT_GROUP_SCHED */
static int sched_rt_global_constraints(void)
{
@@ -9317,8 +9327,7 @@ cpu_cgroup_can_attach(struct cgroup_subsys *ss, struct cgroup *cgrp,
		      struct task_struct *tsk)
{
#ifdef CONFIG_RT_GROUP_SCHED
	/* Don't accept realtime tasks when there is no way for them to run */
	if (rt_task(tsk) && cgroup_tg(cgrp)->rt_bandwidth.rt_runtime == 0)
	if (!sched_rt_can_attach(cgroup_tg(cgrp), tsk))
		return -EINVAL;
#else
	/* We don't support RT-tasks being in separate groups */
+20 −11
Original line number Diff line number Diff line
@@ -571,6 +571,11 @@ static int set_user(struct cred *new)
	if (!new_user)
		return -EAGAIN;

	if (!task_can_switch_user(new_user, current)) {
		free_uid(new_user);
		return -EINVAL;
	}

	if (atomic_read(&new_user->processes) >=
				current->signal->rlim[RLIMIT_NPROC].rlim_cur &&
			new_user != INIT_USER) {
@@ -631,10 +636,11 @@ SYSCALL_DEFINE2(setreuid, uid_t, ruid, uid_t, euid)
			goto error;
	}

	retval = -EAGAIN;
	if (new->uid != old->uid && set_user(new) < 0)
	if (new->uid != old->uid) {
		retval = set_user(new);
		if (retval < 0)
			goto error;

	}
	if (ruid != (uid_t) -1 ||
	    (euid != (uid_t) -1 && euid != old->uid))
		new->suid = new->euid;
@@ -680,8 +686,9 @@ SYSCALL_DEFINE1(setuid, uid_t, uid)
	retval = -EPERM;
	if (capable(CAP_SETUID)) {
		new->suid = new->uid = uid;
		if (uid != old->uid && set_user(new) < 0) {
			retval = -EAGAIN;
		if (uid != old->uid) {
			retval = set_user(new);
			if (retval < 0)
				goto error;
		}
	} else if (uid != old->uid && uid != new->suid) {
@@ -734,12 +741,14 @@ SYSCALL_DEFINE3(setresuid, uid_t, ruid, uid_t, euid, uid_t, suid)
			goto error;
	}

	retval = -EAGAIN;
	if (ruid != (uid_t) -1) {
		new->uid = ruid;
		if (ruid != old->uid && set_user(new) < 0)
		if (ruid != old->uid) {
			retval = set_user(new);
			if (retval < 0)
				goto error;
		}
	}
	if (euid != (uid_t) -1)
		new->euid = euid;
	if (suid != (uid_t) -1)
+18 −0
Original line number Diff line number Diff line
@@ -362,6 +362,24 @@ static void free_user(struct user_struct *up, unsigned long flags)

#endif

#if defined(CONFIG_RT_GROUP_SCHED) && defined(CONFIG_USER_SCHED)
/*
 * We need to check if a setuid can take place. This function should be called
 * before successfully completing the setuid.
 */
int task_can_switch_user(struct user_struct *up, struct task_struct *tsk)
{

	return sched_rt_can_attach(up->tg, tsk);

}
#else
int task_can_switch_user(struct user_struct *up, struct task_struct *tsk)
{
	return 1;
}
#endif

/*
 * Locate the user_struct for the passed UID.  If found, take a ref on it.  The
 * caller must undo that ref with free_uid().