[PATCH] kasan: support use-after-scope detection

From: Dmitry Vyukov
Date: Tue Nov 15 2016 - 11:08:00 EST


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.

Signed-off-by: Dmitry Vyukov <dvyukov@xxxxxxxxxx>
Cc: aryabinin@xxxxxxxxxxxxx
Cc: glider@xxxxxxxxxx
Cc: akpm@xxxxxxxxxxxxxxxxxxxx
Cc: kasan-dev@xxxxxxxxxxxxxxxx
Cc: linux-mm@xxxxxxxxx
Cc: linux-kernel@xxxxxxxxxxxxxxx

---
FTR here are reports from the test with gcc 7:

kasan test: use_after_scope_test use-after-scope on int
==================================================================
BUG: KASAN: use-after-scope in use_after_scope_test+0xe0/0x25b [test_kasan] at addr ffff8800359b72b0
Write of size 1 by task insmod/6644
page:ffffea0000d66dc0 count:0 mapcount:0 mapping: (null) index:0x0
flags: 0x1fffc0000000000()
page dumped because: kasan: bad access detected
CPU: 2 PID: 6644 Comm: insmod Tainted: G B 4.9.0-rc5+ #39
Hardware name: QEMU Standard PC (i440FX + PIIX, 1996), BIOS Bochs 01/01/2011
ffff8800359b71f0 ffffffff834c2999 ffffffff00000002 1ffff10006b36dd1
ffffed0006b36dc9 0000000041b58ab3 ffffffff89575430 ffffffff834c26ab
0000000000000000 0000000000000000 0000000000000001 0000000000000000
Call Trace:
[< inline >] __dump_stack lib/dump_stack.c:15
[<ffffffff834c2999>] dump_stack+0x2ee/0x3f5 lib/dump_stack.c:51
[< inline >] print_address_description mm/kasan/report.c:207
[< inline >] kasan_report_error mm/kasan/report.c:286
[<ffffffff819f0ec0>] kasan_report+0x490/0x4c0 mm/kasan/report.c:306
[<ffffffff819f0fac>] __asan_report_store1_noabort+0x1c/0x20 mm/kasan/report.c:334
[<ffffffffa00102ba>] use_after_scope_test+0xe0/0x25b [test_kasan] lib/test_kasan.c:424
[<ffffffffa00114b8>] kmalloc_tests_init+0x72/0x79 [test_kasan]
[<ffffffff8100244b>] do_one_initcall+0xfb/0x3f0 init/main.c:778
[<ffffffff8184a813>] do_init_module+0x219/0x59c kernel/module.c:3386
[<ffffffff81658218>] load_module+0x5918/0x8c40 kernel/module.c:3706
[<ffffffff8165b939>] SYSC_init_module+0x3f9/0x470 kernel/module.c:3776
[<ffffffff8165bd2e>] SyS_init_module+0xe/0x10 kernel/module.c:3759
[<ffffffff88143885>] entry_SYSCALL_64_fastpath+0x23/0xc6 arch/x86/entry/entry_64.S:209
Memory state around the buggy address:
ffff8800359b7180: 00 00 00 00 00 00 00 00 00 00 00 00 00 00 00 00
ffff8800359b7200: 00 00 00 00 00 00 00 00 00 00 00 00 00 00 00 00
>ffff8800359b7280: 00 00 f1 f1 f1 f1 f8 f2 f2 f2 f2 f2 f2 f2 00 f2
^
ffff8800359b7300: f2 f2 f2 f2 f2 f2 00 00 00 00 00 00 00 00 00 00
ffff8800359b7380: 00 00 00 00 00 00 00 00 00 00 00 00 00 00 00 00
==================================================================
==================================================================
BUG: KASAN: use-after-scope in use_after_scope_test+0x118/0x25b [test_kasan] at addr ffff8800359b72b3
Write of size 1 by task insmod/6644
page:ffffea0000d66dc0 count:0 mapcount:0 mapping: (null) index:0x0
flags: 0x1fffc0000000000()
page dumped because: kasan: bad access detected
CPU: 2 PID: 6644 Comm: insmod Tainted: G B 4.9.0-rc5+ #39
Hardware name: QEMU Standard PC (i440FX + PIIX, 1996), BIOS Bochs 01/01/2011
ffff8800359b71f0 ffffffff834c2999 ffffffff00000002 1ffff10006b36dd1
ffffed0006b36dc9 0000000041b58ab3 ffffffff89575430 ffffffff834c26ab
0000000000000000 0000000000000000 0000000000000001 0000000000000000
Call Trace:
[< inline >] __dump_stack lib/dump_stack.c:15
[<ffffffff834c2999>] dump_stack+0x2ee/0x3f5 lib/dump_stack.c:51
[< inline >] print_address_description mm/kasan/report.c:207
[< inline >] kasan_report_error mm/kasan/report.c:286
[<ffffffff819f0ec0>] kasan_report+0x490/0x4c0 mm/kasan/report.c:306
[<ffffffff819f0fac>] __asan_report_store1_noabort+0x1c/0x20 mm/kasan/report.c:334
[<ffffffffa00102f2>] use_after_scope_test+0x118/0x25b [test_kasan] lib/test_kasan.c:425
[<ffffffffa00114b8>] kmalloc_tests_init+0x72/0x79 [test_kasan]
[<ffffffff8100244b>] do_one_initcall+0xfb/0x3f0 init/main.c:778
[<ffffffff8184a813>] do_init_module+0x219/0x59c kernel/module.c:3386
[<ffffffff81658218>] load_module+0x5918/0x8c40 kernel/module.c:3706
[<ffffffff8165b939>] SYSC_init_module+0x3f9/0x470 kernel/module.c:3776
[<ffffffff8165bd2e>] SyS_init_module+0xe/0x10 kernel/module.c:3759
[<ffffffff88143885>] entry_SYSCALL_64_fastpath+0x23/0xc6 arch/x86/entry/entry_64.S:209
Memory state around the buggy address:
ffff8800359b7180: 00 00 00 00 00 00 00 00 00 00 00 00 00 00 00 00
ffff8800359b7200: 00 00 00 00 00 00 00 00 00 00 00 00 00 00 00 00
>ffff8800359b7280: 00 00 f1 f1 f1 f1 f8 f2 f2 f2 f2 f2 f2 f2 00 f2
^
ffff8800359b7300: f2 f2 f2 f2 f2 f2 00 00 00 00 00 00 00 00 00 00
ffff8800359b7380: 00 00 00 00 00 00 00 00 00 00 00 00 00 00 00 00
==================================================================
kasan test: use_after_scope_test use-after-scope on array
==================================================================
BUG: KASAN: use-after-scope in use_after_scope_test+0x1ee/0x25b [test_kasan] at addr ffff8800359b7330
Write of size 1 by task insmod/6644
page:ffffea0000d66dc0 count:0 mapcount:0 mapping: (null) index:0x0
flags: 0x1fffc0000000000()
page dumped because: kasan: bad access detected
CPU: 2 PID: 6644 Comm: insmod Tainted: G B 4.9.0-rc5+ #39
Hardware name: QEMU Standard PC (i440FX + PIIX, 1996), BIOS Bochs 01/01/2011
ffff8800359b71f0 ffffffff834c2999 ffffffff00000002 1ffff10006b36dd1
ffffed0006b36dc9 0000000041b58ab3 ffffffff89575430 ffffffff834c26ab
0000000000000000 0000000000000000 0000000000000001 0000000000000000
Call Trace:
[< inline >] __dump_stack lib/dump_stack.c:15
[<ffffffff834c2999>] dump_stack+0x2ee/0x3f5 lib/dump_stack.c:51
[< inline >] print_address_description mm/kasan/report.c:207
[< inline >] kasan_report_error mm/kasan/report.c:286
[<ffffffff819f0ec0>] kasan_report+0x490/0x4c0 mm/kasan/report.c:306
[<ffffffff819f0fac>] __asan_report_store1_noabort+0x1c/0x20 mm/kasan/report.c:334
[<ffffffffa00103c8>] use_after_scope_test+0x1ee/0x25b [test_kasan] lib/test_kasan.c:433
[<ffffffffa00114b8>] kmalloc_tests_init+0x72/0x79 [test_kasan]
[<ffffffff8100244b>] do_one_initcall+0xfb/0x3f0 init/main.c:778
[<ffffffff8184a813>] do_init_module+0x219/0x59c kernel/module.c:3386
[<ffffffff81658218>] load_module+0x5918/0x8c40 kernel/module.c:3706
[<ffffffff8165b939>] SYSC_init_module+0x3f9/0x470 kernel/module.c:3776
[<ffffffff8165bd2e>] SyS_init_module+0xe/0x10 kernel/module.c:3759
[<ffffffff88143885>] entry_SYSCALL_64_fastpath+0x23/0xc6 arch/x86/entry/entry_64.S:209
Memory state around the buggy address:
ffff8800359b7200: 00 00 00 00 00 00 00 00 00 00 00 00 00 00 00 00
ffff8800359b7280: 00 00 f1 f1 f1 f1 f8 f2 f2 f2 f2 f2 f2 f2 00 f2
>ffff8800359b7300: f2 f2 f2 f2 f2 f2 f8 f8 f8 f8 f8 f8 f8 f8 f8 f8
^
ffff8800359b7380: f8 f8 f8 f8 f8 f8 f8 f8 f8 f8 f8 f8 f8 f8 f8 f8
ffff8800359b7400: f8 f8 f8 f8 f8 f8 f8 f8 f8 f8 f8 f8 f8 f8 f8 f8
==================================================================
==================================================================
BUG: KASAN: use-after-scope in use_after_scope_test+0x229/0x25b [test_kasan] at addr ffff8800359b772f
Write of size 1 by task insmod/6644
page:ffffea0000d66dc0 count:0 mapcount:0 mapping: (null) index:0x0
flags: 0x1fffc0000000000()
page dumped because: kasan: bad access detected
CPU: 2 PID: 6644 Comm: insmod Tainted: G B 4.9.0-rc5+ #39
Hardware name: QEMU Standard PC (i440FX + PIIX, 1996), BIOS Bochs 01/01/2011
ffff8800359b71f0 ffffffff834c2999 ffffffff00000002 1ffff10006b36dd1
ffffed0006b36dc9 0000000041b58ab3 ffffffff89575430 ffffffff834c26ab
0000000000000000 0000000000000000 0000000000000001 0000000000000000
Call Trace:
[< inline >] __dump_stack lib/dump_stack.c:15
[<ffffffff834c2999>] dump_stack+0x2ee/0x3f5 lib/dump_stack.c:51
[< inline >] print_address_description mm/kasan/report.c:207
[< inline >] kasan_report_error mm/kasan/report.c:286
[<ffffffff819f0ec0>] kasan_report+0x490/0x4c0 mm/kasan/report.c:306
[<ffffffff819f0fac>] __asan_report_store1_noabort+0x1c/0x20 mm/kasan/report.c:334
[<ffffffffa0010403>] use_after_scope_test+0x229/0x25b [test_kasan] lib/test_kasan.c:434
[<ffffffffa00114b8>] kmalloc_tests_init+0x72/0x79 [test_kasan]
[<ffffffff8100244b>] do_one_initcall+0xfb/0x3f0 init/main.c:778
[<ffffffff8184a813>] do_init_module+0x219/0x59c kernel/module.c:3386
[<ffffffff81658218>] load_module+0x5918/0x8c40 kernel/module.c:3706
[<ffffffff8165b939>] SYSC_init_module+0x3f9/0x470 kernel/module.c:3776
[<ffffffff8165bd2e>] SyS_init_module+0xe/0x10 kernel/module.c:3759
[<ffffffff88143885>] entry_SYSCALL_64_fastpath+0x23/0xc6 arch/x86/entry/entry_64.S:209
Memory state around the buggy address:
ffff8800359b7600: f8 f8 f8 f8 f8 f8 f8 f8 f8 f8 f8 f8 f8 f8 f8 f8
ffff8800359b7680: f8 f8 f8 f8 f8 f8 f8 f8 f8 f8 f8 f8 f8 f8 f8 f8
>ffff8800359b7700: f8 f8 f8 f8 f8 f8 f3 f3 f3 f3 00 00 00 00 00 00
^
ffff8800359b7780: 00 00 00 f1 f1 f1 f1 00 f2 f2 f2 f2 f2 f2 f2 00
ffff8800359b7800: f2 f2 f2 f2 f2 f2 f2 00 f2 f2 f2 f2 f2 f2 f2 00
==================================================================
---
lib/test_kasan.c | 24 ++++++++++++++++++++++++
mm/kasan/kasan.c | 19 +++++++++++++++++++
mm/kasan/kasan.h | 1 +
mm/kasan/report.c | 3 +++
4 files changed, 47 insertions(+)

