]> git.itanic.dy.fi Git - linux-stable/commitdiff
cgroup: fix a subtle bug in descendant pre-order walk
authorTejun Heo <tj@kernel.org>
Fri, 24 May 2013 01:50:24 +0000 (10:50 +0900)
committerGreg Kroah-Hartman <gregkh@linuxfoundation.org>
Fri, 7 Jun 2013 19:53:07 +0000 (12:53 -0700)
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>
include/linux/cgroup.h
kernel/cgroup.c

index 16d4d0932d419bf68826c683e88cf4b21d2a461a..6ae7d2c193608dc42bd3fe6acfcdee116542e25d 100644 (file)
@@ -570,7 +570,7 @@ struct cgroup *cgroup_rightmost_descendant(struct cgroup *pos);
  *
  * If a subsystem synchronizes against the parent in its ->css_online() and
  * before starting iterating, and synchronizes against @pos on each
- * iteration, any descendant cgroup which finished ->css_offline() is
+ * iteration, any descendant cgroup which finished ->css_online() is
  * guaranteed to be visible in the future iterations.
  *
  * In other words, the following guarantees that a descendant can't escape
index 95e08e30c176566b8515fc917004158e6b475e55..a48de6ab0668617834dee95aa39a9ab9b1f50e59 100644 (file)
@@ -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;
 }