In alloc_shrinker_info(), when shrinker_unit_alloc() fails for a node, the error handler calls free_shrinker_info() which iterates over ALL nodes and tries to free their shrinker_info. For the failed node, rcu_assign_pointer() was skipped, so its shrinker_info still points to old data. This causes double-free of valid shrinker_info structures. Fix by tracking which node failed and only freeing shrinker_info structures that were successfully assigned via rcu_assign_pointer() in this call. Failed/unhandled nodes are left untouched. Fixes: 4a3ec379aa21 ("mm: shrinker: make shrinker tracking memcg aware") Cc: stable@vger.kernel.org Signed-off-by: Hongling Zeng --- mm/shrinker.c | 13 ++++++++++++- 1 file changed, 12 insertions(+), 1 deletion(-) diff --git a/mm/shrinker.c b/mm/shrinker.c index 7082d01c8c9d..92c6cb455fc9 100644 --- a/mm/shrinker.c +++ b/mm/shrinker.c @@ -78,6 +78,7 @@ int alloc_shrinker_info(struct mem_cgroup *memcg) { int nid, ret = 0; int array_size = 0; + int failed_nid; mutex_lock(&shrinker_mutex); array_size = shrinker_unit_size(shrinker_nr_max); @@ -98,8 +99,18 @@ int alloc_shrinker_info(struct mem_cgroup *memcg) return ret; err: + failed_nid = nid; + for_each_node(nid) { + struct shrinker_info *info; + + if (nid >= failed_nid) + break; + info = shrinker_info_protected(memcg, nid); + rcu_assign_pointer(memcg->nodeinfo[nid]->shrinker_info, NULL); + shrinker_unit_free(info, 0); + kvfree(info); + } mutex_unlock(&shrinker_mutex); - free_shrinker_info(memcg); return -ENOMEM; } -- 2.25.1