diff --git a/lib/test_kasan.c b/lib/test_kasan.c
index 5e51872b..e8d6b88 100644
--- a/lib/test_kasan.c
+++ b/lib/test_kasan.c
@@ -411,6 +411,29 @@ static noinline void __init copy_user_test(void)
kfree(kmem);
}

+static noinline void __init use_after_scope_test(void)
+{
+ volatile char *volatile p;
+
+ pr_info("use-after-scope on int\n");
+ {
+ int local = 0;
+
+ p = (char *)&local;
+ }
+ p[0] = 1;
+ p[3] = 1;
+
+ pr_info("use-after-scope on array\n");
+ {
+ char local[1024] = {0};
+
+ p = local;
+ }
+ p[0] = 1;
+ p[1023] = 1;
+}
+
static int __init kmalloc_tests_init(void)
{
kmalloc_oob_right();
@@ -436,6 +459,7 @@ static int __init kmalloc_tests_init(void)
kasan_global_oob();
ksize_unpoisons_memory();
copy_user_test();
+ use_after_scope_test();
return -EAGAIN;
}

diff --git a/mm/kasan/kasan.c b/mm/kasan/kasan.c
index 70c0097..4653616 100644
--- a/mm/kasan/kasan.c
+++ b/mm/kasan/kasan.c
@@ -764,6 +764,25 @@ EXPORT_SYMBOL(__asan_storeN_noabort);
void __asan_handle_no_return(void) {}
EXPORT_SYMBOL(__asan_handle_no_return);

