Skip to content

Commit

Permalink
Merge branch 'for-5.17-fixes' of git://git.kernel.org/pub/scm/linux/k…
Browse files Browse the repository at this point in the history
…ernel/git/tj/cgroup

Pull cgroup fixes from Tejun Heo:

 - Eric's fix for a long standing cgroup1 permission issue where it only
   checks for uid 0 instead of CAP which inadvertently allows
   unprivileged userns roots to modify release_agent userhelper

 - Fixes for the fallout from Waiman's recent cpuset work

* 'for-5.17-fixes' of git://git.kernel.org/pub/scm/linux/kernel/git/tj/cgroup:
  cgroup/cpuset: Fix "suspicious RCU usage" lockdep warning
  cgroup-v1: Require capabilities to set release_agent
  cpuset: Fix the bug that subpart_cpus updated wrongly in update_cpumask()
  cgroup/cpuset: Make child cpusets restrict parents on v1 hierarchy
  • Loading branch information
torvalds committed Feb 3, 2022
2 parents c36c04c + 2bdfd28 commit 305e6c4
Show file tree
Hide file tree
Showing 2 changed files with 65 additions and 14 deletions.
14 changes: 14 additions & 0 deletions kernel/cgroup/cgroup-v1.c
Original file line number Diff line number Diff line change
Expand Up @@ -549,6 +549,14 @@ static ssize_t cgroup_release_agent_write(struct kernfs_open_file *of,

BUILD_BUG_ON(sizeof(cgrp->root->release_agent_path) < PATH_MAX);

/*
* Release agent gets called with all capabilities,
* require capabilities to set release agent.
*/
if ((of->file->f_cred->user_ns != &init_user_ns) ||
!capable(CAP_SYS_ADMIN))
return -EPERM;

cgrp = cgroup_kn_lock_live(of->kn, false);
if (!cgrp)
return -ENODEV;
Expand Down Expand Up @@ -954,6 +962,12 @@ int cgroup1_parse_param(struct fs_context *fc, struct fs_parameter *param)
/* Specifying two release agents is forbidden */
if (ctx->release_agent)
return invalfc(fc, "release_agent respecified");
/*
* Release agent gets called with all capabilities,
* require capabilities to set release agent.
*/
if ((fc->user_ns != &init_user_ns) || !capable(CAP_SYS_ADMIN))
return invalfc(fc, "Setting release_agent not allowed");
ctx->release_agent = param->string;
param->string = NULL;
break;
Expand Down
65 changes: 51 additions & 14 deletions kernel/cgroup/cpuset.c
Original file line number Diff line number Diff line change
Expand Up @@ -590,6 +590,35 @@ static inline void free_cpuset(struct cpuset *cs)
kfree(cs);
}

/*
* validate_change_legacy() - Validate conditions specific to legacy (v1)
* behavior.
*/
static int validate_change_legacy(struct cpuset *cur, struct cpuset *trial)
{
struct cgroup_subsys_state *css;
struct cpuset *c, *par;
int ret;

WARN_ON_ONCE(!rcu_read_lock_held());

/* Each of our child cpusets must be a subset of us */
ret = -EBUSY;
cpuset_for_each_child(c, css, cur)
if (!is_cpuset_subset(c, trial))
goto out;

/* On legacy hierarchy, we must be a subset of our parent cpuset. */
ret = -EACCES;
par = parent_cs(cur);
if (par && !is_cpuset_subset(trial, par))
goto out;

ret = 0;
out:
return ret;
}

/*
* validate_change() - Used to validate that any proposed cpuset change
* follows the structural rules for cpusets.
Expand All @@ -614,20 +643,21 @@ static int validate_change(struct cpuset *cur, struct cpuset *trial)
{
struct cgroup_subsys_state *css;
struct cpuset *c, *par;
int ret;

/* The checks don't apply to root cpuset */
if (cur == &top_cpuset)
return 0;
int ret = 0;

rcu_read_lock();
par = parent_cs(cur);

/* On legacy hierarchy, we must be a subset of our parent cpuset. */
ret = -EACCES;
if (!is_in_v2_mode() && !is_cpuset_subset(trial, par))
if (!is_in_v2_mode())
ret = validate_change_legacy(cur, trial);
if (ret)
goto out;

/* Remaining checks don't apply to root cpuset */
if (cur == &top_cpuset)
goto out;

par = parent_cs(cur);

/*
* If either I or some sibling (!= me) is exclusive, we can't
* overlap
Expand Down Expand Up @@ -1175,9 +1205,7 @@ enum subparts_cmd {
*
* Because of the implicit cpu exclusive nature of a partition root,
* cpumask changes that violates the cpu exclusivity rule will not be
* permitted when checked by validate_change(). The validate_change()
* function will also prevent any changes to the cpu list if it is not
* a superset of children's cpu lists.
* permitted when checked by validate_change().
*/
static int update_parent_subparts_cpumask(struct cpuset *cpuset, int cmd,
struct cpumask *newmask,
Expand Down Expand Up @@ -1522,19 +1550,29 @@ static void update_sibling_cpumasks(struct cpuset *parent, struct cpuset *cs,
struct cpuset *sibling;
struct cgroup_subsys_state *pos_css;

percpu_rwsem_assert_held(&cpuset_rwsem);

/*
* Check all its siblings and call update_cpumasks_hier()
* if their use_parent_ecpus flag is set in order for them
* to use the right effective_cpus value.
*
* The update_cpumasks_hier() function may sleep. So we have to
* release the RCU read lock before calling it.
*/
rcu_read_lock();
cpuset_for_each_child(sibling, pos_css, parent) {
if (sibling == cs)
continue;
if (!sibling->use_parent_ecpus)
continue;
if (!css_tryget_online(&sibling->css))
continue;

rcu_read_unlock();
update_cpumasks_hier(sibling, tmp);
rcu_read_lock();
css_put(&sibling->css);
}
rcu_read_unlock();
}
Expand Down Expand Up @@ -1607,8 +1645,7 @@ static int update_cpumask(struct cpuset *cs, struct cpuset *trialcs,
* Make sure that subparts_cpus is a subset of cpus_allowed.
*/
if (cs->nr_subparts_cpus) {
cpumask_andnot(cs->subparts_cpus, cs->subparts_cpus,
cs->cpus_allowed);
cpumask_and(cs->subparts_cpus, cs->subparts_cpus, cs->cpus_allowed);
cs->nr_subparts_cpus = cpumask_weight(cs->subparts_cpus);
}
spin_unlock_irq(&callback_lock);
Expand Down

0 comments on commit 305e6c4

Please sign in to comment.