Commit d0b97f38 authored by Linus Torvalds's avatar Linus Torvalds

Merge tag 'cgroup-for-5.19-rc6-fixes' of git://git.kernel.org/pub/scm/linux/kernel/git/tj/cgroup

Pull cgroup fix from Tejun Heo:
 "Fix an old and subtle bug in the migration path.

  css_sets are used to track tasks and migrations are tasks moving from
  a group of css_sets to another group of css_sets. The migration path
  pins all source and destination css_sets in the prep stage.

  Unfortunately, it was overloading the same list_head entry to track
  sources and destinations, which got confused for migrations which are
  partially identity leading to use-after-frees.

  Fixed by using dedicated list_heads for tracking sources and
  destinations"

* tag 'cgroup-for-5.19-rc6-fixes' of git://git.kernel.org/pub/scm/linux/kernel/git/tj/cgroup:
  cgroup: Use separate src/dst nodes when preloading css_sets for migration
parents 5750676b 07fd5b6c
...@@ -264,7 +264,8 @@ struct css_set { ...@@ -264,7 +264,8 @@ struct css_set {
* List of csets participating in the on-going migration either as * List of csets participating in the on-going migration either as
* source or destination. Protected by cgroup_mutex. * source or destination. Protected by cgroup_mutex.
*/ */
struct list_head mg_preload_node; struct list_head mg_src_preload_node;
struct list_head mg_dst_preload_node;
struct list_head mg_node; struct list_head mg_node;
/* /*
......
...@@ -765,7 +765,8 @@ struct css_set init_css_set = { ...@@ -765,7 +765,8 @@ struct css_set init_css_set = {
.task_iters = LIST_HEAD_INIT(init_css_set.task_iters), .task_iters = LIST_HEAD_INIT(init_css_set.task_iters),
.threaded_csets = LIST_HEAD_INIT(init_css_set.threaded_csets), .threaded_csets = LIST_HEAD_INIT(init_css_set.threaded_csets),
.cgrp_links = LIST_HEAD_INIT(init_css_set.cgrp_links), .cgrp_links = LIST_HEAD_INIT(init_css_set.cgrp_links),
.mg_preload_node = LIST_HEAD_INIT(init_css_set.mg_preload_node), .mg_src_preload_node = LIST_HEAD_INIT(init_css_set.mg_src_preload_node),
.mg_dst_preload_node = LIST_HEAD_INIT(init_css_set.mg_dst_preload_node),
.mg_node = LIST_HEAD_INIT(init_css_set.mg_node), .mg_node = LIST_HEAD_INIT(init_css_set.mg_node),
/* /*
...@@ -1240,7 +1241,8 @@ static struct css_set *find_css_set(struct css_set *old_cset, ...@@ -1240,7 +1241,8 @@ static struct css_set *find_css_set(struct css_set *old_cset,
INIT_LIST_HEAD(&cset->threaded_csets); INIT_LIST_HEAD(&cset->threaded_csets);
INIT_HLIST_NODE(&cset->hlist); INIT_HLIST_NODE(&cset->hlist);
INIT_LIST_HEAD(&cset->cgrp_links); INIT_LIST_HEAD(&cset->cgrp_links);
INIT_LIST_HEAD(&cset->mg_preload_node); INIT_LIST_HEAD(&cset->mg_src_preload_node);
INIT_LIST_HEAD(&cset->mg_dst_preload_node);
INIT_LIST_HEAD(&cset->mg_node); INIT_LIST_HEAD(&cset->mg_node);
/* Copy the set of subsystem state objects generated in /* Copy the set of subsystem state objects generated in
...@@ -2597,21 +2599,27 @@ int cgroup_migrate_vet_dst(struct cgroup *dst_cgrp) ...@@ -2597,21 +2599,27 @@ int cgroup_migrate_vet_dst(struct cgroup *dst_cgrp)
*/ */
void cgroup_migrate_finish(struct cgroup_mgctx *mgctx) void cgroup_migrate_finish(struct cgroup_mgctx *mgctx)
{ {
LIST_HEAD(preloaded);
struct css_set *cset, *tmp_cset; struct css_set *cset, *tmp_cset;
lockdep_assert_held(&cgroup_mutex); lockdep_assert_held(&cgroup_mutex);
spin_lock_irq(&css_set_lock); spin_lock_irq(&css_set_lock);
list_splice_tail_init(&mgctx->preloaded_src_csets, &preloaded); list_for_each_entry_safe(cset, tmp_cset, &mgctx->preloaded_src_csets,
list_splice_tail_init(&mgctx->preloaded_dst_csets, &preloaded); mg_src_preload_node) {
cset->mg_src_cgrp = NULL;
cset->mg_dst_cgrp = NULL;
cset->mg_dst_cset = NULL;
list_del_init(&cset->mg_src_preload_node);
put_css_set_locked(cset);
}
list_for_each_entry_safe(cset, tmp_cset, &preloaded, mg_preload_node) { list_for_each_entry_safe(cset, tmp_cset, &mgctx->preloaded_dst_csets,
mg_dst_preload_node) {
cset->mg_src_cgrp = NULL; cset->mg_src_cgrp = NULL;
cset->mg_dst_cgrp = NULL; cset->mg_dst_cgrp = NULL;
cset->mg_dst_cset = NULL; cset->mg_dst_cset = NULL;
list_del_init(&cset->mg_preload_node); list_del_init(&cset->mg_dst_preload_node);
put_css_set_locked(cset); put_css_set_locked(cset);
} }
...@@ -2651,7 +2659,7 @@ void cgroup_migrate_add_src(struct css_set *src_cset, ...@@ -2651,7 +2659,7 @@ void cgroup_migrate_add_src(struct css_set *src_cset,
if (src_cset->dead) if (src_cset->dead)
return; return;
if (!list_empty(&src_cset->mg_preload_node)) if (!list_empty(&src_cset->mg_src_preload_node))
return; return;
src_cgrp = cset_cgroup_from_root(src_cset, dst_cgrp->root); src_cgrp = cset_cgroup_from_root(src_cset, dst_cgrp->root);
...@@ -2664,7 +2672,7 @@ void cgroup_migrate_add_src(struct css_set *src_cset, ...@@ -2664,7 +2672,7 @@ void cgroup_migrate_add_src(struct css_set *src_cset,
src_cset->mg_src_cgrp = src_cgrp; src_cset->mg_src_cgrp = src_cgrp;
src_cset->mg_dst_cgrp = dst_cgrp; src_cset->mg_dst_cgrp = dst_cgrp;
get_css_set(src_cset); get_css_set(src_cset);
list_add_tail(&src_cset->mg_preload_node, &mgctx->preloaded_src_csets); list_add_tail(&src_cset->mg_src_preload_node, &mgctx->preloaded_src_csets);
} }
/** /**
...@@ -2689,7 +2697,7 @@ int cgroup_migrate_prepare_dst(struct cgroup_mgctx *mgctx) ...@@ -2689,7 +2697,7 @@ int cgroup_migrate_prepare_dst(struct cgroup_mgctx *mgctx)
/* look up the dst cset for each src cset and link it to src */ /* look up the dst cset for each src cset and link it to src */
list_for_each_entry_safe(src_cset, tmp_cset, &mgctx->preloaded_src_csets, list_for_each_entry_safe(src_cset, tmp_cset, &mgctx->preloaded_src_csets,
mg_preload_node) { mg_src_preload_node) {
struct css_set *dst_cset; struct css_set *dst_cset;
struct cgroup_subsys *ss; struct cgroup_subsys *ss;
int ssid; int ssid;
...@@ -2708,7 +2716,7 @@ int cgroup_migrate_prepare_dst(struct cgroup_mgctx *mgctx) ...@@ -2708,7 +2716,7 @@ int cgroup_migrate_prepare_dst(struct cgroup_mgctx *mgctx)
if (src_cset == dst_cset) { if (src_cset == dst_cset) {
src_cset->mg_src_cgrp = NULL; src_cset->mg_src_cgrp = NULL;
src_cset->mg_dst_cgrp = NULL; src_cset->mg_dst_cgrp = NULL;
list_del_init(&src_cset->mg_preload_node); list_del_init(&src_cset->mg_src_preload_node);
put_css_set(src_cset); put_css_set(src_cset);
put_css_set(dst_cset); put_css_set(dst_cset);
continue; continue;
...@@ -2716,8 +2724,8 @@ int cgroup_migrate_prepare_dst(struct cgroup_mgctx *mgctx) ...@@ -2716,8 +2724,8 @@ int cgroup_migrate_prepare_dst(struct cgroup_mgctx *mgctx)
src_cset->mg_dst_cset = dst_cset; src_cset->mg_dst_cset = dst_cset;
if (list_empty(&dst_cset->mg_preload_node)) if (list_empty(&dst_cset->mg_dst_preload_node))
list_add_tail(&dst_cset->mg_preload_node, list_add_tail(&dst_cset->mg_dst_preload_node,
&mgctx->preloaded_dst_csets); &mgctx->preloaded_dst_csets);
else else
put_css_set(dst_cset); put_css_set(dst_cset);
...@@ -2963,7 +2971,8 @@ static int cgroup_update_dfl_csses(struct cgroup *cgrp) ...@@ -2963,7 +2971,8 @@ static int cgroup_update_dfl_csses(struct cgroup *cgrp)
goto out_finish; goto out_finish;
spin_lock_irq(&css_set_lock); spin_lock_irq(&css_set_lock);
list_for_each_entry(src_cset, &mgctx.preloaded_src_csets, mg_preload_node) { list_for_each_entry(src_cset, &mgctx.preloaded_src_csets,
mg_src_preload_node) {
struct task_struct *task, *ntask; struct task_struct *task, *ntask;
/* all tasks in src_csets need to be migrated */ /* all tasks in src_csets need to be migrated */
......
Markdown is supported
0%
or
You are about to add 0 people to the discussion. Proceed with caution.
Finish editing this message first!
Please register or to comment