Skip to content

Commit 6f363f5

Browse files
Xiu Jianfenghtejun
authored andcommitted
cgroup: Do not corrupt task iteration when rebinding subsystem
We found a refcount UAF bug as follows: refcount_t: addition on 0; use-after-free. WARNING: CPU: 1 PID: 342 at lib/refcount.c:25 refcount_warn_saturate+0xa0/0x148 Workqueue: events cpuset_hotplug_workfn Call trace: refcount_warn_saturate+0xa0/0x148 __refcount_add.constprop.0+0x5c/0x80 css_task_iter_advance_css_set+0xd8/0x210 css_task_iter_advance+0xa8/0x120 css_task_iter_next+0x94/0x158 update_tasks_root_domain+0x58/0x98 rebuild_root_domains+0xa0/0x1b0 rebuild_sched_domains_locked+0x144/0x188 cpuset_hotplug_workfn+0x138/0x5a0 process_one_work+0x1e8/0x448 worker_thread+0x228/0x3e0 kthread+0xe0/0xf0 ret_from_fork+0x10/0x20 then a kernel panic will be triggered as below: Unable to handle kernel paging request at virtual address 00000000c0000010 Call trace: cgroup_apply_control_disable+0xa4/0x16c rebind_subsystems+0x224/0x590 cgroup_destroy_root+0x64/0x2e0 css_free_rwork_fn+0x198/0x2a0 process_one_work+0x1d4/0x4bc worker_thread+0x158/0x410 kthread+0x108/0x13c ret_from_fork+0x10/0x18 The race that cause this bug can be shown as below: (hotplug cpu) | (umount cpuset) mutex_lock(&cpuset_mutex) | mutex_lock(&cgroup_mutex) cpuset_hotplug_workfn | rebuild_root_domains | rebind_subsystems update_tasks_root_domain | spin_lock_irq(&css_set_lock) css_task_iter_start | list_move_tail(&cset->e_cset_node[ss->id] while(css_task_iter_next) | &dcgrp->e_csets[ss->id]); css_task_iter_end | spin_unlock_irq(&css_set_lock) mutex_unlock(&cpuset_mutex) | mutex_unlock(&cgroup_mutex) Inside css_task_iter_start/next/end, css_set_lock is hold and then released, so when iterating task(left side), the css_set may be moved to another list(right side), then it->cset_head points to the old list head and it->cset_pos->next points to the head node of new list, which can't be used as struct css_set. To fix this issue, switch from all css_sets to only scgrp's css_sets to patch in-flight iterators to preserve correct iteration, and then update it->cset_head as well. Reported-by: Gaosheng Cui <[email protected]> Link: https://www.spinics.net/lists/cgroups/msg37935.html Suggested-by: Michal Koutný <[email protected]> Link: https://lore.kernel.org/all/[email protected]/ Signed-off-by: Xiu Jianfeng <[email protected]> Fixes: 2d8f243 ("cgroup: implement cgroup->e_csets[]") Cc: [email protected] # v3.16+ Signed-off-by: Tejun Heo <[email protected]>
1 parent f0cc749 commit 6f363f5

File tree

1 file changed

+17
-3
lines changed

1 file changed

+17
-3
lines changed

kernel/cgroup/cgroup.c

Lines changed: 17 additions & 3 deletions
Original file line numberDiff line numberDiff line change
@@ -1798,7 +1798,7 @@ int rebind_subsystems(struct cgroup_root *dst_root, u16 ss_mask)
17981798
{
17991799
struct cgroup *dcgrp = &dst_root->cgrp;
18001800
struct cgroup_subsys *ss;
1801-
int ssid, i, ret;
1801+
int ssid, ret;
18021802
u16 dfl_disable_ss_mask = 0;
18031803

18041804
lockdep_assert_held(&cgroup_mutex);
@@ -1842,7 +1842,8 @@ int rebind_subsystems(struct cgroup_root *dst_root, u16 ss_mask)
18421842
struct cgroup_root *src_root = ss->root;
18431843
struct cgroup *scgrp = &src_root->cgrp;
18441844
struct cgroup_subsys_state *css = cgroup_css(scgrp, ss);
1845-
struct css_set *cset;
1845+
struct css_set *cset, *cset_pos;
1846+
struct css_task_iter *it;
18461847

18471848
WARN_ON(!css || cgroup_css(dcgrp, ss));
18481849

@@ -1860,9 +1861,22 @@ int rebind_subsystems(struct cgroup_root *dst_root, u16 ss_mask)
18601861
css->cgroup = dcgrp;
18611862

18621863
spin_lock_irq(&css_set_lock);
1863-
hash_for_each(css_set_table, i, cset, hlist)
1864+
WARN_ON(!list_empty(&dcgrp->e_csets[ss->id]));
1865+
list_for_each_entry_safe(cset, cset_pos, &scgrp->e_csets[ss->id],
1866+
e_cset_node[ss->id]) {
18641867
list_move_tail(&cset->e_cset_node[ss->id],
18651868
&dcgrp->e_csets[ss->id]);
1869+
/*
1870+
* all css_sets of scgrp together in same order to dcgrp,
1871+
* patch in-flight iterators to preserve correct iteration.
1872+
* since the iterator is always advanced right away and
1873+
* finished when it->cset_pos meets it->cset_head, so only
1874+
* update it->cset_head is enough here.
1875+
*/
1876+
list_for_each_entry(it, &cset->task_iters, iters_node)
1877+
if (it->cset_head == &scgrp->e_csets[ss->id])
1878+
it->cset_head = &dcgrp->e_csets[ss->id];
1879+
}
18661880
spin_unlock_irq(&css_set_lock);
18671881

18681882
if (ss->css_rstat_flush) {

0 commit comments

Comments
 (0)