Commit 305e6c42 authored by Linus Torvalds's avatar Linus Torvalds

Merge branch 'for-5.17-fixes' of git://git.kernel.org/pub/scm/linux/kernel/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
parents c36c04c2 2bdfd282
...@@ -549,6 +549,14 @@ static ssize_t cgroup_release_agent_write(struct kernfs_open_file *of, ...@@ -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); 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); cgrp = cgroup_kn_lock_live(of->kn, false);
if (!cgrp) if (!cgrp)
return -ENODEV; return -ENODEV;
...@@ -954,6 +962,12 @@ int cgroup1_parse_param(struct fs_context *fc, struct fs_parameter *param) ...@@ -954,6 +962,12 @@ int cgroup1_parse_param(struct fs_context *fc, struct fs_parameter *param)
/* Specifying two release agents is forbidden */ /* Specifying two release agents is forbidden */
if (ctx->release_agent) if (ctx->release_agent)
return invalfc(fc, "release_agent respecified"); 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; ctx->release_agent = param->string;
param->string = NULL; param->string = NULL;
break; break;
......
...@@ -590,6 +590,35 @@ static inline void free_cpuset(struct cpuset *cs) ...@@ -590,6 +590,35 @@ static inline void free_cpuset(struct cpuset *cs)
kfree(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 * validate_change() - Used to validate that any proposed cpuset change
* follows the structural rules for cpusets. * follows the structural rules for cpusets.
...@@ -614,20 +643,21 @@ static int validate_change(struct cpuset *cur, struct cpuset *trial) ...@@ -614,20 +643,21 @@ static int validate_change(struct cpuset *cur, struct cpuset *trial)
{ {
struct cgroup_subsys_state *css; struct cgroup_subsys_state *css;
struct cpuset *c, *par; struct cpuset *c, *par;
int ret; int ret = 0;
/* The checks don't apply to root cpuset */
if (cur == &top_cpuset)
return 0;
rcu_read_lock(); rcu_read_lock();
par = parent_cs(cur);
/* On legacy hierarchy, we must be a subset of our parent cpuset. */ if (!is_in_v2_mode())
ret = -EACCES; ret = validate_change_legacy(cur, trial);
if (!is_in_v2_mode() && !is_cpuset_subset(trial, par)) if (ret)
goto out;
/* Remaining checks don't apply to root cpuset */
if (cur == &top_cpuset)
goto out; goto out;
par = parent_cs(cur);
/* /*
* If either I or some sibling (!= me) is exclusive, we can't * If either I or some sibling (!= me) is exclusive, we can't
* overlap * overlap
...@@ -1175,9 +1205,7 @@ enum subparts_cmd { ...@@ -1175,9 +1205,7 @@ enum subparts_cmd {
* *
* Because of the implicit cpu exclusive nature of a partition root, * Because of the implicit cpu exclusive nature of a partition root,
* cpumask changes that violates the cpu exclusivity rule will not be * cpumask changes that violates the cpu exclusivity rule will not be
* permitted when checked by validate_change(). The validate_change() * permitted when checked by validate_change().
* function will also prevent any changes to the cpu list if it is not
* a superset of children's cpu lists.
*/ */
static int update_parent_subparts_cpumask(struct cpuset *cpuset, int cmd, static int update_parent_subparts_cpumask(struct cpuset *cpuset, int cmd,
struct cpumask *newmask, struct cpumask *newmask,
...@@ -1522,10 +1550,15 @@ static void update_sibling_cpumasks(struct cpuset *parent, struct cpuset *cs, ...@@ -1522,10 +1550,15 @@ static void update_sibling_cpumasks(struct cpuset *parent, struct cpuset *cs,
struct cpuset *sibling; struct cpuset *sibling;
struct cgroup_subsys_state *pos_css; struct cgroup_subsys_state *pos_css;
percpu_rwsem_assert_held(&cpuset_rwsem);
/* /*
* Check all its siblings and call update_cpumasks_hier() * Check all its siblings and call update_cpumasks_hier()
* if their use_parent_ecpus flag is set in order for them * if their use_parent_ecpus flag is set in order for them
* to use the right effective_cpus value. * 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(); rcu_read_lock();
cpuset_for_each_child(sibling, pos_css, parent) { cpuset_for_each_child(sibling, pos_css, parent) {
...@@ -1533,8 +1566,13 @@ static void update_sibling_cpumasks(struct cpuset *parent, struct cpuset *cs, ...@@ -1533,8 +1566,13 @@ static void update_sibling_cpumasks(struct cpuset *parent, struct cpuset *cs,
continue; continue;
if (!sibling->use_parent_ecpus) if (!sibling->use_parent_ecpus)
continue; continue;
if (!css_tryget_online(&sibling->css))
continue;
rcu_read_unlock();
update_cpumasks_hier(sibling, tmp); update_cpumasks_hier(sibling, tmp);
rcu_read_lock();
css_put(&sibling->css);
} }
rcu_read_unlock(); rcu_read_unlock();
} }
...@@ -1607,8 +1645,7 @@ static int update_cpumask(struct cpuset *cs, struct cpuset *trialcs, ...@@ -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. * Make sure that subparts_cpus is a subset of cpus_allowed.
*/ */
if (cs->nr_subparts_cpus) { if (cs->nr_subparts_cpus) {
cpumask_andnot(cs->subparts_cpus, cs->subparts_cpus, cpumask_and(cs->subparts_cpus, cs->subparts_cpus, cs->cpus_allowed);
cs->cpus_allowed);
cs->nr_subparts_cpus = cpumask_weight(cs->subparts_cpus); cs->nr_subparts_cpus = cpumask_weight(cs->subparts_cpus);
} }
spin_unlock_irq(&callback_lock); spin_unlock_irq(&callback_lock);
......
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