Skip to content

Commit bebf56a

Browse files
aryabinintorvalds
authored andcommitted
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 <[email protected]> Cc: Dmitry Vyukov <[email protected]> Cc: Konstantin Serebryany <[email protected]> Cc: Dmitry Chernenkov <[email protected]> Signed-off-by: Andrey Konovalov <[email protected]> Cc: Yuri Gribov <[email protected]> Cc: Konstantin Khlebnikov <[email protected]> Cc: Sasha Levin <[email protected]> Cc: Christoph Lameter <[email protected]> Cc: Joonsoo Kim <[email protected]> Cc: Dave Hansen <[email protected]> Cc: Andi Kleen <[email protected]> Cc: Ingo Molnar <[email protected]> Cc: Thomas Gleixner <[email protected]> Cc: "H. Peter Anvin" <[email protected]> Cc: Christoph Lameter <[email protected]> Cc: Pekka Enberg <[email protected]> Cc: David Rientjes <[email protected]> Signed-off-by: Andrew Morton <[email protected]> Signed-off-by: Linus Torvalds <[email protected]>
1 parent 6301939 commit bebf56a

File tree

12 files changed

+132
-4
lines changed

12 files changed

+132
-4
lines changed

Documentation/kasan.txt

Lines changed: 1 addition & 1 deletion
Original file line numberDiff line numberDiff line change
@@ -9,7 +9,7 @@ a fast and comprehensive solution for finding use-after-free and out-of-bounds
99
bugs.
1010

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

1414
Currently KASan is supported only for x86_64 architecture and requires that the
1515
kernel be built with the SLUB allocator.

arch/x86/kernel/module.c

Lines changed: 11 additions & 1 deletion
Original file line numberDiff line numberDiff line change
@@ -24,6 +24,7 @@
2424
#include <linux/fs.h>
2525
#include <linux/string.h>
2626
#include <linux/kernel.h>
27+
#include <linux/kasan.h>
2728
#include <linux/bug.h>
2829
#include <linux/mm.h>
2930
#include <linux/gfp.h>
@@ -83,13 +84,22 @@ static unsigned long int get_module_load_offset(void)
8384

8485
void *module_alloc(unsigned long size)
8586
{
87+
void *p;
88+
8689
if (PAGE_ALIGN(size) > MODULES_LEN)
8790
return NULL;
88-
return __vmalloc_node_range(size, 1,
91+
92+
p = __vmalloc_node_range(size, MODULE_ALIGN,
8993
MODULES_VADDR + get_module_load_offset(),
9094
MODULES_END, GFP_KERNEL | __GFP_HIGHMEM,
9195
PAGE_KERNEL_EXEC, 0, NUMA_NO_NODE,
9296
__builtin_return_address(0));
97+
if (p && (kasan_module_alloc(p, size) < 0)) {
98+
vfree(p);
99+
return NULL;
100+
}
101+
102+
return p;
93103
}
94104

95105
#ifdef CONFIG_X86_32

arch/x86/mm/kasan_init_64.c

Lines changed: 1 addition & 1 deletion
Original file line numberDiff line numberDiff line change
@@ -196,7 +196,7 @@ void __init kasan_init(void)
196196
(unsigned long)kasan_mem_to_shadow(_end),
197197
NUMA_NO_NODE);
198198

