diff options
author | Dmitry Vyukov <dvyukov@google.com> | 2016-11-30 18:54:16 -0500 |
---|---|---|
committer | Linus Torvalds <torvalds@linux-foundation.org> | 2016-11-30 19:32:52 -0500 |
commit | 828347f8f9a558cf1af2faa46387a26564f2ac3e (patch) | |
tree | 4acb63366cc89b3e87e85805530911300dd4ab19 | |
parent | 045d599a286bc01daa3510d59272440a17b23c2e (diff) |
kasan: support use-after-scope detection
Gcc revision 241896 implements use-after-scope detection. Will be
available in gcc 7. Support it in KASAN.
Gcc emits 2 new callbacks to poison/unpoison large stack objects when
they go in/out of scope. Implement the callbacks and add a test.
[dvyukov@google.com: v3]
Link: http://lkml.kernel.org/r/1479998292-144502-1-git-send-email-dvyukov@google.com
Link: http://lkml.kernel.org/r/1479226045-145148-1-git-send-email-dvyukov@google.com
Signed-off-by: Dmitry Vyukov <dvyukov@google.com>
Acked-by: Andrey Ryabinin <aryabinin@virtuozzo.com>
Cc: Alexander Potapenko <glider@google.com>
Cc: <stable@vger.kernel.org> [4.0+]
Signed-off-by: Andrew Morton <akpm@linux-foundation.org>
Signed-off-by: Linus Torvalds <torvalds@linux-foundation.org>
-rw-r--r-- | lib/test_kasan.c | 29 | ||||
-rw-r--r-- | mm/kasan/kasan.c | 19 | ||||
-rw-r--r-- | mm/kasan/kasan.h | 1 | ||||
-rw-r--r-- | mm/kasan/report.c | 3 |
4 files changed, 52 insertions, 0 deletions
diff --git a/lib/test_kasan.c b/lib/test_kasan.c index 5e51872b3fc1..fbdf87920093 100644 --- a/lib/test_kasan.c +++ b/lib/test_kasan.c | |||
@@ -20,6 +20,11 @@ | |||
20 | #include <linux/uaccess.h> | 20 | #include <linux/uaccess.h> |
21 | #include <linux/module.h> | 21 | #include <linux/module.h> |
22 | 22 | ||
23 | /* | ||
24 | * Note: test functions are marked noinline so that their names appear in | ||
25 | * reports. | ||
26 | */ | ||
27 | |||
23 | static noinline void __init kmalloc_oob_right(void) | 28 | static noinline void __init kmalloc_oob_right(void) |
24 | { | 29 | { |
25 | char *ptr; | 30 | char *ptr; |
@@ -411,6 +416,29 @@ static noinline void __init copy_user_test(void) | |||
411 | kfree(kmem); | 416 | kfree(kmem); |
412 | } | 417 | } |
413 | 418 | ||
419 | static noinline void __init use_after_scope_test(void) | ||
420 | { | ||
421 | volatile char *volatile p; | ||
422 | |||
423 | pr_info("use-after-scope on int\n"); | ||
424 | { | ||
425 | int local = 0; | ||
426 | |||
427 | p = (char *)&local; | ||
428 | } | ||
429 | p[0] = 1; | ||
430 | p[3] = 1; | ||
431 | |||
432 | pr_info("use-after-scope on array\n"); | ||
433 | { | ||
434 | char local[1024] = {0}; | ||
435 | |||
436 | p = local; | ||
437 | } | ||
438 | p[0] = 1; | ||
439 | p[1023] = 1; | ||
440 | } | ||
441 | |||
414 | static int __init kmalloc_tests_init(void) | 442 | static int __init kmalloc_tests_init(void) |
415 | { | 443 | { |
416 | kmalloc_oob_right(); | 444 | kmalloc_oob_right(); |
@@ -436,6 +464,7 @@ static int __init kmalloc_tests_init(void) | |||
436 | kasan_global_oob(); | 464 | kasan_global_oob(); |
437 | ksize_unpoisons_memory(); | 465 | ksize_unpoisons_memory(); |
438 | copy_user_test(); | 466 | copy_user_test(); |
467 | use_after_scope_test(); | ||
439 | return -EAGAIN; | 468 | return -EAGAIN; |
440 | } | 469 | } |
441 | 470 | ||
diff --git a/mm/kasan/kasan.c b/mm/kasan/kasan.c index 70c009741aab..0e9505f66ec1 100644 --- a/mm/kasan/kasan.c +++ b/mm/kasan/kasan.c | |||
@@ -764,6 +764,25 @@ EXPORT_SYMBOL(__asan_storeN_noabort); | |||
764 | void __asan_handle_no_return(void) {} | 764 | void __asan_handle_no_return(void) {} |
765 | EXPORT_SYMBOL(__asan_handle_no_return); | 765 | EXPORT_SYMBOL(__asan_handle_no_return); |
766 | 766 | ||
767 | /* Emitted by compiler to poison large objects when they go out of scope. */ | ||
768 | void __asan_poison_stack_memory(const void *addr, size_t size) | ||
769 | { | ||
770 | /* | ||
771 | * Addr is KASAN_SHADOW_SCALE_SIZE-aligned and the object is surrounded | ||
772 | * by redzones, so we simply round up size to simplify logic. | ||
773 | */ | ||
774 | kasan_poison_shadow(addr, round_up(size, KASAN_SHADOW_SCALE_SIZE), | ||
775 | KASAN_USE_AFTER_SCOPE); | ||
776 | } | ||
777 | EXPORT_SYMBOL(__asan_poison_stack_memory); | ||
778 | |||
779 | /* Emitted by compiler to unpoison large objects when they go into scope. */ | ||
780 | void __asan_unpoison_stack_memory(const void *addr, size_t size) | ||
781 | { | ||
782 | kasan_unpoison_shadow(addr, size); | ||
783 | } | ||
784 | EXPORT_SYMBOL(__asan_unpoison_stack_memory); | ||
785 | |||
767 | #ifdef CONFIG_MEMORY_HOTPLUG | 786 | #ifdef CONFIG_MEMORY_HOTPLUG |
768 | static int kasan_mem_notifier(struct notifier_block *nb, | 787 | static int kasan_mem_notifier(struct notifier_block *nb, |
769 | unsigned long action, void *data) | 788 | unsigned long action, void *data) |
diff --git a/mm/kasan/kasan.h b/mm/kasan/kasan.h index 03f4545b103d..1c260e6b3b3c 100644 --- a/mm/kasan/kasan.h +++ b/mm/kasan/kasan.h | |||
@@ -21,6 +21,7 @@ | |||
21 | #define KASAN_STACK_MID 0xF2 | 21 | #define KASAN_STACK_MID 0xF2 |
22 | #define KASAN_STACK_RIGHT 0xF3 | 22 | #define KASAN_STACK_RIGHT 0xF3 |
23 | #define KASAN_STACK_PARTIAL 0xF4 | 23 | #define KASAN_STACK_PARTIAL 0xF4 |
24 | #define KASAN_USE_AFTER_SCOPE 0xF8 | ||
24 | 25 | ||
25 | /* Don't break randconfig/all*config builds */ | 26 | /* Don't break randconfig/all*config builds */ |
26 | #ifndef KASAN_ABI_VERSION | 27 | #ifndef KASAN_ABI_VERSION |
diff --git a/mm/kasan/report.c b/mm/kasan/report.c index 24c1211fe9d5..073325aedc68 100644 --- a/mm/kasan/report.c +++ b/mm/kasan/report.c | |||
@@ -90,6 +90,9 @@ static void print_error_description(struct kasan_access_info *info) | |||
90 | case KASAN_KMALLOC_FREE: | 90 | case KASAN_KMALLOC_FREE: |
91 | bug_type = "use-after-free"; | 91 | bug_type = "use-after-free"; |
92 | break; | 92 | break; |
93 | case KASAN_USE_AFTER_SCOPE: | ||
94 | bug_type = "use-after-scope"; | ||
95 | break; | ||
93 | } | 96 | } |
94 | 97 | ||
95 | pr_err("BUG: KASAN: %s in %pS at addr %p\n", | 98 | pr_err("BUG: KASAN: %s in %pS at addr %p\n", |