diff options
-rw-r--r-- | Documentation/security/self-protection.rst | 10 | ||||
-rw-r--r-- | Documentation/sysctl/kernel.txt | 18 | ||||
-rw-r--r-- | Documentation/x86/x86_64/mm.txt | 3 | ||||
-rw-r--r-- | arch/Kconfig | 7 | ||||
-rw-r--r-- | arch/arm64/kernel/process.c | 22 | ||||
-rw-r--r-- | arch/x86/Kconfig | 1 | ||||
-rw-r--r-- | arch/x86/entry/calling.h | 14 | ||||
-rw-r--r-- | arch/x86/entry/entry_32.S | 7 | ||||
-rw-r--r-- | arch/x86/entry/entry_64.S | 3 | ||||
-rw-r--r-- | arch/x86/entry/entry_64_compat.S | 5 | ||||
-rw-r--r-- | drivers/misc/lkdtm/Makefile | 2 | ||||
-rw-r--r-- | drivers/misc/lkdtm/core.c | 1 | ||||
-rw-r--r-- | drivers/misc/lkdtm/lkdtm.h | 3 | ||||
-rw-r--r-- | drivers/misc/lkdtm/stackleak.c | 73 | ||||
-rw-r--r-- | fs/proc/base.c | 18 | ||||
-rw-r--r-- | include/linux/sched.h | 5 | ||||
-rw-r--r-- | include/linux/stackleak.h | 35 | ||||
-rw-r--r-- | kernel/Makefile | 4 | ||||
-rw-r--r-- | kernel/fork.c | 3 | ||||
-rw-r--r-- | kernel/stackleak.c | 132 | ||||
-rw-r--r-- | kernel/sysctl.c | 15 | ||||
-rw-r--r-- | scripts/Makefile.gcc-plugins | 10 | ||||
-rw-r--r-- | scripts/gcc-plugins/Kconfig | 51 | ||||
-rw-r--r-- | scripts/gcc-plugins/stackleak_plugin.c | 427 |
24 files changed, 841 insertions, 28 deletions
diff --git a/Documentation/security/self-protection.rst b/Documentation/security/self-protection.rst index e1ca698e0006..f584fb74b4ff 100644 --- a/Documentation/security/self-protection.rst +++ b/Documentation/security/self-protection.rst | |||
@@ -302,11 +302,11 @@ sure structure holes are cleared. | |||
302 | Memory poisoning | 302 | Memory poisoning |
303 | ---------------- | 303 | ---------------- |
304 | 304 | ||
305 | When releasing memory, it is best to poison the contents (clear stack on | 305 | When releasing memory, it is best to poison the contents, to avoid reuse |
306 | syscall return, wipe heap memory on a free), to avoid reuse attacks that | 306 | attacks that rely on the old contents of memory. E.g., clear stack on a |
307 | rely on the old contents of memory. This frustrates many uninitialized | 307 | syscall return (``CONFIG_GCC_PLUGIN_STACKLEAK``), wipe heap memory on a |
308 | variable attacks, stack content exposures, heap content exposures, and | 308 | free. This frustrates many uninitialized variable attacks, stack content |
309 | use-after-free attacks. | 309 | exposures, heap content exposures, and use-after-free attacks. |
310 | 310 | ||
311 | Destination tracking | 311 | Destination tracking |
312 | -------------------- | 312 | -------------------- |
diff --git a/Documentation/sysctl/kernel.txt b/Documentation/sysctl/kernel.txt index 37a679501ddc..1b8775298cf7 100644 --- a/Documentation/sysctl/kernel.txt +++ b/Documentation/sysctl/kernel.txt | |||
@@ -89,6 +89,7 @@ show up in /proc/sys/kernel: | |||
89 | - shmmni | 89 | - shmmni |
90 | - softlockup_all_cpu_backtrace | 90 | - softlockup_all_cpu_backtrace |
91 | - soft_watchdog | 91 | - soft_watchdog |
92 | - stack_erasing | ||
92 | - stop-a [ SPARC only ] | 93 | - stop-a [ SPARC only ] |
93 | - sysrq ==> Documentation/admin-guide/sysrq.rst | 94 | - sysrq ==> Documentation/admin-guide/sysrq.rst |
94 | - sysctl_writes_strict | 95 | - sysctl_writes_strict |
@@ -987,6 +988,23 @@ detect a hard lockup condition. | |||
987 | 988 | ||
988 | ============================================================== | 989 | ============================================================== |
989 | 990 | ||
991 | stack_erasing | ||
992 | |||
993 | This parameter can be used to control kernel stack erasing at the end | ||
994 | of syscalls for kernels built with CONFIG_GCC_PLUGIN_STACKLEAK. | ||
995 | |||
996 | That erasing reduces the information which kernel stack leak bugs | ||
997 | can reveal and blocks some uninitialized stack variable attacks. | ||
998 | The tradeoff is the performance impact: on a single CPU system kernel | ||
999 | compilation sees a 1% slowdown, other systems and workloads may vary. | ||
1000 | |||
1001 | 0: kernel stack erasing is disabled, STACKLEAK_METRICS are not updated. | ||
1002 | |||
1003 | 1: kernel stack erasing is enabled (default), it is performed before | ||
1004 | returning to the userspace at the end of syscalls. | ||
1005 | |||
1006 | ============================================================== | ||
1007 | |||
990 | tainted: | 1008 | tainted: |
991 | 1009 | ||
992 | Non-zero if the kernel has been tainted. Numeric values, which can be | 1010 | Non-zero if the kernel has been tainted. Numeric values, which can be |
diff --git a/Documentation/x86/x86_64/mm.txt b/Documentation/x86/x86_64/mm.txt index 702898633b00..73aaaa3da436 100644 --- a/Documentation/x86/x86_64/mm.txt +++ b/Documentation/x86/x86_64/mm.txt | |||
@@ -146,3 +146,6 @@ Their order is preserved but their base will be offset early at boot time. | |||
146 | Be very careful vs. KASLR when changing anything here. The KASLR address | 146 | Be very careful vs. KASLR when changing anything here. The KASLR address |
147 | range must not overlap with anything except the KASAN shadow area, which is | 147 | range must not overlap with anything except the KASAN shadow area, which is |
148 | correct as KASAN disables KASLR. | 148 | correct as KASAN disables KASLR. |
149 | |||
150 | For both 4- and 5-level layouts, the STACKLEAK_POISON value in the last 2MB | ||
151 | hole: ffffffffffff4111 | ||
diff --git a/arch/Kconfig b/arch/Kconfig index ed27fd262627..e1e540ffa979 100644 --- a/arch/Kconfig +++ b/arch/Kconfig | |||
@@ -429,6 +429,13 @@ config SECCOMP_FILTER | |||
429 | 429 | ||
430 | See Documentation/userspace-api/seccomp_filter.rst for details. | 430 | See Documentation/userspace-api/seccomp_filter.rst for details. |
431 | 431 | ||
432 | config HAVE_ARCH_STACKLEAK | ||
433 | bool | ||
434 | help | ||
435 | An architecture should select this if it has the code which | ||
436 | fills the used part of the kernel stack with the STACKLEAK_POISON | ||
437 | value before returning from system calls. | ||
438 | |||
432 | config HAVE_STACKPROTECTOR | 439 | config HAVE_STACKPROTECTOR |
433 | bool | 440 | bool |
434 | help | 441 | help |
diff --git a/arch/arm64/kernel/process.c b/arch/arm64/kernel/process.c index ce99c58cd1f1..d9a4c2d6dd8b 100644 --- a/arch/arm64/kernel/process.c +++ b/arch/arm64/kernel/process.c | |||
@@ -497,25 +497,3 @@ void arch_setup_new_exec(void) | |||
497 | { | 497 | { |
498 | current->mm->context.flags = is_compat_task() ? MMCF_AARCH32 : 0; | 498 | current->mm->context.flags = is_compat_task() ? MMCF_AARCH32 : 0; |
499 | } | 499 | } |
500 | |||
501 | #ifdef CONFIG_GCC_PLUGIN_STACKLEAK | ||
502 | void __used stackleak_check_alloca(unsigned long size) | ||
503 | { | ||
504 | unsigned long stack_left; | ||
505 | unsigned long current_sp = current_stack_pointer; | ||
506 | struct stack_info info; | ||
507 | |||
508 | BUG_ON(!on_accessible_stack(current, current_sp, &info)); | ||
509 | |||
510 | stack_left = current_sp - info.low; | ||
511 | |||
512 | /* | ||
513 | * There's a good chance we're almost out of stack space if this | ||
514 | * is true. Using panic() over BUG() is more likely to give | ||
515 | * reliable debugging output. | ||
516 | */ | ||
517 | if (size >= stack_left) | ||
518 | panic("alloca() over the kernel stack boundary\n"); | ||
519 | } | ||
520 | EXPORT_SYMBOL(stackleak_check_alloca); | ||
521 | #endif | ||
diff --git a/arch/x86/Kconfig b/arch/x86/Kconfig index c51c989c19c0..ba7e3464ee92 100644 --- a/arch/x86/Kconfig +++ b/arch/x86/Kconfig | |||
@@ -129,6 +129,7 @@ config X86 | |||
129 | select HAVE_ARCH_PREL32_RELOCATIONS | 129 | select HAVE_ARCH_PREL32_RELOCATIONS |
130 | select HAVE_ARCH_SECCOMP_FILTER | 130 | select HAVE_ARCH_SECCOMP_FILTER |
131 | select HAVE_ARCH_THREAD_STRUCT_WHITELIST | 131 | select HAVE_ARCH_THREAD_STRUCT_WHITELIST |
132 | select HAVE_ARCH_STACKLEAK | ||
132 | select HAVE_ARCH_TRACEHOOK | 133 | select HAVE_ARCH_TRACEHOOK |
133 | select HAVE_ARCH_TRANSPARENT_HUGEPAGE | 134 | select HAVE_ARCH_TRANSPARENT_HUGEPAGE |
134 | select HAVE_ARCH_TRANSPARENT_HUGEPAGE_PUD if X86_64 | 135 | select HAVE_ARCH_TRANSPARENT_HUGEPAGE_PUD if X86_64 |
diff --git a/arch/x86/entry/calling.h b/arch/x86/entry/calling.h index 708b46a54578..25e5a6bda8c3 100644 --- a/arch/x86/entry/calling.h +++ b/arch/x86/entry/calling.h | |||
@@ -329,8 +329,22 @@ For 32-bit we have the following conventions - kernel is built with | |||
329 | 329 | ||
330 | #endif | 330 | #endif |
331 | 331 | ||
332 | .macro STACKLEAK_ERASE_NOCLOBBER | ||
333 | #ifdef CONFIG_GCC_PLUGIN_STACKLEAK | ||
334 | PUSH_AND_CLEAR_REGS | ||
335 | call stackleak_erase | ||
336 | POP_REGS | ||
337 | #endif | ||
338 | .endm | ||
339 | |||
332 | #endif /* CONFIG_X86_64 */ | 340 | #endif /* CONFIG_X86_64 */ |
333 | 341 | ||
342 | .macro STACKLEAK_ERASE | ||
343 | #ifdef CONFIG_GCC_PLUGIN_STACKLEAK | ||
344 | call stackleak_erase | ||
345 | #endif | ||
346 | .endm | ||
347 | |||
334 | /* | 348 | /* |
335 | * This does 'call enter_from_user_mode' unless we can avoid it based on | 349 | * This does 'call enter_from_user_mode' unless we can avoid it based on |
336 | * kernel config or using the static jump infrastructure. | 350 | * kernel config or using the static jump infrastructure. |
diff --git a/arch/x86/entry/entry_32.S b/arch/x86/entry/entry_32.S index 687e47f8a796..d309f30cf7af 100644 --- a/arch/x86/entry/entry_32.S +++ b/arch/x86/entry/entry_32.S | |||
@@ -46,6 +46,8 @@ | |||
46 | #include <asm/frame.h> | 46 | #include <asm/frame.h> |
47 | #include <asm/nospec-branch.h> | 47 | #include <asm/nospec-branch.h> |
48 | 48 | ||
49 | #include "calling.h" | ||
50 | |||
49 | .section .entry.text, "ax" | 51 | .section .entry.text, "ax" |
50 | 52 | ||
51 | /* | 53 | /* |
@@ -712,6 +714,7 @@ ENTRY(ret_from_fork) | |||
712 | /* When we fork, we trace the syscall return in the child, too. */ | 714 | /* When we fork, we trace the syscall return in the child, too. */ |
713 | movl %esp, %eax | 715 | movl %esp, %eax |
714 | call syscall_return_slowpath | 716 | call syscall_return_slowpath |
717 | STACKLEAK_ERASE | ||
715 | jmp restore_all | 718 | jmp restore_all |
716 | 719 | ||
717 | /* kernel thread */ | 720 | /* kernel thread */ |
@@ -886,6 +889,8 @@ ENTRY(entry_SYSENTER_32) | |||
886 | ALTERNATIVE "testl %eax, %eax; jz .Lsyscall_32_done", \ | 889 | ALTERNATIVE "testl %eax, %eax; jz .Lsyscall_32_done", \ |
887 | "jmp .Lsyscall_32_done", X86_FEATURE_XENPV | 890 | "jmp .Lsyscall_32_done", X86_FEATURE_XENPV |
888 | 891 | ||
892 | STACKLEAK_ERASE | ||
893 | |||
889 | /* Opportunistic SYSEXIT */ | 894 | /* Opportunistic SYSEXIT */ |
890 | TRACE_IRQS_ON /* User mode traces as IRQs on. */ | 895 | TRACE_IRQS_ON /* User mode traces as IRQs on. */ |
891 | 896 | ||
@@ -997,6 +1002,8 @@ ENTRY(entry_INT80_32) | |||
997 | call do_int80_syscall_32 | 1002 | call do_int80_syscall_32 |
998 | .Lsyscall_32_done: | 1003 | .Lsyscall_32_done: |
999 | 1004 | ||
1005 | STACKLEAK_ERASE | ||
1006 | |||
1000 | restore_all: | 1007 | restore_all: |
1001 | TRACE_IRQS_IRET | 1008 | TRACE_IRQS_IRET |
1002 | SWITCH_TO_ENTRY_STACK | 1009 | SWITCH_TO_ENTRY_STACK |
diff --git a/arch/x86/entry/entry_64.S b/arch/x86/entry/entry_64.S index 4d7a2d9d44cf..ce25d84023c0 100644 --- a/arch/x86/entry/entry_64.S +++ b/arch/x86/entry/entry_64.S | |||
@@ -266,6 +266,8 @@ syscall_return_via_sysret: | |||
266 | * We are on the trampoline stack. All regs except RDI are live. | 266 | * We are on the trampoline stack. All regs except RDI are live. |
267 | * We can do future final exit work right here. | 267 | * We can do future final exit work right here. |
268 | */ | 268 | */ |
269 | STACKLEAK_ERASE_NOCLOBBER | ||
270 | |||
269 | SWITCH_TO_USER_CR3_STACK scratch_reg=%rdi | 271 | SWITCH_TO_USER_CR3_STACK scratch_reg=%rdi |
270 | 272 | ||
271 | popq %rdi | 273 | popq %rdi |
@@ -625,6 +627,7 @@ GLOBAL(swapgs_restore_regs_and_return_to_usermode) | |||
625 | * We are on the trampoline stack. All regs except RDI are live. | 627 | * We are on the trampoline stack. All regs except RDI are live. |
626 | * We can do future final exit work right here. | 628 | * We can do future final exit work right here. |
627 | */ | 629 | */ |
630 | STACKLEAK_ERASE_NOCLOBBER | ||
628 | 631 | ||
629 | SWITCH_TO_USER_CR3_STACK scratch_reg=%rdi | 632 | SWITCH_TO_USER_CR3_STACK scratch_reg=%rdi |
630 | 633 | ||
diff --git a/arch/x86/entry/entry_64_compat.S b/arch/x86/entry/entry_64_compat.S index 7d0df78db727..8eaf8952c408 100644 --- a/arch/x86/entry/entry_64_compat.S +++ b/arch/x86/entry/entry_64_compat.S | |||
@@ -261,6 +261,11 @@ GLOBAL(entry_SYSCALL_compat_after_hwframe) | |||
261 | 261 | ||
262 | /* Opportunistic SYSRET */ | 262 | /* Opportunistic SYSRET */ |
263 | sysret32_from_system_call: | 263 | sysret32_from_system_call: |
264 | /* | ||
265 | * We are not going to return to userspace from the trampoline | ||
266 | * stack. So let's erase the thread stack right now. | ||
267 | */ | ||
268 | STACKLEAK_ERASE | ||
264 | TRACE_IRQS_ON /* User mode traces as IRQs on. */ | 269 | TRACE_IRQS_ON /* User mode traces as IRQs on. */ |
265 | movq RBX(%rsp), %rbx /* pt_regs->rbx */ | 270 | movq RBX(%rsp), %rbx /* pt_regs->rbx */ |
266 | movq RBP(%rsp), %rbp /* pt_regs->rbp */ | 271 | movq RBP(%rsp), %rbp /* pt_regs->rbp */ |
diff --git a/drivers/misc/lkdtm/Makefile b/drivers/misc/lkdtm/Makefile index 3370a4138e94..951c984de61a 100644 --- a/drivers/misc/lkdtm/Makefile +++ b/drivers/misc/lkdtm/Makefile | |||
@@ -8,7 +8,9 @@ lkdtm-$(CONFIG_LKDTM) += perms.o | |||
8 | lkdtm-$(CONFIG_LKDTM) += refcount.o | 8 | lkdtm-$(CONFIG_LKDTM) += refcount.o |
9 | lkdtm-$(CONFIG_LKDTM) += rodata_objcopy.o | 9 | lkdtm-$(CONFIG_LKDTM) += rodata_objcopy.o |
10 | lkdtm-$(CONFIG_LKDTM) += usercopy.o | 10 | lkdtm-$(CONFIG_LKDTM) += usercopy.o |
11 | lkdtm-$(CONFIG_LKDTM) += stackleak.o | ||
11 | 12 | ||
13 | KASAN_SANITIZE_stackleak.o := n | ||
12 | KCOV_INSTRUMENT_rodata.o := n | 14 | KCOV_INSTRUMENT_rodata.o := n |
13 | 15 | ||
14 | OBJCOPYFLAGS := | 16 | OBJCOPYFLAGS := |
diff --git a/drivers/misc/lkdtm/core.c b/drivers/misc/lkdtm/core.c index 5a755590d3dc..2837dc77478e 100644 --- a/drivers/misc/lkdtm/core.c +++ b/drivers/misc/lkdtm/core.c | |||
@@ -184,6 +184,7 @@ static const struct crashtype crashtypes[] = { | |||
184 | CRASHTYPE(USERCOPY_STACK_BEYOND), | 184 | CRASHTYPE(USERCOPY_STACK_BEYOND), |
185 | CRASHTYPE(USERCOPY_KERNEL), | 185 | CRASHTYPE(USERCOPY_KERNEL), |
186 | CRASHTYPE(USERCOPY_KERNEL_DS), | 186 | CRASHTYPE(USERCOPY_KERNEL_DS), |
187 | CRASHTYPE(STACKLEAK_ERASING), | ||
187 | }; | 188 | }; |
188 | 189 | ||
189 | 190 | ||
diff --git a/drivers/misc/lkdtm/lkdtm.h b/drivers/misc/lkdtm/lkdtm.h index 07db641d71d0..3c6fd327e166 100644 --- a/drivers/misc/lkdtm/lkdtm.h +++ b/drivers/misc/lkdtm/lkdtm.h | |||
@@ -84,4 +84,7 @@ void lkdtm_USERCOPY_STACK_BEYOND(void); | |||
84 | void lkdtm_USERCOPY_KERNEL(void); | 84 | void lkdtm_USERCOPY_KERNEL(void); |
85 | void lkdtm_USERCOPY_KERNEL_DS(void); | 85 | void lkdtm_USERCOPY_KERNEL_DS(void); |
86 | 86 | ||
87 | /* lkdtm_stackleak.c */ | ||
88 | void lkdtm_STACKLEAK_ERASING(void); | ||
89 | |||
87 | #endif | 90 | #endif |
diff --git a/drivers/misc/lkdtm/stackleak.c b/drivers/misc/lkdtm/stackleak.c new file mode 100644 index 000000000000..d5a084475abc --- /dev/null +++ b/drivers/misc/lkdtm/stackleak.c | |||
@@ -0,0 +1,73 @@ | |||
1 | // SPDX-License-Identifier: GPL-2.0 | ||
2 | /* | ||
3 | * This code tests that the current task stack is properly erased (filled | ||
4 | * with STACKLEAK_POISON). | ||
5 | * | ||
6 | * Authors: | ||
7 | * Alexander Popov <alex.popov@linux.com> | ||
8 | * Tycho Andersen <tycho@tycho.ws> | ||
9 | */ | ||
10 | |||
11 | #include "lkdtm.h" | ||
12 | #include <linux/stackleak.h> | ||
13 | |||
14 | void lkdtm_STACKLEAK_ERASING(void) | ||
15 | { | ||
16 | unsigned long *sp, left, found, i; | ||
17 | const unsigned long check_depth = | ||
18 | STACKLEAK_SEARCH_DEPTH / sizeof(unsigned long); | ||
19 | |||
20 | /* | ||
21 | * For the details about the alignment of the poison values, see | ||
22 | * the comment in stackleak_track_stack(). | ||
23 | */ | ||
24 | sp = PTR_ALIGN(&i, sizeof(unsigned long)); | ||
25 | |||
26 | left = ((unsigned long)sp & (THREAD_SIZE - 1)) / sizeof(unsigned long); | ||
27 | sp--; | ||
28 | |||
29 | /* | ||
30 | * One 'long int' at the bottom of the thread stack is reserved | ||
31 | * and not poisoned. | ||
32 | */ | ||
33 | if (left > 1) { | ||
34 | left--; | ||
35 | } else { | ||
36 | pr_err("FAIL: not enough stack space for the test\n"); | ||
37 | return; | ||
38 | } | ||
39 | |||
40 | pr_info("checking unused part of the thread stack (%lu bytes)...\n", | ||
41 | left * sizeof(unsigned long)); | ||
42 | |||
43 | /* | ||
44 | * Search for 'check_depth' poison values in a row (just like | ||
45 | * stackleak_erase() does). | ||
46 | */ | ||
47 | for (i = 0, found = 0; i < left && found <= check_depth; i++) { | ||
48 | if (*(sp - i) == STACKLEAK_POISON) | ||
49 | found++; | ||
50 | else | ||
51 | found = 0; | ||
52 | } | ||
53 | |||
54 | if (found <= check_depth) { | ||
55 | pr_err("FAIL: thread stack is not erased (checked %lu bytes)\n", | ||
56 | i * sizeof(unsigned long)); | ||
57 | return; | ||
58 | } | ||
59 | |||
60 | pr_info("first %lu bytes are unpoisoned\n", | ||
61 | (i - found) * sizeof(unsigned long)); | ||
62 | |||
63 | /* The rest of thread stack should be erased */ | ||
64 | for (; i < left; i++) { | ||
65 | if (*(sp - i) != STACKLEAK_POISON) { | ||
66 | pr_err("FAIL: thread stack is NOT properly erased\n"); | ||
67 | return; | ||
68 | } | ||
69 | } | ||
70 | |||
71 | pr_info("OK: the rest of the thread stack is properly erased\n"); | ||
72 | return; | ||
73 | } | ||
diff --git a/fs/proc/base.c b/fs/proc/base.c index 7e9f07bf260d..ce3465479447 100644 --- a/fs/proc/base.c +++ b/fs/proc/base.c | |||
@@ -2905,6 +2905,21 @@ static int proc_pid_patch_state(struct seq_file *m, struct pid_namespace *ns, | |||
2905 | } | 2905 | } |
2906 | #endif /* CONFIG_LIVEPATCH */ | 2906 | #endif /* CONFIG_LIVEPATCH */ |
2907 | 2907 | ||
2908 | #ifdef CONFIG_STACKLEAK_METRICS | ||
2909 | static int proc_stack_depth(struct seq_file *m, struct pid_namespace *ns, | ||
2910 | struct pid *pid, struct task_struct *task) | ||
2911 | { | ||
2912 | unsigned long prev_depth = THREAD_SIZE - | ||
2913 | (task->prev_lowest_stack & (THREAD_SIZE - 1)); | ||
2914 | unsigned long depth = THREAD_SIZE - | ||
2915 | (task->lowest_stack & (THREAD_SIZE - 1)); | ||
2916 | |||
2917 | seq_printf(m, "previous stack depth: %lu\nstack depth: %lu\n", | ||
2918 | prev_depth, depth); | ||
2919 | return 0; | ||
2920 | } | ||
2921 | #endif /* CONFIG_STACKLEAK_METRICS */ | ||
2922 | |||
2908 | /* | 2923 | /* |
2909 | * Thread groups | 2924 | * Thread groups |
2910 | */ | 2925 | */ |
@@ -3006,6 +3021,9 @@ static const struct pid_entry tgid_base_stuff[] = { | |||
3006 | #ifdef CONFIG_LIVEPATCH | 3021 | #ifdef CONFIG_LIVEPATCH |
3007 | ONE("patch_state", S_IRUSR, proc_pid_patch_state), | 3022 | ONE("patch_state", S_IRUSR, proc_pid_patch_state), |
3008 | #endif | 3023 | #endif |
3024 | #ifdef CONFIG_STACKLEAK_METRICS | ||
3025 | ONE("stack_depth", S_IRUGO, proc_stack_depth), | ||
3026 | #endif | ||
3009 | }; | 3027 | }; |
3010 | 3028 | ||
3011 | static int proc_tgid_base_readdir(struct file *file, struct dir_context *ctx) | 3029 | static int proc_tgid_base_readdir(struct file *file, struct dir_context *ctx) |
diff --git a/include/linux/sched.h b/include/linux/sched.h index 8f8a5418b627..a51c13c2b1a0 100644 --- a/include/linux/sched.h +++ b/include/linux/sched.h | |||
@@ -1200,6 +1200,11 @@ struct task_struct { | |||
1200 | void *security; | 1200 | void *security; |
1201 | #endif | 1201 | #endif |
1202 | 1202 | ||
1203 | #ifdef CONFIG_GCC_PLUGIN_STACKLEAK | ||
1204 | unsigned long lowest_stack; | ||
1205 | unsigned long prev_lowest_stack; | ||
1206 | #endif | ||
1207 | |||
1203 | /* | 1208 | /* |
1204 | * New fields for task_struct should be added above here, so that | 1209 | * New fields for task_struct should be added above here, so that |
1205 | * they are included in the randomized portion of task_struct. | 1210 | * they are included in the randomized portion of task_struct. |
diff --git a/include/linux/stackleak.h b/include/linux/stackleak.h new file mode 100644 index 000000000000..3d5c3271a9a8 --- /dev/null +++ b/include/linux/stackleak.h | |||
@@ -0,0 +1,35 @@ | |||
1 | /* SPDX-License-Identifier: GPL-2.0 */ | ||
2 | #ifndef _LINUX_STACKLEAK_H | ||
3 | #define _LINUX_STACKLEAK_H | ||
4 | |||
5 | #include <linux/sched.h> | ||
6 | #include <linux/sched/task_stack.h> | ||
7 | |||
8 | /* | ||
9 | * Check that the poison value points to the unused hole in the | ||
10 | * virtual memory map for your platform. | ||
11 | */ | ||
12 | #define STACKLEAK_POISON -0xBEEF | ||
13 | #define STACKLEAK_SEARCH_DEPTH 128 | ||
14 | |||
15 | #ifdef CONFIG_GCC_PLUGIN_STACKLEAK | ||
16 | #include <asm/stacktrace.h> | ||
17 | |||
18 | static inline void stackleak_task_init(struct task_struct *t) | ||
19 | { | ||
20 | t->lowest_stack = (unsigned long)end_of_stack(t) + sizeof(unsigned long); | ||
21 | # ifdef CONFIG_STACKLEAK_METRICS | ||
22 | t->prev_lowest_stack = t->lowest_stack; | ||
23 | # endif | ||
24 | } | ||
25 | |||
26 | #ifdef CONFIG_STACKLEAK_RUNTIME_DISABLE | ||
27 | int stack_erasing_sysctl(struct ctl_table *table, int write, | ||
28 | void __user *buffer, size_t *lenp, loff_t *ppos); | ||
29 | #endif | ||
30 | |||
31 | #else /* !CONFIG_GCC_PLUGIN_STACKLEAK */ | ||
32 | static inline void stackleak_task_init(struct task_struct *t) { } | ||
33 | #endif | ||
34 | |||
35 | #endif | ||
diff --git a/kernel/Makefile b/kernel/Makefile index 7a63d567fdb5..7343b3a9bff0 100644 --- a/kernel/Makefile +++ b/kernel/Makefile | |||
@@ -117,6 +117,10 @@ obj-$(CONFIG_HAS_IOMEM) += iomem.o | |||
117 | obj-$(CONFIG_ZONE_DEVICE) += memremap.o | 117 | obj-$(CONFIG_ZONE_DEVICE) += memremap.o |
118 | obj-$(CONFIG_RSEQ) += rseq.o | 118 | obj-$(CONFIG_RSEQ) += rseq.o |
119 | 119 | ||
120 | obj-$(CONFIG_GCC_PLUGIN_STACKLEAK) += stackleak.o | ||
121 | KASAN_SANITIZE_stackleak.o := n | ||
122 | KCOV_INSTRUMENT_stackleak.o := n | ||
123 | |||
120 | $(obj)/configs.o: $(obj)/config_data.h | 124 | $(obj)/configs.o: $(obj)/config_data.h |
121 | 125 | ||
122 | targets += config_data.gz | 126 | targets += config_data.gz |
diff --git a/kernel/fork.c b/kernel/fork.c index 8f82a3bdcb8f..07cddff89c7b 100644 --- a/kernel/fork.c +++ b/kernel/fork.c | |||
@@ -91,6 +91,7 @@ | |||
91 | #include <linux/kcov.h> | 91 | #include <linux/kcov.h> |
92 | #include <linux/livepatch.h> | 92 | #include <linux/livepatch.h> |
93 | #include <linux/thread_info.h> | 93 | #include <linux/thread_info.h> |
94 | #include <linux/stackleak.h> | ||
94 | 95 | ||
95 | #include <asm/pgtable.h> | 96 | #include <asm/pgtable.h> |
96 | #include <asm/pgalloc.h> | 97 | #include <asm/pgalloc.h> |
@@ -1926,6 +1927,8 @@ static __latent_entropy struct task_struct *copy_process( | |||
1926 | if (retval) | 1927 | if (retval) |
1927 | goto bad_fork_cleanup_io; | 1928 | goto bad_fork_cleanup_io; |
1928 | 1929 | ||
1930 | stackleak_task_init(p); | ||
1931 | |||
1929 | if (pid != &init_struct_pid) { | 1932 | if (pid != &init_struct_pid) { |
1930 | pid = alloc_pid(p->nsproxy->pid_ns_for_children); | 1933 | pid = alloc_pid(p->nsproxy->pid_ns_for_children); |
1931 | if (IS_ERR(pid)) { | 1934 | if (IS_ERR(pid)) { |
diff --git a/kernel/stackleak.c b/kernel/stackleak.c new file mode 100644 index 000000000000..e42892926244 --- /dev/null +++ b/kernel/stackleak.c | |||
@@ -0,0 +1,132 @@ | |||
1 | // SPDX-License-Identifier: GPL-2.0 | ||
2 | /* | ||
3 | * This code fills the used part of the kernel stack with a poison value | ||
4 | * before returning to userspace. It's part of the STACKLEAK feature | ||
5 | * ported from grsecurity/PaX. | ||
6 | * | ||
7 | * Author: Alexander Popov <alex.popov@linux.com> | ||
8 | * | ||
9 | * STACKLEAK reduces the information which kernel stack leak bugs can | ||
10 | * reveal and blocks some uninitialized stack variable attacks. | ||
11 | */ | ||
12 | |||
13 | #include <linux/stackleak.h> | ||
14 | |||
15 | #ifdef CONFIG_STACKLEAK_RUNTIME_DISABLE | ||
16 | #include <linux/jump_label.h> | ||
17 | #include <linux/sysctl.h> | ||
18 | |||
19 | static DEFINE_STATIC_KEY_FALSE(stack_erasing_bypass); | ||
20 | |||
21 | int stack_erasing_sysctl(struct ctl_table *table, int write, | ||
22 | void __user *buffer, size_t *lenp, loff_t *ppos) | ||
23 | { | ||
24 | int ret = 0; | ||
25 | int state = !static_branch_unlikely(&stack_erasing_bypass); | ||
26 | int prev_state = state; | ||
27 | |||
28 | table->data = &state; | ||
29 | table->maxlen = sizeof(int); | ||
30 | ret = proc_dointvec_minmax(table, write, buffer, lenp, ppos); | ||
31 | state = !!state; | ||
32 | if (ret || !write || state == prev_state) | ||
33 | return ret; | ||
34 | |||
35 | if (state) | ||
36 | static_branch_disable(&stack_erasing_bypass); | ||
37 | else | ||
38 | static_branch_enable(&stack_erasing_bypass); | ||
39 | |||
40 | pr_warn("stackleak: kernel stack erasing is %s\n", | ||
41 | state ? "enabled" : "disabled"); | ||
42 | return ret; | ||
43 | } | ||
44 | |||
45 | #define skip_erasing() static_branch_unlikely(&stack_erasing_bypass) | ||
46 | #else | ||
47 | #define skip_erasing() false | ||
48 | #endif /* CONFIG_STACKLEAK_RUNTIME_DISABLE */ | ||
49 | |||
50 | asmlinkage void stackleak_erase(void) | ||
51 | { | ||
52 | /* It would be nice not to have 'kstack_ptr' and 'boundary' on stack */ | ||
53 | unsigned long kstack_ptr = current->lowest_stack; | ||
54 | unsigned long boundary = (unsigned long)end_of_stack(current); | ||
55 | unsigned int poison_count = 0; | ||
56 | const unsigned int depth = STACKLEAK_SEARCH_DEPTH / sizeof(unsigned long); | ||
57 | |||
58 | if (skip_erasing()) | ||
59 | return; | ||
60 | |||
61 | /* Check that 'lowest_stack' value is sane */ | ||
62 | if (unlikely(kstack_ptr - boundary >= THREAD_SIZE)) | ||
63 | kstack_ptr = boundary; | ||
64 | |||
65 | /* Search for the poison value in the kernel stack */ | ||
66 | while (kstack_ptr > boundary && poison_count <= depth) { | ||
67 | if (*(unsigned long *)kstack_ptr == STACKLEAK_POISON) | ||
68 | poison_count++; | ||
69 | else | ||
70 | poison_count = 0; | ||
71 | |||
72 | kstack_ptr -= sizeof(unsigned long); | ||
73 | } | ||
74 | |||
75 | /* | ||
76 | * One 'long int' at the bottom of the thread stack is reserved and | ||
77 | * should not be poisoned (see CONFIG_SCHED_STACK_END_CHECK=y). | ||
78 | */ | ||
79 | if (kstack_ptr == boundary) | ||
80 | kstack_ptr += sizeof(unsigned long); | ||
81 | |||
82 | #ifdef CONFIG_STACKLEAK_METRICS | ||
83 | current->prev_lowest_stack = kstack_ptr; | ||
84 | #endif | ||
85 | |||
86 | /* | ||
87 | * Now write the poison value to the kernel stack. Start from | ||
88 | * 'kstack_ptr' and move up till the new 'boundary'. We assume that | ||
89 | * the stack pointer doesn't change when we write poison. | ||
90 | */ | ||
91 | if (on_thread_stack()) | ||
92 | boundary = current_stack_pointer; | ||
93 | else | ||
94 | boundary = current_top_of_stack(); | ||
95 | |||
96 | while (kstack_ptr < boundary) { | ||
97 | *(unsigned long *)kstack_ptr = STACKLEAK_POISON; | ||
98 | kstack_ptr += sizeof(unsigned long); | ||
99 | } | ||
100 | |||
101 | /* Reset the 'lowest_stack' value for the next syscall */ | ||
102 | current->lowest_stack = current_top_of_stack() - THREAD_SIZE/64; | ||
103 | } | ||
104 | |||
105 | void __used stackleak_track_stack(void) | ||
106 | { | ||
107 | /* | ||
108 | * N.B. stackleak_erase() fills the kernel stack with the poison value, | ||
109 | * which has the register width. That code assumes that the value | ||
110 | * of 'lowest_stack' is aligned on the register width boundary. | ||
111 | * | ||
112 | * That is true for x86 and x86_64 because of the kernel stack | ||
113 | * alignment on these platforms (for details, see 'cc_stack_align' in | ||
114 | * arch/x86/Makefile). Take care of that when you port STACKLEAK to | ||
115 | * new platforms. | ||
116 | */ | ||
117 | unsigned long sp = (unsigned long)&sp; | ||
118 | |||
119 | /* | ||
120 | * Having CONFIG_STACKLEAK_TRACK_MIN_SIZE larger than | ||
121 | * STACKLEAK_SEARCH_DEPTH makes the poison search in | ||
122 | * stackleak_erase() unreliable. Let's prevent that. | ||
123 | */ | ||
124 | BUILD_BUG_ON(CONFIG_STACKLEAK_TRACK_MIN_SIZE > STACKLEAK_SEARCH_DEPTH); | ||
125 | |||
126 | if (sp < current->lowest_stack && | ||
127 | sp >= (unsigned long)task_stack_page(current) + | ||
128 | sizeof(unsigned long)) { | ||
129 | current->lowest_stack = sp; | ||
130 | } | ||
131 | } | ||
132 | EXPORT_SYMBOL(stackleak_track_stack); | ||
diff --git a/kernel/sysctl.c b/kernel/sysctl.c index cc02050fd0c4..3ae223f7b5df 100644 --- a/kernel/sysctl.c +++ b/kernel/sysctl.c | |||
@@ -91,7 +91,9 @@ | |||
91 | #ifdef CONFIG_CHR_DEV_SG | 91 | #ifdef CONFIG_CHR_DEV_SG |
92 | #include <scsi/sg.h> | 92 | #include <scsi/sg.h> |
93 | #endif | 93 | #endif |
94 | 94 | #ifdef CONFIG_STACKLEAK_RUNTIME_DISABLE | |
95 | #include <linux/stackleak.h> | ||
96 | #endif | ||
95 | #ifdef CONFIG_LOCKUP_DETECTOR | 97 | #ifdef CONFIG_LOCKUP_DETECTOR |
96 | #include <linux/nmi.h> | 98 | #include <linux/nmi.h> |
97 | #endif | 99 | #endif |
@@ -1233,6 +1235,17 @@ static struct ctl_table kern_table[] = { | |||
1233 | .extra2 = &one, | 1235 | .extra2 = &one, |
1234 | }, | 1236 | }, |
1235 | #endif | 1237 | #endif |
1238 | #ifdef CONFIG_STACKLEAK_RUNTIME_DISABLE | ||
1239 | { | ||
1240 | .procname = "stack_erasing", | ||
1241 | .data = NULL, | ||
1242 | .maxlen = sizeof(int), | ||
1243 | .mode = 0600, | ||
1244 | .proc_handler = stack_erasing_sysctl, | ||
1245 | .extra1 = &zero, | ||
1246 | .extra2 = &one, | ||
1247 | }, | ||
1248 | #endif | ||
1236 | { } | 1249 | { } |
1237 | }; | 1250 | }; |
1238 | 1251 | ||
diff --git a/scripts/Makefile.gcc-plugins b/scripts/Makefile.gcc-plugins index 0a482f341576..46c5c6809806 100644 --- a/scripts/Makefile.gcc-plugins +++ b/scripts/Makefile.gcc-plugins | |||
@@ -26,6 +26,16 @@ gcc-plugin-cflags-$(CONFIG_GCC_PLUGIN_RANDSTRUCT) \ | |||
26 | gcc-plugin-cflags-$(CONFIG_GCC_PLUGIN_RANDSTRUCT_PERFORMANCE) \ | 26 | gcc-plugin-cflags-$(CONFIG_GCC_PLUGIN_RANDSTRUCT_PERFORMANCE) \ |
27 | += -fplugin-arg-randomize_layout_plugin-performance-mode | 27 | += -fplugin-arg-randomize_layout_plugin-performance-mode |
28 | 28 | ||
29 | gcc-plugin-$(CONFIG_GCC_PLUGIN_STACKLEAK) += stackleak_plugin.so | ||
30 | gcc-plugin-cflags-$(CONFIG_GCC_PLUGIN_STACKLEAK) \ | ||
31 | += -DSTACKLEAK_PLUGIN | ||
32 | gcc-plugin-cflags-$(CONFIG_GCC_PLUGIN_STACKLEAK) \ | ||
33 | += -fplugin-arg-stackleak_plugin-track-min-size=$(CONFIG_STACKLEAK_TRACK_MIN_SIZE) | ||
34 | ifdef CONFIG_GCC_PLUGIN_STACKLEAK | ||
35 | DISABLE_STACKLEAK_PLUGIN += -fplugin-arg-stackleak_plugin-disable | ||
36 | endif | ||
37 | export DISABLE_STACKLEAK_PLUGIN | ||
38 | |||
29 | # All the plugin CFLAGS are collected here in case a build target needs to | 39 | # All the plugin CFLAGS are collected here in case a build target needs to |
30 | # filter them out of the KBUILD_CFLAGS. | 40 | # filter them out of the KBUILD_CFLAGS. |
31 | GCC_PLUGINS_CFLAGS := $(strip $(addprefix -fplugin=$(objtree)/scripts/gcc-plugins/, $(gcc-plugin-y)) $(gcc-plugin-cflags-y)) | 41 | GCC_PLUGINS_CFLAGS := $(strip $(addprefix -fplugin=$(objtree)/scripts/gcc-plugins/, $(gcc-plugin-y)) $(gcc-plugin-cflags-y)) |
diff --git a/scripts/gcc-plugins/Kconfig b/scripts/gcc-plugins/Kconfig index cb0c889e13aa..0d5c799688f0 100644 --- a/scripts/gcc-plugins/Kconfig +++ b/scripts/gcc-plugins/Kconfig | |||
@@ -139,4 +139,55 @@ config GCC_PLUGIN_RANDSTRUCT_PERFORMANCE | |||
139 | in structures. This reduces the performance hit of RANDSTRUCT | 139 | in structures. This reduces the performance hit of RANDSTRUCT |
140 | at the cost of weakened randomization. | 140 | at the cost of weakened randomization. |
141 | 141 | ||
142 | config GCC_PLUGIN_STACKLEAK | ||
143 | bool "Erase the kernel stack before returning from syscalls" | ||
144 | depends on GCC_PLUGINS | ||
145 | depends on HAVE_ARCH_STACKLEAK | ||
146 | help | ||
147 | This option makes the kernel erase the kernel stack before | ||
148 | returning from system calls. That reduces the information which | ||
149 | kernel stack leak bugs can reveal and blocks some uninitialized | ||
150 | stack variable attacks. | ||
151 | |||
152 | The tradeoff is the performance impact: on a single CPU system kernel | ||
153 | compilation sees a 1% slowdown, other systems and workloads may vary | ||
154 | and you are advised to test this feature on your expected workload | ||
155 | before deploying it. | ||
156 | |||
157 | This plugin was ported from grsecurity/PaX. More information at: | ||
158 | * https://grsecurity.net/ | ||
159 | * https://pax.grsecurity.net/ | ||
160 | |||
161 | config STACKLEAK_TRACK_MIN_SIZE | ||
162 | int "Minimum stack frame size of functions tracked by STACKLEAK" | ||
163 | default 100 | ||
164 | range 0 4096 | ||
165 | depends on GCC_PLUGIN_STACKLEAK | ||
166 | help | ||
167 | The STACKLEAK gcc plugin instruments the kernel code for tracking | ||
168 | the lowest border of the kernel stack (and for some other purposes). | ||
169 | It inserts the stackleak_track_stack() call for the functions with | ||
170 | a stack frame size greater than or equal to this parameter. | ||
171 | If unsure, leave the default value 100. | ||
172 | |||
173 | config STACKLEAK_METRICS | ||
174 | bool "Show STACKLEAK metrics in the /proc file system" | ||
175 | depends on GCC_PLUGIN_STACKLEAK | ||
176 | depends on PROC_FS | ||
177 | help | ||
178 | If this is set, STACKLEAK metrics for every task are available in | ||
179 | the /proc file system. In particular, /proc/<pid>/stack_depth | ||
180 | shows the maximum kernel stack consumption for the current and | ||
181 | previous syscalls. Although this information is not precise, it | ||
182 | can be useful for estimating the STACKLEAK performance impact for | ||
183 | your workloads. | ||
184 | |||
185 | config STACKLEAK_RUNTIME_DISABLE | ||
186 | bool "Allow runtime disabling of kernel stack erasing" | ||
187 | depends on GCC_PLUGIN_STACKLEAK | ||
188 | help | ||
189 | This option provides 'stack_erasing' sysctl, which can be used in | ||
190 | runtime to control kernel stack erasing for kernels built with | ||
191 | CONFIG_GCC_PLUGIN_STACKLEAK. | ||
192 | |||
142 | endif | 193 | endif |
diff --git a/scripts/gcc-plugins/stackleak_plugin.c b/scripts/gcc-plugins/stackleak_plugin.c new file mode 100644 index 000000000000..2f48da98b5d4 --- /dev/null +++ b/scripts/gcc-plugins/stackleak_plugin.c | |||
@@ -0,0 +1,427 @@ | |||
1 | /* | ||
2 | * Copyright 2011-2017 by the PaX Team <pageexec@freemail.hu> | ||
3 | * Modified by Alexander Popov <alex.popov@linux.com> | ||
4 | * Licensed under the GPL v2 | ||
5 | * | ||
6 | * Note: the choice of the license means that the compilation process is | ||
7 | * NOT 'eligible' as defined by gcc's library exception to the GPL v3, | ||
8 | * but for the kernel it doesn't matter since it doesn't link against | ||
9 | * any of the gcc libraries | ||
10 | * | ||
11 | * This gcc plugin is needed for tracking the lowest border of the kernel stack. | ||
12 | * It instruments the kernel code inserting stackleak_track_stack() calls: | ||
13 | * - after alloca(); | ||
14 | * - for the functions with a stack frame size greater than or equal | ||
15 | * to the "track-min-size" plugin parameter. | ||
16 | * | ||
17 | * This plugin is ported from grsecurity/PaX. For more information see: | ||
18 | * https://grsecurity.net/ | ||
19 | * https://pax.grsecurity.net/ | ||
20 | * | ||
21 | * Debugging: | ||
22 | * - use fprintf() to stderr, debug_generic_expr(), debug_gimple_stmt(), | ||
23 | * print_rtl() and print_simple_rtl(); | ||
24 | * - add "-fdump-tree-all -fdump-rtl-all" to the plugin CFLAGS in | ||
25 | * Makefile.gcc-plugins to see the verbose dumps of the gcc passes; | ||
26 | * - use gcc -E to understand the preprocessing shenanigans; | ||
27 | * - use gcc with enabled CFG/GIMPLE/SSA verification (--enable-checking). | ||
28 | */ | ||
29 | |||
30 | #include "gcc-common.h" | ||
31 | |||
32 | __visible int plugin_is_GPL_compatible; | ||
33 | |||
34 | static int track_frame_size = -1; | ||
35 | static const char track_function[] = "stackleak_track_stack"; | ||
36 | |||
37 | /* | ||
38 | * Mark these global variables (roots) for gcc garbage collector since | ||
39 | * they point to the garbage-collected memory. | ||
40 | */ | ||
41 | static GTY(()) tree track_function_decl; | ||
42 | |||
43 | static struct plugin_info stackleak_plugin_info = { | ||
44 | .version = "201707101337", | ||
45 | .help = "track-min-size=nn\ttrack stack for functions with a stack frame size >= nn bytes\n" | ||
46 | "disable\t\tdo not activate the plugin\n" | ||
47 | }; | ||
48 | |||
49 | static void stackleak_add_track_stack(gimple_stmt_iterator *gsi, bool after) | ||
50 | { | ||
51 | gimple stmt; | ||
52 | gcall *stackleak_track_stack; | ||
53 | cgraph_node_ptr node; | ||
54 | int frequency; | ||
55 | basic_block bb; | ||
56 | |||
57 | /* Insert call to void stackleak_track_stack(void) */ | ||
58 | stmt = gimple_build_call(track_function_decl, 0); | ||
59 | stackleak_track_stack = as_a_gcall(stmt); | ||
60 | if (after) { | ||
61 | gsi_insert_after(gsi, stackleak_track_stack, | ||
62 | GSI_CONTINUE_LINKING); | ||
63 | } else { | ||
64 | gsi_insert_before(gsi, stackleak_track_stack, GSI_SAME_STMT); | ||
65 | } | ||
66 | |||
67 | /* Update the cgraph */ | ||
68 | bb = gimple_bb(stackleak_track_stack); | ||
69 | node = cgraph_get_create_node(track_function_decl); | ||
70 | gcc_assert(node); | ||
71 | frequency = compute_call_stmt_bb_frequency(current_function_decl, bb); | ||
72 | cgraph_create_edge(cgraph_get_node(current_function_decl), node, | ||
73 | stackleak_track_stack, bb->count, frequency); | ||
74 | } | ||
75 | |||
76 | static bool is_alloca(gimple stmt) | ||
77 | { | ||
78 | if (gimple_call_builtin_p(stmt, BUILT_IN_ALLOCA)) | ||
79 | return true; | ||
80 | |||
81 | #if BUILDING_GCC_VERSION >= 4007 | ||
82 | if (gimple_call_builtin_p(stmt, BUILT_IN_ALLOCA_WITH_ALIGN)) | ||
83 | return true; | ||
84 | #endif | ||
85 | |||
86 | return false; | ||
87 | } | ||
88 | |||
89 | /* | ||
90 | * Work with the GIMPLE representation of the code. Insert the | ||
91 | * stackleak_track_stack() call after alloca() and into the beginning | ||
92 | * of the function if it is not instrumented. | ||
93 | */ | ||
94 | static unsigned int stackleak_instrument_execute(void) | ||
95 | { | ||
96 | basic_block bb, entry_bb; | ||
97 | bool prologue_instrumented = false, is_leaf = true; | ||
98 | gimple_stmt_iterator gsi; | ||
99 | |||
100 | /* | ||
101 | * ENTRY_BLOCK_PTR is a basic block which represents possible entry | ||
102 | * point of a function. This block does not contain any code and | ||
103 | * has a CFG edge to its successor. | ||
104 | */ | ||
105 | gcc_assert(single_succ_p(ENTRY_BLOCK_PTR_FOR_FN(cfun))); | ||
106 | entry_bb = single_succ(ENTRY_BLOCK_PTR_FOR_FN(cfun)); | ||
107 | |||
108 | /* | ||
109 | * Loop through the GIMPLE statements in each of cfun basic blocks. | ||
110 | * cfun is a global variable which represents the function that is | ||
111 | * currently processed. | ||
112 | */ | ||
113 | FOR_EACH_BB_FN(bb, cfun) { | ||
114 | for (gsi = gsi_start_bb(bb); !gsi_end_p(gsi); gsi_next(&gsi)) { | ||
115 | gimple stmt; | ||
116 | |||
117 | stmt = gsi_stmt(gsi); | ||
118 | |||
119 | /* Leaf function is a function which makes no calls */ | ||
120 | if (is_gimple_call(stmt)) | ||
121 | is_leaf = false; | ||
122 | |||
123 | if (!is_alloca(stmt)) | ||
124 | continue; | ||
125 | |||
126 | /* Insert stackleak_track_stack() call after alloca() */ | ||
127 | stackleak_add_track_stack(&gsi, true); | ||
128 | if (bb == entry_bb) | ||
129 | prologue_instrumented = true; | ||
130 | } | ||
131 | } | ||
132 | |||
133 | if (prologue_instrumented) | ||
134 | return 0; | ||
135 | |||
136 | /* | ||
137 | * Special cases to skip the instrumentation. | ||
138 | * | ||
139 | * Taking the address of static inline functions materializes them, | ||
140 | * but we mustn't instrument some of them as the resulting stack | ||
141 | * alignment required by the function call ABI will break other | ||
142 | * assumptions regarding the expected (but not otherwise enforced) | ||
143 | * register clobbering ABI. | ||
144 | * | ||
145 | * Case in point: native_save_fl on amd64 when optimized for size | ||
146 | * clobbers rdx if it were instrumented here. | ||
147 | * | ||
148 | * TODO: any more special cases? | ||
149 | */ | ||
150 | if (is_leaf && | ||
151 | !TREE_PUBLIC(current_function_decl) && | ||
152 | DECL_DECLARED_INLINE_P(current_function_decl)) { | ||
153 | return 0; | ||
154 | } | ||
155 | |||
156 | if (is_leaf && | ||
157 | !strncmp(IDENTIFIER_POINTER(DECL_NAME(current_function_decl)), | ||
158 | "_paravirt_", 10)) { | ||
159 | return 0; | ||
160 | } | ||
161 | |||
162 | /* Insert stackleak_track_stack() call at the function beginning */ | ||
163 | bb = entry_bb; | ||
164 | if (!single_pred_p(bb)) { | ||
165 | /* gcc_assert(bb_loop_depth(bb) || | ||
166 | (bb->flags & BB_IRREDUCIBLE_LOOP)); */ | ||
167 | split_edge(single_succ_edge(ENTRY_BLOCK_PTR_FOR_FN(cfun))); | ||
168 | gcc_assert(single_succ_p(ENTRY_BLOCK_PTR_FOR_FN(cfun))); | ||
169 | bb = single_succ(ENTRY_BLOCK_PTR_FOR_FN(cfun)); | ||
170 | } | ||
171 | gsi = gsi_after_labels(bb); | ||
172 | stackleak_add_track_stack(&gsi, false); | ||
173 | |||
174 | return 0; | ||
175 | } | ||
176 | |||
177 | static bool large_stack_frame(void) | ||
178 | { | ||
179 | #if BUILDING_GCC_VERSION >= 8000 | ||
180 | return maybe_ge(get_frame_size(), track_frame_size); | ||
181 | #else | ||
182 | return (get_frame_size() >= track_frame_size); | ||
183 | #endif | ||
184 | } | ||
185 | |||
186 | /* | ||
187 | * Work with the RTL representation of the code. | ||
188 | * Remove the unneeded stackleak_track_stack() calls from the functions | ||
189 | * which don't call alloca() and don't have a large enough stack frame size. | ||
190 | */ | ||
191 | static unsigned int stackleak_cleanup_execute(void) | ||
192 | { | ||
193 | rtx_insn *insn, *next; | ||
194 | |||
195 | if (cfun->calls_alloca) | ||
196 | return 0; | ||
197 | |||
198 | if (large_stack_frame()) | ||
199 | return 0; | ||
200 | |||
201 | /* | ||
202 | * Find stackleak_track_stack() calls. Loop through the chain of insns, | ||
203 | * which is an RTL representation of the code for a function. | ||
204 | * | ||
205 | * The example of a matching insn: | ||
206 | * (call_insn 8 4 10 2 (call (mem (symbol_ref ("stackleak_track_stack") | ||
207 | * [flags 0x41] <function_decl 0x7f7cd3302a80 stackleak_track_stack>) | ||
208 | * [0 stackleak_track_stack S1 A8]) (0)) 675 {*call} (expr_list | ||
209 | * (symbol_ref ("stackleak_track_stack") [flags 0x41] <function_decl | ||
210 | * 0x7f7cd3302a80 stackleak_track_stack>) (expr_list (0) (nil))) (nil)) | ||
211 | */ | ||
212 | for (insn = get_insns(); insn; insn = next) { | ||
213 | rtx body; | ||
214 | |||
215 | next = NEXT_INSN(insn); | ||
216 | |||
217 | /* Check the expression code of the insn */ | ||
218 | if (!CALL_P(insn)) | ||
219 | continue; | ||
220 | |||
221 | /* | ||
222 | * Check the expression code of the insn body, which is an RTL | ||
223 | * Expression (RTX) describing the side effect performed by | ||
224 | * that insn. | ||
225 | */ | ||
226 | body = PATTERN(insn); | ||
227 | |||
228 | if (GET_CODE(body) == PARALLEL) | ||
229 | body = XVECEXP(body, 0, 0); | ||
230 | |||
231 | if (GET_CODE(body) != CALL) | ||
232 | continue; | ||
233 | |||
234 | /* | ||
235 | * Check the first operand of the call expression. It should | ||
236 | * be a mem RTX describing the needed subroutine with a | ||
237 | * symbol_ref RTX. | ||
238 | */ | ||
239 | body = XEXP(body, 0); | ||
240 | if (GET_CODE(body) != MEM) | ||
241 | continue; | ||
242 | |||
243 | body = XEXP(body, 0); | ||
244 | if (GET_CODE(body) != SYMBOL_REF) | ||
245 | continue; | ||
246 | |||
247 | if (SYMBOL_REF_DECL(body) != track_function_decl) | ||
248 | continue; | ||
249 | |||
250 | /* Delete the stackleak_track_stack() call */ | ||
251 | delete_insn_and_edges(insn); | ||
252 | #if BUILDING_GCC_VERSION >= 4007 && BUILDING_GCC_VERSION < 8000 | ||
253 | if (GET_CODE(next) == NOTE && | ||
254 | NOTE_KIND(next) == NOTE_INSN_CALL_ARG_LOCATION) { | ||
255 | insn = next; | ||
256 | next = NEXT_INSN(insn); | ||
257 | delete_insn_and_edges(insn); | ||
258 | } | ||
259 | #endif | ||
260 | } | ||
261 | |||
262 | return 0; | ||
263 | } | ||
264 | |||
265 | static bool stackleak_gate(void) | ||
266 | { | ||
267 | tree section; | ||
268 | |||
269 | section = lookup_attribute("section", | ||
270 | DECL_ATTRIBUTES(current_function_decl)); | ||
271 | if (section && TREE_VALUE(section)) { | ||
272 | section = TREE_VALUE(TREE_VALUE(section)); | ||
273 | |||
274 | if (!strncmp(TREE_STRING_POINTER(section), ".init.text", 10)) | ||
275 | return false; | ||
276 | if (!strncmp(TREE_STRING_POINTER(section), ".devinit.text", 13)) | ||
277 | return false; | ||
278 | if (!strncmp(TREE_STRING_POINTER(section), ".cpuinit.text", 13)) | ||
279 | return false; | ||
280 | if (!strncmp(TREE_STRING_POINTER(section), ".meminit.text", 13)) | ||
281 | return false; | ||
282 | } | ||
283 | |||
284 | return track_frame_size >= 0; | ||
285 | } | ||
286 | |||
287 | /* Build the function declaration for stackleak_track_stack() */ | ||
288 | static void stackleak_start_unit(void *gcc_data __unused, | ||
289 | void *user_data __unused) | ||
290 | { | ||
291 | tree fntype; | ||
292 | |||
293 | /* void stackleak_track_stack(void) */ | ||
294 | fntype = build_function_type_list(void_type_node, NULL_TREE); | ||
295 | track_function_decl = build_fn_decl(track_function, fntype); | ||
296 | DECL_ASSEMBLER_NAME(track_function_decl); /* for LTO */ | ||
297 | TREE_PUBLIC(track_function_decl) = 1; | ||
298 | TREE_USED(track_function_decl) = 1; | ||
299 | DECL_EXTERNAL(track_function_decl) = 1; | ||
300 | DECL_ARTIFICIAL(track_function_decl) = 1; | ||
301 | DECL_PRESERVE_P(track_function_decl) = 1; | ||
302 | } | ||
303 | |||
304 | /* | ||
305 | * Pass gate function is a predicate function that gets executed before the | ||
306 | * corresponding pass. If the return value is 'true' the pass gets executed, | ||
307 | * otherwise, it is skipped. | ||
308 | */ | ||
309 | static bool stackleak_instrument_gate(void) | ||
310 | { | ||
311 | return stackleak_gate(); | ||
312 | } | ||
313 | |||
314 | #define PASS_NAME stackleak_instrument | ||
315 | #define PROPERTIES_REQUIRED PROP_gimple_leh | PROP_cfg | ||
316 | #define TODO_FLAGS_START TODO_verify_ssa | TODO_verify_flow | TODO_verify_stmts | ||
317 | #define TODO_FLAGS_FINISH TODO_verify_ssa | TODO_verify_stmts | TODO_dump_func \ | ||
318 | | TODO_update_ssa | TODO_rebuild_cgraph_edges | ||
319 | #include "gcc-generate-gimple-pass.h" | ||
320 | |||
321 | static bool stackleak_cleanup_gate(void) | ||
322 | { | ||
323 | return stackleak_gate(); | ||
324 | } | ||
325 | |||
326 | #define PASS_NAME stackleak_cleanup | ||
327 | #define TODO_FLAGS_FINISH TODO_dump_func | ||
328 | #include "gcc-generate-rtl-pass.h" | ||
329 | |||
330 | /* | ||
331 | * Every gcc plugin exports a plugin_init() function that is called right | ||
332 | * after the plugin is loaded. This function is responsible for registering | ||
333 | * the plugin callbacks and doing other required initialization. | ||
334 | */ | ||
335 | __visible int plugin_init(struct plugin_name_args *plugin_info, | ||
336 | struct plugin_gcc_version *version) | ||
337 | { | ||
338 | const char * const plugin_name = plugin_info->base_name; | ||
339 | const int argc = plugin_info->argc; | ||
340 | const struct plugin_argument * const argv = plugin_info->argv; | ||
341 | int i = 0; | ||
342 | |||
343 | /* Extra GGC root tables describing our GTY-ed data */ | ||
344 | static const struct ggc_root_tab gt_ggc_r_gt_stackleak[] = { | ||
345 | { | ||
346 | .base = &track_function_decl, | ||
347 | .nelt = 1, | ||
348 | .stride = sizeof(track_function_decl), | ||
349 | .cb = >_ggc_mx_tree_node, | ||
350 | .pchw = >_pch_nx_tree_node | ||
351 | }, | ||
352 | LAST_GGC_ROOT_TAB | ||
353 | }; | ||
354 | |||
355 | /* | ||
356 | * The stackleak_instrument pass should be executed before the | ||
357 | * "optimized" pass, which is the control flow graph cleanup that is | ||
358 | * performed just before expanding gcc trees to the RTL. In former | ||
359 | * versions of the plugin this new pass was inserted before the | ||
360 | * "tree_profile" pass, which is currently called "profile". | ||
361 | */ | ||
362 | PASS_INFO(stackleak_instrument, "optimized", 1, | ||
363 | PASS_POS_INSERT_BEFORE); | ||
364 | |||
365 | /* | ||
366 | * The stackleak_cleanup pass should be executed after the | ||
367 | * "reload" pass, when the stack frame size is final. | ||
368 | */ | ||
369 | PASS_INFO(stackleak_cleanup, "reload", 1, PASS_POS_INSERT_AFTER); | ||
370 | |||
371 | if (!plugin_default_version_check(version, &gcc_version)) { | ||
372 | error(G_("incompatible gcc/plugin versions")); | ||
373 | return 1; | ||
374 | } | ||
375 | |||
376 | /* Parse the plugin arguments */ | ||
377 | for (i = 0; i < argc; i++) { | ||
378 | if (!strcmp(argv[i].key, "disable")) | ||
379 | return 0; | ||
380 | |||
381 | if (!strcmp(argv[i].key, "track-min-size")) { | ||
382 | if (!argv[i].value) { | ||
383 | error(G_("no value supplied for option '-fplugin-arg-%s-%s'"), | ||
384 | plugin_name, argv[i].key); | ||
385 | return 1; | ||
386 | } | ||
387 | |||
388 | track_frame_size = atoi(argv[i].value); | ||
389 | if (track_frame_size < 0) { | ||
390 | error(G_("invalid option argument '-fplugin-arg-%s-%s=%s'"), | ||
391 | plugin_name, argv[i].key, argv[i].value); | ||
392 | return 1; | ||
393 | } | ||
394 | } else { | ||
395 | error(G_("unknown option '-fplugin-arg-%s-%s'"), | ||
396 | plugin_name, argv[i].key); | ||
397 | return 1; | ||
398 | } | ||
399 | } | ||
400 | |||
401 | /* Give the information about the plugin */ | ||
402 | register_callback(plugin_name, PLUGIN_INFO, NULL, | ||
403 | &stackleak_plugin_info); | ||
404 | |||
405 | /* Register to be called before processing a translation unit */ | ||
406 | register_callback(plugin_name, PLUGIN_START_UNIT, | ||
407 | &stackleak_start_unit, NULL); | ||
408 | |||
409 | /* Register an extra GCC garbage collector (GGC) root table */ | ||
410 | register_callback(plugin_name, PLUGIN_REGISTER_GGC_ROOTS, NULL, | ||
411 | (void *)>_ggc_r_gt_stackleak); | ||
412 | |||
413 | /* | ||
414 | * Hook into the Pass Manager to register new gcc passes. | ||
415 | * | ||
416 | * The stack frame size info is available only at the last RTL pass, | ||
417 | * when it's too late to insert complex code like a function call. | ||
418 | * So we register two gcc passes to instrument every function at first | ||
419 | * and remove the unneeded instrumentation later. | ||
420 | */ | ||
421 | register_callback(plugin_name, PLUGIN_PASS_MANAGER_SETUP, NULL, | ||
422 | &stackleak_instrument_pass_info); | ||
423 | register_callback(plugin_name, PLUGIN_PASS_MANAGER_SETUP, NULL, | ||
424 | &stackleak_cleanup_pass_info); | ||
425 | |||
426 | return 0; | ||
427 | } | ||