path: root/mm
diff options
authorGreg Thelen <>2017-02-24 15:00:05 -0800
committerLinus Torvalds <>2017-02-24 17:46:56 -0800
commitf9fa1d919c696e90c887d8742198023e7639d139 (patch)
treeacc56d7b44857938665f29c17d51b122697dc8f5 /mm
parentdc18d706a4367454ad1fc51e06148d54e8ecfaa0 (diff)
kasan: drain quarantine of memcg slab objects
Per memcg slab accounting and kasan have a problem with kmem_cache destruction. - kmem_cache_create() allocates a kmem_cache, which is used for allocations from processes running in root (top) memcg. - Processes running in non root memcg and allocating with either __GFP_ACCOUNT or from a SLAB_ACCOUNT cache use a per memcg kmem_cache. - Kasan catches use-after-free by having kfree() and kmem_cache_free() defer freeing of objects. Objects are placed in a quarantine. - kmem_cache_destroy() destroys root and non root kmem_caches. It takes care to drain the quarantine of objects from the root memcg's kmem_cache, but ignores objects associated with non root memcg. This causes leaks because quarantined per memcg objects refer to per memcg kmem cache being destroyed. To see the problem: 1) create a slab cache with kmem_cache_create(,,,SLAB_ACCOUNT,) 2) from non root memcg, allocate and free a few objects from cache 3) dispose of the cache with kmem_cache_destroy() kmem_cache_destroy() will trigger a "Slab cache still has objects" warning indicating that the per memcg kmem_cache structure was leaked. Fix the leak by draining kasan quarantined objects allocated from non root memcg. Racing memcg deletion is tricky, but handled. kmem_cache_destroy() => shutdown_memcg_caches() => __shutdown_memcg_cache() => shutdown_cache() flushes per memcg quarantined objects, even if that memcg has been rmdir'd and gone through memcg_deactivate_kmem_caches(). This leak only affects destroyed SLAB_ACCOUNT kmem caches when kasan is enabled. So I don't think it's worth patching stable kernels. Link: Signed-off-by: Greg Thelen <> Reviewed-by: Vladimir Davydov <> Acked-by: Andrey Ryabinin <> Cc: Alexander Potapenko <> Cc: Dmitry Vyukov <> Cc: Christoph Lameter <> Cc: Pekka Enberg <> Cc: David Rientjes <> Cc: Joonsoo Kim <> Signed-off-by: Andrew Morton <> Signed-off-by: Linus Torvalds <>
Diffstat (limited to 'mm')
3 files changed, 5 insertions, 2 deletions
diff --git a/mm/kasan/kasan.c b/mm/kasan/kasan.c
index b2a0cff..25f0e65 100644
--- a/mm/kasan/kasan.c
+++ b/mm/kasan/kasan.c
@@ -435,7 +435,7 @@ void kasan_cache_shrink(struct kmem_cache *cache)
-void kasan_cache_destroy(struct kmem_cache *cache)
+void kasan_cache_shutdown(struct kmem_cache *cache)
diff --git a/mm/kasan/quarantine.c b/mm/kasan/quarantine.c
index dae929c..6f1ed16 100644
--- a/mm/kasan/quarantine.c
+++ b/mm/kasan/quarantine.c
@@ -274,6 +274,7 @@ static void per_cpu_remove_cache(void *arg)
qlist_free_all(&to_free, cache);
+/* Free all quarantined objects belonging to cache. */
void quarantine_remove_cache(struct kmem_cache *cache)
unsigned long flags, i;
diff --git a/mm/slab_common.c b/mm/slab_common.c
index 23ff74e..09d0e84 100644
--- a/mm/slab_common.c
+++ b/mm/slab_common.c
@@ -528,6 +528,9 @@ static void slab_caches_to_rcu_destroy_workfn(struct work_struct *work)
static int shutdown_cache(struct kmem_cache *s)
+ /* free asan quarantined objects */
+ kasan_cache_shutdown(s);
if (__kmem_cache_shutdown(s) != 0)
return -EBUSY;
@@ -816,7 +819,6 @@ void kmem_cache_destroy(struct kmem_cache *s)
- kasan_cache_destroy(s);