aboutsummaryrefslogtreecommitdiffstats
path: root/kernel
diff options
context:
space:
mode:
authorHans de Goede <hdegoede@redhat.com>2013-01-10 10:08:44 +0100
committerZiyan <jaraidaniel@gmail.com>2016-03-11 15:56:54 +0100
commitf3c3c31e215a3cebbee8206a93d791d9f403d56f (patch)
tree376b6aad58ca16ae004a26fe6e1ee529fb67b645 /kernel
parente049dcbb3e8ba8aeb42e3ae5787c471732dfd029 (diff)
downloadkernel_samsung_espresso10-f3c3c31e215a3cebbee8206a93d791d9f403d56f.zip
kernel_samsung_espresso10-f3c3c31e215a3cebbee8206a93d791d9f403d56f.tar.gz
kernel_samsung_espresso10-f3c3c31e215a3cebbee8206a93d791d9f403d56f.tar.bz2
cgroup: Fix use after free of cgrp (cgrp->css_sets)
Running a 3.4 kernel + Fedora-18 (systemd) userland on my Allwinner A10 (arm cortex a8), I'm seeing repeated, reproducable list_del list corruption errors when build with CONFIG_DEBUG_LIST, and the backtrace always shows free_css_set_work as the function making the problematic list_del call. I've tracked this doen to a use after free of the cgrp struct, specifically of the cgrp->css_sets list_head, which gets cleared by free_css_set_work. Since free_css_set_work runs form a workqueue, it is possible for it to not be done with clearing the list when the cgrp gets free-ed. To avoid this the code adding the links increases cgrp->count, and the freeing code running from the workqueue decreases cgrp->count *after* doing list_del, and then if the count goes to 0 calls cgroup_wakeup_rmdir_waiter(). However cgroup_rmdir() is missing a check for cgrp->count != 0, causing it to still continue with the rmdir (which leads to the free-ing of the cgrp), before free_css_set_work is done. Sometimes the free-ed memory is re-used before free_css_set_work gets around to unlinking link->cgrp_link_list, triggering the list_del list corruption messages. This patch fixes this by properly checking for cgrp->count != 0 and waiting for the cgroup_rmdir_waitq in that case. Change-Id: I9dbc02a0a75d5dffa1b65d67456e00139dea57c3 Signed-off-by: Hans de Goede <hdegoede@redhat.com>
Diffstat (limited to 'kernel')
-rw-r--r--kernel/cgroup.c6
1 files changed, 5 insertions, 1 deletions
diff --git a/kernel/cgroup.c b/kernel/cgroup.c
index ca8bfff..a4d2e51 100644
--- a/kernel/cgroup.c
+++ b/kernel/cgroup.c
@@ -270,7 +270,7 @@ static void check_for_release(struct cgroup *cgrp);
/*
* A queue for waiters to do rmdir() cgroup. A tasks will sleep when
- * cgroup->count == 0 && list_empty(&cgroup->children) && subsys has some
+ * list_empty(&cgroup->children) && subsys has some
* reference to css->refcnt. In general, this refcnt is expected to goes down
* to zero, soon.
*
@@ -3938,6 +3938,10 @@ static int cgroup_clear_css_refs(struct cgroup *cgrp)
struct cgroup_subsys *ss;
unsigned long flags;
bool failed = false;
+
+ if (atomic_read(&cgrp->count) != 0)
+ return false;
+
local_irq_save(flags);
for_each_subsys(cgrp->root, ss) {
struct cgroup_subsys_state *css = cgrp->subsys[ss->subsys_id];