diff options
-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, | ||
416 | __builtin_return_address(0)); | ||
417 | return ret ? 0 : -ENOMEM; | ||
418 | } | ||
419 | |||
420 | void kasan_module_free(void *addr) | ||
421 | { | ||
422 | vfree(kasan_mem_to_shadow(addr)); | ||
423 | } | ||
424 | |||
425 | static void register_global(struct kasan_global *global) | ||
426 | { | ||
427 | size_t aligned_size = round_up(global->size, KASAN_SHADOW_SCALE_SIZE); | ||
428 | |||
429 | kasan_unpoison_shadow(global->beg, global->size); | ||
430 | |||
431 | kasan_poison_shadow(global->beg + aligned_size, | ||
432 | global->size_with_redzone - aligned_size, | ||
433 | KASAN_GLOBAL_REDZONE); | ||
434 | } | ||
435 | |||
436 | void __asan_register_globals(struct kasan_global *globals, size_t size) | ||
437 | { | ||
438 | int i; | ||
439 | |||
440 | for (i = 0; i < size; i++) | ||
441 | register_global(&globals[i]); | ||
442 | } | ||
443 | EXPORT_SYMBOL(__asan_register_globals); | ||
444 | |||
445 | void __asan_unregister_globals(struct kasan_global *globals, size_t size) | ||
446 | { | ||
447 | } | ||
448 | EXPORT_SYMBOL(__asan_unregister_globals); | ||
449 | |||
398 | #define DEFINE_ASAN_LOAD_STORE(size) \ | 450 | #define DEFINE_ASAN_LOAD_STORE(size) \ |
399 | void __asan_load##size(unsigned long addr) \ | 451 | void __asan_load##size(unsigned long addr) \ |
400 | { \ | 452 | { \ |
diff --git a/mm/kasan/kasan.h b/mm/kasan/kasan.h index 1fcc1d81a9cf..4986b0acab21 100644 --- a/mm/kasan/kasan.h +++ b/mm/kasan/kasan.h | |||
@@ -11,6 +11,7 @@ | |||
11 | #define KASAN_PAGE_REDZONE 0xFE /* redzone for kmalloc_large allocations */ | 11 | #define KASAN_PAGE_REDZONE 0xFE /* redzone for kmalloc_large allocations */ |
12 | #define KASAN_KMALLOC_REDZONE 0xFC /* redzone inside slub object */ | 12 | #define KASAN_KMALLOC_REDZONE 0xFC /* redzone inside slub object */ |
13 | #define KASAN_KMALLOC_FREE 0xFB /* object was freed (kmem_cache_free/kfree) */ | 13 | #define KASAN_KMALLOC_FREE 0xFB /* object was freed (kmem_cache_free/kfree) */ |
14 | #define KASAN_GLOBAL_REDZONE 0xFA /* redzone for global variable */ | ||
14 | 15 | ||
15 | /* | 16 | /* |
16 | * Stack redzone shadow values | 17 | * Stack redzone shadow values |
@@ -21,6 +22,10 @@ | |||
21 | #define KASAN_STACK_RIGHT 0xF3 | 22 | #define KASAN_STACK_RIGHT 0xF3 |
22 | #define KASAN_STACK_PARTIAL 0xF4 | 23 | #define KASAN_STACK_PARTIAL 0xF4 |
23 | 24 | ||
25 | /* Don't break randconfig/all*config builds */ | ||
26 | #ifndef KASAN_ABI_VERSION | ||
27 | #define KASAN_ABI_VERSION 1 | ||
28 | #endif | ||
24 | 29 | ||
25 | struct kasan_access_info { | 30 | struct kasan_access_info { |
26 | const void *access_addr; | 31 | const void *access_addr; |
@@ -30,6 +35,26 @@ struct kasan_access_info { | |||
30 | unsigned long ip; | 35 | unsigned long ip; |
31 | }; | 36 | }; |
32 | 37 | ||
38 | /* The layout of struct dictated by compiler */ | ||
39 | struct kasan_source_location { | ||
40 | const char *filename; | ||
41 | int line_no; | ||
42 | int column_no; | ||
43 | }; | ||
44 | |||
45 | /* The layout of struct dictated by compiler */ | ||
46 | struct kasan_global { | ||
47 | const void *beg; /* Address of the beginning of the global variable. */ | ||
48 | size_t size; /* Size of the global variable. */ | ||
49 | size_t size_with_redzone; /* Size of the variable + size of the red zone. 32 bytes aligned */ | ||
50 | const void *name; | ||
51 | const void *module_name; /* Name of the module where the global variable is declared. */ | ||
52 | unsigned long has_dynamic_init; /* This needed for C++ */ | ||
53 | #if KASAN_ABI_VERSION >= 4 | ||
54 | struct kasan_source_location *location; | ||
55 | #endif | ||
56 | }; | ||
57 | |||
33 | void kasan_report_error(struct kasan_access_info *info); | 58 | void kasan_report_error(struct kasan_access_info *info); |
34 | void kasan_report_user_access(struct kasan_access_info *info); | 59 | void kasan_report_user_access(struct kasan_access_info *info); |
35 | 60 | ||
diff --git a/mm/kasan/report.c b/mm/kasan/report.c index 866732ef3db3..680ceedf810a 100644 --- a/mm/kasan/report.c +++ b/mm/kasan/report.c | |||
@@ -23,6 +23,8 @@ | |||
23 | #include <linux/types.h> | 23 | #include <linux/types.h> |
24 | #include <linux/kasan.h> | 24 | #include <linux/kasan.h> |
25 | 25 | ||
26 | #include <asm/sections.h> | ||
27 | |||
26 | #include "kasan.h" | 28 | #include "kasan.h" |
27 | #include "../slab.h" | 29 | #include "../slab.h" |
28 | 30 | ||
@@ -61,6 +63,7 @@ static void print_error_description(struct kasan_access_info *info) | |||
61 | break; | 63 | break; |
62 | case KASAN_PAGE_REDZONE: | 64 | case KASAN_PAGE_REDZONE: |
63 | case KASAN_KMALLOC_REDZONE: | 65 | case KASAN_KMALLOC_REDZONE: |
66 | case KASAN_GLOBAL_REDZONE: | ||
64 | case 0 ... KASAN_SHADOW_SCALE_SIZE - 1: | 67 | case 0 ... KASAN_SHADOW_SCALE_SIZE - 1: |
65 | bug_type = "out of bounds access"; | 68 | bug_type = "out of bounds access"; |
66 | break; | 69 | break; |
@@ -80,6 +83,20 @@ static void print_error_description(struct kasan_access_info *info) | |||
80 | info->access_size, current->comm, task_pid_nr(current)); | 83 | info->access_size, current->comm, task_pid_nr(current)); |
81 | } | 84 | } |
82 | 85 | ||
86 | static inline bool kernel_or_module_addr(const void *addr) | ||
87 | { | ||
88 | return (addr >= (void *)_stext && addr < (void *)_end) | ||
89 | || (addr >= (void *)MODULES_VADDR | ||
90 | && addr < (void *)MODULES_END); | ||
91 | } | ||
92 | |||
93 | static inline bool init_task_stack_addr(const void *addr) | ||
94 | { | ||
95 | return addr >= (void *)&init_thread_union.stack && | ||
96 | (addr <= (void *)&init_thread_union.stack + | ||
97 | sizeof(init_thread_union.stack)); | ||
98 | } | ||
99 | |||
83 | static void print_address_description(struct kasan_access_info *info) | 100 | static void print_address_description(struct kasan_access_info *info) |
84 | { | 101 | { |
85 | const void *addr = info->access_addr; | 102 | const void *addr = info->access_addr; |
@@ -107,6 +124,11 @@ static void print_address_description(struct kasan_access_info *info) | |||
107 | dump_page(page, "kasan: bad access detected"); | 124 | dump_page(page, "kasan: bad access detected"); |
108 | } | 125 | } |
109 | 126 | ||
127 | if (kernel_or_module_addr(addr)) { | ||
128 | if (!init_task_stack_addr(addr)) | ||
129 | pr_err("Address belongs to variable %pS\n", addr); | ||
130 | } | ||
131 | |||
110 | dump_stack(); | 132 | dump_stack(); |
111 | } | 133 | } |
112 | 134 | ||
diff --git a/scripts/Makefile.kasan b/scripts/Makefile.kasan index 2163b8cc446e..631619b2b118 100644 --- a/scripts/Makefile.kasan +++ b/scripts/Makefile.kasan | |||
@@ -9,7 +9,7 @@ CFLAGS_KASAN_MINIMAL := -fsanitize=kernel-address | |||
9 | 9 | ||
10 | CFLAGS_KASAN := $(call cc-option, -fsanitize=kernel-address \ | 10 | CFLAGS_KASAN := $(call cc-option, -fsanitize=kernel-address \ |
11 | -fasan-shadow-offset=$(CONFIG_KASAN_SHADOW_OFFSET) \ | 11 | -fasan-shadow-offset=$(CONFIG_KASAN_SHADOW_OFFSET) \ |
12 | --param asan-stack=1 \ | 12 | --param asan-stack=1 --param asan-globals=1 \ |
13 | --param asan-instrumentation-with-call-threshold=$(call_threshold)) | 13 | --param asan-instrumentation-with-call-threshold=$(call_threshold)) |
14 | 14 | ||
15 | ifeq ($(call cc-option, $(CFLAGS_KASAN_MINIMAL) -Werror),) | 15 | ifeq ($(call cc-option, $(CFLAGS_KASAN_MINIMAL) -Werror),) |