History

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.

Andrey Ryabinin committed 11 years ago

Commit bebf56a1 parent 630193912 files · +132 −4Browse files

Unified · Split

12 files
  1. Documentation/kasan.txt +1 −1
  2. arch/x86/kernel/module.c +11 −1
  3. arch/x86/mm/kasan_init_64.c +1 −1
  4. include/linux/compiler-gcc4.h +4 −0
  5. include/linux/compiler-gcc5.h +2 −0
  6. include/linux/kasan.h +10 −0
  7. kernel/module.c +2 −0
  8. lib/Kconfig.kasan +1 −0
  9. mm/kasan/kasan.c +52 −0
  10. mm/kasan/kasan.h +25 −0
  11. mm/kasan/report.c +22 −0
  12. scripts/Makefile.kasan +1 −1
Documentation/kasan.txt+1−1
@@ -9,7 +9,7 @@ a fast and comprehensive solution for finding use-after-free and out-of-bounds · 8 unmodified lines
9bugs.9bugs.
10 10
11KASan uses compile-time instrumentation for checking every memory access,11KASan uses compile-time instrumentation for checking every memory access,
12therefore you will need a certain version of GCC >= 4.9.212therefore you will need a certain version of GCC > 4.9.2
13 13
14Currently KASan is supported only for x86_64 architecture and requires that the14Currently KASan is supported only for x86_64 architecture and requires that the
15kernel be built with the SLUB allocator.15kernel be built with the SLUB allocator.
arch/x86/kernel/module.c+11−1
@@ -24,6 +24,7 @@ · 23 unmodified lines
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) · 53 unmodified lines
83 84
84void *module_alloc(unsigned long size)85void *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_32105#ifdef CONFIG_X86_32
arch/x86/mm/kasan_init_64.c+1−1
@@ -196,7 +196,7 @@ void __init kasan_init(void) · 195 unmodified lines
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);
include/linux/compiler-gcc4.h+4−0
@@ -85,3 +85,7 @@ · 84 unmodified lines
85#define __HAVE_BUILTIN_BSWAP16__85#define __HAVE_BUILTIN_BSWAP16__
86#endif86#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
include/linux/compiler-gcc5.h+2−0
@@ -63,3 +63,5 @@ · 62 unmodified lines
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
include/linux/kasan.h+10−0
@@ -49,8 +49,15 @@ void kasan_krealloc(const void *object, size_t new_size); · 48 unmodified lines
49void kasan_slab_alloc(struct kmem_cache *s, void *object);49void kasan_slab_alloc(struct kmem_cache *s, void *object);
50void kasan_slab_free(struct kmem_cache *s, void *object);50void kasan_slab_free(struct kmem_cache *s, void *object);
51 51
52#define MODULE_ALIGN (PAGE_SIZE << KASAN_SHADOW_SCALE_SHIFT)
53
54int kasan_module_alloc(void *addr, size_t size);
55void kasan_module_free(void *addr);
56
52#else /* CONFIG_KASAN */57#else /* CONFIG_KASAN */
53 58
59#define MODULE_ALIGN 1
60
54static inline void kasan_unpoison_shadow(const void *address, size_t size) {}61static inline void kasan_unpoison_shadow(const void *address, size_t size) {}
55 62
56static inline void kasan_enable_current(void) {}63static inline void kasan_enable_current(void) {}
@@ -74,6 +81,9 @@ static inline void kasan_krealloc(const void *object, size_t new_size) {} · 17 unmodified lines
74static inline void kasan_slab_alloc(struct kmem_cache *s, void *object) {}81static inline void kasan_slab_alloc(struct kmem_cache *s, void *object) {}
75static inline void kasan_slab_free(struct kmem_cache *s, void *object) {}82static inline void kasan_slab_free(struct kmem_cache *s, void *object) {}
76 83
84static inline int kasan_module_alloc(void *addr, size_t size) { return 0; }
85static 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 */
kernel/module.c+2−0
@@ -56,6 +56,7 @@ · 55 unmodified lines
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) { } · 1751 unmodified lines
1813void __weak module_memfree(void *module_region)1814void __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
1818void __weak module_arch_cleanup(struct module *mod)1820void __weak module_arch_cleanup(struct module *mod)
lib/Kconfig.kasan+1−0
@@ -6,6 +6,7 @@ if HAVE_ARCH_KASAN · 5 unmodified lines
6config KASAN6config KASAN
7 bool "KASan: runtime memory debugger"7 bool "KASan: runtime memory debugger"
8 depends on SLUB_DEBUG8 depends on SLUB_DEBUG
9 select CONSTRUCTORS
9 help10 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.
mm/kasan/kasan.c+52−0
@@ -22,6 +22,7 @@ · 21 unmodified lines
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) · 367 unmodified lines
395 KASAN_FREE_PAGE);396 KASAN_FREE_PAGE);
396}397}
397 398
399int 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
420void kasan_module_free(void *addr)
421{
422 vfree(kasan_mem_to_shadow(addr));
423}
424
425static 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
436void __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}
443EXPORT_SYMBOL(__asan_register_globals);
444
445void __asan_unregister_globals(struct kasan_global *globals, size_t size)
446{
447}
448EXPORT_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 { \
mm/kasan/kasan.h+25−0
@@ -11,6 +11,7 @@ · 10 unmodified lines
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 values17 * Stack redzone shadow values
@@ -21,6 +22,10 @@ · 4 unmodified lines
21#define KASAN_STACK_RIGHT 0xF322#define KASAN_STACK_RIGHT 0xF3
22#define KASAN_STACK_PARTIAL 0xF423#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
25struct kasan_access_info {30struct kasan_access_info {
26 const void *access_addr;31 const void *access_addr;
@@ -30,6 +35,26 @@ struct kasan_access_info { · 3 unmodified lines
30 unsigned long ip;35 unsigned long ip;
31};36};
32 37
38/* The layout of struct dictated by compiler */
39struct 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 */
46struct 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
33void kasan_report_error(struct kasan_access_info *info);58void kasan_report_error(struct kasan_access_info *info);
34void kasan_report_user_access(struct kasan_access_info *info);59void kasan_report_user_access(struct kasan_access_info *info);
35 60
mm/kasan/report.c+22−0
@@ -23,6 +23,8 @@ · 22 unmodified lines
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) · 32 unmodified lines
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) · 13 unmodified lines
80 info->access_size, current->comm, task_pid_nr(current));83 info->access_size, current->comm, task_pid_nr(current));
81}84}
82 85
86static 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
93static 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
83static void print_address_description(struct kasan_access_info *info)100static 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) · 21 unmodified lines
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
scripts/Makefile.kasan+1−1
@@ -9,7 +9,7 @@ CFLAGS_KASAN_MINIMAL := -fsanitize=kernel-address · 8 unmodified lines
9 9
10CFLAGS_KASAN := $(call cc-option, -fsanitize=kernel-address \10CFLAGS_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
15ifeq ($(call cc-option, $(CFLAGS_KASAN_MINIMAL) -Werror),)15ifeq ($(call cc-option, $(CFLAGS_KASAN_MINIMAL) -Werror),)

Keyboard shortcuts

General

Show keyboard shortcuts
?
Focus the current search
/

Issues and pull requests

Open filters
f

Issues, pull requests and Inbox

Select next item
j↓
Select previous item
k↑
Open selected item
Enter
Dismiss filters or clear selection
Esc

Repository code

Go to file
t