diff options
Diffstat (limited to 'mm')
-rw-r--r-- | mm/kasan/kasan.c | 52 | ||||
-rw-r--r-- | mm/kasan/kasan.h | 25 | ||||
-rw-r--r-- | mm/kasan/report.c | 22 |
3 files changed, 99 insertions, 0 deletions
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 | ||