aboutsummaryrefslogtreecommitdiff
path: root/kernel
diff options
context:
space:
mode:
authorTejun Heo <tj@kernel.org>2013-05-24 10:50:24 +0900
committerGreg Kroah-Hartman <gregkh@linuxfoundation.org>2013-06-07 12:53:07 -0700
commite92b3f9342bd09ba9707d0500b46c6500de1b1fc (patch)
tree5e9f65e15c43bb2ad73dce17f50fcb11220d2ba3 /kernel
parent24622f8bb1d75e005f6cd1ccaea82b72e31b6d6c (diff)
cgroup: fix a subtle bug in descendant pre-order walk
commit 7805d000db30a3787a4c969bab6ae4d8a5fd8ce6 upstream. When cgroup_next_descendant_pre() initiates a walk, it checks whether the subtree root doesn't have any children and if not returns NULL. Later code assumes that the subtree isn't empty. This is broken because the subtree may become empty inbetween, which can lead to the traversal escaping the subtree by walking to the sibling of the subtree root. There's no reason to have the early exit path. Remove it along with the later assumption that the subtree isn't empty. This simplifies the code a bit and fixes the subtle bug. While at it, fix the comment of cgroup_for_each_descendant_pre() which was incorrectly referring to ->css_offline() instead of ->css_online(). Signed-off-by: Tejun Heo <tj@kernel.org> Reviewed-by: Michal Hocko <mhocko@suse.cz> Signed-off-by: Greg Kroah-Hartman <gregkh@linuxfoundation.org>
Diffstat (limited to 'kernel')
-rw-r--r--kernel/cgroup.c9
1 files changed, 3 insertions, 6 deletions
diff --git a/kernel/cgroup.c b/kernel/cgroup.c
index 95e08e30c17..a48de6ab066 100644
--- a/kernel/cgroup.c
+++ b/kernel/cgroup.c
@@ -3000,11 +3000,8 @@ struct cgroup *cgroup_next_descendant_pre(struct cgroup *pos,
WARN_ON_ONCE(!rcu_read_lock_held());
/* if first iteration, pretend we just visited @cgroup */
- if (!pos) {
- if (list_empty(&cgroup->children))
- return NULL;
+ if (!pos)
pos = cgroup;
- }
/* visit the first child if exists */
next = list_first_or_null_rcu(&pos->children, struct cgroup, sibling);
@@ -3012,14 +3009,14 @@ struct cgroup *cgroup_next_descendant_pre(struct cgroup *pos,
return next;
/* no child, visit my or the closest ancestor's next sibling */
- do {
+ while (pos != cgroup) {
next = list_entry_rcu(pos->sibling.next, struct cgroup,
sibling);
if (&next->sibling != &pos->parent->children)
return next;
pos = pos->parent;
- } while (pos != cgroup);
+ }
return NULL;
}