+/* Emitted by compiler to poison large objects when they go out of scope. */
+void __asan_poison_stack_memory(const void *addr, size_t size)
+{
+ /*
+ * Addr is KASAN_SHADOW_SCALE_SIZE-aligned and the object is surrounded
+ * by redzones, so we simply round up size to simplify logic.
+ */
+ kasan_poison_shadow(addr, round_up(size, KASAN_SHADOW_SCALE_SIZE),
+ KASAN_USE_AFTER_SCOPE);
+}
+EXPORT_SYMBOL(__asan_poison_stack_memory);
+
+/* Emitted by compiler to unpoison large objects when they go into of scope. */
+void __asan_unpoison_stack_memory(const void *addr, size_t size)
+{
+ kasan_unpoison_shadow(addr, size);
+}
+EXPORT_SYMBOL(__asan_unpoison_stack_memory);
+
#ifdef CONFIG_MEMORY_HOTPLUG
static int kasan_mem_notifier(struct notifier_block *nb,
unsigned long action, void *data)
diff --git a/mm/kasan/kasan.h b/mm/kasan/kasan.h
index e5c2181..46fb5ca 100644
--- a/mm/kasan/kasan.h
+++ b/mm/kasan/kasan.h
@@ -21,6 +21,7 @@
#define KASAN_STACK_MID 0xF2
#define KASAN_STACK_RIGHT 0xF3
#define KASAN_STACK_PARTIAL 0xF4
+#define KASAN_USE_AFTER_SCOPE 0xF8

/* Don't break randconfig/all*config builds */
#ifndef KASAN_ABI_VERSION
diff --git a/mm/kasan/report.c b/mm/kasan/report.c
index 24c1211..073325a 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)
case KASAN_KMALLOC_FREE:
bug_type = "use-after-free";
break;
+ case KASAN_USE_AFTER_SCOPE:
+ bug_type = "use-after-scope";
+ break;
}

pr_err("BUG: KASAN: %s in %pS at addr %p\n",
--
2.8.0.rc3.226.g39d4020