199-
populate_zero_shadow(kasan_mem_to_shadow((void *)MODULES_VADDR),
199+
populate_zero_shadow(kasan_mem_to_shadow((void *)MODULES_END),
200200
(void *)KASAN_SHADOW_END);
201201

202202
memset(kasan_zero_page, 0, PAGE_SIZE);

include/linux/compiler-gcc4.h

Lines changed: 4 additions & 0 deletions
Original file line numberDiff line numberDiff line change
@@ -85,3 +85,7 @@
8585
#define __HAVE_BUILTIN_BSWAP16__
8686
#endif
8787
#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

Lines changed: 2 additions & 0 deletions
Original file line numberDiff line numberDiff line change
@@ -63,3 +63,5 @@
6363
#define __HAVE_BUILTIN_BSWAP64__
6464
#define __HAVE_BUILTIN_BSWAP16__
6565
#endif /* CONFIG_ARCH_USE_BUILTIN_BSWAP */
66+
67+
#define KASAN_ABI_VERSION 4

include/linux/kasan.h

Lines changed: 10 additions & 0 deletions
Original file line numberDiff line numberDiff line change
@@ -49,8 +49,15 @@ void kasan_krealloc(const void *object, size_t new_size);
4949
void kasan_slab_alloc(struct kmem_cache *s, void *object);
5050
void kasan_slab_free(struct kmem_cache *s, void *object);
5151

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+
5257
#else /* CONFIG_KASAN */
5358

59+
#define MODULE_ALIGN 1
60+
5461
static inline void kasan_unpoison_shadow(const void *address, size_t size) {}
5562

5663
static inline void kasan_enable_current(void) {}
@@ -74,6 +81,9 @@ static inline void kasan_krealloc(const void *object, size_t new_size) {}
7481
static inline void kasan_slab_alloc(struct kmem_cache *s, void *object) {}
7582
static inline void kasan_slab_free(struct kmem_cache *s, void *object) {}
7683

84+
static inline int kasan_module_alloc(void *addr, size_t size) { return 0; }
85+
static inline void kasan_module_free(void *addr) {}
86+
7787
#endif /* CONFIG_KASAN */
7888

7989
#endif /* LINUX_KASAN_H */

kernel/module.c

Lines changed: 2 additions & 0 deletions
Original file line numberDiff line numberDiff line change
@@ -56,6 +56,7 @@
5656
#include <linux/async.h>
5757
#include <linux/percpu.h>
5858
#include <linux/kmemleak.h>
59+
#include <linux/kasan.h>
5960
#include <linux/jump_label.h>
6061
#include <linux/pfn.h>
6162
#include <linux/bsearch.h>
@@ -1813,6 +1814,7 @@ static void unset_module_init_ro_nx(struct module *mod) { }
18131814
void __weak module_memfree(void *module_region)
18141815
{
18151816
vfree(module_region);
1817+
kasan_module_free(module_region);
18161818
}
18171819

18181820
void __weak module_arch_cleanup(struct module *mod)

lib/Kconfig.kasan

Lines changed: 1 addition & 0 deletions
Original file line numberDiff line numberDiff line change
@@ -6,6 +6,7 @@ if HAVE_ARCH_KASAN
66
config KASAN
77
bool "KASan: runtime memory debugger"
88
depends on SLUB_DEBUG
9+
select CONSTRUCTORS
910
help
1011
Enables kernel address sanitizer - runtime memory debugger,
1112
designed to find out-of-bounds accesses and use-after-free bugs.

mm/kasan/kasan.c

Lines changed: 52 additions & 0 deletions
Original file line numberDiff line numberDiff line change
@@ -22,6 +22,7 @@
2222
#include <linux/memblock.h>
2323
#include <linux/memory.h>
2424
#include <linux/mm.h>
25+
#include <linux/module.h>
2526
#include <linux/printk.h>
2627
#include <linux/sched.h>
2728
#include <linux/slab.h>
@@ -395,6 +396,57 @@ void kasan_kfree_large(const void *ptr)
395396
KASAN_FREE_PAGE);
396397
}
397398

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+
398450
#define DEFINE_ASAN_LOAD_STORE(size) \
399451
void __asan_load##size(unsigned long addr) \
400452
{ \

mm/kasan/kasan.h

Lines changed: 25 additions & 0 deletions
Original file line numberDiff line numberDiff line change
@@ -11,6 +11,7 @@
1111
#define KASAN_PAGE_REDZONE 0xFE /* redzone for kmalloc_large allocations */
1212
#define KASAN_KMALLOC_REDZONE 0xFC /* redzone inside slub object */
1313
#define KASAN_KMALLOC_FREE 0xFB /* object was freed (kmem_cache_free/kfree) */
14+
#define KASAN_GLOBAL_REDZONE 0xFA /* redzone for global variable */
1415

1516
/*
1617
* Stack redzone shadow values
@@ -21,6 +22,10 @@
2122
#define KASAN_STACK_RIGHT 0xF3
2223
#define KASAN_STACK_PARTIAL 0xF4
2324

25+
/* Don't break randconfig/all*config builds */
26+
#ifndef KASAN_ABI_VERSION
27+
#define KASAN_ABI_VERSION 1
28+
#endif
2429

2530
struct kasan_access_info {
2631
const void *access_addr;
@@ -30,6 +35,26 @@ struct kasan_access_info {
3035
unsigned long ip;
3136
};
3237

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+
3358
void kasan_report_error(struct kasan_access_info *info);
3459
void kasan_report_user_access(struct kasan_access_info *info);
3560

mm/kasan/report.c

Lines changed: 22 additions & 0 deletions
Original file line numberDiff line numberDiff line change
@@ -23,6 +23,8 @@
2323
#include <linux/types.h>
2424
#include <linux/kasan.h>
2525

26+
#include <asm/sections.h>
27+
2628
#include "kasan.h"
2729
#include "../slab.h"
2830

@@ -61,6 +63,7 @@ static void print_error_description(struct kasan_access_info *info)
6163
break;
6264
case KASAN_PAGE_REDZONE:
6365
case KASAN_KMALLOC_REDZONE:
66+
case KASAN_GLOBAL_REDZONE:
6467
case 0 ... KASAN_SHADOW_SCALE_SIZE - 1:
6568
bug_type = "out of bounds access";
6669
break;
@@ -80,6 +83,20 @@ static void print_error_description(struct kasan_access_info *info)
8083
info->access_size, current->comm, task_pid_nr(current));
8184
}
8285

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+
83100
static void print_address_description(struct kasan_access_info *info)
84101
{
85102
const void *addr = info->access_addr;
@@ -107,6 +124,11 @@ static void print_address_description(struct kasan_access_info *info)
107124
dump_page(page, "kasan: bad access detected");
108125
}
109126

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+
110132
dump_stack();
111133
}
112134

scripts/Makefile.kasan

Lines changed: 1 addition & 1 deletion
Original file line numberDiff line numberDiff line change
@@ -9,7 +9,7 @@ CFLAGS_KASAN_MINIMAL := -fsanitize=kernel-address
99

1010
CFLAGS_KASAN := $(call cc-option, -fsanitize=kernel-address \
1111
-fasan-shadow-offset=$(CONFIG_KASAN_SHADOW_OFFSET) \
12-
--param asan-stack=1 \
12+
--param asan-stack=1 --param asan-globals=1 \
1313
--param asan-instrumentation-with-call-threshold=$(call_threshold))
1414

1515
ifeq ($(call cc-option, $(CFLAGS_KASAN_MINIMAL) -Werror),)

0 commit comments

Comments
 (0)