Skip to contentSkip to conversation

SLAB support for KASAN

PATCH Superseded Alexander Potapenko 3 participants 21 messages Mar 9, 2016 – Mar 11, 2016

Subsystems (21)

v4 of 8 Superseded Latest is v8 →

lore
Share & tools
lore

Alexander Potapenko

author reviewer
#0 lore raw

v4 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
---

 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                          |   3 +
 lib/Kconfig.kasan                    |   5 +-
 lib/Makefile                         |   3 +
 lib/stackdepot.c                     | 275 +++++++++++++++++++++++++++++++
 lib/test_kasan.c                     |  59 ++++++-
 mm/Makefile                          |   1 +
 mm/kasan/Makefile                    |   4 +
 mm/kasan/kasan.c                     | 221 +++++++++++++++++++++++--
 mm/kasan/kasan.h                     |  45 ++++++
 mm/kasan/quarantine.c                | 306 +++++++++++++++++++++++++++++++++++
 mm/kasan/report.c                    |  64 ++++++--
 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, 1205 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>
author reviewer
#1 lore raw

PATCH v5 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
index c32f3b0..90ad74f 100644
@@ -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>
author reviewer
#2 lore raw

PATCH v5 2/7 mm, kasan: SLAB support

In reply to Alexander Potapenko

Add 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
index aa1e0c9..7dd95b3 100644
@@ -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
index 4b9f85c..4405a35 100644
@@ -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
index e4b5687..aa61595 100644
@@ -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
index e878ba3..9edbbf3 100644
@@ -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
index a33869b..feb1dc9 100644
@@ -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
index 0fee5ac..0e4d2b3 100644
@@ -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
index 4f0f135..3ac70df 100644
@@ -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
index 4f6c62e..7b9e4ab9 100644
@@ -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
index 8addc3c..242e6fa 100644
@@ -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>
maintainer
#3 lore raw

Re: [PATCH v5 2/7] mm, kasan: SLAB support

2016-03-09 14:05 GMT+03:00 Alexander Potapenko <[email protected]>:

