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: http://lkml.kernel.org/r/1482257462-36948-1-git-send-email-gthelen@google.com Signed-off-by: Greg Thelen <gthelen@google.com> Reviewed-by: Vladimir Davydov <vdavydov.dev@gmail.com> Acked-by: Andrey Ryabinin <aryabinin@virtuozzo.com> Cc: Alexander Potapenko <glider@google.com> Cc: Dmitry Vyukov <dvyukov@google.com> Cc: Christoph Lameter <cl@linux.com> Cc: Pekka Enberg <penberg@kernel.org> Cc: David Rientjes <rientjes@google.com> Cc: Joonsoo Kim <iamjoonsoo.kim@lge.com> Signed-off-by: Andrew Morton <akpm@linux-foundation.org> Signed-off-by: Linus Torvalds <torvalds@linux-foundation.org>
This commit is contained in:
parent
dc18d706a4
commit
f9fa1d919c
|
@ -52,7 +52,7 @@ void kasan_free_pages(struct page *page, unsigned int order);
|
||||||
void kasan_cache_create(struct kmem_cache *cache, size_t *size,
|
void kasan_cache_create(struct kmem_cache *cache, size_t *size,
|
||||||
unsigned long *flags);
|
unsigned long *flags);
|
||||||
void kasan_cache_shrink(struct kmem_cache *cache);
|
void kasan_cache_shrink(struct kmem_cache *cache);
|
||||||
void kasan_cache_destroy(struct kmem_cache *cache);
|
void kasan_cache_shutdown(struct kmem_cache *cache);
|
||||||
|
|
||||||
void kasan_poison_slab(struct page *page);
|
void kasan_poison_slab(struct page *page);
|
||||||
void kasan_unpoison_object_data(struct kmem_cache *cache, void *object);
|
void kasan_unpoison_object_data(struct kmem_cache *cache, void *object);
|
||||||
|
@ -98,7 +98,7 @@ static inline void kasan_cache_create(struct kmem_cache *cache,
|
||||||
size_t *size,
|
size_t *size,
|
||||||
unsigned long *flags) {}
|
unsigned long *flags) {}
|
||||||
static inline void kasan_cache_shrink(struct kmem_cache *cache) {}
|
static inline void kasan_cache_shrink(struct kmem_cache *cache) {}
|
||||||
static inline void kasan_cache_destroy(struct kmem_cache *cache) {}
|
static inline void kasan_cache_shutdown(struct kmem_cache *cache) {}
|
||||||
|
|
||||||
static inline void kasan_poison_slab(struct page *page) {}
|
static inline void kasan_poison_slab(struct page *page) {}
|
||||||
static inline void kasan_unpoison_object_data(struct kmem_cache *cache,
|
static inline void kasan_unpoison_object_data(struct kmem_cache *cache,
|
||||||
|
|
|
@ -435,7 +435,7 @@ void kasan_cache_shrink(struct kmem_cache *cache)
|
||||||
quarantine_remove_cache(cache);
|
quarantine_remove_cache(cache);
|
||||||
}
|
}
|
||||||
|
|
||||||
void kasan_cache_destroy(struct kmem_cache *cache)
|
void kasan_cache_shutdown(struct kmem_cache *cache)
|
||||||
{
|
{
|
||||||
quarantine_remove_cache(cache);
|
quarantine_remove_cache(cache);
|
||||||
}
|
}
|
||||||
|
|
|
@ -274,6 +274,7 @@ static void per_cpu_remove_cache(void *arg)
|
||||||
qlist_free_all(&to_free, cache);
|
qlist_free_all(&to_free, cache);
|
||||||
}
|
}
|
||||||
|
|
||||||
|
/* Free all quarantined objects belonging to cache. */
|
||||||
void quarantine_remove_cache(struct kmem_cache *cache)
|
void quarantine_remove_cache(struct kmem_cache *cache)
|
||||||
{
|
{
|
||||||
unsigned long flags, i;
|
unsigned long flags, i;
|
||||||
|
|
|
@ -528,6 +528,9 @@ static void slab_caches_to_rcu_destroy_workfn(struct work_struct *work)
|
||||||
|
|
||||||
static int shutdown_cache(struct kmem_cache *s)
|
static int shutdown_cache(struct kmem_cache *s)
|
||||||
{
|
{
|
||||||
|
/* free asan quarantined objects */
|
||||||
|
kasan_cache_shutdown(s);
|
||||||
|
|
||||||
if (__kmem_cache_shutdown(s) != 0)
|
if (__kmem_cache_shutdown(s) != 0)
|
||||||
return -EBUSY;
|
return -EBUSY;
|
||||||
|
|
||||||
|
@ -816,7 +819,6 @@ void kmem_cache_destroy(struct kmem_cache *s)
|
||||||
get_online_cpus();
|
get_online_cpus();
|
||||||
get_online_mems();
|
get_online_mems();
|
||||||
|
|
||||||
kasan_cache_destroy(s);
|
|
||||||
mutex_lock(&slab_mutex);
|
mutex_lock(&slab_mutex);
|
||||||
|
|
||||||
s->refcount--;
|
s->refcount--;
|
||||||
|
|
Loading…
Reference in New Issue