mm: kasan: don't touch metadata in kasan_[un]poison_element()
Subsystems (3)
Alexander Potapenko
reviewerPATCH 1/1 mm: kasan: don't touch metadata in kasan_[un]poison_element()
To avoid draining the mempools, KASAN shouldn't put the mempool elements into the quarantine upon mempool_free(). It shouldn't store allocation/deallocation stacks upon mempool_alloc()/mempool_free() either. Therefore make kasan_[un]poison_element() just change the shadow memory, not the metadata.
--- include/linux/kasan.h | 8 ++++++-- mm/kasan/kasan.c | 48 +++++++++++++++++++++++++++++++++++++++++++++--- mm/mempool.c | 5 +++-- mm/slab.c | 4 ++-- mm/slab.h | 2 +- 5 files changed, 57 insertions(+), 10 deletions(-)
include/linux/kasan.h +6 -2
@@ -63,8 +63,10 @@ void kasan_kfree(void *ptr);void kasan_kmalloc (struct kmem_cache *s,const void *object,size_t size,gfp_t flags);void kasan_krealloc (const void *object,size_t new_size,gfp_t flags); +void kasan_unpoison_kmalloc (const void *object,size_t size,gfp_t flags); -void kasan_slab_alloc (struct kmem_cache *s,void *object,gfp_t flags); +void kasan_slab_alloc (struct kmem_cache *s,void *object,bool just_unpoison, +gfp_t flags);bool kasan_slab_free (struct kmem_cache *s,void *object);void kasan_poison_slab_free (struct kmem_cache *s,void *object);
@@ -107,9 +109,11 @@ static inline void kasan_kmalloc(struct kmem_cache *s, const void *object, size_t size, gfp_t flags) {}static inline void kasan_krealloc (const void *object,size_t new_size,gfp_t flags) {} +static inline void kasan_unpoison_kmalloc (const void *object,size_t size, +gfp_t flags) {}static inline void kasan_slab_alloc (struct kmem_cache *s,void *object, -gfp_t flags) {} +bool just_unpoison,gfp_t flags) {}static inline bool kasan_slab_free (struct kmem_cache *s,void *object) {return false;
mm/kasan/kasan.c +45 -3
67 lines not on this page · raw message
mm/mempool.c +3 -2
@@ -115,9 +115,10 @@ static void kasan_poison_element(mempool_t *pool, void *element)static void kasan_unpoison_element (mempool_t *pool,void *element,gfp_t flags) {if (pool->alloc == mempool_alloc_slab) -kasan_slab_alloc (pool->pool_data, element, flags); +kasan_slab_alloc (pool->pool_data, element, +/*just_unpoison*/ false, flags);if (pool->alloc == mempool_kmalloc) -kasan_krealloc (element, (size_t )pool->pool_data, flags); +kasan_unpoison_kmalloc (element, (size_t )pool->pool_data, flags);if (pool->alloc == mempool_alloc_pages)kasan_alloc_pages (element, (unsigned long )pool->pool_data); }
mm/slab.c +2 -2
@@ -3606,7 +3606,7 @@ void *kmem_cache_alloc(struct kmem_cache *cachep, gfp_t flags) {void *ret =slab_alloc (cachep, flags, _RET_IP_); -kasan_slab_alloc (cachep, ret, flags); +kasan_slab_alloc (cachep, ret,/*just_unpoison*/ false, flags);trace_kmem_cache_alloc (_RET_IP_, ret, cachep->object_size, cachep->size, flags);
@@ -3696,7 +3696,7 @@ void *kmem_cache_alloc_node(struct kmem_cache *cachep, gfp_t flags, int nodeid) {void *ret =slab_alloc_node (cachep, flags, nodeid, _RET_IP_); -kasan_slab_alloc (cachep, ret, flags); +kasan_slab_alloc (cachep, ret,/*just_unpoison*/ false, flags);trace_kmem_cache_alloc_node (_RET_IP_, ret, cachep->object_size, cachep->size, flags, nodeid);
mm/slab.h +1 -1
@@ -405,7 +405,7 @@ static inline void slab_post_alloc_hook(struct kmem_cache *s, gfp_t flags,kmemcheck_slab_alloc (s, flags, object,slab_ksize (s));kmemleak_alloc_recursive (object, s->object_size,1 , s->flags, flags); -kasan_slab_alloc (s, object, flags); +kasan_slab_alloc (s, object,/*just_unpoison*/ false, flags); }memcg_kmem_put_cache (s); }
Signature · 7 lines
2.8.0.rc3.226.g39d4020 -- To unsubscribe, send a message with 'unsubscribe linux-mm' in the body to [email protected]. For more info on Linux MM, see: http://www.linux-mm.org/ . Don't email: <a href=mailto:"[email protected]"> [email protected] </a>
Quoted 2 lines · Alexander Potapenko
To avoid draining the mempools, KASAN shouldn't put the mempool elements into the quarantine upon
mempool_free().
Correct, but unfortunately this patch doesn't fix that.
Quoted 2 lines
It shouldn't store
allocation/deallocation stacks upon mempool_alloc()/mempool_free() either.
Why not?
Quoted 6 lines
Therefore make kasan_[un]poison_element() just change the shadow memory, not the metadata.
Signed-off-by Alexander Potapenko <[email protected]>Reported-by Kuthonuzo Luruo <[email protected]>---
[...]
mm/kasan/kasan.c struct kasan_free_meta *get_free_info(struct kmem_cache *cache, · line 509
Quoted 5 lines
mm/kasan/kasan.c +3
-1+void kasan_slab_alloc (struct kmem_cache *cache,void *object, +bool just_unpoison,gfp_t flags) { -kasan_kmalloc (cache, object, cache->object_size, flags); +if (just_unpoison)
This set to 'false' in all call sites.
Quoted 39 lines
mm/kasan/kasan.c +28
-0+kasan_unpoison_shadow (object, cache->object_size); + else + kasan_kmalloc(cache, object, cache->object_size, flags); }void kasan_poison_slab_free (struct kmem_cache *cache,void *object)@@ -611,6 +615,31 @@ void kasan_kmalloc_large(const void *ptr, size_t size, gfp_t flags) KASAN_PAGE_REDZONE); } +void kasan_unpoison_kmalloc (const void *object,size_t size,gfp_t flags) +{ +struct page *page; +unsigned long redzone_start; +unsigned long redzone_end; + +if (unlikely (object ==ZERO_SIZE_PTR ) || (object ==NULL )) +return ; + + page =virt_to_head_page (object); + redzone_start =round_up ((unsigned long )(object + size), +KASAN_SHADOW_SCALE_SIZE ); + +if (unlikely (!PageSlab (page))) + redzone_end = (unsigned long )object + + (PAGE_SIZE <<compound_order (page)); +else + redzone_end =round_up ( + (unsigned long )object + page->slab_cache->object_size, +KASAN_SHADOW_SCALE_SIZE ); +kasan_unpoison_shadow (object, size); +kasan_poison_shadow ((void *)redzone_start, redzone_end - redzone_start, +KASAN_KMALLOC_REDZONE ); +} +void kasan_krealloc (const void *object,size_t size,gfp_t flags) {struct page *page;
mm/kasan/kasan.c void kasan_kfree(void *ptr) · line 671
Quoted 8 lines
mm/kasan/kasan.c +4
-1@@ -636,7 +665,20 @@ void kasan_kfree(void *ptr)kasan_poison_shadow (ptr,PAGE_SIZE <<compound_order (page),KASAN_FREE_PAGE ); else - kasan_slab_free(page->slab_cache, ptr); + kasan_poison_slab_free(page->slab_cache, ptr); +} + +void kasan_poison_kfree (void *ptr)
Unused
Quoted 18 lines
mm/kasan/kasan.c +10
-0+{ +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_poison_slab_free (page->slab_cache, ptr); }void kasan_kfree_large (const void *ptr)mm/mempool.c +0
-0
mm/mempool.c static void kasan_poison_element(mempool_t *pool, void *element) · line 121
Quoted 9 lines
mm/mempool.c +3
-2@@ -115,9 +115,10 @@ static void kasan_poison_element(mempool_t *pool, void *element)static void kasan_unpoison_element (mempool_t *pool,void *element,gfp_t flags) {if (pool->alloc == mempool_alloc_slab) -kasan_slab_alloc (pool->pool_data, element, flags); +kasan_slab_alloc (pool->pool_data, element, +/*just_unpoison*/ false, flags);if (pool->alloc == mempool_kmalloc) -kasan_krealloc (element, (size_t )pool->pool_data, flags); +kasan_unpoison_kmalloc (element, (size_t )pool->pool_data, flags);
I think, that the current code here is fine.
We only need to fix kasan_poison_element() which calls kasan_kfree() that puts objects into quarantine.
--
To unsubscribe, send a message with 'unsubscribe linux-mm' in the body to [email protected]. For more info on Linux MM,
see: http://www.linux-mm.org/ .
Don't email: <a href=mailto:"[email protected]"> [email protected] </a>
PATCH 1/1 mm: mempool: kasan: don't poot mempool objects in quarantine
Quoted 8 lines · Andrey Ryabinin
Quoted 2 lines · Alexander Potapenko
To avoid draining the mempools, KASAN shouldn't put the mempool elements into the quarantine upon
mempool_free().Correct, but unfortunately this patch doesn't fix that.
So, I made this:
From: Andrey Ryabinin <[email protected]>
Subject: [PATCH] mm: mempool: kasan: don't poot mempool objects in quarantine
Currently we may put reserved by mempool elements into quarantine via kasan_kfree(). This is totally wrong since quarantine may really free these objects. So when mempool will try to use such element, use-after-free will happen. Or mempool may decide that it no longer need that element and double-free it.
So don't put object into quarantine in kasan_kfree(), just poison it. Rename kasan_kfree() to kasan_poison_kfree() to respect that.
Also, we shouldn't use kasan_slab_alloc()/kasan_krealloc() in kasan_unpoison_element() because those functions may update allocation stacktrace. This would be wrong for the most of the remove_element call sites.
(The only call site where we may want to update alloc stacktrace is
in mempool_alloc(). Kmemleak solves this by calling kmemleak_update_trace(), so we could make something like that too. But this is out of scope of this patch).
--- include/linux/kasan.h | 11 +++++++---- mm/kasan/kasan.c | 6 +++--- mm/mempool.c | 12 ++++-------- 3 files changed, 14 insertions(+), 15 deletions(-)
include/linux/kasan.h +7 -4
@@ -59,14 +59,13 @@ void kasan_poison_object_data(struct kmem_cache *cache, void *object);void kasan_kmalloc_large (const void *ptr,size_t size,gfp_t flags);void kasan_kfree_large (const void *ptr); -void kasan_kfree (void *ptr); +void kasan_poison_kfree (void *ptr);void kasan_kmalloc (struct kmem_cache *s,const void *object,size_t size,gfp_t flags);void kasan_krealloc (const void *object,size_t new_size,gfp_t flags);void kasan_slab_alloc (struct kmem_cache *s,void *object,gfp_t flags);bool kasan_slab_free (struct kmem_cache *s,void *object); -void kasan_poison_slab_free (struct kmem_cache *s,void *object);struct kasan_cache {int alloc_meta_offset;
@@ -76,6 +75,9 @@ struct kasan_cache {int kasan_module_alloc (void *addr,size_t size);void kasan_free_shadow (const struct vm_struct *vm); +size_t ksize (const void *); +static inline void kasan_unpoison_slab (const void *ptr) {ksize (ptr); } +#else /* CONFIG_KASAN */ static inline void kasan_unpoison_shadow (const void *address,size_t size) {}
@@ -102,7 +104,7 @@ static inline void kasan_poison_object_data(struct kmem_cache *cache,static inline void kasan_kmalloc_large (void *ptr,size_t size,gfp_t flags) {}static inline void kasan_kfree_large (const void *ptr) {} -static inline void kasan_kfree (void *ptr) {} +static inline void kasan_poison_kfree (void *ptr) {}static inline void kasan_kmalloc (struct kmem_cache *s,const void *object,size_t size,gfp_t flags) {} static inline void kasan_krealloc(const void *object, size_t new_size,
@@ -114,11 +116,12 @@ static inline bool kasan_slab_free(struct kmem_cache *s, void *object) {return false; } -static inline void kasan_poison_slab_free (struct kmem_cache *s,void *object) {}static inline int kasan_module_alloc (void *addr,size_t size) {return 0 ; }static inline void kasan_free_shadow (const struct vm_struct *vm) {} +static inline void kasan_unpoison_slab (const void *ptr) { } +#endif /* CONFIG_KASAN */ #endif /* LINUX_KASAN_H */
mm/kasan/kasan.c +3 -3
@@ -508,7 +508,7 @@ void kasan_slab_alloc(struct kmem_cache *cache, void *object, gfp_t flags)kasan_kmalloc (cache, object, cache->object_size, flags); } -void kasan_poison_slab_free (struct kmem_cache *cache,void *object) +static void kasan_poison_slab_free (struct kmem_cache *cache,void *object) {unsigned long size = cache->object_size;unsigned long rounded_up_size =round_up (size,KASAN_SHADOW_SCALE_SIZE );
@@ -626,7 +626,7 @@ void kasan_krealloc(const void *object, size_t size, gfp_t flags)kasan_kmalloc (page->slab_cache, object, size, flags); } -void kasan_kfree (void *ptr) +void kasan_poison_kfree (void *ptr) {struct page *page;
@@ -636,7 +636,7 @@ void kasan_kfree(void *ptr)kasan_poison_shadow (ptr,PAGE_SIZE <<compound_order (page),KASAN_FREE_PAGE ); else - kasan_slab_free(page->slab_cache, ptr); + kasan_poison_slab_free(page->slab_cache, ptr); }void kasan_kfree_large (const void *ptr)
mm/mempool.c +4 -8
@@ -104,20 +104,16 @@ static inline void poison_element(mempool_t *pool, void *element)static void kasan_poison_element (mempool_t *pool,void *element) { -if (pool->alloc == mempool_alloc_slab) -kasan_poison_slab_free (pool->pool_data, element); -if (pool->alloc == mempool_kmalloc) -kasan_kfree (element); +if (pool->alloc == mempool_alloc_slab || pool->alloc == mempool_kmalloc) +kasan_poison_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,gfp_t flags) { -if (pool->alloc == mempool_alloc_slab) -kasan_slab_alloc (pool->pool_data, element, flags); -if (pool->alloc == mempool_kmalloc) -kasan_krealloc (element, (size_t )pool->pool_data, flags); +if (pool->alloc == mempool_alloc_slab || pool->alloc == mempool_kmalloc) +kasan_unpoison_slab (element);if (pool->alloc == mempool_alloc_pages)kasan_alloc_pages (element, (unsigned long )pool->pool_data); }
Signature · 8 lines
2.7.3 -- To unsubscribe, send a message with 'unsubscribe linux-mm' in the body to [email protected]. For more info on Linux MM, see: http://www.linux-mm.org/ . Don't email: <a href=mailto:"[email protected]"> [email protected] </a>
Re: [PATCH] mm: mempool: kasan: don't poot mempool objects in quarantine
Quoted 11 lines · Andrey Ryabinin
Quoted 8 lines · Andrey Ryabinin
Quoted 2 lines · Alexander Potapenko
To avoid draining the mempools, KASAN shouldn't put the mempool elements into the quarantine upon
mempool_free().Correct, but unfortunately this patch doesn't fix that.
So, I made this:
You beat me to it :)
Thanks!
Quoted 19 lines
From: Andrey Ryabinin <[email protected]>
Subject: [PATCH] mm: mempool: kasan: don't poot mempool objects in quarantineCurrently we may put reserved by mempool elements into quarantine via
kasan_kfree(). This is totally wrong since quarantine may really free these objects. So when mempool will try to use such element, use-after-free will happen. Or mempool may decide that it no longer need that element and double-free it.So don't put object into quarantine in
kasan_kfree(), just poison it. Renamekasan_kfree()tokasan_poison_kfree()to respect that.Also, we shouldn't use kasan_slab_alloc()/kasan_krealloc() in
kasan_unpoison_element()because those functions may update allocation stacktrace. This would be wrong for the most of theremove_elementcall sites.
Quoted 8 lines
(The only call site where we may want to update alloc stacktrace is
inmempool_alloc(). Kmemleak solves this by callingkmemleak_update_trace(), so we could make something like that too. But this is out of scope of this patch).Fixes 55834c59098d ("mm: kasan: initial memory quarantine implementation")Signed-off-by Andrey Ryabinin <[email protected]>Reported-by Kuthonuzo Luruo <[email protected]>
Quoted 123 lines
--- include/linux/kasan.h | 11 +++++++---- mm/kasan/kasan.c | 6 +++--- mm/mempool.c | 12 ++++-------- 3 files changed, 14 insertions(+), 15 deletions(-)include/linux/kasan.h +7
-4@@ -59,14 +59,13 @@ void kasan_poison_object_data(struct kmem_cache *cache, void *object);void kasan_kmalloc_large (const void *ptr,size_t size,gfp_t flags);void kasan_kfree_large (const void *ptr); -void kasan_kfree (void *ptr); +void kasan_poison_kfree (void *ptr);void kasan_kmalloc (struct kmem_cache *s,const void *object,size_t size,gfp_t flags);void kasan_krealloc (const void *object,size_t new_size,gfp_t flags);void kasan_slab_alloc (struct kmem_cache *s,void *object,gfp_t flags);bool kasan_slab_free (struct kmem_cache *s,void *object); -void kasan_poison_slab_free (struct kmem_cache *s,void *object);struct kasan_cache {int alloc_meta_offset;@@ -76,6 +75,9 @@ struct kasan_cache {int kasan_module_alloc (void *addr,size_t size);void kasan_free_shadow (const struct vm_struct *vm); +size_t ksize (const void *); +static inline void kasan_unpoison_slab (const void *ptr) {ksize (ptr); } +#else /* CONFIG_KASAN */ static inline void kasan_unpoison_shadow (const void *address,size_t size) {}@@ -102,7 +104,7 @@ static inline void kasan_poison_object_data(struct kmem_cache *cache,static inline void kasan_kmalloc_large (void *ptr,size_t size,gfp_t flags) {}static inline void kasan_kfree_large (const void *ptr) {} -static inline void kasan_kfree (void *ptr) {} +static inline void kasan_poison_kfree (void *ptr) {}static inline void kasan_kmalloc (struct kmem_cache *s,const void *object,size_t size,gfp_t flags) {} static inline void kasan_krealloc(const void *object, size_t new_size,@@ -114,11 +116,12 @@ static inline bool kasan_slab_free(struct kmem_cache *s, void *object) {return false; } -static inline void kasan_poison_slab_free (struct kmem_cache *s,void *object) {}static inline int kasan_module_alloc (void *addr,size_t size) {return 0 ; }static inline void kasan_free_shadow (const struct vm_struct *vm) {} +static inline void kasan_unpoison_slab (const void *ptr) { } +#endif /* CONFIG_KASAN */ #endif /* LINUX_KASAN_H */ mm/kasan/kasan.c +3
-3@@ -508,7 +508,7 @@ void kasan_slab_alloc(struct kmem_cache *cache, void *object, gfp_t flags)kasan_kmalloc (cache, object, cache->object_size, flags); } -void kasan_poison_slab_free (struct kmem_cache *cache,void *object) +static void kasan_poison_slab_free (struct kmem_cache *cache,void *object) {unsigned long size = cache->object_size;unsigned long rounded_up_size =round_up (size,KASAN_SHADOW_SCALE_SIZE );@@ -626,7 +626,7 @@ void kasan_krealloc(const void *object, size_t size, gfp_t flags)kasan_kmalloc (page->slab_cache, object, size, flags); } -void kasan_kfree (void *ptr) +void kasan_poison_kfree (void *ptr) {struct page *page;@@ -636,7 +636,7 @@ void kasan_kfree(void *ptr)kasan_poison_shadow (ptr,PAGE_SIZE <<compound_order (page),KASAN_FREE_PAGE ); else - kasan_slab_free(page->slab_cache, ptr); + kasan_poison_slab_free(page->slab_cache, ptr); }void kasan_kfree_large (const void *ptr)mm/mempool.c +4
-9@@ -104,20 +104,16 @@ static inline void poison_element(mempool_t *pool, void *element)static void kasan_poison_element (mempool_t *pool,void *element) { -if (pool->alloc == mempool_alloc_slab) -kasan_poison_slab_free (pool->pool_data, element); -if (pool->alloc == mempool_kmalloc) -kasan_kfree (element); +if (pool->alloc == mempool_alloc_slab || pool->alloc == mempool_kmalloc) +kasan_poison_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,gfp_t flags) { -if (pool->alloc == mempool_alloc_slab) -kasan_slab_alloc (pool->pool_data, element, flags); -if (pool->alloc == mempool_kmalloc) -kasan_krealloc (element, (size_t )pool->pool_data, flags); +if (pool->alloc == mempool_alloc_slab || pool->alloc == mempool_kmalloc) +kasan_unpoison_slab (element);if (pool->alloc == mempool_alloc_pages)kasan_alloc_pages (element, (unsigned long )pool->pool_data); } --2.7.3
Signature · 16 lines
Alexander Potapenko Software Engineer Google Germany GmbH Erika-Mann-Straße, 33 80636 München Geschäftsführer: Matthew Scott Sucherman, Paul Terence Manicle Registergericht und -nummer: Hamburg, HRB 86891 Sitz der Gesellschaft: Hamburg -- To unsubscribe, send a message with 'unsubscribe linux-mm' in the body to [email protected]. For more info on Linux MM, see: http://www.linux-mm.org/ . Don't email: <a href=mailto:"[email protected]"> [email protected] </a>