mm/kasan/kasan.h struct kasan_global { · line 68

Quoted 2 lines
mm/kasan/kasan.h +2 -0
+struct kasan_track {
+       u64 cpu : 6;                    /* for NR_CPUS = 64 */

What about NR_CPUS > 64 ?

mm/kasan/kasan.h struct kasan_global { · line 72

Quoted 4 lines
mm/kasan/kasan.h +4 -0
+       u64 pid : 16;                   /* 65536 processes */
+       u64 when : 42;                  /* ~140 years */
+};
+

--
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>

author reviewer
#4 lore raw

Re: [PATCH v5 2/7] mm, kasan: SLAB support

Quoted 6 lines · Andrey Ryabinin

2016-03-09 14:05 GMT+03:00 Alexander Potapenko <[email protected]>:

mm/kasan/kasan.h struct kasan_global { · line 68

Quoted 2 lines
mm/kasan/kasan.h +2 -0
+struct kasan_track {
+       u64 cpu : 6;                    /* for NR_CPUS = 64 */

What about NR_CPUS > 64 ?

After a discussion with Dmitry we've decided to drop |cpu| and |when| at all, as they do not actually help debugging.
This way we'll make kasan_track only 8 bytes (4 bytes for PID, 4 bytes for stack handle).
Then the meta structures will be smaller and have nice alignment:

struct kasan_track {
        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;
        u32 reserved;  /* we can use it to store an additional stack

handle, e.g. for debugging RCU */
};

struct kasan_free_meta {
        /* 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;
};
Quoted 4 lines
Quoted 4 lines
mm/kasan/kasan.h +4 -0
+       u64 pid : 16;                   /* 65536 processes */
+       u64 when : 42;                  /* ~140 years */
+};
+
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>
maintainer
#5 lore raw

Re: [PATCH v5 2/7] mm, kasan: SLAB support

2016-03-11 16:05 GMT+03:00 Alexander Potapenko <[email protected]>:

Quoted 7 lines
Quoted 5 lines · Andrey Ryabinin

2016-03-09 14:05 GMT+03:00 Alexander Potapenko <[email protected]>:

Quoted 2 lines
mm/kasan/kasan.h +2 -0
+struct kasan_track {
+       u64 cpu : 6;                    /* for NR_CPUS = 64 */
Quoted 7 lines
Quoted 1 line

What about NR_CPUS > 64 ?

After a discussion with Dmitry we've decided to drop |cpu| and |when| at all, as they do not actually help debugging.
This way we'll make kasan_track only 8 bytes (4 bytes for PID, 4 bytes for stack handle).
Then the meta structures will be smaller and have nice alignment:

Sounds good.

Quoted 33 lines
struct kasan_track {
        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;
        u32 reserved;  /* we can use it to store an additional stack

handle, e.g. for debugging RCU */
};

struct kasan_free_meta {
        /* 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;
};
Quoted 4 lines

mm/kasan/kasan.h struct kasan_global { · line 72

Quoted 4 lines
mm/kasan/kasan.h +4 -0
+       u64 pid : 16;                   /* 65536 processes */
+       u64 when : 42;                  /* ~140 years */
+};
+

--
Alexander Potapenko
Software Engineer

Quoted 7 lines

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>

author reviewer
#6 lore raw

PATCH v5 3/7 mm, kasan: Added GFP flags to KASAN API

In reply to Alexander Potapenko

Add 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
index 4405a35..bf71ab0 100644
@@ -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
index aa61595..508bd82 100644
@@ -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
index d26ffb4..95b2267 100644
@@ -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
index 5669f4d..716efa8 100644
@@ -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
index 95dd196..7d27277 100644
@@ -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
index ff39a8f..5969769 100644
@@ -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
index 242e6fa..07690d3 100644
@@ -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
index d86720d..4e63f3b 100644
@@ -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>
author reviewer
#7 lore raw

PATCH v5 4/7 arch, ftrace: For KASAN put hard/soft IRQ entries into separate sections

In reply to Alexander Potapenko

KASAN 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
index 5abaf5b..bf19912 100644
@@ -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
index 1fab979..e2c6da0 100644
@@ -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
index 6cb7e1a..0c2eec4 100644
@@ -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
index 37f624df..5a1939a 100644
@@ -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
index c9eec84..d920b95 100644
@@ -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
index 5a6e141..50bc10f 100644
@@ -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
index e12055e..150ace9 100644
@@ -24,6 +24,7 @@ SECTIONS
 	LOCK_TEXT
 	KPROBES_TEXT
 	IRQENTRY_TEXT
+	SOFTIRQENTRY_TEXT
 	*(.text.*)
 	*(.gnu.warning)
 	}
arch/microblaze/kernel/vmlinux.lds.S +1 -0
index be9488d..0a47f04 100644
@@ -36,6 +36,7 @@ SECTIONS {
 		LOCK_TEXT
 		KPROBES_TEXT
 		IRQENTRY_TEXT
+		SOFTIRQENTRY_TEXT
 		. = ALIGN (4) ;
 		_etext = . ;
 	}
arch/mips/kernel/vmlinux.lds.S +1 -0
index 0a93e83..54d653e 100644
@@ -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
index 326fab4..e23e895 100644
@@ -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
index 2d69a85..d936de4 100644
@@ -50,6 +50,7 @@ SECTIONS
 	  LOCK_TEXT
 	  KPROBES_TEXT
 	  IRQENTRY_TEXT
+	  SOFTIRQENTRY_TEXT
 	  *(.fixup)
 	  *(.text.__*)
 	  _etext = .;
arch/parisc/kernel/vmlinux.lds.S +1 -0
index 308f290..f3ead0b 100644
@@ -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
index d41fd0a..2dd91f7 100644
@@ -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
index 445657f..0f41a82 100644
@@ -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
index db88cbf..235a410 100644
@@ -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
index f1a2f68..aadd321 100644
@@ -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
index 0e059a0..378f5d8 100644
@@ -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
index 74adf67..02f14cf 100644
@@ -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
index 8f5a12a..339125b 100644
@@ -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
index 6d9df3f..dea12a6 100644
@@ -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
index 0e95fcc..1dcecaf 100644
@@ -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
index 8aae49d..17caf4b 100644
@@ -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
index a663cbb..3e6f7d4 100644
@@ -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>
author reviewer
#8 lore raw

PATCH v5 5/7 mm, kasan: Stackdepot implementation. Enable stackdepot for SLAB

In reply to Alexander Potapenko

Implement 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

---
 arch/x86/kernel/Makefile   |   1 +
 include/linux/stackdepot.h |  32 ++++++
 lib/Kconfig                |   3 +
 lib/Kconfig.kasan          |   1 +
 lib/Makefile               |   3 +
 lib/stackdepot.c           | 275 +++++++++++++++++++++++++++++++++++++++++++++
 mm/kasan/kasan.c           |  51 ++++++++-
 mm/kasan/kasan.h           |   4 +
 mm/kasan/report.c          |   9 ++
 9 files changed, 376 insertions(+), 3 deletions(-)
arch/x86/kernel/Makefile +1 -0
index edfa3ec..f656d6e 100644
@@ -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
new file mode 100644
index 0000000..b6cbe05
@@ -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;
+
+struct stack_trace;
+
+depot_stack_handle depot_save_stack(struct stack_trace *trace, gfp_t flags);
+
+void depot_fetch_stack(depot_stack_handle handle, struct stack_trace *trace);
+
+#endif
lib/Kconfig +3 -0
index ee38a3f..8a60a53 100644
@@ -543,4 +543,7 @@ config ARCH_HAS_PMEM_API
 config ARCH_HAS_MMIO_FLUSH
 	bool
 
+config STACKDEPOT
+  bool
+
 endmenu
lib/Kconfig.kasan +1 -0
index 0e4d2b3..468316d 100644
@@ -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
index 50b31e2..0123abc 100644
@@ -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 +275 -0

275 lines not on this page · raw message

mm/kasan/kasan.c +48 -3

92 lines not on this page · raw message

mm/kasan/kasan.h +4 -0
index 7b9e4ab9..b4e5942 100644
@@ -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,10 +65,13 @@ 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 */
+	depot_stack_handle stack : sizeof(depot_stack_handle);
 };
 
 struct kasan_alloc_meta {
mm/kasan/report.c +9 -0
index 0a4fde9..bab41cb 100644
@@ -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>
@@ -120,6 +121,14 @@ 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);
+	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>
maintainer
#9 lore raw

Re: [PATCH v5 5/7] mm, kasan: Stackdepot implementation. Enable stackdepot for SLAB

Quoted 9 lines · On Wed, 9 Mar 2016 12:05:46 +0100 Alexander Potapenko <[email protected]>…

Implement 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.

Quoted 8 lines

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.

Have you identified other potential clients for the stackdepot code?

Quoted 17 lines
include/linux/stackdepot.h new file +14 -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,

include/linux/stackdepot.h line 24

Quoted 10 lines
include/linux/stackdepot.h new file +10 -0
+ * 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;

I'll rename this to depot_stack_handle_t, which is a pretty strong kernel convention.

Quoted 24 lines
include/linux/stackdepot.h new file +7 -0
+struct stack_trace;
+
+depot_stack_handle depot_save_stack(struct stack_trace *trace, gfp_t flags);
+
+void depot_fetch_stack(depot_stack_handle handle, struct stack_trace *trace);
+
+#endif
lib/Kconfig +3 -0
index ee38a3f..8a60a53 100644
@@ -543,4 +543,7 @@ config ARCH_HAS_PMEM_API
 config ARCH_HAS_MMIO_FLUSH
 	bool
 
+config STACKDEPOT
+  bool
+
 endmenu
lib/Kconfig.kasan +0 -0
index 0e4d2b3..468316d 100644

lib/Kconfig.kasan config KASAN · line 13

Quoted 7 lines
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.

Something weird happened to the Kconfig whitespace. I'll fix that.

--
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>

author reviewer
#10 lore raw

Re: [PATCH v5 5/7] mm, kasan: Stackdepot implementation. Enable stackdepot for SLAB

Quoted 16 lines · Andrew Morton
Quoted 14 lines · On Wed, 9 Mar 2016 12:05:46 +0100 Alexander Potapenko <[email protected]>…

Implement 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.

Quoted 5 lines
Quoted 3 lines

This patch is based on the "mm: kasan: stack depots" patch originally prepared by Dmitry Chernenkov.

Have you identified other potential clients for the stackdepot code?

Joonsoo Kim said he is planning to use stackdepot for the page owner.

Quoted 22 lines
Quoted 22 lines
include/linux/stackdepot.h new file +19 -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.
+ *
+ */
Quoted 8 lines

include/linux/stackdepot.h line 24

Quoted 5 lines
include/linux/stackdepot.h new file +5 -0
+
+#ifndef _LINUX_STACKDEPOT_H
+#define _LINUX_STACKDEPOT_H
+
+typedef u32 depot_stack_handle;

I'll rename this to depot_stack_handle_t, which is a pretty strong kernel convention.

Ok, thank you!

Quoted 25 lines
Quoted 25 lines
include/linux/stackdepot.h new file +7 -0
+struct stack_trace;
+
+depot_stack_handle depot_save_stack(struct stack_trace *trace, gfp_t flags);
+
+void depot_fetch_stack(depot_stack_handle handle, struct stack_trace *trace);
+
+#endif
lib/Kconfig +3 -0
index ee38a3f..8a60a53 100644
@@ -543,4 +543,7 @@ config ARCH_HAS_PMEM_API
 config ARCH_HAS_MMIO_FLUSH
      bool
 
+config STACKDEPOT
+  bool
+
 endmenu
lib/Kconfig.kasan +0 -0
index 0e4d2b3..468316d 100644
@@ -7,6 +7,7 @@ config KASAN
      bool "KASan: runtime memory debugger"
Quoted 8 lines

lib/Kconfig.kasan config KASAN · line 13

Quoted 6 lines
lib/Kconfig.kasan +1 -0
      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.

Something weird happened to the Kconfig whitespace. I'll fix that.

Thanks!

Quoted 1 line
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>
author reviewer
#11 lore raw

PATCH v5 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
index 90ad74f..82169fb 100644
@@ -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>
author reviewer
#12 lore raw

PATCH v5 7/7 mm: kasan: Initial memory quarantine implementation

In reply to Alexander Potapenko

Quarantine 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).

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)

---
 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 | 306 ++++++++++++++++++++++++++++++++++++++++++++++++++
 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, 455 insertions(+), 29 deletions(-)
include/linux/kasan.h +22 -8
index bf71ab0..355e722 100644
@@ -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
index 82169fb..799c98e 100644
@@ -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
index 131daad..63b54aa 100644
@@ -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
index b4e5942..37e0b3a 100644
@@ -62,6 +62,7 @@ struct kasan_global {
 enum kasan_state {
 	KASAN_STATE_INIT,
 	KASAN_STATE_ALLOC,
+	KASAN_STATE_QUARANTINE,
 	KASAN_STATE_FREE
 };
 
@@ -81,8 +82,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;
 };
 
@@ -106,4 +109,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 +306 -0

306 lines not on this page · raw message

mm/kasan/report.c +1 -0
index bab41cb..0eead8f 100644
@@ -152,6 +152,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
index 716efa8..9da9bef 100644
@@ -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
index 1993894..0cadb5d 100644
@@ -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
index 7d27277..222a3bf 100644
@@ -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
index 5969769..dedb1a9 100644
@@ -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
index 07690d3..b8502a2 100644
@@ -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
index 4e63f3b..c76fd2e 100644
@@ -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>
maintainer
#13 lore raw

Re: [PATCH v5 7/7] mm: kasan: Initial memory quarantine implementation

Quoted 3 lines · On Wed, 9 Mar 2016 12:05:48 +0100 Alexander Potapenko <[email protected]>…

Quarantine isolates freed objects in a separate queue. The objects are returned to the allocator later, which helps to detect use-after-free errors.

I'd like to see some more details on precisely *how* the parking of objects in the qlists helps "detect use-after-free"?

Quoted 8 lines

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).

