diff options
| author | Andrey Ryabinin <a.ryabinin@samsung.com> | 2015-02-13 17:40:17 -0500 |
|---|---|---|
| committer | Linus Torvalds <torvalds@linux-foundation.org> | 2015-02-14 00:21:42 -0500 |
| commit | bebf56a1b176c2e1c9efe44e7e6915532cc682cf (patch) | |
| tree | 4b967827878142197f2b62cd0b89652873631192 | |
| parent | 6301939d97d079f0d3dbe71e750f4daf5d39fc33 (diff) | |
kasan: enable instrumentation of global variables
This feature let us to detect accesses out of bounds of global variables.
This will work as for globals in kernel image, so for globals in modules.
Currently this won't work for symbols in user-specified sections (e.g.
__init, __read_mostly, ...)
The idea of this is simple. Compiler increases each global variable by
redzone size and add constructors invoking __asan_register_globals()
function. Information about global variable (address, size, size with
redzone ...) passed to __asan_register_globals() so we could poison
variable's redzone.
This patch also forces module_alloc() to return 8*PAGE_SIZE aligned
address making shadow memory handling (
kasan_module_alloc()/kasan_module_free() ) more simple. Such alignment
guarantees that each shadow page backing modules address space correspond
to only one module_alloc() allocation.
Signed-off-by: Andrey Ryabinin <a.ryabinin@samsung.com>
Cc: Dmitry Vyukov <dvyukov@google.com>
Cc: Konstantin Serebryany <kcc@google.com>
Cc: Dmitry Chernenkov <dmitryc@google.com>
Signed-off-by: Andrey Konovalov <adech.fo@gmail.com>
Cc: Yuri Gribov <tetra2005@gmail.com>
Cc: Konstantin Khlebnikov <koct9i@gmail.com>
Cc: Sasha Levin <sasha.levin@oracle.com>
Cc: Christoph Lameter <cl@linux.com>
Cc: Joonsoo Kim <iamjoonsoo.kim@lge.com>
Cc: Dave Hansen <dave.hansen@intel.com>
Cc: Andi Kleen <andi@firstfloor.org>
Cc: Ingo Molnar <mingo@elte.hu>
Cc: Thomas Gleixner <tglx@linutronix.de>
Cc: "H. Peter Anvin" <hpa@zytor.com>
Cc: Christoph Lameter <cl@linux.com>
Cc: Pekka Enberg <penberg@kernel.org>
Cc: David Rientjes <rientjes@google.com>
Signed-off-by: Andrew Morton <akpm@linux-foundation.org>
Signed-off-by: Linus Torvalds <torvalds@linux-foundation.org>
| -rw-r--r-- | Documentation/kasan.txt | 2 | ||||
| -rw-r--r-- | arch/x86/kernel/module.c | 12 | ||||
| -rw-r--r-- | arch/x86/mm/kasan_init_64.c | 2 | ||||
| -rw-r--r-- | include/linux/compiler-gcc4.h | 4 | ||||
| -rw-r--r-- | include/linux/compiler-gcc5.h | 2 | ||||
| -rw-r--r-- | include/linux/kasan.h | 10 | ||||
| -rw-r--r-- | kernel/module.c | 2 | ||||
| -rw-r--r-- | lib/Kconfig.kasan | 1 | ||||
| -rw-r--r-- | mm/kasan/kasan.c | 52 | ||||
| -rw-r--r-- | mm/kasan/kasan.h | 25 | ||||
| -rw-r--r-- | mm/kasan/report.c | 22 | ||||
| -rw-r--r-- | scripts/Makefile.kasan | 2 |
12 files changed, 132 insertions, 4 deletions
diff --git a/Documentation/kasan.txt b/Documentation/kasan.txt index f0645a8a992f..092fc10961fe 100644 --- a/Documentation/kasan.txt +++ b/Documentation/kasan.txt | |||
| @@ -9,7 +9,7 @@ a fast and comprehensive solution for finding use-after-free and out-of-bounds | |||
| 9 | bugs. | 9 | bugs. |
| 10 | 10 | ||
| 11 | KASan uses compile-time instrumentation for checking every memory access, | 11 | KASan uses compile-time instrumentation for checking every memory access, |
| 12 | therefore you will need a certain version of GCC >= 4.9.2 | 12 | therefore you will need a certain version of GCC > 4.9.2 |
| 13 | 13 | ||
| 14 | Currently KASan is supported only for x86_64 architecture and requires that the | 14 | Currently KASan is supported only for x86_64 architecture and requires that the |
| 15 | kernel be built with the SLUB allocator. | 15 | kernel be built with the SLUB allocator. |
diff --git a/arch/x86/kernel/module.c b/arch/x86/kernel/module.c index e830e61aae05..d1ac80b72c72 100644 --- a/arch/x86/kernel/module.c +++ b/arch/x86/kernel/module.c | |||
| @@ -24,6 +24,7 @@ | |||
| 24 | #include <linux/fs.h> | 24 | #include <linux/fs.h> |
| 25 | #include <linux/string.h> | 25 | #include <linux/string.h> |
| 26 | #include <linux/kernel.h> | 26 | #include <linux/kernel.h> |
| 27 | #include <linux/kasan.h> | ||
| 27 | #include <linux/bug.h> | 28 | #include <linux/bug.h> |
| 28 | #include <linux/mm.h> | 29 | #include <linux/mm.h> |
| 29 | #include <linux/gfp.h> | 30 | #include <linux/gfp.h> |
| @@ -83,13 +84,22 @@ static unsigned long int get_module_load_offset(void) | |||
| 83 | 84 | ||
| 84 | void *module_alloc(unsigned long size) | 85 | void *module_alloc(unsigned long size) |
| 85 | { | 86 | { |
| 87 | void *p; | ||
| 88 | |||
| 86 | if (PAGE_ALIGN(size) > MODULES_LEN) | 89 | if (PAGE_ALIGN(size) > MODULES_LEN) |
| 87 | return NULL; | 90 | return NULL; |
| 88 | return __vmalloc_node_range(size, 1, | 91 | |
| 92 | p = __vmalloc_node_range(size, MODULE_ALIGN, | ||
| 89 | MODULES_VADDR + get_module_load_offset(), | 93 | MODULES_VADDR + get_module_load_offset(), |
| 90 | MODULES_END, GFP_KERNEL | __GFP_HIGHMEM, | 94 | MODULES_END, GFP_KERNEL | __GFP_HIGHMEM, |
| 91 | PAGE_KERNEL_EXEC, 0, NUMA_NO_NODE, | 95 | PAGE_KERNEL_EXEC, 0, NUMA_NO_NODE, |
| 92 | __builtin_return_address(0)); | 96 | __builtin_return_address(0)); |
| 97 | if (p && (kasan_module_alloc(p, size) < 0)) { | ||
| 98 | vfree(p); | ||
| 99 | return NULL; | ||
| 100 | } | ||
| 101 | |||
| 102 | return p; | ||
| 93 | } | 103 | } |
| 94 | 104 | ||
| 95 | #ifdef CONFIG_X86_32 | 105 | #ifdef CONFIG_X86_32 |
diff --git a/arch/x86/mm/kasan_init_64.c b/arch/x86/mm/kasan_init_64.c index 53508708b7aa..4860906c6b9f 100644 --- a/arch/x86/mm/kasan_init_64.c +++ b/arch/x86/mm/kasan_init_64.c | |||
| @@ -196,7 +196,7 @@ void __init kasan_init(void) | |||
| 196 | (unsigned long)kasan_mem_to_shadow(_end), | 196 | (unsigned long)kasan_mem_to_shadow(_end), |
| 197 | NUMA_NO_NODE); | 197 | NUMA_NO_NODE); |
| 198 | 198 | ||
| 199 | populate_zero_shadow(kasan_mem_to_shadow((void *)MODULES_VADDR), | 199 | populate_zero_shadow(kasan_mem_to_shadow((void *)MODULES_END), |
| 200 | (void *)KASAN_SHADOW_END); | 200 | (void *)KASAN_SHADOW_END); |
| 201 | 201 | ||
| 202 | memset(kasan_zero_page, 0, PAGE_SIZE); | 202 | memset(kasan_zero_page, 0, PAGE_SIZE); |
diff --git a/include/linux/compiler-gcc4.h b/include/linux/compiler-gcc4.h index d1a558239b1a..769e19864632 100644 --- a/include/linux/compiler-gcc4.h +++ b/include/linux/compiler-gcc4.h | |||
| @@ -85,3 +85,7 @@ | |||
| 85 | #define __HAVE_BUILTIN_BSWAP16__ | 85 | #define __HAVE_BUILTIN_BSWAP16__ |
| 86 | #endif | 86 | #endif |
| 87 | #endif /* CONFIG_ARCH_USE_BUILTIN_BSWAP */ | 87 | #endif /* CONFIG_ARCH_USE_BUILTIN_BSWAP */ |
| 88 | |||
| 89 | #if GCC_VERSION >= 40902 | ||
| 90 | #define KASAN_ABI_VERSION 3 | ||
| 91 | #endif | ||
diff --git a/include/linux/compiler-gcc5.h b/include/linux/compiler-gcc5.h index c8c565952548..efee493714eb 100644 --- a/include/linux/compiler-gcc5.h +++ b/include/linux/compiler-gcc5.h | |||
| @@ -63,3 +63,5 @@ | |||
| 63 | #define __HAVE_BUILTIN_BSWAP64__ | 63 | #define __HAVE_BUILTIN_BSWAP64__ |
| 64 | #define __HAVE_BUILTIN_BSWAP16__ | 64 | #define __HAVE_BUILTIN_BSWAP16__ |
| 65 | #endif /* CONFIG_ARCH_USE_BUILTIN_BSWAP */ | 65 | #endif /* CONFIG_ARCH_USE_BUILTIN_BSWAP */ |
| 66 | |||
| 67 | #define KASAN_ABI_VERSION 4 | ||
diff --git a/include/linux/kasan.h b/include/linux/kasan.h index d5310eef3e38..72ba725ddf9c 100644 --- a/include/linux/kasan.h +++ b/include/linux/kasan.h | |||
| @@ -49,8 +49,15 @@ void kasan_krealloc(const void *object, size_t new_size); | |||
| 49 | void kasan_slab_alloc(struct kmem_cache *s, void *object); | 49 | void kasan_slab_alloc(struct kmem_cache *s, void *object); |
| 50 | void kasan_slab_free(struct kmem_cache *s, void *object); | 50 | void kasan_slab_free(struct kmem_cache *s, void *object); |
| 51 | 51 | ||
| 52 | #define MODULE_ALIGN (PAGE_SIZE << KASAN_SHADOW_SCALE_SHIFT) | ||
| 53 | |||
| 54 | int kasan_module_alloc(void *addr, size_t size); | ||
| 55 | void kasan_module_free(void *addr); | ||
| 56 | |||
| 52 | #else /* CONFIG_KASAN */ | 57 | #else /* CONFIG_KASAN */ |
| 53 | 58 | ||
| 59 | #define MODULE_ALIGN 1 | ||
| 60 | |||
| 54 | static inline void kasan_unpoison_shadow(const void *address, size_t size) {} | 61 | static inline void kasan_unpoison_shadow(const void *address, size_t size) {} |
| 55 | 62 | ||
| 56 | static inline void kasan_enable_current(void) {} | 63 | static inline void kasan_enable_current(void) {} |
| @@ -74,6 +81,9 @@ static inline void kasan_krealloc(const void *object, size_t new_size) {} | |||
| 74 | static inline void kasan_slab_alloc(struct kmem_cache *s, void *object) {} | 81 | static inline void kasan_slab_alloc(struct kmem_cache *s, void *object) {} |
| 75 | static inline void kasan_slab_free(struct kmem_cache *s, void *object) {} | 82 | static inline void kasan_slab_free(struct kmem_cache *s, void *object) {} |
| 76 | 83 | ||
| 84 | static inline int kasan_module_alloc(void *addr, size_t size) { return 0; } | ||
| 85 | static inline void kasan_module_free(void *addr) {} | ||
| 86 | |||
| 77 | #endif /* CONFIG_KASAN */ | 87 | #endif /* CONFIG_KASAN */ |
| 78 | 88 | ||
| 79 | #endif /* LINUX_KASAN_H */ | 89 | #endif /* LINUX_KASAN_H */ |
diff --git a/kernel/module.c b/kernel/module.c index 82dc1f899e6d..8426ad48362c 100644 --- a/kernel/module.c +++ b/kernel/module.c | |||
| @@ -56,6 +56,7 @@ | |||
| 56 | #include <linux/async.h> | 56 | #include <linux/async.h> |
| 57 | #include <linux/percpu.h> | 57 | #include <linux/percpu.h> |
| 58 | #include <linux/kmemleak.h> | 58 | #include <linux/kmemleak.h> |
| 59 | #include <linux/kasan.h> | ||
| 59 | #include <linux/jump_label.h> | 60 | #include <linux/jump_label.h> |
| 60 | #include <linux/pfn.h> | 61 | #include <linux/pfn.h> |
| 61 | #include <linux/bsearch.h> | 62 | #include <linux/bsearch.h> |
| @@ -1813,6 +1814,7 @@ static void unset_module_init_ro_nx(struct module *mod) { } | |||
| 1813 | void __weak module_memfree(void *module_region) | 1814 | void __weak module_memfree(void *module_region) |
| 1814 | { | 1815 | { |
| 1815 | vfree(module_region); | 1816 | vfree(module_region); |
| 1817 | kasan_module_free(module_region); | ||
| 1816 | } | 1818 | } |
| 1817 | 1819 | ||
| 1818 | void __weak module_arch_cleanup(struct module *mod) | 1820 | void __weak module_arch_cleanup(struct module *mod) |
diff --git a/lib/Kconfig.kasan b/lib/Kconfig.kasan index 4d47d874335c..4fecaedc80a2 100644 --- a/lib/Kconfig.kasan +++ b/lib/Kconfig.kasan | |||
| @@ -6,6 +6,7 @@ if HAVE_ARCH_KASAN | |||
| 6 | config KASAN | 6 | config KASAN |
| 7 | bool "KASan: runtime memory debugger" | 7 | bool "KASan: runtime memory debugger" |
| 8 | depends on SLUB_DEBUG | 8 | depends on SLUB_DEBUG |
| 9 | select CONSTRUCTORS | ||
| 9 | help | 10 | help |
| 10 | Enables kernel address sanitizer - runtime memory debugger, | 11 | Enables kernel address sanitizer - runtime memory debugger, |
| 11 | designed to find out-of-bounds accesses and use-after-free bugs. | 12 | designed to find out-of-bounds accesses and use-after-free bugs. |
diff --git a/mm/kasan/kasan.c b/mm/kasan/kasan.c index 799c52b9826c..78fee632a7ee 100644 --- a/mm/kasan/kasan.c +++ b/mm/kasan/kasan.c | |||
| @@ -22,6 +22,7 @@ | |||
| 22 | #include <linux/memblock.h> | 22 | #include <linux/memblock.h> |
| 23 | #include <linux/memory.h> | 23 | #include <linux/memory.h> |
| 24 | #include <linux/mm.h> | 24 | #include <linux/mm.h> |
| 25 | #include <linux/module.h> | ||
| 25 | #include <linux/printk.h> | 26 | #include <linux/printk.h> |
| 26 | #include <linux/sched.h> | 27 | #include <linux/sched.h> |
| 27 | #include <linux/slab.h> | 28 | #include <linux/slab.h> |
| @@ -395,6 +396,57 @@ void kasan_kfree_large(const void *ptr) | |||
| 395 | KASAN_FREE_PAGE); | 396 | KASAN_FREE_PAGE); |
| 396 | } | 397 | } |
| 397 | 398 | ||
| 399 | int kasan_module_alloc(void *addr, size_t size) | ||
| 400 | { | ||
| 401 | void *ret; | ||
| 402 | size_t shadow_size; | ||
| 403 | unsigned long shadow_start; | ||
| 404 | |||
| 405 | shadow_start = (unsigned long)kasan_mem_to_shadow(addr); | ||
| 406 | shadow_size = round_up(size >> KASAN_SHADOW_SCALE_SHIFT, | ||
| 407 | PAGE_SIZE); | ||
| 408 | |||
| 409 | if (WARN_ON(!PAGE_ALIGNED(shadow_start))) | ||
| 410 | return -EINVAL; | ||
| 411 | |||
| 412 | ret = __vmalloc_node_range(shadow_size, 1, shadow_start, | ||
| 413 | shadow_start + shadow_size, | ||
| 414 | GFP_KERNEL | __GFP_HIGHMEM | __GFP_ZERO, | ||
| 415 | PAGE_KERNEL, VM_NO_GUARD, NUMA_NO_NODE, | ||
