aboutsummaryrefslogtreecommitdiffstats
diff options
context:
space:
mode:
authorAndrey Ryabinin <a.ryabinin@samsung.com>2015-04-15 19:15:05 -0400
committerLinus Torvalds <torvalds@linux-foundation.org>2015-04-15 19:35:20 -0400
commit923936157b158f36bd6a3d86496dce82b1a957de (patch)
tree4a78fc384c220efad52d250b49795b535fbc2db0
parentbda6d33042a486c8f7b15bf15a80fd07d4eab204 (diff)
mm/mempool.c: kasan: poison mempool elements
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>
-rw-r--r--include/linux/kasan.h2
-rw-r--r--mm/kasan/kasan.c13
-rw-r--r--mm/mempool.c23
3 files changed, 38 insertions, 0 deletions
diff --git a/include/linux/kasan.h b/include/linux/kasan.h
index 5bb074431eb0..5486d777b706 100644
--- a/include/linux/kasan.h
+++ b/include/linux/kasan.h
@@ -44,6 +44,7 @@ void kasan_poison_object_data(struct kmem_cache *cache, void *object);
44 44
45void kasan_kmalloc_large(const void *ptr, size_t size); 45void kasan_kmalloc_large(const void *ptr, size_t size);
46void kasan_kfree_large(const void *ptr); 46void kasan_kfree_large(const void *ptr);
47void kasan_kfree(void *ptr);
47void kasan_kmalloc(struct kmem_cache *s, const void *object, size_t size); 48void kasan_kmalloc(struct kmem_cache *s, const void *object, size_t size);
48void kasan_krealloc(const void *object, size_t new_size); 49void kasan_krealloc(const void *object, size_t new_size);
49 50
@@ -71,6 +72,7 @@ static inline void kasan_poison_object_data(struct kmem_cache *cache,
71 72
72static inline void kasan_kmalloc_large(void *ptr, size_t size) {} 73static inline void kasan_kmalloc_large(void *ptr, size_t size) {}
73static inline void kasan_kfree_large(const void *ptr) {} 74static inline void kasan_kfree_large(const void *ptr) {}
75static inline void kasan_kfree(void *ptr) {}
74static inline void kasan_kmalloc(struct kmem_cache *s, const void *object, 76static inline void kasan_kmalloc(struct kmem_cache *s, const void *object,
75 size_t size) {} 77 size_t size) {}
76static inline void kasan_krealloc(const void *object, size_t new_size) {} 78static inline void kasan_krealloc(const void *object, size_t new_size) {}
diff --git a/mm/kasan/kasan.c b/mm/kasan/kasan.c
index 936d81661c47..6c513a63ea84 100644
--- a/mm/kasan/kasan.c
+++ b/mm/kasan/kasan.c
@@ -389,6 +389,19 @@ void kasan_krealloc(const void *object, size_t size)
389 kasan_kmalloc(page->slab_cache, object, size); 389 kasan_kmalloc(page->slab_cache, object, size);
390} 390}
391 391
392void kasan_kfree(void *ptr)
393{
394 struct page *page;
395
396 page = virt_to_head_page(ptr);
397
398 if (unlikely(!PageSlab(page)))
399 kasan_poison_shadow(ptr, PAGE_SIZE << compound_order(page),
400 KASAN_FREE_PAGE);
401 else
402 kasan_slab_free(page->slab_cache, ptr);
403}
404
392void kasan_kfree_large(const void *ptr) 405void kasan_kfree_large(const void *ptr)
393{ 406{
394 struct page *page = virt_to_page(ptr); 407 struct page *page = virt_to_page(ptr);
diff --git a/mm/mempool.c b/mm/mempool.c
index 2884d5bad77e..2cc08de8b1db 100644
--- a/mm/mempool.c
+++ b/mm/mempool.c
@@ -12,6 +12,7 @@
12#include <linux/mm.h> 12#include <linux/mm.h>
13#include <linux/slab.h> 13#include <linux/slab.h>
14#include <linux/highmem.h> 14#include <linux/highmem.h>
15#include <linux/kasan.h>
15#include <linux/kmemleak.h> 16#include <linux/kmemleak.h>
16#include <linux/export.h> 17#include <linux/export.h>
17#include <linux/mempool.h> 18#include <linux/mempool.h>
@@ -101,10 +102,31 @@ static inline void poison_element(mempool_t *pool, void *element)
101} 102}
102#endif /* CONFIG_DEBUG_SLAB || CONFIG_SLUB_DEBUG_ON */ 103#endif /* CONFIG_DEBUG_SLAB || CONFIG_SLUB_DEBUG_ON */
103 104
105static void kasan_poison_element(mempool_t *pool, void *element)
106{
107 if (pool->alloc == mempool_alloc_slab)
108 kasan_slab_free(pool->pool_data, element);
109 if (pool->alloc == mempool_kmalloc)
110 kasan_kfree(element);
111 if (pool->alloc == mempool_alloc_pages)
112 kasan_free_pages(element, (unsigned long)pool->pool_data);
113}
114
115static void kasan_unpoison_element(mempool_t *pool, void *element)
116{
117 if (pool->alloc == mempool_alloc_slab)
118 kasan_slab_alloc(pool->pool_data, element);
119 if (pool->alloc == mempool_kmalloc)
120 kasan_krealloc(element, (size_t)pool->pool_data);
121 if (pool->alloc == mempool_alloc_pages)
122 kasan_alloc_pages(element, (unsigned long)pool->pool_data);
123}
124
104static void add_element(mempool_t *pool, void *element) 125static void add_element(mempool_t *pool, void *element)
105{ 126{
106 BUG_ON(pool->curr_nr >= pool->min_nr); 127 BUG_ON(pool->curr_nr >= pool->min_nr);
107 poison_element(pool, element); 128 poison_element(pool, element);
129 kasan_poison_element(pool, element);
108 pool->elements[pool->curr_nr++] = element; 130 pool->elements[pool->curr_nr++] = element;
109} 131}
110 132
@@ -114,6 +136,7 @@ static void *remove_element(mempool_t *pool)
114 136
115 BUG_ON(pool->curr_nr < 0); 137 BUG_ON(pool->curr_nr < 0);
116 check_element(pool, element); 138 check_element(pool, element);
139 kasan_unpoison_element(pool, element);
117 return element; 140 return element;
118} 141}
119 142