• Xiu Jianfeng's avatar
    cgroup: Do not corrupt task iteration when rebinding subsystem · 6f363f5a
    Xiu Jianfeng authored
    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: default avatarGaosheng Cui <cuigaosheng1@huawei.com>
    Link: https://www.spinics.net/lists/cgroups/msg37935.htmlSuggested-by: default avatarMichal Koutný <mkoutny@suse.com>
    Link: https://lore.kernel.org/all/20230526114139.70274-1-xiujianfeng@huaweicloud.com/Signed-off-by: default avatarXiu Jianfeng <xiujianfeng@huawei.com>
    Fixes: 2d8f243a ("cgroup: implement cgroup->e_csets[]")
    Cc: stable@vger.kernel.org # v3.16+
    Signed-off-by: default avatarTejun Heo <tj@kernel.org>
    6f363f5a
cgroup.c 184 KB