SLAB support for KASAN
Subsystems (21)
Alexander Potapenko
reviewerv7 cover SLAB support for KASAN
This patch set implements SLAB support for KASAN
Unlike SLUB, SLAB doesn't store allocation/deallocation stacks for heap objects, therefore we reimplement this feature in mm/kasan/stackdepot.c. The intention is to ultimately switch SLUB to use this implementation as well, which will save a lot of memory (right now SLUB bloats each object by 256 bytes to store the allocation/deallocation stacks).
Also neither SLUB nor SLAB delay the reuse of freed memory chunks, which is necessary for better detection of use-after-free errors. We introduce memory quarantine (mm/kasan/quarantine.c), which allows delayed reuse of deallocated memory.
Alexander Potapenko (7):
kasan: Modify kmalloc_large_oob_right(), add
kmalloc_pagealloc_oob_right()
mm, kasan: SLAB support
mm, kasan: Added GFP flags to KASAN API
arch, ftrace: For KASAN put hard/soft IRQ entries into separate
sections
mm, kasan: Stackdepot implementation. Enable stackdepot for SLAB
kasan: Test fix: Warn if the UAF could not be detected in kmalloc_uaf2
mm: kasan: Initial memory quarantine implementation
---
v2: - merged two patches that touched kmalloc_large_oob_right
- moved stackdepot implementation to lib/
- moved IRQ definitions to include/linux/interrupt.h
v3: - minor description changes
- store deallocation info in the "mm, kasan: SLAB support" patch
v4: - fix kbuild error reports
v5: - SLAB allocator, stackdepot: adopted suggestions by Andrey Ryabinin
- IRQ: fixed kbuild warnings
v6: - stackdepot: fixed kbuild warnings, simplified kasan_track, use vmalloc() for depot when possible
- quarantine: improved patch description, removed dead code
v7: - fix kbuild error reports
---
Documentation/kasan.txt | 5 +- arch/arm/include/asm/exception.h | 2 +- arch/arm/kernel/vmlinux.lds.S | 1 + arch/arm64/include/asm/exception.h | 2 +- arch/arm64/kernel/vmlinux.lds.S | 1 + arch/blackfin/kernel/vmlinux.lds.S | 1 + arch/c6x/kernel/vmlinux.lds.S | 1 + arch/metag/kernel/vmlinux.lds.S | 1 + arch/microblaze/kernel/vmlinux.lds.S | 1 + arch/mips/kernel/vmlinux.lds.S | 1 + arch/nios2/kernel/vmlinux.lds.S | 1 + arch/openrisc/kernel/vmlinux.lds.S | 1 + arch/parisc/kernel/vmlinux.lds.S | 1 + arch/powerpc/kernel/vmlinux.lds.S | 1 + arch/s390/kernel/vmlinux.lds.S | 1 + arch/sh/kernel/vmlinux.lds.S | 1 + arch/sparc/kernel/vmlinux.lds.S | 1 + arch/tile/kernel/vmlinux.lds.S | 1 + arch/x86/kernel/Makefile | 1 + arch/x86/kernel/vmlinux.lds.S | 1 + include/asm-generic/vmlinux.lds.h | 12 +- include/linux/ftrace.h | 11 -- include/linux/interrupt.h | 20 +++ include/linux/kasan.h | 63 ++++++-- include/linux/slab.h | 10 +- include/linux/slab_def.h | 14 ++ include/linux/slub_def.h | 11 ++ include/linux/stackdepot.h | 32 ++++ kernel/softirq.c | 2 +- kernel/trace/trace_functions_graph.c | 1 + lib/Kconfig | 4 + lib/Kconfig.kasan | 5 +- lib/Makefile | 3 + lib/stackdepot.c | 301 +++++++++++++++++++++++++++++++++++ lib/test_kasan.c | 59 ++++++- mm/Makefile | 1 + mm/kasan/Makefile | 4 + mm/kasan/kasan.c | 219 +++++++++++++++++++++++-- mm/kasan/kasan.h | 44 +++++ mm/kasan/quarantine.c | 289 +++++++++++++++++++++++++++++++++ mm/kasan/report.c | 63 ++++++-- mm/mempool.c | 23 +-- mm/page_alloc.c | 2 +- mm/slab.c | 53 +++++- mm/slab.h | 4 +- mm/slab_common.c | 8 +- mm/slub.c | 19 +-- 47 files changed, 1211 insertions(+), 92 deletions(-) create mode 100644 include/linux/stackdepot.h create mode 100644 lib/stackdepot.c create mode 100644 mm/kasan/quarantine.c
Signature · 7 lines
2.7.0.rc3.207.g0ac5344 -- 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>
Alexander Potapenko
reviewerPATCH v7 1/7 kasan: Modify kmalloc_large_oob_right(), add kmalloc_pagealloc_oob_right()
Rename kmalloc_large_oob_right() to kmalloc_pagealloc_oob_right(), as the test only checks the page allocator functionality.
Also reimplement kmalloc_large_oob_right() so that the test allocates a large enough chunk of memory that still does not trigger the page allocator fallback.
---
v2: - Merged "kasan: Change the behavior of kmalloc_large_oob_right" and
"kasan: Changed kmalloc_large_oob_right, added kmalloc_pagealloc_oob_right" from v1
v3: - Minor description changes
--- lib/test_kasan.c | 28 +++++++++++++++++++++++++++- 1 file changed, 27 insertions(+), 1 deletion(-)
lib/test_kasan.c +27 -1
@@ -65,11 +65,34 @@ static noinline void __init kmalloc_node_oob_right(void)kfree (ptr); } -static noinline void __init kmalloc_large_oob_right(void) +#ifdef CONFIG_SLUB +static noinline void __init kmalloc_pagealloc_oob_right(void) {char *ptr;size_t size =KMALLOC_MAX_CACHE_SIZE +10 ; +/* Allocate a chunk that does not fit into a SLUB cache to trigger +* the page allocator fallback. +*/ +pr_info ("kmalloc pagealloc allocation: out-of-bounds to right\n" ); + ptr =kmalloc (size,GFP_KERNEL ); +if (!ptr) { +pr_err ("Allocation failed\n" ); +return ; + } + + ptr[size] =0 ; +kfree (ptr); +} +#endif + +static noinline void __init kmalloc_large_oob_right(void) +{ +char *ptr; +size_t size =KMALLOC_MAX_CACHE_SIZE -256 ; +/* Allocate a chunk that is large enough, but still fits into a slab +* and does not trigger the page allocator fallback in SLUB. +*/ pr_info ("kmalloc large allocation: out-of-bounds to right\n" ); ptr =kmalloc (size,GFP_KERNEL );if (!ptr) {
@@ -324,6 +347,9 @@ static int __init kmalloc_tests_init(void)kmalloc_oob_right ();kmalloc_oob_left ();kmalloc_node_oob_right (); +#ifdef CONFIG_SLUB +kmalloc_pagealloc_oob_right (); +#endif kmalloc_large_oob_right ();kmalloc_oob_krealloc_more ();kmalloc_oob_krealloc_less ();
Signature · 7 lines
2.7.0.rc3.207.g0ac5344 -- 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>
Alexander Potapenko
reviewerPATCH v7 2/7 mm, kasan: SLAB support
In reply to Alexander PotapenkoAdd KASAN hooks to SLAB allocator.
This patch is based on the "mm: kasan: unified support for SLUB and SLAB allocators" patch originally prepared by Dmitry Chernenkov.
---
v3: - minor description changes
- store deallocation info in kasan_slab_free()
v4: - fix kbuild compile-time warnings in print_track()
v5: - adopted suggestions by Andrey Ryabinin:
-- kasan_kmalloc() can handle NULL, no need to check for it -- simplified error printing code, removed unnecessary #ifdefs
--- Documentation/kasan.txt | 5 +-- include/linux/kasan.h | 12 ++++++ include/linux/slab.h | 6 +++ include/linux/slab_def.h | 14 +++++++ include/linux/slub_def.h | 11 +++++ lib/Kconfig.kasan | 4 +- mm/Makefile | 1 + mm/kasan/kasan.c | 102 +++++++++++++++++++++++++++++++++++++++++++++++ mm/kasan/kasan.h | 34 ++++++++++++++++ mm/kasan/report.c | 54 ++++++++++++++++++++----- mm/slab.c | 43 +++++++++++++++++--- mm/slab_common.c | 2 +- 12 files changed, 266 insertions(+), 22 deletions(-)
Documentation/kasan.txt +2 -3
@@ -12,8 +12,7 @@ KASAN uses compile-time instrumentation for checking every memory access, therefore you will need a GCC version 4.9.2 or later. GCC 5.0 or later is required for detection of out-of-bounds accesses to stack or global variables. -Currently KASAN is supported only for x86_64 architecture and requires the -kernel to be built with the SLUB allocator. +Currently KASAN is supported only for x86_64 architecture. 1. Usage ========
@@ -27,7 +26,7 @@ inline are compiler instrumentation types. The former produces smaller binary the latter is 1.1 - 2 times faster. Inline instrumentation requires a GCC version 5.0 or later. -Currently KASAN works only with the SLUB memory allocator. +KASAN works with both SLUB and SLAB memory allocators. For better bug detection and nicer reporting, enable CONFIG_STACKTRACE. To disable instrumentation for specific files or directories, add a line
include/linux/kasan.h +12 -0
@@ -46,6 +46,9 @@ void kasan_unpoison_shadow(const void *address, size_t size);void kasan_alloc_pages (struct page *page,unsigned int order);void kasan_free_pages (struct page *page,unsigned int order); +void kasan_cache_create (struct kmem_cache *cache,size_t *size, +unsigned long *flags); +void kasan_poison_slab (struct page *page);void kasan_unpoison_object_data (struct kmem_cache *cache,void *object);void kasan_poison_object_data (struct kmem_cache *cache,void *object);
@@ -59,6 +62,11 @@ void kasan_krealloc(const void *object, size_t new_size);void kasan_slab_alloc (struct kmem_cache *s,void *object);void kasan_slab_free (struct kmem_cache *s,void *object); +struct kasan_cache { +int alloc_meta_offset; +int free_meta_offset; +}; +int kasan_module_alloc (void *addr,size_t size);void kasan_free_shadow (const struct vm_struct *vm);
@@ -72,6 +80,10 @@ static inline void kasan_disable_current(void) {}static inline void kasan_alloc_pages (struct page *page,unsigned int order) {}static inline void kasan_free_pages (struct page *page,unsigned int order) {} +static inline void kasan_cache_create (struct kmem_cache *cache, +size_t *size, +unsigned long *flags) {} +static inline void kasan_poison_slab (struct page *page) {}static inline void kasan_unpoison_object_data (struct kmem_cache *cache,void *object) {}
include/linux/slab.h +6 -0
@@ -92,6 +92,12 @@# define SLAB_ACCOUNT 0x00000000UL#endif +#ifdef CONFIG_KASAN +#define SLAB_KASAN 0x08000000UL +#else +#define SLAB_KASAN 0x00000000UL +#endif +/* The following flags affect the page allocator grouping pages by mobility */ #define SLAB_RECLAIM_ACCOUNT 0x00020000UL/* Objects are reclaimable */ #define SLAB_TEMPORARY SLAB_RECLAIM_ACCOUNT/* Objects are short-lived */
include/linux/slab_def.h +14 -0
@@ -76,8 +76,22 @@ struct kmem_cache {#ifdef CONFIG_MEMCG struct memcg_cache_params memcg_params;#endif +#ifdef CONFIG_KASAN +struct kasan_cache kasan_info; +#endif struct kmem_cache_node *node[MAX_NUMNODES]; }; +static inline void *nearest_obj (struct kmem_cache *cache,struct page *page, +void *x) { +void *object = x - (x - page->s_mem) % cache->size; +void *last_object = page->s_mem + (cache->num -1 ) * cache->size; + +if (unlikely (object > last_object)) +return last_object; +else +return object; +} +#endif /* _LINUX_SLAB_DEF_H */
include/linux/slub_def.h +11 -0
@@ -130,4 +130,15 @@ static inline void *virt_to_obj(struct kmem_cache *s,void object_err (struct kmem_cache *s,struct page *page,u8 *object,char *reason); +static inline void *nearest_obj (struct kmem_cache *cache,struct page *page, +void *x) { +void *object = x - (x -page_address (page)) % cache->size; +void *last_object =page_address (page) + + (page->objects -1 ) * cache->size; +if (unlikely (object > last_object)) +return last_object; +else +return object; +} +#endif /* _LINUX_SLUB_DEF_H */
lib/Kconfig.kasan +3 -1
@@ -5,7 +5,7 @@ if HAVE_ARCH_KASAN config KASAN bool "KASan: runtime memory debugger" - depends on SLUB_DEBUG + depends on SLUB_DEBUG || (SLAB && !DEBUG_SLAB) select CONSTRUCTORS help Enables kernel address sanitizer - runtime memory debugger,
@@ -16,6 +16,8 @@ config KASAN This feature consumes about 1/8 of available memory and brings about ~x3 performance slowdown. For better error detection enable CONFIG_STACKTRACE. + Currently CONFIG_KASAN doesn't work with CONFIG_DEBUG_SLAB + (the resulting kernel does not boot). choice prompt "Instrumentation type"
mm/Makefile +1 -0
@@ -3,6 +3,7 @@ # KASAN_SANITIZE_slab_common.o := n +KASAN_SANITIZE_slab.o := n KASAN_SANITIZE_slub.o := n # These files are disabled because they produce non-interesting and/or
mm/kasan/kasan.c +102 -0
128 lines not on this page · raw message
mm/kasan/kasan.h +34 -0
@@ -54,6 +54,40 @@ struct kasan_global {#endif }; +/** +* Structures to keep alloc and free tracks * +*/ + +enum kasan_state { +KASAN_STATE_INIT , +KASAN_STATE_ALLOC , +KASAN_STATE_FREE +}; + +struct kasan_track { +u64 cpu :6 ;/* for NR_CPUS = 64 */ +u64 pid :16 ;/* 65536 processes */ +u64 when :42 ;/* ~140 years */ +}; + +struct kasan_alloc_meta { +u32 state :2 ;/* enum kasan_state */ +u32 alloc_size :30 ; +struct kasan_track track; +}; + +struct kasan_free_meta { +/* Allocator freelist pointer, unused by KASAN. */ +void **freelist; +struct kasan_track track; +}; + +struct kasan_alloc_meta *get_alloc_info (struct kmem_cache *cache, +const void *object); +struct kasan_free_meta *get_free_info (struct kmem_cache *cache, +const void *object); + +static inline const void *kasan_shadow_to_mem (const void *shadow_addr) { return (void *)(((unsigned long)shadow_addr - KASAN_SHADOW_OFFSET)
mm/kasan/report.c +43 -11
73 lines not on this page · raw message
mm/slab.c +37 -6
129 lines not on this page · raw message
mm/slab_common.c +1 -1
@@ -35,7 +35,7 @@ struct kmem_cache *kmem_cache;*/ #define SLAB_NEVER_MERGE (SLAB_RED_ZONE | SLAB_POISON | SLAB_STORE_USER | \ SLAB_TRACE | SLAB_DESTROY_BY_RCU | SLAB_NOLEAKTRACE | \ - SLAB_FAILSLAB) + SLAB_FAILSLAB | SLAB_KASAN)#define SLAB_MERGE_SAME (SLAB_RECLAIM_ACCOUNT | SLAB_CACHE_DMA | \ SLAB_NOTRACK | SLAB_ACCOUNT)
Signature · 7 lines
2.7.0.rc3.207.g0ac5344 -- 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>
Alexander Potapenko
reviewerPATCH v7 3/7 mm, kasan: Added GFP flags to KASAN API
In reply to Alexander PotapenkoAdd GFP flags to KASAN hooks for future patches to use.
This patch is based on the "mm: kasan: unified support for SLUB and SLAB allocators" patch originally prepared by Dmitry Chernenkov.
---
v4: - fix kbuild compilation error (missing parameter for kasan_kmalloc())
--- include/linux/kasan.h | 19 +++++++++++-------- include/linux/slab.h | 4 ++-- mm/kasan/kasan.c | 15 ++++++++------- mm/mempool.c | 16 ++++++++-------- mm/slab.c | 15 ++++++++------- mm/slab.h | 2 +- mm/slab_common.c | 4 ++-- mm/slub.c | 15 ++++++++------- 8 files changed, 48 insertions(+), 42 deletions(-)
include/linux/kasan.h +11 -8
@@ -53,13 +53,14 @@ void kasan_poison_slab(struct page *page);void kasan_unpoison_object_data (struct kmem_cache *cache,void *object);void kasan_poison_object_data (struct kmem_cache *cache,void *object); -void kasan_kmalloc_large (const void *ptr,size_t size); +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_kmalloc (struct kmem_cache *s,const void *object,size_t size); -void kasan_krealloc (const void *object,size_t new_size); +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); +void kasan_slab_alloc (struct kmem_cache *s,void *object,gfp_t flags);void kasan_slab_free (struct kmem_cache *s,void *object);struct kasan_cache {
@@ -90,14 +91,16 @@ static inline void kasan_unpoison_object_data(struct kmem_cache *cache,static inline void kasan_poison_object_data (struct kmem_cache *cache,void *object) {} -static inline void kasan_kmalloc_large (void *ptr,size_t size) {} +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_kmalloc (struct kmem_cache *s,const void *object, -size_t size) {} -static inline void kasan_krealloc (const void *object,size_t new_size) {} +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_slab_alloc (struct kmem_cache *s,void *object) {} +static inline void kasan_slab_alloc (struct kmem_cache *s,void *object, +gfp_t flags) {}static inline void kasan_slab_free (struct kmem_cache *s,void *object) {}static inline int kasan_module_alloc (void *addr,size_t size) {return 0 ; }
include/linux/slab.h +2 -2
@@ -376,7 +376,7 @@ static __always_inline void *kmem_cache_alloc_trace(struct kmem_cache *s, {void *ret =kmem_cache_alloc (s, flags); -kasan_kmalloc (s, ret, size); +kasan_kmalloc (s, ret, size, flags);return ret; }
@@ -387,7 +387,7 @@ kmem_cache_alloc_node_trace(struct kmem_cache *s, {void *ret =kmem_cache_alloc_node (s, gfpflags, node); -kasan_kmalloc (s, ret, size); +kasan_kmalloc (s, ret, size, gfpflags);return ret; }#endif /* CONFIG_TRACING */
mm/kasan/kasan.c +8 -7
@@ -414,9 +414,9 @@ struct kasan_free_meta *get_free_info(struct kmem_cache *cache, }#endif -void kasan_slab_alloc (struct kmem_cache *cache,void *object) +void kasan_slab_alloc (struct kmem_cache *cache,void *object,gfp_t flags) { -kasan_kmalloc (cache, object, cache->object_size); +kasan_kmalloc (cache, object, cache->object_size, flags); }void kasan_slab_free (struct kmem_cache *cache,void *object)
@@ -442,7 +442,8 @@ void kasan_slab_free(struct kmem_cache *cache, void *object)kasan_poison_shadow (object, rounded_up_size,KASAN_KMALLOC_FREE ); } -void kasan_kmalloc (struct kmem_cache *cache,const void *object,size_t size) +void kasan_kmalloc (struct kmem_cache *cache,const void *object,size_t size, +gfp_t flags) {unsigned long redzone_start;unsigned long redzone_end;
@@ -471,7 +472,7 @@ void kasan_kmalloc(struct kmem_cache *cache, const void *object, size_t size) }EXPORT_SYMBOL (kasan_kmalloc); -void kasan_kmalloc_large (const void *ptr,size_t size) +void kasan_kmalloc_large (const void *ptr,size_t size,gfp_t flags) {struct page *page;unsigned long redzone_start;
@@ -490,7 +491,7 @@ void kasan_kmalloc_large(const void *ptr, size_t size) KASAN_PAGE_REDZONE); } -void kasan_krealloc (const void *object,size_t size) +void kasan_krealloc (const void *object,size_t size,gfp_t flags) {struct page *page;
@@ -500,9 +501,9 @@ void kasan_krealloc(const void *object, size_t size) page =virt_to_head_page (object);if (unlikely (!PageSlab (page))) -kasan_kmalloc_large (object, size); +kasan_kmalloc_large (object, size, flags);else -kasan_kmalloc (page->slab_cache, object, size); +kasan_kmalloc (page->slab_cache, object, size, flags); }void kasan_kfree (void *ptr)
mm/mempool.c +8 -8
@@ -112,12 +112,12 @@ static void kasan_poison_element(mempool_t *pool, void *element)kasan_free_pages (element, (unsigned long )pool->pool_data); } -static void kasan_unpoison_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); +kasan_slab_alloc (pool->pool_data, element, flags);if (pool->alloc == mempool_kmalloc) -kasan_krealloc (element, (size_t )pool->pool_data); +kasan_krealloc (element, (size_t )pool->pool_data, flags);if (pool->alloc == mempool_alloc_pages)kasan_alloc_pages (element, (unsigned long )pool->pool_data); }
@@ -130,13 +130,13 @@ static void add_element(mempool_t *pool, void *element) pool->elements[pool->curr_nr++] = element; } -static void *remove_element (mempool_t *pool) +static void *remove_element (mempool_t *pool,gfp_t flags) {void *element = pool->elements[--pool->curr_nr];BUG_ON (pool->curr_nr <0 );check_element (pool, element); -kasan_unpoison_element (pool, element); +kasan_unpoison_element (pool, element, flags);return element; }
@@ -154,7 +154,7 @@ void mempool_destroy(mempool_t *pool)return ;while (pool->curr_nr) { -void *element =remove_element (pool); +void *element =remove_element (pool,GFP_KERNEL ); pool->free (element, pool->pool_data); }kfree (pool->elements);
@@ -250,7 +250,7 @@ int mempool_resize(mempool_t *pool, int new_min_nr)spin_lock_irqsave (&pool->lock, flags);if (new_min_nr <= pool->min_nr) {while (new_min_nr < pool->curr_nr) { - element =remove_element (pool); + element =remove_element (pool,GFP_KERNEL );spin_unlock_irqrestore (&pool->lock, flags); pool->free (element, pool->pool_data);spin_lock_irqsave (&pool->lock, flags);
@@ -347,7 +347,7 @@ repeat_alloc:spin_lock_irqsave (&pool->lock, flags);if (likely (pool->curr_nr)) { - element =remove_element (pool); + element =remove_element (pool, gfp_temp);spin_unlock_irqrestore (&pool->lock, flags);/* paired with rmb in mempool_free(), read comment there */ smp_wmb ();
mm/slab.c +8 -7
@@ -3387,7 +3387,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); +kasan_slab_alloc (cachep, ret, flags);trace_kmem_cache_alloc (_RET_IP_, ret, cachep->object_size, cachep->size, flags);
@@ -3453,7 +3453,7 @@ kmem_cache_alloc_trace(struct kmem_cache *cachep, gfp_t flags, size_t size) ret =slab_alloc (cachep, flags, _RET_IP_); -kasan_kmalloc (cachep, ret, size); +kasan_kmalloc (cachep, ret, size, flags);trace_kmalloc (_RET_IP_, ret, size, cachep->size, flags);return ret;
@@ -3477,7 +3477,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); +kasan_slab_alloc (cachep, ret, flags);trace_kmem_cache_alloc_node (_RET_IP_, ret, cachep->object_size, cachep->size, flags, nodeid);
@@ -3495,7 +3495,8 @@ void *kmem_cache_alloc_node_trace(struct kmem_cache *cachep,void *ret; ret =slab_alloc_node (cachep, flags, nodeid, _RET_IP_); -kasan_kmalloc (cachep, ret, size); + +kasan_kmalloc (cachep, ret, size, flags);trace_kmalloc_node (_RET_IP_, ret, size, cachep->size, flags, nodeid);
@@ -3514,7 +3515,7 @@ __do_kmalloc_node(size_t size, gfp_t flags, int node, unsigned long caller)if (unlikely (ZERO_OR_NULL_PTR (cachep)))return cachep; ret =kmem_cache_alloc_node_trace (cachep, flags, node, size); -kasan_kmalloc (cachep, ret, size); +kasan_kmalloc (cachep, ret, size, flags);return ret; }
@@ -3550,7 +3551,7 @@ static __always_inline void *__do_kmalloc(size_t size, gfp_t flags,return cachep; ret =slab_alloc (cachep, flags, caller); -kasan_kmalloc (cachep, ret, size); +kasan_kmalloc (cachep, ret, size, flags);trace_kmalloc (caller, ret, size, cachep->size, flags);
@@ -4333,7 +4334,7 @@ size_t ksize(const void *objp)/* We assume that ksize callers could use the whole allocated area, * so we need to unpoison this area. */ -kasan_krealloc (objp, size); +kasan_krealloc (objp, size,GFP_NOWAIT );return size; }
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); +kasan_slab_alloc (s, object, flags); }memcg_kmem_put_cache (s); }
mm/slab_common.c +2 -2
@@ -1013,7 +1013,7 @@ void *kmalloc_order(size_t size, gfp_t flags, unsigned int order) page =alloc_kmem_pages (flags, order); ret = page ?page_address (page) :NULL ;kmemleak_alloc (ret, size,1 , flags); -kasan_kmalloc_large (ret, size); +kasan_kmalloc_large (ret, size, flags);return ret; }EXPORT_SYMBOL (kmalloc_order);
@@ -1194,7 +1194,7 @@ static __always_inline void *__do_krealloc(const void *p, size_t new_size, ks =ksize (p);if (ks >= new_size) { -kasan_krealloc ((void *)p, new_size); +kasan_krealloc ((void *)p, new_size, flags);return (void *)p; }
mm/slub.c +8 -7
@@ -1318,7 +1318,7 @@ static inline void dec_slabs_node(struct kmem_cache *s, int node,static inline void kmalloc_large_node_hook (void *ptr,size_t size,gfp_t flags) {kmemleak_alloc (ptr, size,1 , flags); -kasan_kmalloc_large (ptr, size); +kasan_kmalloc_large (ptr, size, flags); }static inline void kfree_hook (const void *x)
@@ -2608,7 +2608,7 @@ void *kmem_cache_alloc_trace(struct kmem_cache *s, gfp_t gfpflags, size_t size) {void *ret =slab_alloc (s, gfpflags, _RET_IP_);trace_kmalloc (_RET_IP_, ret, size, s->size, gfpflags); -kasan_kmalloc (s, ret, size); +kasan_kmalloc (s, ret, size, gfpflags);return ret; }EXPORT_SYMBOL (kmem_cache_alloc_trace);
@@ -2636,7 +2636,7 @@ void *kmem_cache_alloc_node_trace(struct kmem_cache *s,trace_kmalloc_node (_RET_IP_, ret, size, s->size, gfpflags, node); -kasan_kmalloc (s, ret, size); +kasan_kmalloc (s, ret, size, gfpflags);return ret; }EXPORT_SYMBOL (kmem_cache_alloc_node_trace);
@@ -3194,7 +3194,8 @@ static void early_kmem_cache_node_alloc(int node)init_object (kmem_cache_node, n,SLUB_RED_ACTIVE );init_tracking (kmem_cache_node, n);#endif -kasan_kmalloc (kmem_cache_node, n,sizeof (struct kmem_cache_node )); +kasan_kmalloc (kmem_cache_node, n,sizeof (struct kmem_cache_node ), +GFP_KERNEL );init_kmem_cache_node (n);inc_slabs_node (kmem_cache_node, node, page->objects);
@@ -3581,7 +3582,7 @@ void *__kmalloc(size_t size, gfp_t flags)trace_kmalloc (_RET_IP_, ret, size, s->size, flags); -kasan_kmalloc (s, ret, size); +kasan_kmalloc (s, ret, size, flags);return ret; }
@@ -3626,7 +3627,7 @@ void *__kmalloc_node(size_t size, gfp_t flags, int node)trace_kmalloc_node (_RET_IP_, ret, size, s->size, flags, node); -kasan_kmalloc (s, ret, size); +kasan_kmalloc (s, ret, size, flags);return ret; }
@@ -3655,7 +3656,7 @@ size_t ksize(const void *object)size_t size =__ksize (object);/* We assume that ksize callers could use whole allocated area, so we need unpoison this area. */ -kasan_krealloc (object, size); +kasan_krealloc (object, size,GFP_NOWAIT );return size; }EXPORT_SYMBOL (ksize);
Signature · 7 lines
2.7.0.rc3.207.g0ac5344 -- 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>
Alexander Potapenko
reviewerPATCH v7 4/7 arch, ftrace: For KASAN put hard/soft IRQ entries into separate sections
In reply to Alexander PotapenkoKASAN needs to know whether the allocation happens in an IRQ handler. This lets us strip everything below the IRQ entry point to reduce the number of unique stack traces needed to be stored.
Move the definition of __irq_entry to <linux/interrupt.h> so that the users don't need to pull in <linux/ftrace.h>. Also introduce the __softirq_entry macro which is similar to __irq_entry, but puts the corresponding functions to the .softirqentry.text section.
---
v2: - per request from Steven Rostedt, moved the declarations of __softirq_entry and __irq_entry to <linux/interrupt.h>
v3: - minor description changes
v5: - fixed kbuild warnings in files that define __exception_irq_entry
--- arch/arm/include/asm/exception.h | 2 +- arch/arm/kernel/vmlinux.lds.S | 1 + arch/arm64/include/asm/exception.h | 2 +- arch/arm64/kernel/vmlinux.lds.S | 1 + arch/blackfin/kernel/vmlinux.lds.S | 1 + arch/c6x/kernel/vmlinux.lds.S | 1 + arch/metag/kernel/vmlinux.lds.S | 1 + arch/microblaze/kernel/vmlinux.lds.S | 1 + arch/mips/kernel/vmlinux.lds.S | 1 + arch/nios2/kernel/vmlinux.lds.S | 1 + arch/openrisc/kernel/vmlinux.lds.S | 1 + arch/parisc/kernel/vmlinux.lds.S | 1 + arch/powerpc/kernel/vmlinux.lds.S | 1 + arch/s390/kernel/vmlinux.lds.S | 1 + arch/sh/kernel/vmlinux.lds.S | 1 + arch/sparc/kernel/vmlinux.lds.S | 1 + arch/tile/kernel/vmlinux.lds.S | 1 + arch/x86/kernel/vmlinux.lds.S | 1 + include/asm-generic/vmlinux.lds.h | 12 +++++++++++- include/linux/ftrace.h | 11 ----------- include/linux/interrupt.h | 20 ++++++++++++++++++++ kernel/softirq.c | 2 +- kernel/trace/trace_functions_graph.c | 1 + 23 files changed, 51 insertions(+), 15 deletions(-)
arch/arm/include/asm/exception.h +1 -1
@@ -7,7 +7,7 @@#ifndef __ASM_ARM_EXCEPTION_H#define __ASM_ARM_EXCEPTION_H -#include <linux/ftrace.h> +#include <linux/interrupt.h> #define __exception __attribute__((section(".exception.text")))#ifdef CONFIG_FUNCTION_GRAPH_TRACER
arch/arm/kernel/vmlinux.lds.S +1 -0
@@ -108,6 +108,7 @@ SECTIONS *(.exception.text) __exception_text_end = .; IRQENTRY_TEXT + SOFTIRQENTRY_TEXT TEXT_TEXT SCHED_TEXT LOCK_TEXT
arch/arm64/include/asm/exception.h +1 -1
@@ -18,7 +18,7 @@#ifndef __ASM_EXCEPTION_H#define __ASM_EXCEPTION_H -#include <linux/ftrace.h> +#include <linux/interrupt.h> #define __exception __attribute__((section(".exception.text")))#ifdef CONFIG_FUNCTION_GRAPH_TRACER
arch/arm64/kernel/vmlinux.lds.S +1 -0
@@ -103,6 +103,7 @@ SECTIONS *(.exception.text) __exception_text_end = .; IRQENTRY_TEXT + SOFTIRQENTRY_TEXT TEXT_TEXT SCHED_TEXT LOCK_TEXT
arch/blackfin/kernel/vmlinux.lds.S +1 -0
@@ -35,6 +35,7 @@ SECTIONS #endif LOCK_TEXT IRQENTRY_TEXT + SOFTIRQENTRY_TEXT KPROBES_TEXT #ifdef CONFIG_ROMKERNEL __sinittext = .;
arch/c6x/kernel/vmlinux.lds.S +1 -0
@@ -72,6 +72,7 @@ SECTIONS SCHED_TEXT LOCK_TEXT IRQENTRY_TEXT + SOFTIRQENTRY_TEXT KPROBES_TEXT *(.fixup) *(.gnu.warning)
arch/metag/kernel/vmlinux.lds.S +1 -0
@@ -24,6 +24,7 @@ SECTIONS LOCK_TEXT KPROBES_TEXT IRQENTRY_TEXT + SOFTIRQENTRY_TEXT *(.text.*) *(.gnu.warning) }
arch/microblaze/kernel/vmlinux.lds.S +1 -0
@@ -36,6 +36,7 @@ SECTIONS { LOCK_TEXT KPROBES_TEXT IRQENTRY_TEXT + SOFTIRQENTRY_TEXT . = ALIGN (4) ; _etext = . ; }
arch/mips/kernel/vmlinux.lds.S +1 -0
@@ -58,6 +58,7 @@ SECTIONS LOCK_TEXT KPROBES_TEXT IRQENTRY_TEXT + SOFTIRQENTRY_TEXT *(.text.*) *(.fixup) *(.gnu.warning)
arch/nios2/kernel/vmlinux.lds.S +1 -0
@@ -39,6 +39,7 @@ SECTIONS SCHED_TEXT LOCK_TEXT IRQENTRY_TEXT + SOFTIRQENTRY_TEXT KPROBES_TEXT } =0 _etext = .;
arch/openrisc/kernel/vmlinux.lds.S +1 -0
@@ -50,6 +50,7 @@ SECTIONS LOCK_TEXT KPROBES_TEXT IRQENTRY_TEXT + SOFTIRQENTRY_TEXT *(.fixup) *(.text.__*) _etext = .;
arch/parisc/kernel/vmlinux.lds.S +1 -0
@@ -72,6 +72,7 @@ SECTIONS LOCK_TEXT KPROBES_TEXT IRQENTRY_TEXT + SOFTIRQENTRY_TEXT *(.text.do_softirq) *(.text.sys_exit) *(.text.do_sigaltstack)
arch/powerpc/kernel/vmlinux.lds.S +1 -0
@@ -55,6 +55,7 @@ SECTIONS LOCK_TEXT KPROBES_TEXT IRQENTRY_TEXT + SOFTIRQENTRY_TEXT #ifdef CONFIG_PPC32 *(.got1)
arch/s390/kernel/vmlinux.lds.S +1 -0
@@ -28,6 +28,7 @@ SECTIONS LOCK_TEXT KPROBES_TEXT IRQENTRY_TEXT + SOFTIRQENTRY_TEXT *(.fixup) *(.gnu.warning) } :text = 0x0700
arch/sh/kernel/vmlinux.lds.S +1 -0
@@ -39,6 +39,7 @@ SECTIONS LOCK_TEXT KPROBES_TEXT IRQENTRY_TEXT + SOFTIRQENTRY_TEXT *(.fixup) *(.gnu.warning) _etext = .; /* End of text section */
arch/sparc/kernel/vmlinux.lds.S +1 -0
@@ -48,6 +48,7 @@ SECTIONS LOCK_TEXT KPROBES_TEXT IRQENTRY_TEXT + SOFTIRQENTRY_TEXT *(.gnu.warning) } = 0 _etext = .;
arch/tile/kernel/vmlinux.lds.S +1 -0
@@ -45,6 +45,7 @@ SECTIONS LOCK_TEXT KPROBES_TEXT IRQENTRY_TEXT + SOFTIRQENTRY_TEXT __fix_text_end = .; /* tile-cpack won't rearrange before this */ ALIGN_FUNCTION(); *(.hottext*)
arch/x86/kernel/vmlinux.lds.S +1 -0
@@ -101,6 +101,7 @@ SECTIONS KPROBES_TEXT ENTRY_TEXT IRQENTRY_TEXT + SOFTIRQENTRY_TEXT *(.fixup) *(.gnu.warning) /* End of text section */
include/asm-generic/vmlinux.lds.h +11 -1
@@ -456,7 +456,7 @@ *(.entry.text) \ VMLINUX_SYMBOL(__entry_text_end) = .; -#ifdef CONFIG_FUNCTION_GRAPH_TRACER +#if defined(CONFIG_FUNCTION_GRAPH_TRACER ) || defined(CONFIG_KASAN )#define IRQENTRY_TEXT \ ALIGN_FUNCTION(); \ VMLINUX_SYMBOL(__irqentry_text_start) = .; \
@@ -466,6 +466,16 @@#define IRQENTRY_TEXT #endif +#if defined(CONFIG_FUNCTION_GRAPH_TRACER ) || defined(CONFIG_KASAN ) +#define SOFTIRQENTRY_TEXT \ + ALIGN_FUNCTION(); \ + VMLINUX_SYMBOL(__softirqentry_text_start) = .; \ + *(.softirqentry.text) \ + VMLINUX_SYMBOL(__softirqentry_text_end) = .; +#else +#define SOFTIRQENTRY_TEXT +#endif +/* Section used for early init (in .S files) */ #define HEAD_TEXT *(.head.text)
include/linux/ftrace.h +0 -11
@@ -811,16 +811,6 @@ ftrace_push_return_trace(unsigned long ret, unsigned long func, int *depth,*/ #define __notrace_funcgraph notrace -/* -* We want to which function is an entrypoint of a hardirq. -* That will help us to put a signal on output. -*/ -#define __irq_entry __attribute__((__section__(".irqentry.text"))) - -/* Limits of hardirq entrypoints */ -extern char __irqentry_text_start[]; -extern char __irqentry_text_end[]; -#define FTRACE_NOTRACE_DEPTH 65536#define FTRACE_RETFUNC_DEPTH 50#define FTRACE_RETSTACK_ALLOC_SIZE 32
@@ -857,7 +847,6 @@ static inline void unpause_graph_tracing(void)#else /* !CONFIG_FUNCTION_GRAPH_TRACER */ #define __notrace_funcgraph -#define __irq_entry#define INIT_FTRACE_GRAPH static inline void ftrace_graph_init_task (struct task_struct *t) { }
include/linux/interrupt.h +20 -0
@@ -673,4 +673,24 @@ extern int early_irq_init(void);extern int arch_probe_nr_irqs (void );extern int arch_early_irq_init (void ); +#if defined(CONFIG_FUNCTION_GRAPH_TRACER ) || defined(CONFIG_KASAN ) +/* +* We want to know which function is an entrypoint of a hardirq or a softirq. +*/ +#define __irq_entry __attribute__((__section__(".irqentry.text"))) +#define __softirq_entry \ + __attribute__((__section__(".softirqentry.text"))) + +/* Limits of hardirq entrypoints */ +extern char __irqentry_text_start[]; +extern char __irqentry_text_end[]; +/* Limits of softirq entrypoints */ +extern char __softirqentry_text_start[]; +extern char __softirqentry_text_end[]; + +#else +#define __irq_entry +#define __softirq_entry +#endif +#endif
kernel/softirq.c +1 -1
@@ -227,7 +227,7 @@ static inline bool lockdep_softirq_start(void) { return false; }static inline void lockdep_softirq_end (bool in_hardirq) { }#endif -asmlinkage __visible void __do_softirq(void) +asmlinkage __visible void __softirq_entry __do_softirq(void) {unsigned long end = jiffies +MAX_SOFTIRQ_TIME ;unsigned long old_flags = current->flags;
kernel/trace/trace_functions_graph.c +1 -0
@@ -8,6 +8,7 @@*/ #include <linux/uaccess.h> #include <linux/ftrace.h> +#include <linux/interrupt.h> #include <linux/slab.h> #include <linux/fs.h>
Signature · 7 lines
2.7.0.rc3.207.g0ac5344 -- 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>
Alexander Potapenko
reviewerPATCH v7 5/7 mm, kasan: Stackdepot implementation. Enable stackdepot for SLAB
In reply to Alexander PotapenkoImplement the stack depot and provide CONFIG_STACKDEPOT. Stack depot will allow KASAN store allocation/deallocation stack traces for memory chunks. The stack traces are stored in a hash table and referenced by handles which reside in the kasan_alloc_meta and kasan_free_meta structures in the allocated memory chunks.
IRQ stack traces are cut below the IRQ entry point to avoid unnecessary duplication.
Right now stackdepot support is only enabled in SLAB allocator. Once KASAN features in SLAB are on par with those in SLUB we can switch SLUB to stackdepot as well, thus removing the dependency on SLUB stack bookkeeping, which wastes a lot of memory.
This patch is based on the "mm: kasan: stack depots" patch originally prepared by Dmitry Chernenkov.
---
v2: - per request from Joonsoo Kim, moved the stackdepot implementation to lib/, as there's a plan to use it for page owner
- added copyright comments
- added comments about smp_load_acquire()/smp_store_release()
v3: - minor description changes
v5: - decreased STACK_ALLOC_ORDER to reduce fragmentation
- declared CONFIG_STACKDEPOT
- replaced __memcpy() with memcpy()
- simplified GFP flags
v6: - renamed depot_stack_handle to depot_stack_handle_t
- made CONFIG_STACKDEPOT depend on CONFIG_STACKTRACE (fix kbuild errors)
- made depot_save_stack() allocate memory via vmalloc() when possible
- added a reentrancy flag to avoid saving stacks from recursivedepot_save_stack() calls
- simplified kasan_track (dropped CPU number and allocation time)
v7: - fixed kbuild errors (made several functions SLAB-only)
--- arch/x86/kernel/Makefile | 1 + include/linux/stackdepot.h | 32 +++++ lib/Kconfig | 4 + lib/Kconfig.kasan | 1 + lib/Makefile | 3 + lib/stackdepot.c | 301 +++++++++++++++++++++++++++++++++++++++++++++ mm/kasan/kasan.c | 55 ++++++++- mm/kasan/kasan.h | 11 +- mm/kasan/report.c | 12 +- 9 files changed, 408 insertions(+), 12 deletions(-)
arch/x86/kernel/Makefile +1 -0
@@ -19,6 +19,7 @@ endif KASAN_SANITIZE_head$(BITS).o := n KASAN_SANITIZE_dumpstack.o := n KASAN_SANITIZE_dumpstack_$(BITS).o := n +KASAN_SANITIZE_stacktrace.o := n OBJECT_FILES_NON_STANDARD_head_$(BITS).o := y OBJECT_FILES_NON_STANDARD_relocate_kernel_$(BITS).o := y
include/linux/stackdepot.h new file +32 -0
@@ -0,0 +1,32 @@ +/* +* A generic stack depot implementation +* +* Author: Alexander Potapenko <[email protected]> +* Copyright (C) 2016 Google, Inc. +* +* Based on code by Dmitry Chernenkov. +* +* This program is free software; you can redistribute it and/or modify +* it under the terms of the GNU General Public License as published by +* the Free Software Foundation; either version 2 of the License, or +* (at your option) any later version. +* +* This program is distributed in the hope that it will be useful, +* but WITHOUT ANY WARRANTY; without even the implied warranty of +* MERCHANTABILITY or FITNESS FOR A PARTICULAR PURPOSE. See the +* GNU General Public License for more details. +* +*/ + +#ifndef _LINUX_STACKDEPOT_H +#define _LINUX_STACKDEPOT_H + +typedef u32 depot_stack_handle_t ; + +struct stack_trace ; + +depot_stack_handle_t depot_save_stack (struct stack_trace *trace,gfp_t flags); + +void depot_fetch_stack (depot_stack_handle_t handle,struct stack_trace *trace); + +#endif
lib/Kconfig +4 -0
@@ -543,4 +543,8 @@ config ARCH_HAS_PMEM_API config ARCH_HAS_MMIO_FLUSH bool +config STACKDEPOT + bool + select STACKTRACE + endmenu
lib/Kconfig.kasan +1 -0
@@ -7,6 +7,7 @@ config KASAN bool "KASan: runtime memory debugger" depends on SLUB_DEBUG || (SLAB && !DEBUG_SLAB) select CONSTRUCTORS + select STACKDEPOT if SLAB help Enables kernel address sanitizer - runtime memory debugger, designed to find out-of-bounds accesses and use-after-free bugs.
lib/Makefile +3 -0
@@ -182,6 +182,9 @@ obj-$(CONFIG_SG_SPLIT) += sg_split.o obj-$(CONFIG_STMP_DEVICE) += stmp_device.o obj-$(CONFIG_IRQ_POLL) += irq_poll.o +obj-$(CONFIG_STACKDEPOT) += stackdepot.o +KASAN_SANITIZE_stackdepot.o := n + libfdt_files = fdt.o fdt_ro.o fdt_wip.o fdt_rw.o fdt_sw.o fdt_strerror.o \ fdt_empty_tree.o $(foreach file, $(libfdt_files), \
lib/stackdepot.c new file +301 -0
301 lines not on this page · raw message
mm/kasan/kasan.c +49 -6
93 lines not on this page · raw message
mm/kasan/kasan.h +7 -4
@@ -2,6 +2,7 @@#define __MM_KASAN_KASAN_H#include <linux/kasan.h> +#include <linux/stackdepot.h> #define KASAN_SHADOW_SCALE_SIZE (1UL << KASAN_SHADOW_SCALE_SHIFT)#define KASAN_SHADOW_MASK (KASAN_SHADOW_SCALE_SIZE - 1)
@@ -64,16 +65,18 @@ enum kasan_state {KASAN_STATE_FREE }; +#define KASAN_STACK_DEPTH 64 +struct kasan_track { -u64 cpu :6 ;/* for NR_CPUS = 64 */ -u64 pid :16 ;/* 65536 processes */ -u64 when :42 ;/* ~140 years */ +u32 pid; +depot_stack_handle_t stack; };struct kasan_alloc_meta { +struct kasan_track track;u32 state :2 ;/* enum kasan_state */ u32 alloc_size :30 ; -struct kasan_track track; +u32 reserved; };struct kasan_free_meta {
mm/kasan/report.c +10 -2
@@ -18,6 +18,7 @@#include <linux/printk.h> #include <linux/sched.h> #include <linux/slab.h> +#include <linux/stackdepot.h> #include <linux/stacktrace.h> #include <linux/string.h> #include <linux/types.h>
@@ -118,8 +119,15 @@ static inline bool init_task_stack_addr(const void *addr)#ifdef CONFIG_SLAB static void print_track (struct kasan_track *track) { -pr_err ("PID = %u, CPU = %u, timestamp = %lu\n" , track->pid, - track->cpu, (unsigned long )track->when); +pr_err ("PID = %u\n" , track->pid); +if (track->stack) { +struct stack_trace trace; + +depot_fetch_stack (track->stack, &trace); +print_stack_trace (&trace,0 ); + }else { +pr_err ("(stack is not available)\n" ); + } } static void object_err(struct kmem_cache *cache, struct page *page,
Signature · 7 lines
2.7.0.rc3.207.g0ac5344 -- 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>
Andrey Ryabinin
maintainerRe: [PATCH v7 5/7] mm, kasan: Stackdepot implementation. Enable stackdepot for SLAB
2016-03-14 13:43 GMT+03:00 Alexander Potapenko <[email protected]>:
lib/stackdepot.c line 216
Quoted 6 lines
lib/stackdepot.c new file +6
-0+ + rec =this_cpu_ptr (&depot_recursion); +/* Don't store the stack if we've been called recursively. */ +if (unlikely (*rec)) + goto fast_exit; + *rec = true;
This just can't work. As long as preemption enabled, task could migrate on another cpu anytime.
You could use per-task flag, although it's possible to miss some in-irq stacktraces:
depot_save_stack()
if (current->stackdeport_recursion)
goto fast_exit;
current->stackdepot_recursion++
<IRQ>
....
depot_save_stack()
if (current->stackdeport_recursion)
goto fast_exit;lib/stackdepot.c line 254
Quoted 13 lines
lib/stackdepot.c new file +13
-0+if (unlikely (!smp_load_acquire (&next_slab_inited))) { +/* Zero out zone modifiers, as we don't have specific zone +* requirements. Keep the flags related to allocation in atomic +* contexts and I/O. +*/ + alloc_flags &= ~GFP_ZONEMASK ; + alloc_flags &= (GFP_ATOMIC |GFP_KERNEL ); +/* When possible, allocate using vmalloc() to reduce physical +* address space fragmentation. vmalloc() doesn't work if +* kmalloc caches haven't been initialized or if it's being +* called from an interrupt handler. +*/ +if (kmalloc_caches[KMALLOC_SHIFT_HIGH ] && !in_interrupt ()) {
This is clearly a wrong way to check whether is slab available or not. Besides you need to checkvmalloc() for availability, not slab.
Given that STAC_ALLOC_ORDER is 2 now, I think it should be fine to use alloc_pages() all the time.
Or fix condition, up to you.
lib/stackdepot.c line 263
Quoted 9 lines
lib/stackdepot.c new file +9
-0+ prealloc =__vmalloc ( +STACK_ALLOC_SIZE , alloc_flags,PAGE_KERNEL ); + }else { + page =alloc_pages (alloc_flags,STACK_ALLOC_ORDER ); +if (page) + prealloc =page_address (page); + } + } +
--
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>
Alexander Potapenko
reviewerRe: [PATCH v7 5/7] mm, kasan: Stackdepot implementation. Enable stackdepot for SLAB
Quoted 10 lines · Andrey Ryabinin
2016-03-14 13:43 GMT+03:00 Alexander Potapenko <[email protected]>:
lib/stackdepot.c line 216
Quoted 6 lines
lib/stackdepot.c new file +6
-0+ + rec =this_cpu_ptr (&depot_recursion); +/* Don't store the stack if we've been called recursively. */ +if (unlikely (*rec)) + goto fast_exit; + *rec = true;
Quoted 2 lines
This just can't work. As long as preemption enabled, task could migrate on another cpu anytime.
Ah, you're right.
Do you think disabling preemption around memory allocation is an option here?
Quoted 29 lines
You could use per-task flag, although it's possible to miss some in-irq stacktraces:
depot_save_stack()if (current->stackdeport_recursion) goto fast_exit; current->stackdepot_recursion++ <IRQ> .... depot_save_stack() if (current->stackdeport_recursion) goto fast_exit;lib/stackdepot.c line 254
Quoted 13 lines
lib/stackdepot.c new file +13
-0+if (unlikely (!smp_load_acquire (&next_slab_inited))) { +/* Zero out zone modifiers, as we don't have specific zone +* requirements. Keep the flags related to allocation in atomic +* contexts and I/O. +*/ + alloc_flags &= ~GFP_ZONEMASK ; + alloc_flags &= (GFP_ATOMIC |GFP_KERNEL ); +/* When possible, allocate using vmalloc() to reduce physical +* address space fragmentation. vmalloc() doesn't work if +* kmalloc caches haven't been initialized or if it's being +* called from an interrupt handler. +*/ +if (kmalloc_caches[KMALLOC_SHIFT_HIGH ] && !in_interrupt ()) {
Quoted 1 line
This is clearly a wrong way to check whether is slab available or not.
Well, I don't think either vmalloc() or kmalloc() provide any interface to check if they are available.
Quoted 2 lines
Besides you need to check
vmalloc()for availability, not slab.
The problem was in kmalloc caches being unavailable, although I can imagine other problems could have arose.
Perhaps we can drill a hole to get the value of vmap_initialized?
Quoted 3 lines
Given that
STAC_ALLOC_ORDERis 2 now, I think it should be fine to usealloc_pages()all the time.
Or fix condition, up to you.
Ok, I'm going to drop vmalloc() for now, we can always implement this later. Note that this also removes the necessity to check for recursion.
Quoted 9 lines
Quoted 9 lines
lib/stackdepot.c new file +9
-0+ prealloc =__vmalloc ( +STACK_ALLOC_SIZE , alloc_flags,PAGE_KERNEL ); + }else { + page =alloc_pages (alloc_flags,STACK_ALLOC_ORDER ); +if (page) + prealloc =page_address (page); + } + } +
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>
Andrey Ryabinin
maintainerRe: [PATCH v7 5/7] mm, kasan: Stackdepot implementation. Enable stackdepot for SLAB
2016-03-15 12:27 GMT+03:00 Alexander Potapenko <[email protected]>:
Quoted 11 lines · Andrey Ryabinin
Quoted 10 lines
2016-03-14 13:43 GMT+03:00 Alexander Potapenko <[email protected]>:
Quoted 6 lines
lib/stackdepot.c new file +6
-0+ + rec =this_cpu_ptr (&depot_recursion); +/* Don't store the stack if we've been called recursively. */ +if (unlikely (*rec)) + goto fast_exit; + *rec = true;
Quoted 4 lines
Quoted 2 lines
This just can't work. As long as preemption enabled, task could migrate on another cpu anytime.
Ah, you're right.
Do you think disabling preemption around memory allocation is an option here?
It's definitely not an option. Flag on current doesn't have any disadvantage over per-cpu approach
and it doesn't require preemption safe context.
However, making the allocation in a separate context would be a better way to eliminate recursion.
i.e. instead of allocating memory depot_save_stack() kicks a work which allocates memory.
Quoted 59 lines
Quoted 29 lines
You could use per-task flag, although it's possible to miss some in-irq stacktraces:
depot_save_stack()if (current->stackdeport_recursion) goto fast_exit; current->stackdepot_recursion++ <IRQ> .... depot_save_stack() if (current->stackdeport_recursion) goto fast_exit;lib/stackdepot.c line 254
Quoted 13 lines
lib/stackdepot.c new file +13
-0+if (unlikely (!smp_load_acquire (&next_slab_inited))) { +/* Zero out zone modifiers, as we don't have specific zone +* requirements. Keep the flags related to allocation in atomic +* contexts and I/O. +*/ + alloc_flags &= ~GFP_ZONEMASK ; + alloc_flags &= (GFP_ATOMIC |GFP_KERNEL ); +/* When possible, allocate using vmalloc() to reduce physical +* address space fragmentation. vmalloc() doesn't work if +* kmalloc caches haven't been initialized or if it's being +* called from an interrupt handler. +*/ +if (kmalloc_caches[KMALLOC_SHIFT_HIGH ] && !in_interrupt ()) {Quoted 1 line
This is clearly a wrong way to check whether is slab available or not.
Well, I don't think either
vmalloc()orkmalloc()provide any interface to check if they are available.Quoted 2 lines
Besides you need to check
vmalloc()for availability, not slab.The problem was in kmalloc caches being unavailable, although I can imagine other problems could have arose.
Perhaps we can drill a hole to get the value ofvmap_initialized?Quoted 3 lines
Given that
STAC_ALLOC_ORDERis 2 now, I think it should be fine to usealloc_pages()all the time.
Or fix condition, up to you.Ok, I'm going to drop
vmalloc()for now, we can always implement this later. Note that this also removes the necessity to check for recursion.Quoted 9 lines
lib/stackdepot.c line 263
Quoted 9 lines
lib/stackdepot.c new file +9
-0+ prealloc =__vmalloc ( +STACK_ALLOC_SIZE , alloc_flags,PAGE_KERNEL ); + }else { + page =alloc_pages (alloc_flags,STACK_ALLOC_ORDER ); +if (page) + prealloc =page_address (page); + } + } +--
Alexander Potapenko
Software Engineer
Quoted 7 lines
Google Germany GmbH
Erika-Mann-Straße, 33
80636 MünchenGeschä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>
Alexander Potapenko
reviewerRe: [PATCH v7 5/7] mm, kasan: Stackdepot implementation. Enable stackdepot for SLAB
Quoted 17 lines · Andrey Ryabinin
2016-03-15 12:27 GMT+03:00 Alexander Potapenko <[email protected]>:
Quoted 11 lines · Andrey Ryabinin
Quoted 10 lines
2016-03-14 13:43 GMT+03:00 Alexander Potapenko <[email protected]>:
Quoted 6 lines
lib/stackdepot.c new file +6
-0+ + rec =this_cpu_ptr (&depot_recursion); +/* Don't store the stack if we've been called recursively. */ +if (unlikely (*rec)) + goto fast_exit; + *rec = true;Quoted 4 lines
Quoted 2 lines
This just can't work. As long as preemption enabled, task could migrate on another cpu anytime.
Ah, you're right.
Do you think disabling preemption around memory allocation is an option here?
Quoted 7 lines
It's definitely not an option. Flag on current doesn't have any disadvantage over per-cpu approach
and it doesn't require preemption safe context.
However, making the allocation in a separate context would be a better way to eliminate recursion.
i.e. instead of allocating memorydepot_save_stack()kicks a work which allocates memory.
For the record, I've removed the vmalloc code and reinstated alloc_pages(), so that there's no more recursion.
Making the allocation in a separate worker will remove the recursion, but may complicate the synchronization.
I'd refrain from that since we don't have problems with recursion right now.
Quoted 67 lines
Quoted 67 lines
Quoted 30 lines
You could use per-task flag, although it's possible to miss some in-irq stacktraces:
depot_save_stack()if (current->stackdeport_recursion) goto fast_exit; current->stackdepot_recursion++ <IRQ> .... depot_save_stack() if (current->stackdeport_recursion) goto fast_exit;lib/stackdepot.c line 254
Quoted 13 lines
lib/stackdepot.c new file +13
-0+if (unlikely (!smp_load_acquire (&next_slab_inited))) { +/* Zero out zone modifiers, as we don't have specific zone +* requirements. Keep the flags related to allocation in atomic +* contexts and I/O. +*/ + alloc_flags &= ~GFP_ZONEMASK ; + alloc_flags &= (GFP_ATOMIC |GFP_KERNEL ); +/* When possible, allocate using vmalloc() to reduce physical +* address space fragmentation. vmalloc() doesn't work if +* kmalloc caches haven't been initialized or if it's being +* called from an interrupt handler. +*/ +if (kmalloc_caches[KMALLOC_SHIFT_HIGH ] && !in_interrupt ()) {Quoted 1 line
This is clearly a wrong way to check whether is slab available or not.
Well, I don't think either
vmalloc()orkmalloc()provide any interface to check if they are available.Quoted 2 lines
Besides you need to check
vmalloc()for availability, not slab.The problem was in kmalloc caches being unavailable, although I can imagine other problems could have arose.
Perhaps we can drill a hole to get the value ofvmap_initialized?Quoted 3 lines
Given that
STAC_ALLOC_ORDERis 2 now, I think it should be fine to usealloc_pages()all the time.
Or fix condition, up to you.Ok, I'm going to drop
vmalloc()for now, we can always implement this later. Note that this also removes the necessity to check for recursion.Quoted 9 lines
lib/stackdepot.c line 263
Quoted 9 lines
lib/stackdepot.c new file +9
-0+ prealloc =__vmalloc ( +STACK_ALLOC_SIZE , alloc_flags,PAGE_KERNEL ); + }else { + page =alloc_pages (alloc_flags,STACK_ALLOC_ORDER ); +if (page) + prealloc =page_address (page); + } + } +--
Alexander Potapenko
Software EngineerGoogle Germany GmbH
Erika-Mann-Straße, 33
80636 MünchenGeschäftsführer: Matthew Scott Sucherman, Paul Terence Manicle Registergericht und -nummer: Hamburg, HRB 86891
Sitz der Gesellschaft: Hamburg
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>
Re: [PATCH v7 5/7] mm, kasan: Stackdepot implementation. Enable stackdepot for SLAB
In reply to Alexander PotapenkoQuoted 14 lines · Alexander Potapenko
lib/stackdepot.c new file +14
-0+depot_stack_handle_t depot_save_stack (struct stack_trace *trace, +gfp_t alloc_flags) +{ +u32 hash; +depot_stack_handle_t retval =0 ; +struct stack_record *found =NULL , **bucket; +unsigned long flags; +struct page *page =NULL ; +void *prealloc =NULL ; +bool *rec; + +if (unlikely (trace->nr_entries ==0 )) + goto fast_exit; +
lib/stackdepot.c line 221
Quoted 10 lines
lib/stackdepot.c new file +10
-0+ rec =this_cpu_ptr (&depot_recursion); +/* Don't store the stack if we've been called recursively. */ +if (unlikely (*rec)) + goto fast_exit; + *rec = true; + + hash =hash_stack (trace->entries, trace->nr_entries); +/* Bad luck, we won't store this stack. */ +if (hash ==0 ) + goto exit;
Hello,
why is hash == 0 skipped?
Thanks.
--
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>
Alexander Potapenko
reviewerRe: [PATCH v7 5/7] mm, kasan: Stackdepot implementation. Enable stackdepot for SLAB
Quoted 28 lines · Joonsoo Kim
Quoted 14 lines · Alexander Potapenko
lib/stackdepot.c new file +14
-0+depot_stack_handle_t depot_save_stack (struct stack_trace *trace, +gfp_t alloc_flags) +{ +u32 hash; +depot_stack_handle_t retval =0 ; +struct stack_record *found =NULL , **bucket; +unsigned long flags; +struct page *page =NULL ; +void *prealloc =NULL ; +bool *rec; + +if (unlikely (trace->nr_entries ==0 )) + goto fast_exit; +lib/stackdepot.c line 221
Quoted 10 lines
lib/stackdepot.c new file +10
-0+ rec =this_cpu_ptr (&depot_recursion); +/* Don't store the stack if we've been called recursively. */ +if (unlikely (*rec)) + goto fast_exit; + *rec = true; + + hash =hash_stack (trace->entries, trace->nr_entries); +/* Bad luck, we won't store this stack. */ +if (hash ==0 ) + goto exit;Hello,
Quoted 3 lines
why is hash == 0 skipped?
Thanks.
We have to keep a special value to distinguish allocations for which we don't have the stack trace for some reason.
Making 0 such a value seems natural.
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>
Alexander Potapenko
reviewerRe: [PATCH v7 5/7] mm, kasan: Stackdepot implementation. Enable stackdepot for SLAB
Quoted 29 lines · Alexander Potapenko
Quoted 28 lines · Joonsoo Kim
Quoted 14 lines · Alexander Potapenko
lib/stackdepot.c new file +14
-0+depot_stack_handle_t depot_save_stack (struct stack_trace *trace, +gfp_t alloc_flags) +{ +u32 hash; +depot_stack_handle_t retval =0 ; +struct stack_record *found =NULL , **bucket; +unsigned long flags; +struct page *page =NULL ; +void *prealloc =NULL ; +bool *rec; + +if (unlikely (trace->nr_entries ==0 )) + goto fast_exit; +lib/stackdepot.c line 221
Quoted 10 lines
lib/stackdepot.c new file +10
-0+ rec =this_cpu_ptr (&depot_recursion); +/* Don't store the stack if we've been called recursively. */ +if (unlikely (*rec)) + goto fast_exit; + *rec = true; + + hash =hash_stack (trace->entries, trace->nr_entries); +/* Bad luck, we won't store this stack. */ +if (hash ==0 ) + goto exit;Hello,
Quoted 6 lines
Quoted 3 lines
why is hash == 0 skipped?
Thanks.
We have to keep a special value to distinguish allocations for which we don't have the stack trace for some reason.
Making 0 such a value seems natural.
Well, the above statement is false.
Because we only compare the hash to the records that are already in the depot, there's no point in reserving this value.
Quoted 12 lines
--
Alexander Potapenko
Software EngineerGoogle Germany GmbH
Erika-Mann-Straße, 33
80636 MünchenGeschäftsführer: Matthew Scott Sucherman, Paul Terence Manicle Registergericht und -nummer: Hamburg, HRB 86891
Sitz der Gesellschaft: Hamburg
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>
Re: [PATCH v7 5/7] mm, kasan: Stackdepot implementation. Enable stackdepot for SLAB
Quoted 32 lines · Alexander Potapenko
Quoted 31 lines · Alexander Potapenko
Quoted 30 lines · Joonsoo Kim
Quoted 14 lines · Alexander Potapenko
lib/stackdepot.c new file +14
-0+depot_stack_handle_t depot_save_stack (struct stack_trace *trace, +gfp_t alloc_flags) +{ +u32 hash; +depot_stack_handle_t retval =0 ; +struct stack_record *found =NULL , **bucket; +unsigned long flags; +struct page *page =NULL ; +void *prealloc =NULL ; +bool *rec; + +if (unlikely (trace->nr_entries ==0 )) + goto fast_exit; +lib/stackdepot.c line 221
Quoted 10 lines
lib/stackdepot.c new file +10
-0+ rec =this_cpu_ptr (&depot_recursion); +/* Don't store the stack if we've been called recursively. */ +if (unlikely (*rec)) + goto fast_exit; + *rec = true; + + hash =hash_stack (trace->entries, trace->nr_entries); +/* Bad luck, we won't store this stack. */ +if (hash ==0 ) + goto exit;Hello,
why is hash == 0 skipped?
Quoted 7 lines
Quoted 4 lines
Quoted 1 line
Thanks.
We have to keep a special value to distinguish allocations for which we don't have the stack trace for some reason.
Making 0 such a value seems natural.Well, the above statement is false.
Because we only compare the hash to the records that are already in the depot, there's no point in reserving this value.
So, could you make a patch for it?
Thanks.
--
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>
Alexander Potapenko
reviewerPATCH v7 6/7 kasan: Test fix: Warn if the UAF could not be detected in kmalloc_uaf2
In reply to Alexander Potapenko--- lib/test_kasan.c | 2 ++ 1 file changed, 2 insertions(+)
lib/test_kasan.c +2 -0
@@ -294,6 +294,8 @@ static noinline void __init kmalloc_uaf2(void) } ptr1[40 ] ='x' ; +if (ptr1 == ptr2) +pr_err ("Could not detect use-after-free: ptr1 == ptr2\n" );kfree (ptr2); }
Signature · 7 lines
2.7.0.rc3.207.g0ac5344 -- 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>
Alexander Potapenko
reviewerPATCH v7 7/7 mm: kasan: Initial memory quarantine implementation
In reply to Alexander PotapenkoQuarantine isolates freed objects in a separate queue. The objects are returned to the allocator later, which helps to detect use-after-free errors.
Freed objects are first added to per-cpu quarantine queues. When a cache is destroyed or memory shrinking is requested, the objects are moved into the global quarantine queue. Whenever a kmalloc call allows memory reclaiming, the oldest objects are popped out of the global queue until the total size of objects in quarantine is less than 3/4 of the maximum quarantine size (which is a fraction of installed physical memory).
As long as an object remains in the quarantine, KASAN is able to report accesses to it, so the chance of reporting a use-after-free is increased. Once the object leaves quarantine, the allocator may reuse it, in which case the object is unpoisoned and KASAN can't detect incorrect accesses to it.
Right now quarantine support is only enabled in SLAB allocator. Unification of KASAN features in SLAB and SLUB will be done later.
This patch is based on the "mm: kasan: quarantine" patch originally prepared by Dmitry Chernenkov.
---
v2: - added copyright comments
- per request from Joonsoo Kim made __cache_free() more straightforward
- added comments for smp_load_acquire()/smp_store_release()
v3: - incorporate changes introduced by the "mm, kasan: SLAB support" patch
v4: - fix kbuild compile-time error (missing ___cache_free() declaration)
and a warning (wrong format specifier)
v6: - extended the patch description
- dropped the unused qlist_remove() function
--- include/linux/kasan.h | 30 ++++-- lib/test_kasan.c | 29 +++++ mm/kasan/Makefile | 4 + mm/kasan/kasan.c | 71 +++++++++++-- mm/kasan/kasan.h | 11 +- mm/kasan/quarantine.c | 289 ++++++++++++++++++++++++++++++++++++++++++++++++++ mm/kasan/report.c | 1 + mm/mempool.c | 7 +- mm/page_alloc.c | 2 +- mm/slab.c | 15 ++- mm/slab.h | 2 + mm/slab_common.c | 2 + mm/slub.c | 4 +- 13 files changed, 438 insertions(+), 29 deletions(-)
include/linux/kasan.h +22 -8
@@ -44,24 +44,29 @@ static inline void kasan_disable_current(void)void kasan_unpoison_shadow (const void *address,size_t size);void kasan_alloc_pages (struct page *page,unsigned int order); -void kasan_free_pages (struct page *page,unsigned int order); +void kasan_poison_free_pages (struct page *page,unsigned int order);void kasan_cache_create (struct kmem_cache *cache,size_t *size,unsigned long *flags); +void kasan_cache_shrink (struct kmem_cache *cache); +void kasan_cache_destroy (struct kmem_cache *cache);void kasan_poison_slab (struct page *page);void kasan_unpoison_object_data (struct kmem_cache *cache,void *object);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_large (const 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); -void kasan_slab_free (struct kmem_cache *s,void *object); +/* kasan_slab_free() returns true if the object has been put into quarantine. +*/ +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;
@@ -79,11 +84,14 @@ static inline void kasan_enable_current(void) {}static inline void kasan_disable_current (void ) {}static inline void kasan_alloc_pages (struct page *page,unsigned int order) {} -static inline void kasan_free_pages (struct page *page,unsigned int order) {} +static inline void kasan_poison_free_pages (struct page *page, +unsigned int order) {}static inline void kasan_cache_create (struct kmem_cache *cache,size_t *size,unsigned long *flags) {} +static inline void kasan_cache_shrink (struct kmem_cache *cache) {} +static inline void kasan_cache_destroy (struct kmem_cache *cache) {}static inline void kasan_poison_slab (struct page *page) {} static inline void kasan_unpoison_object_data(struct kmem_cache *cache,
@@ -92,8 +100,8 @@ static inline void kasan_poison_object_data(struct kmem_cache *cache, void *object) {}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_large (const 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,
@@ -101,7 +109,13 @@ static inline void kasan_krealloc(const void *object, size_t new_size,static inline void kasan_slab_alloc (struct kmem_cache *s,void *object,gfp_t flags) {} -static inline void kasan_slab_free (struct kmem_cache *s,void *object) {} +/* kasan_slab_free() returns true if the object has been put into quarantine. +*/ +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) {}
lib/test_kasan.c +29 -0
@@ -344,6 +344,32 @@ static noinline void __init kasan_stack_oob(void) *(volatile char *)p; } +#ifdef CONFIG_SLAB +static noinline void __init kasan_quarantine_cache(void) +{ +struct kmem_cache *cache =kmem_cache_create ( +"test" ,137 ,8 ,GFP_KERNEL ,NULL ); +int i; + +for (i =0 ; i <100 ; i++) { +void *p =kmem_cache_alloc (cache,GFP_KERNEL ); + +kmem_cache_free (cache, p); + p =kmalloc (sizeof (u64),GFP_KERNEL ); +kfree (p); + } +kmem_cache_shrink (cache); +for (i =0 ; i <100 ; i++) { +u64 *p =kmem_cache_alloc (cache,GFP_KERNEL ); + +kmem_cache_free (cache, p); + p =kmalloc (sizeof (u64),GFP_KERNEL ); +kfree (p); + } +kmem_cache_destroy (cache); +} +#endif +static int __init kmalloc_tests_init (void ) {kmalloc_oob_right ();
@@ -367,6 +393,9 @@ static int __init kmalloc_tests_init(void)kmem_cache_oob ();kasan_stack_oob ();kasan_global_oob (); +#ifdef CONFIG_SLAB +kasan_quarantine_cache (); +#endif return -EAGAIN ; }
mm/kasan/Makefile +4 -0
@@ -8,3 +8,7 @@ CFLAGS_REMOVE_kasan.o = -pg CFLAGS_kasan.o := $(call cc-option, -fno-conserve-stack -fno-stack-protector) obj-y := kasan.o report.o kasan_init.o + +ifdef CONFIG_SLAB + obj-y += quarantine.o +endif
mm/kasan/kasan.c +60 -11
124 lines not on this page · raw message
mm/kasan/kasan.h +9 -2
@@ -62,6 +62,7 @@ struct kasan_global {enum kasan_state {KASAN_STATE_INIT ,KASAN_STATE_ALLOC , +KASAN_STATE_QUARANTINE ,KASAN_STATE_FREE };
@@ -80,8 +81,10 @@ struct kasan_alloc_meta { };struct kasan_free_meta { -/* Allocator freelist pointer, unused by KASAN. */ -void **freelist; +/* This field is used while the object is in the quarantine. +* Otherwise it might be used for the allocator freelist. +*/ +void **quarantine_link;struct kasan_track track; };
@@ -105,4 +108,8 @@ static inline bool kasan_report_enabled(void)void kasan_report (unsigned long addr,size_t size,bool is_write,unsigned long ip); +void quarantine_put (struct kasan_free_meta *info,struct kmem_cache *cache); +void quarantine_reduce (void ); +void quarantine_remove_cache (struct kmem_cache *cache); +#endif
mm/kasan/quarantine.c new file +289 -0
289 lines not on this page · raw message
mm/kasan/report.c +1 -0
@@ -151,6 +151,7 @@ static void object_err(struct kmem_cache *cache, struct page *page,print_track (&alloc_info->track);break ;case KASAN_STATE_FREE : +case KASAN_STATE_QUARANTINE :pr_err ("Object freed, allocated with size %u bytes\n" , alloc_info->alloc_size); free_info =get_free_info (cache, object);
mm/mempool.c +4 -3
@@ -105,11 +105,12 @@ 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_slab_free (pool->pool_data, element); +kasan_poison_slab_free (pool->pool_data, element);if (pool->alloc == mempool_kmalloc) -kasan_kfree (element); +kasan_poison_kfree (element);if (pool->alloc == mempool_alloc_pages) -kasan_free_pages (element, (unsigned long )pool->pool_data); +kasan_poison_free_pages (element, + (unsigned long )pool->pool_data); }static void kasan_unpoison_element (mempool_t *pool,void *element,gfp_t flags)
mm/page_alloc.c +1 -1
@@ -1005,7 +1005,7 @@ static bool free_pages_prepare(struct page *page, unsigned int order)trace_mm_page_free (page, order);kmemcheck_free_shadow (page, order); -kasan_free_pages (page, order); +kasan_poison_free_pages (page, order);if (PageAnon (page)) page->mapping =NULL ;
mm/slab.c +13 -2
@@ -3336,9 +3336,20 @@ free_done:static inline void __cache_free (struct kmem_cache *cachep,void *objp,unsigned long caller) { -struct array_cache *ac =cpu_cache_get (cachep); +#ifdef CONFIG_KASAN +if (kasan_slab_free (cachep, objp)) +/* The object has been put into the quarantine, don't touch it +* for now. +*/ +return ; +#endif +___cache_free (cachep, objp, caller); +} -kasan_slab_free (cachep, objp); +void ___cache_free (struct kmem_cache *cachep,void *objp, +unsigned long caller) +{ +struct array_cache *ac =cpu_cache_get (cachep);check_irq_off ();kmemleak_free_recursive (objp, cachep->flags);
mm/slab.h +2 -0
@@ -462,4 +462,6 @@ void *slab_next(struct seq_file *m, void *p, loff_t *pos);void slab_stop (struct seq_file *m,void *p);int memcg_slab_show (struct seq_file *m,void *p); +void ___cache_free (struct kmem_cache *cache,void *x,unsigned long addr); +#endif /* MM_SLAB_H */
mm/slab_common.c +2 -0
@@ -715,6 +715,7 @@ void kmem_cache_destroy(struct kmem_cache *s)get_online_cpus ();get_online_mems (); +kasan_cache_destroy (s);mutex_lock (&slab_mutex); s->refcount--;
@@ -753,6 +754,7 @@ int kmem_cache_shrink(struct kmem_cache *cachep)get_online_cpus ();get_online_mems (); +kasan_cache_shrink (cachep); ret =__kmem_cache_shrink (cachep, false);put_online_mems ();put_online_cpus ();
mm/slub.c +2 -2
@@ -1324,7 +1324,7 @@ static inline void kmalloc_large_node_hook(void *ptr, size_t size, gfp_t flags)static inline void kfree_hook (const void *x) {kmemleak_free (x); -kasan_kfree_large (x); +kasan_poison_kfree_large (x); }static inline void slab_free_hook (struct kmem_cache *s,void *x)
@@ -1349,7 +1349,7 @@ static inline void slab_free_hook(struct kmem_cache *s, void *x)if (!(s->flags &SLAB_DEBUG_OBJECTS ))debug_check_no_obj_freed (x, s->object_size); -kasan_slab_free (s, x); +kasan_poison_slab_free (s, x); } static inline void slab_free_freelist_hook(struct kmem_cache *s,
Signature · 7 lines
2.7.0.rc3.207.g0ac5344 -- 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>