mm/mempool.c: kasan: poison mempool elements
authorAndrey Ryabinin <a.ryabinin@samsung.com>
Wed, 15 Apr 2015 23:15:05 +0000 (16:15 -0700)
committerLinus Torvalds <torvalds@linux-foundation.org>
Wed, 15 Apr 2015 23:35:20 +0000 (16:35 -0700)
Mempools keep allocated objects in reserved for situations when ordinary
allocation may not be possible to satisfy.  These objects shouldn't be
accessed before they leave the pool.

This patch poison elements when get into the pool and unpoison when they
leave it.  This will let KASan to detect use-after-free of mempool's
elements.

Signed-off-by: Andrey Ryabinin <a.ryabinin@samsung.com>
Tested-by: David Rientjes <rientjes@google.com>
Cc: Catalin Marinas <catalin.marinas@arm.com>
Cc: Dmitry Chernenkov <drcheren@gmail.com>
Cc: Dmitry Vyukov <dvyukov@google.com>
Cc: Alexander Potapenko <glider@google.com>
Signed-off-by: Andrew Morton <akpm@linux-foundation.org>
Signed-off-by: Linus Torvalds <torvalds@linux-foundation.org>
include/linux/kasan.h
mm/kasan/kasan.c
mm/mempool.c

index 5bb074431eb0ce571b32d785b45ad85ee8c22b34..5486d777b706edeb20db7782e27ff12b6a558945 100644 (file)
@@ -44,6 +44,7 @@ void kasan_poison_object_data(struct kmem_cache *cache, void *object);
 
 void kasan_kmalloc_large(const void *ptr, size_t size);
 void kasan_kfree_large(const void *ptr);
+void kasan_kfree(void *ptr);
 void kasan_kmalloc(struct kmem_cache *s, const void *object, size_t size);
 void kasan_krealloc(const void *object, size_t new_size);
 
@@ -71,6 +72,7 @@ static inline void kasan_poison_object_data(struct kmem_cache *cache,
 
 static inline void kasan_kmalloc_large(void *ptr, size_t size) {}
 static inline void kasan_kfree_large(const void *ptr) {}
+static inline void kasan_kfree(void *ptr) {}
 static inline void kasan_kmalloc(struct kmem_cache *s, const void *object,
                                size_t size) {}
 static inline void kasan_krealloc(const void *object, size_t new_size) {}
index 936d81661c478a89fd797e8b311b4c52291f2414..6c513a63ea84c3c7ffd41201b7a419ff7b6dfd5d 100644 (file)
@@ -389,6 +389,19 @@ void kasan_krealloc(const void *object, size_t size)
                kasan_kmalloc(page->slab_cache, object, size);
 }
 
+void kasan_kfree(void *ptr)
+{
+       struct page *page;
+
+       page = virt_to_head_page(ptr);
+
+       if (unlikely(!PageSlab(page)))
+               kasan_poison_shadow(ptr, PAGE_SIZE << compound_order(page),
+                               KASAN_FREE_PAGE);
+       else
+               kasan_slab_free(page->slab_cache, ptr);
+}
+
 void kasan_kfree_large(const void *ptr)
 {
        struct page *page = virt_to_page(ptr);
index 2884d5bad77ee388a4f7913a0dd0f93d03cd1635..2cc08de8b1db259c70ad8d3aa28d0691f7ada544 100644 (file)
@@ -12,6 +12,7 @@
 #include <linux/mm.h>
 #include <linux/slab.h>
 #include <linux/highmem.h>
+#include <linux/kasan.h>
 #include <linux/kmemleak.h>
 #include <linux/export.h>
 #include <linux/mempool.h>
@@ -101,10 +102,31 @@ static inline void poison_element(mempool_t *pool, void *element)
 }
 #endif /* CONFIG_DEBUG_SLAB || CONFIG_SLUB_DEBUG_ON */
 
+static void kasan_poison_element(mempool_t *pool, void *element)
+{
+       if (pool->alloc == mempool_alloc_slab)
+               kasan_slab_free(pool->pool_data, element);
+       if (pool->alloc == mempool_kmalloc)
+               kasan_kfree(element);
+       if (pool->alloc == mempool_alloc_pages)
+               kasan_free_pages(element, (unsigned long)pool->pool_data);
+}
+
+static void kasan_unpoison_element(mempool_t *pool, void *element)
+{
+       if (pool->alloc == mempool_alloc_slab)
+               kasan_slab_alloc(pool->pool_data, element);
+       if (pool->alloc == mempool_kmalloc)
+               kasan_krealloc(element, (size_t)pool->pool_data);
+       if (pool->alloc == mempool_alloc_pages)
+               kasan_alloc_pages(element, (unsigned long)pool->pool_data);
+}
+
 static void add_element(mempool_t *pool, void *element)
 {
        BUG_ON(pool->curr_nr >= pool->min_nr);
        poison_element(pool, element);
+       kasan_poison_element(pool, element);
        pool->elements[pool->curr_nr++] = element;
 }
 
@@ -114,6 +136,7 @@ static void *remove_element(mempool_t *pool)
 
        BUG_ON(pool->curr_nr < 0);
        check_element(pool, element);
+       kasan_unpoison_element(pool, element);
        return element;
 }