Quoted 6 lines

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.

qlists look awfully like list_heads. Some explanation of why a new container mechanism was needed would be good to see - wht are existing ones unsuitable?

Quoted 6 lines

...

mm/kasan/kasan.c +3 -0
+void kasan_cache_shrink(struct kmem_cache *cache)
+{
+#ifdef CONFIG_SLAB

mm/kasan/kasan.c void kasan_cache_create(struct kmem_cache *cache, size_t *size, · line 383

Quoted 10 lines
mm/kasan/kasan.c +10 -0
+	quarantine_remove_cache(cache);
+#endif
+}
+
+void kasan_cache_destroy(struct kmem_cache *cache)
+{
+#ifdef CONFIG_SLAB
+	quarantine_remove_cache(cache);
+#endif
+}

We could avoid th4ese ifdefs in the usual way: an empty version of quarantine_remove_cache() if CONFIG_SLAB=n.

mm/kasan/kasan.c void kasan_kmalloc(struct kmem_cache *cache, const void *object, size_t size, · line 538

Quoted 11 lines

...

mm/kasan/kasan.c +4 -0
@@ -493,6 +532,11 @@ void kasan_kmalloc(struct kmem_cache *cache, const void *object, size_t size,
 	unsigned long redzone_start;
 	unsigned long redzone_end;
 
+#ifdef CONFIG_SLAB
+	if (flags & __GFP_RECLAIM)
+		quarantine_reduce();
+#endif

Here also.

Quoted 32 lines
mm/kasan/kasan.c +0 -0
 	if (unlikely(object == NULL))
 		return;
 
mm/kasan/quarantine.c new file +26 -0
@@ -0,0 +1,306 @@
+/*
+ * KASAN quarantine.
+ *
+ * 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
+ * version 2 as published by the Free Software Foundation.
+ *
+ * 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.
+ *
+ */
+
+#include <linux/gfp.h>
+#include <linux/hash.h>
+#include <linux/kernel.h>
+#include <linux/mm.h>
+#include <linux/percpu.h>
+#include <linux/printk.h>
+#include <linux/shrinker.h>

mm/kasan/quarantine.c line 36

Quoted 10 lines
mm/kasan/quarantine.c new file +10 -0
+#include <linux/slab.h>
+#include <linux/string.h>
+#include <linux/types.h>
+
+#include "../slab.h"
+#include "kasan.h"
+
+/* Data structure and operations for quarantine queues. */
+
+/* Each queue is a signled-linked list, which also stores the total size of

tpyo

Quoted 4 lines
mm/kasan/quarantine.c new file +4 -0
+ * objects inside of it.
+ */
+struct qlist {
+	void **head;

mm/kasan/quarantine.c line 50

Quoted 10 lines
mm/kasan/quarantine.c new file +10 -0
+	void **tail;
+	size_t bytes;
+};
+
+#define QLIST_INIT { NULL, NULL, 0 }
+
+static inline bool empty_qlist(struct qlist *q)
+{
+	return !q->head;
+}

Should be "qlist_empty()".

mm/kasan/quarantine.c line 56

Quoted 5 lines
mm/kasan/quarantine.c new file +5 -0
+static inline void init_qlist(struct qlist *q)
+{
+	q->head = q->tail = NULL;
+	q->bytes = 0;
+}

"qlist_init()"

Quoted 53 lines
mm/kasan/quarantine.c new file +53 -0
+static inline void qlist_put(struct qlist *q, void **qlink, size_t size)
+{
+	if (unlikely(empty_qlist(q)))
+		q->head = qlink;
+	else
+		*q->tail = qlink;
+	q->tail = qlink;
+	*qlink = NULL;
+	q->bytes += size;
+}
+
+static inline void **qlist_remove(struct qlist *q, void ***prev,
+				 size_t size)
+{
+	void **qlink = *prev;
+
+	*prev = *qlink;
+	if (q->tail == qlink) {
+		if (q->head == qlink)
+			q->tail = NULL;
+		else
+			q->tail = (void **)prev;
+	}
+	q->bytes -= size;
+
+	return qlink;
+}
+
+static inline void qlist_move_all(struct qlist *from, struct qlist *to)
+{
+	if (unlikely(empty_qlist(from)))
+		return;
+
+	if (empty_qlist(to)) {
+		*to = *from;
+		init_qlist(from);
+		return;
+	}
+
+	*to->tail = from->head;
+	to->tail = from->tail;
+	to->bytes += from->bytes;
+
+	init_qlist(from);
+}
+
+static inline void qlist_move(struct qlist *from, void **last, struct qlist *to,
+			  size_t size)
+{
+	if (unlikely(last == from->tail)) {
+		qlist_move_all(from, to);
+		return;
+	}

mm/kasan/quarantine.c line 120

Quoted 10 lines
mm/kasan/quarantine.c new file +10 -0
+	if (empty_qlist(to))
+		to->head = from->head;
+	else
+		*to->tail = from->head;
+	to->tail = last;
+	from->head = *last;
+	*last = NULL;
+	from->bytes -= size;
+	to->bytes += size;
+}

The above code is a candidate for hoisting out into a generic library facility, so let's impement it that way (ie: get the naming right).

All the inlining looks excessive, and the compiler will defeat it anyway if it thinks that is best.

Quoted 3 lines

...

--
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>

author reviewer
#14 lore raw

Re: [PATCH v5 7/7] mm: kasan: Initial memory quarantine implementation

Quoted 8 lines · Andrew Morton
Quoted 3 lines · On Wed, 9 Mar 2016 12:05:48 +0100 Alexander Potapenko <[email protected]>…

Quarantine isolates freed objects in a separate queue. The objects are returned to the allocator later, which helps to detect use-after-free errors.

I'd like to see some more details on precisely *how* the parking of objects in the qlists helps "detect use-after-free"?

When the object is freed, its state changes from KASAN_STATE_ALLOC to KASAN_STATE_QUARANTINE. The object is poisoned and put into quarantine instead of being returned to the allocator, therefore every subsequent access to that object triggers a KASAN error, and the error handler is able to say where the object has been allocated and deallocated. When it's time for the object to leave quarantine, its state becomes KASAN_STATE_FREE and it's returned to the allocator. From now on the allocator may reuse it for another allocation.
Before that happens, it's still possible to detect a use-after free on that object (it retains the allocation/deallocation stacks). When the allocator reuses this object, the shadow is unpoisoned and old allocation/deallocation stacks are wiped. Therefore a use of this object, even an incorrect one, won't trigger ASan warning. Without the quarantine, it's not guaranteed that the objects aren't reused immediately, that's why the probability of catching a use-after-free is lower than with quarantine in place.

Quoted 11 lines
Quoted 11 lines

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).

Right now quarantine support is only enabled in SLAB allocator. Unification of KASAN features in SLAB and SLUB will be done later.

Quoted 7 lines
Quoted 3 lines

This patch is based on the "mm: kasan: quarantine" patch originally prepared by Dmitry Chernenkov.

qlists look awfully like list_heads. Some explanation of why a new container mechanism was needed would be good to see - wht are existing ones unsuitable?

Most of the code in quarantine.c is actually the code that moves the queues around (merges them, frees a given portion of the quarantine, filters the elements belonging to a specific cache) and calculates the sizes. I don't think there're off-the-shelf solutions for this. qlist is a FIFO queue that keeps pointers to the head and the tail of a linked list. That's semantically different from list_head. There's include/linux/kfifo.h, but that also appears to be completely different.

Quoted 4 lines
Quoted 4 lines

...

Quoted 16 lines

mm/kasan/kasan.c void kasan_cache_create(struct kmem_cache *cache, size_t *size, · line 383

Quoted 13 lines
mm/kasan/kasan.c +13 -0
+void kasan_cache_shrink(struct kmem_cache *cache)
+{
+#ifdef CONFIG_SLAB
+     quarantine_remove_cache(cache);
+#endif
+}
+
+void kasan_cache_destroy(struct kmem_cache *cache)
+{
+#ifdef CONFIG_SLAB
+     quarantine_remove_cache(cache);
+#endif
+}

We could avoid th4ese ifdefs in the usual way: an empty version of quarantine_remove_cache() if CONFIG_SLAB=n.

Yes, agreed.
I am sorry, I don't fully understand the review process now, when you've pulled the patches into mm-tree.
Shall I send the new patch series version, as before, or is anything else needs to be done?
Do I need to rebase against mm- or linux-next? Thanks in advance.

Quoted 7 lines
Quoted 7 lines

...

mm/kasan/kasan.c +0 -0
@@ -493,6 +532,11 @@ void kasan_kmalloc(struct kmem_cache *cache, const void *object, size_t size,
      unsigned long redzone_start;
      unsigned long redzone_end;
 
Quoted 6 lines

mm/kasan/kasan.c void kasan_kmalloc(struct kmem_cache *cache, const void *object, size_t size, · line 538

Quoted 4 lines
mm/kasan/kasan.c +4 -0
+#ifdef CONFIG_SLAB
+     if (flags & __GFP_RECLAIM)
+             quarantine_reduce();
+#endif

Here also.

Ack.

Quoted 37 lines
Quoted 37 lines
mm/kasan/kasan.c +0 -0
      if (unlikely(object == NULL))
              return;
 
mm/kasan/quarantine.c new file +30 -0
@@ -0,0 +1,306 @@
+/*
+ * KASAN quarantine.
+ *
+ * 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
+ * version 2 as published by the Free Software Foundation.
+ *
+ * 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.
+ *
+ */
+
+#include <linux/gfp.h>
+#include <linux/hash.h>
+#include <linux/kernel.h>
+#include <linux/mm.h>
+#include <linux/percpu.h>
+#include <linux/printk.h>
+#include <linux/shrinker.h>
+#include <linux/slab.h>
+#include <linux/string.h>
+#include <linux/types.h>
+
Quoted 8 lines

mm/kasan/quarantine.c line 36

Quoted 6 lines
mm/kasan/quarantine.c new file +6 -0
+#include "../slab.h"
+#include "kasan.h"
+
+/* Data structure and operations for quarantine queues. */
+
+/* Each queue is a signled-linked list, which also stores the total size of

tpyo

Ack.

Quoted 17 lines
Quoted 5 lines
mm/kasan/quarantine.c new file +4 -0
+ * objects inside of it.
+ */
+struct qlist {
+     void **head;

mm/kasan/quarantine.c line 50

Quoted 10 lines
mm/kasan/quarantine.c new file +10 -0
+     void **tail;
+     size_t bytes;
+};
+
+#define QLIST_INIT { NULL, NULL, 0 }
+
+static inline bool empty_qlist(struct qlist *q)
+{
+     return !q->head;
+}

Should be "qlist_empty()".

Ack.

Quoted 8 lines

mm/kasan/quarantine.c line 56

Quoted 6 lines
mm/kasan/quarantine.c new file +5 -0
+static inline void init_qlist(struct qlist *q)
+{
+     q->head = q->tail = NULL;
+     q->bytes = 0;
+}

"qlist_init()"

Ack.

Quoted 59 lines
Quoted 59 lines
mm/kasan/quarantine.c new file +58 -0
+static inline void qlist_put(struct qlist *q, void **qlink, size_t size)
+{
+     if (unlikely(empty_qlist(q)))
+             q->head = qlink;
+     else
+             *q->tail = qlink;
+     q->tail = qlink;
+     *qlink = NULL;
+     q->bytes += size;
+}
+
+static inline void **qlist_remove(struct qlist *q, void ***prev,
+                              size_t size)
+{
+     void **qlink = *prev;
+
+     *prev = *qlink;
+     if (q->tail == qlink) {
+             if (q->head == qlink)
+                     q->tail = NULL;
+             else
+                     q->tail = (void **)prev;
+     }
+     q->bytes -= size;
+
+     return qlink;
+}
+
+static inline void qlist_move_all(struct qlist *from, struct qlist *to)
+{
+     if (unlikely(empty_qlist(from)))
+             return;
+
+     if (empty_qlist(to)) {
+             *to = *from;
+             init_qlist(from);
+             return;
+     }
+
+     *to->tail = from->head;
+     to->tail = from->tail;
+     to->bytes += from->bytes;
+
+     init_qlist(from);
+}
+
+static inline void qlist_move(struct qlist *from, void **last, struct qlist *to,
+                       size_t size)
+{
+     if (unlikely(last == from->tail)) {
+             qlist_move_all(from, to);
+             return;
+     }
+     if (empty_qlist(to))
+             to->head = from->head;
+     else
+             *to->tail = from->head;
+     to->tail = last;
Quoted 8 lines

mm/kasan/quarantine.c line 120

Quoted 5 lines
mm/kasan/quarantine.c new file +5 -0
+     from->head = *last;
+     *last = NULL;
+     from->bytes -= size;
+     to->bytes += size;
+}

The above code is a candidate for hoisting out into a generic library facility, so let's impement it that way (ie: get the naming right).

Ack.

Quoted 2 lines

All the inlining looks excessive, and the compiler will defeat it anyway if it thinks that is best.

Ack.

Quoted 5 lines
Quoted 3 lines

...

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>
maintainer
#15 lore raw

Re: [PATCH v5 7/7] mm: kasan: Initial memory quarantine implementation

Quoted 9 lines · On Thu, 10 Mar 2016 14:50:56 +0100 Alexander Potapenko <[email protected]>…
Quoted 8 lines · Andrew Morton
Quoted 3 lines · On Wed, 9 Mar 2016 12:05:48 +0100 Alexander Potapenko <[email protected]>…

Quarantine isolates freed objects in a separate queue. The objects are returned to the allocator later, which helps to detect use-after-free errors.

I'd like to see some more details on precisely *how* the parking of objects in the qlists helps "detect use-after-free"?

Quoted 16 lines

When the object is freed, its state changes from KASAN_STATE_ALLOC to KASAN_STATE_QUARANTINE. The object is poisoned and put into quarantine instead of being returned to the allocator, therefore every subsequent access to that object triggers a KASAN error, and the error handler is able to say where the object has been allocated and deallocated. When it's time for the object to leave quarantine, its state becomes KASAN_STATE_FREE and it's returned to the allocator. From now on the allocator may reuse it for another allocation.
Before that happens, it's still possible to detect a use-after free on that object (it retains the allocation/deallocation stacks). When the allocator reuses this object, the shadow is unpoisoned and old allocation/deallocation stacks are wiped. Therefore a use of this object, even an incorrect one, won't trigger ASan warning. Without the quarantine, it's not guaranteed that the objects aren't reused immediately, that's why the probability of catching a use-after-free is lower than with quarantine in place.

I see, thanks. I'll slurp that into the changelog for posterity.

Quoted 10 lines
Quoted 4 lines
Quoted 1 line

+}

We could avoid th4ese ifdefs in the usual way: an empty version of quarantine_remove_cache() if CONFIG_SLAB=n.

Yes, agreed.
I am sorry, I don't fully understand the review process now, when you've pulled the patches into mm-tree.
Shall I send the new patch series version, as before, or is anything else needs to be done?
Do I need to rebase against mm- or linux-next? Thanks in advance.

I like to queue a delta patch so I and others can see what changed and also to keep track of who fixed what and why. It's a bit harsh on the reviewers to send them a slightly altered version of a 500 line patch which they've already read through.

Before sending the patch up to Linus I'll clump everything into a single patch and a lot of that history is somewhat lost.

Sending a replacement patch is often more convenient for the originator so that's fine - I'll turn the replacement into a delta locally and will review then queue that delta. Also a new revision of a patch has an altered changelog so I'll manually move that into the older original patch's changelog immediately.

IOW: either a new patch or a delta is fine.

Your patch is in linux-next now so a diff against -next will work OK.

Probably the easiest thing for you to do is to just alter the patch you have in-place and send out the new one. A "[v2" in the Subject: helps people keep track of things.

--
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>

author reviewer
#16 lore raw

Re: [PATCH v5 7/7] mm: kasan: Initial memory quarantine implementation

Quoted 28 lines · Andrew Morton
Quoted 9 lines · On Thu, 10 Mar 2016 14:50:56 +0100 Alexander Potapenko <[email protected]>…
Quoted 8 lines · Andrew Morton
Quoted 3 lines · On Wed, 9 Mar 2016 12:05:48 +0100 Alexander Potapenko <[email protected]>…

Quarantine isolates freed objects in a separate queue. The objects are returned to the allocator later, which helps to detect use-after-free errors.

I'd like to see some more details on precisely *how* the parking of objects in the qlists helps "detect use-after-free"?

Quoted 16 lines

When the object is freed, its state changes from KASAN_STATE_ALLOC to KASAN_STATE_QUARANTINE. The object is poisoned and put into quarantine instead of being returned to the allocator, therefore every subsequent access to that object triggers a KASAN error, and the error handler is able to say where the object has been allocated and deallocated. When it's time for the object to leave quarantine, its state becomes KASAN_STATE_FREE and it's returned to the allocator. From now on the allocator may reuse it for another allocation.
Before that happens, it's still possible to detect a use-after free on that object (it retains the allocation/deallocation stacks). When the allocator reuses this object, the shadow is unpoisoned and old allocation/deallocation stacks are wiped. Therefore a use of this object, even an incorrect one, won't trigger ASan warning. Without the quarantine, it's not guaranteed that the objects aren't reused immediately, that's why the probability of catching a use-after-free is lower than with quarantine in place.

Quoted 1 line

I see, thanks. I'll slurp that into the changelog for posterity.

I've also added a paragraph about that to the patch description.

Quoted 11 lines
Quoted 10 lines
Quoted 4 lines
Quoted 1 line

+}

We could avoid th4ese ifdefs in the usual way: an empty version of quarantine_remove_cache() if CONFIG_SLAB=n.

Yes, agreed.
I am sorry, I don't fully understand the review process now, when you've pulled the patches into mm-tree.
Shall I send the new patch series version, as before, or is anything else needs to be done?
Do I need to rebase against mm- or linux-next? Thanks in advance.

Quoted 4 lines

I like to queue a delta patch so I and others can see what changed and also to keep track of who fixed what and why. It's a bit harsh on the reviewers to send them a slightly altered version of a 500 line patch which they've already read through.

I'm listing the differences between patch versions after the patch description (between the triple dashes), hope that helps the reviewers.

Quoted 10 lines

Before sending the patch up to Linus I'll clump everything into a single patch and a lot of that history is somewhat lost.

Sending a replacement patch is often more convenient for the originator so that's fine - I'll turn the replacement into a delta locally and will review then queue that delta. Also a new revision of a patch has an altered changelog so I'll manually move that into the older original patch's changelog immediately.

IOW: either a new patch or a delta is fine.

Ok, got it.

Quoted 5 lines

Your patch is in linux-next now so a diff against -next will work OK.

Probably the easiest thing for you to do is to just alter the patch you have in-place and send out the new one. A "[v2" in the Subject: helps people keep track of things.

Ok, will do.

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>
author reviewer
#17 lore raw

Re: [PATCH v5 7/7] mm: kasan: Initial memory quarantine implementation

In reply to Alexander Potapenko
Quoted 44 lines · Alexander Potapenko
Quoted 8 lines · Andrew Morton
Quoted 3 lines · On Wed, 9 Mar 2016 12:05:48 +0100 Alexander Potapenko <[email protected]>…

Quarantine isolates freed objects in a separate queue. The objects are returned to the allocator later, which helps to detect use-after-free errors.

I'd like to see some more details on precisely *how* the parking of objects in the qlists helps "detect use-after-free"?

When the object is freed, its state changes from KASAN_STATE_ALLOC to KASAN_STATE_QUARANTINE. The object is poisoned and put into quarantine instead of being returned to the allocator, therefore every subsequent access to that object triggers a KASAN error, and the error handler is able to say where the object has been allocated and deallocated. When it's time for the object to leave quarantine, its state becomes KASAN_STATE_FREE and it's returned to the allocator. From now on the allocator may reuse it for another allocation.
Before that happens, it's still possible to detect a use-after free on that object (it retains the allocation/deallocation stacks). When the allocator reuses this object, the shadow is unpoisoned and old allocation/deallocation stacks are wiped. Therefore a use of this object, even an incorrect one, won't trigger ASan warning. Without the quarantine, it's not guaranteed that the objects aren't reused immediately, that's why the probability of catching a use-after-free is lower than with quarantine in place.

Quoted 11 lines
Quoted 11 lines

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).

Right now quarantine support is only enabled in SLAB allocator. Unification of KASAN features in SLAB and SLUB will be done later.

Quoted 7 lines
Quoted 3 lines

This patch is based on the "mm: kasan: quarantine" patch originally prepared by Dmitry Chernenkov.

qlists look awfully like list_heads. Some explanation of why a new container mechanism was needed would be good to see - wht are existing ones unsuitable?

Quoted 28 lines

Most of the code in quarantine.c is actually the code that moves the queues around (merges them, frees a given portion of the quarantine, filters the elements belonging to a specific cache) and calculates the sizes. I don't think there're off-the-shelf solutions for this. qlist is a FIFO queue that keeps pointers to the head and the tail of a linked list. That's semantically different from list_head. There's include/linux/kfifo.h, but that also appears to be completely different.

Quoted 20 lines
Quoted 7 lines

...

mm/kasan/kasan.c +3 -0
+void kasan_cache_shrink(struct kmem_cache *cache)
+{
+#ifdef CONFIG_SLAB

mm/kasan/kasan.c void kasan_cache_create(struct kmem_cache *cache, size_t *size, · line 383

Quoted 10 lines
mm/kasan/kasan.c +10 -0
+     quarantine_remove_cache(cache);
+#endif
+}
+
+void kasan_cache_destroy(struct kmem_cache *cache)
+{
+#ifdef CONFIG_SLAB
+     quarantine_remove_cache(cache);
+#endif
+}

We could avoid th4ese ifdefs in the usual way: an empty version of quarantine_remove_cache() if CONFIG_SLAB=n.

After thinking a while, I don't think it's necessary to get rid of these ifdefs right now.
Right now the kernel depends on mm/kasan/quarantine.c iff CONFIG_SLAB is on. If we declare empty quarantine_remove_cache() and friends, we'll have to either do it in slub.c (and then remove it after we make SLUB also use quarantine), or add the dependency on quarantine.c and put the ifdefs there.
Given that the number of ifdefs is small, and I'm planning to follow-up with a patch that switches SLUB to using quarantine, I suppose it should be fine to keep the ifdefs for the transition period.
Hope you're fine with that.

Quoted 183 lines

Yes, agreed.
I am sorry, I don't fully understand the review process now, when you've pulled the patches into mm-tree.
Shall I send the new patch series version, as before, or is anything else needs to be done?
Do I need to rebase against mm- or linux-next? Thanks in advance.

Quoted 7 lines
Quoted 7 lines

...

mm/kasan/kasan.c +0 -0
@@ -493,6 +532,11 @@ void kasan_kmalloc(struct kmem_cache *cache, const void *object, size_t size,
      unsigned long redzone_start;
      unsigned long redzone_end;
 
Quoted 6 lines

mm/kasan/kasan.c void kasan_kmalloc(struct kmem_cache *cache, const void *object, size_t size, · line 538

Quoted 4 lines
mm/kasan/kasan.c +4 -0
+#ifdef CONFIG_SLAB
+     if (flags & __GFP_RECLAIM)
+             quarantine_reduce();
+#endif

Here also.

Ack.

Quoted 37 lines
Quoted 37 lines
mm/kasan/kasan.c +0 -0
      if (unlikely(object == NULL))
              return;
 
mm/kasan/quarantine.c new file +30 -0
@@ -0,0 +1,306 @@
+/*
+ * KASAN quarantine.
+ *
+ * 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
+ * version 2 as published by the Free Software Foundation.
+ *
+ * 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.
+ *
+ */
+
+#include <linux/gfp.h>
+#include <linux/hash.h>
+#include <linux/kernel.h>
+#include <linux/mm.h>
+#include <linux/percpu.h>
+#include <linux/printk.h>
+#include <linux/shrinker.h>
+#include <linux/slab.h>
+#include <linux/string.h>
+#include <linux/types.h>
+
Quoted 8 lines

mm/kasan/quarantine.c line 36

Quoted 6 lines
mm/kasan/quarantine.c new file +6 -0
+#include "../slab.h"
+#include "kasan.h"
+
+/* Data structure and operations for quarantine queues. */
+
+/* Each queue is a signled-linked list, which also stores the total size of

tpyo

Ack.

Quoted 17 lines
Quoted 5 lines
mm/kasan/quarantine.c new file +4 -0
+ * objects inside of it.
+ */
+struct qlist {
+     void **head;

mm/kasan/quarantine.c line 50

Quoted 10 lines
mm/kasan/quarantine.c new file +10 -0
+     void **tail;
+     size_t bytes;
+};
+
+#define QLIST_INIT { NULL, NULL, 0 }
+
+static inline bool empty_qlist(struct qlist *q)
+{
+     return !q->head;
+}

Should be "qlist_empty()".

Ack.

Quoted 8 lines

mm/kasan/quarantine.c line 56

Quoted 6 lines
mm/kasan/quarantine.c new file +5 -0
+static inline void init_qlist(struct qlist *q)
+{
+     q->head = q->tail = NULL;
+     q->bytes = 0;
+}

"qlist_init()"

Ack.

Quoted 59 lines
Quoted 59 lines
mm/kasan/quarantine.c new file +58 -0
+static inline void qlist_put(struct qlist *q, void **qlink, size_t size)
+{
+     if (unlikely(empty_qlist(q)))
+             q->head = qlink;
+     else
+             *q->tail = qlink;
+     q->tail = qlink;
+     *qlink = NULL;
+     q->bytes += size;
+}
+
+static inline void **qlist_remove(struct qlist *q, void ***prev,
+                              size_t size)
+{
+     void **qlink = *prev;
+
+     *prev = *qlink;
+     if (q->tail == qlink) {
+             if (q->head == qlink)
+                     q->tail = NULL;
+             else
+                     q->tail = (void **)prev;
+     }
+     q->bytes -= size;
+
+     return qlink;
+}
+
+static inline void qlist_move_all(struct qlist *from, struct qlist *to)
+{
+     if (unlikely(empty_qlist(from)))
+             return;
+
+     if (empty_qlist(to)) {
+             *to = *from;
+             init_qlist(from);
+             return;
+     }
+
+     *to->tail = from->head;
+     to->tail = from->tail;
+     to->bytes += from->bytes;
+
+     init_qlist(from);
+}
+
+static inline void qlist_move(struct qlist *from, void **last, struct qlist *to,
+                       size_t size)
+{
+     if (unlikely(last == from->tail)) {
+             qlist_move_all(from, to);
+             return;
+     }
+     if (empty_qlist(to))
+             to->head = from->head;
+     else
+             *to->tail = from->head;
+     to->tail = last;
Quoted 8 lines

mm/kasan/quarantine.c line 120

Quoted 5 lines
mm/kasan/quarantine.c new file +5 -0
+     from->head = *last;
+     *last = NULL;
+     from->bytes -= size;
+     to->bytes += size;
+}

The above code is a candidate for hoisting out into a generic library facility, so let's impement it that way (ie: get the naming right).

Ack.

Quoted 2 lines

All the inlining looks excessive, and the compiler will defeat it anyway if it thinks that is best.

Ack.

Quoted 5 lines
Quoted 3 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

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>
author reviewer
#18 lore raw
In reply to Alexander Potapenko

Sorry, this cover letter had incorrect version, I've resent it with the corrected one.

Quoted 92 lines · Alexander Potapenko

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
---

 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                          |   3 +
 lib/Kconfig.kasan                    |   5 +-
 lib/Makefile                         |   3 +
 lib/stackdepot.c                     | 275 +++++++++++++++++++++++++++++++
 lib/test_kasan.c                     |  59 ++++++-
 mm/Makefile                          |   1 +
 mm/kasan/Makefile                    |   4 +
 mm/kasan/kasan.c                     | 221 +++++++++++++++++++++++--
 mm/kasan/kasan.h                     |  45 ++++++
 mm/kasan/quarantine.c                | 306 +++++++++++++++++++++++++++++++++++
 mm/kasan/report.c                    |  64 ++++++--
 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, 1205 insertions(+), 92 deletions(-)
 create mode 100644 include/linux/stackdepot.h
 create mode 100644 lib/stackdepot.c
 create mode 100644 mm/kasan/quarantine.c

--
2.7.0.rc3.207.g0ac5344

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>
maintainer
#19 lore raw
In reply to Alexander Potapenko
Quoted 1 line · On Wed, 9 Mar 2016 12:10:13 +0100 Alexander Potapenko <[email protected]>…

This patch set implements SLAB support for KASAN

I'll queue all this up for some testing. I'm undecided about feeding it into 4.5 - it is very late. I'll be interested in advice from others on this...

--
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>

maintainer
#20 lore raw

2016-03-09 23:23 GMT+03:00 Andrew Morton <[email protected]>:

Quoted 8 lines · On Wed, 9 Mar 2016 12:10:13 +0100 Alexander Potapenko <[email protected]>…
Quoted 2 lines

This patch set implements SLAB support for KASAN

I'll queue all this up for some testing. I'm undecided about feeding it into 4.5 - it is very late. I'll be interested in advice from others on this...

You mean 4.6 of course. I we shoudn't rush with this.

--
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>