diff options
author | Eric W. Biederman <ebiederm@xmission.com> | 2016-07-15 07:35:51 -0400 |
---|---|---|
committer | Tejun Heo <tj@kernel.org> | 2016-07-15 07:56:38 -0400 |
commit | eedd0f4cbf5f3b81e82649832091e1d9d53f0709 (patch) | |
tree | b806976542cf2731a4382414a28326706cb01f7c | |
parent | 7bd8830875bfa380c68f390efbad893293749324 (diff) |
cgroupns: Close race between cgroup_post_fork and copy_cgroup_ns
In most code paths involving cgroup migration cgroup_threadgroup_rwsem
is taken. There are two exceptions:
- remove_tasks_in_empty_cpuset calls cgroup_transfer_tasks
- vhost_attach_cgroups_work calls cgroup_attach_task_all
With cgroup_threadgroup_rwsem held it is guaranteed that cgroup_post_fork
and copy_cgroup_ns will reference the same css_set from the process calling
fork.
Without such an interlock there process after fork could reference one
css_set from it's new cgroup namespace and another css_set from
task->cgroups, which semantically is nonsensical.
Cc: stable@vger.kernel.org
Fixes: a79a908fd2b0 ("cgroup: introduce cgroup namespaces")
Signed-off-by: "Eric W. Biederman" <ebiederm@xmission.com>
Signed-off-by: Tejun Heo <tj@kernel.org>
-rw-r--r-- | kernel/cgroup.c | 5 |
1 files changed, 5 insertions, 0 deletions
diff --git a/kernel/cgroup.c b/kernel/cgroup.c index 5f01e00cffc4..e75efa819911 100644 --- a/kernel/cgroup.c +++ b/kernel/cgroup.c | |||
@@ -2962,6 +2962,7 @@ int cgroup_attach_task_all(struct task_struct *from, struct task_struct *tsk) | |||
2962 | int retval = 0; | 2962 | int retval = 0; |
2963 | 2963 | ||
2964 | mutex_lock(&cgroup_mutex); | 2964 | mutex_lock(&cgroup_mutex); |
2965 | percpu_down_write(&cgroup_threadgroup_rwsem); | ||
2965 | for_each_root(root) { | 2966 | for_each_root(root) { |
2966 | struct cgroup *from_cgrp; | 2967 | struct cgroup *from_cgrp; |
2967 | 2968 | ||
@@ -2976,6 +2977,7 @@ int cgroup_attach_task_all(struct task_struct *from, struct task_struct *tsk) | |||
2976 | if (retval) | 2977 | if (retval) |
2977 | break; | 2978 | break; |
2978 | } | 2979 | } |
2980 | percpu_up_write(&cgroup_threadgroup_rwsem); | ||
2979 | mutex_unlock(&cgroup_mutex); | 2981 | mutex_unlock(&cgroup_mutex); |
2980 | 2982 | ||
2981 | return retval; | 2983 | return retval; |
@@ -4343,6 +4345,8 @@ int cgroup_transfer_tasks(struct cgroup *to, struct cgroup *from) | |||
4343 | 4345 | ||
4344 | mutex_lock(&cgroup_mutex); | 4346 | mutex_lock(&cgroup_mutex); |
4345 | 4347 | ||
4348 | percpu_down_write(&cgroup_threadgroup_rwsem); | ||
4349 | |||
4346 | /* all tasks in @from are being moved, all csets are source */ | 4350 | /* all tasks in @from are being moved, all csets are source */ |
4347 | spin_lock_irq(&css_set_lock); | 4351 | spin_lock_irq(&css_set_lock); |
4348 | list_for_each_entry(link, &from->cset_links, cset_link) | 4352 | list_for_each_entry(link, &from->cset_links, cset_link) |
@@ -4371,6 +4375,7 @@ int cgroup_transfer_tasks(struct cgroup *to, struct cgroup *from) | |||
4371 | } while (task && !ret); | 4375 | } while (task && !ret); |
4372 | out_err: | 4376 | out_err: |
4373 | cgroup_migrate_finish(&preloaded_csets); | 4377 | cgroup_migrate_finish(&preloaded_csets); |
4378 | percpu_up_write(&cgroup_threadgroup_rwsem); | ||
4374 | mutex_unlock(&cgroup_mutex); | 4379 | mutex_unlock(&cgroup_mutex); |
4375 | return ret; | 4380 | return ret; |
4376 | } | 4381 | } |