Linux kernel mirror (for testing) git.kernel.org/pub/scm/linux/kernel/git/torvalds/linux.git
kernel os linux
1
fork

Configure Feed

Select the types of activity you want to include in your feed.

mm: slab: fix potential double free in ___cache_free

With the commit 10befea91b61 ("mm: memcg/slab: use a single set of
kmem_caches for all allocations"), it becomes possible to call kfree()
from the slabs_destroy().

The functions cache_flusharray() and do_drain() calls slabs_destroy() on
array_cache of the local CPU without updating the size of the
array_cache. This enables the kfree() call from the slabs_destroy() to
recursively call cache_flusharray() which can potentially call
free_block() on the same elements of the array_cache of the local CPU
and causing double free and memory corruption.

To fix the issue, simply update the local CPU array_cache cache before
calling slabs_destroy().

Fixes: 10befea91b61 ("mm: memcg/slab: use a single set of kmem_caches for all allocations")
Signed-off-by: Shakeel Butt <shakeelb@google.com>
Reviewed-by: Roman Gushchin <guro@fb.com>
Tested-by: Ming Lei <ming.lei@redhat.com>
Reported-by: kernel test robot <rong.a.chen@intel.com>
Cc: Andrew Morton <akpm@linux-foundation.org>
Cc: Ted Ts'o <tytso@mit.edu>
Signed-off-by: Linus Torvalds <torvalds@linux-foundation.org>

authored by

Shakeel Butt and committed by
Linus Torvalds
678ff6a7 7c7ec322

+6 -2
+6 -2
mm/slab.c
··· 1632 1632 kmem_cache_free(cachep->freelist_cache, freelist); 1633 1633 } 1634 1634 1635 + /* 1636 + * Update the size of the caches before calling slabs_destroy as it may 1637 + * recursively call kfree. 1638 + */ 1635 1639 static void slabs_destroy(struct kmem_cache *cachep, struct list_head *list) 1636 1640 { 1637 1641 struct page *page, *n; ··· 2157 2153 spin_lock(&n->list_lock); 2158 2154 free_block(cachep, ac->entry, ac->avail, node, &list); 2159 2155 spin_unlock(&n->list_lock); 2160 - slabs_destroy(cachep, &list); 2161 2156 ac->avail = 0; 2157 + slabs_destroy(cachep, &list); 2162 2158 } 2163 2159 2164 2160 static void drain_cpu_caches(struct kmem_cache *cachep) ··· 3406 3402 } 3407 3403 #endif 3408 3404 spin_unlock(&n->list_lock); 3409 - slabs_destroy(cachep, &list); 3410 3405 ac->avail -= batchcount; 3411 3406 memmove(ac->entry, &(ac->entry[batchcount]), sizeof(void *)*ac->avail); 3407 + slabs_destroy(cachep, &list); 3412 3408 } 3413 3409 3414 3410 /*