aboutsummaryrefslogtreecommitdiffstats
diff options
context:
space:
mode:
authorLinus Torvalds <torvalds@linux-foundation.org>2018-11-01 14:46:27 -0400
committerLinus Torvalds <torvalds@linux-foundation.org>2018-11-01 14:46:27 -0400
commit2d6bb6adb714b133db92ccd4bfc9c20f75f71f3f (patch)
treeaef040a1ee4b8b6edc5a4fa2b3c6a2c48219f27a
parent7c6c54b505b8aea1782ce6a6e8f3b8297d179937 (diff)
parent6fcde90466738b84a073e4f4d18c50015ee29fb2 (diff)
Merge tag 'stackleak-v4.20-rc1' of git://git.kernel.org/pub/scm/linux/kernel/git/kees/linux
Pull stackleak gcc plugin from Kees Cook: "Please pull this new GCC plugin, stackleak, for v4.20-rc1. This plugin was ported from grsecurity by Alexander Popov. It provides efficient stack content poisoning at syscall exit. This creates a defense against at least two classes of flaws: - Uninitialized stack usage. (We continue to work on improving the compiler to do this in other ways: e.g. unconditional zero init was proposed to GCC and Clang, and more plugin work has started too). - Stack content exposure. By greatly reducing the lifetime of valid stack contents, exposures via either direct read bugs or unknown cache side-channels become much more difficult to exploit. This complements the existing buddy and heap poisoning options, but provides the coverage for stacks. The x86 hooks are included in this series (which have been reviewed by Ingo, Dave Hansen, and Thomas Gleixner). The arm64 hooks have already been merged through the arm64 tree (written by Laura Abbott and reviewed by Mark Rutland and Will Deacon). With VLAs having been removed this release, there is no need for alloca() protection, so it has been removed from the plugin" * tag 'stackleak-v4.20-rc1' of git://git.kernel.org/pub/scm/linux/kernel/git/kees/linux: arm64: Drop unneeded stackleak_check_alloca() stackleak: Allow runtime disabling of kernel stack erasing doc: self-protection: Add information about STACKLEAK feature fs/proc: Show STACKLEAK metrics in the /proc file system lkdtm: Add a test for STACKLEAK gcc-plugins: Add STACKLEAK plugin for tracking the kernel stack x86/entry: Add STACKLEAK erasing the kernel stack at the end of syscalls
-rw-r--r--Documentation/security/self-protection.rst10
-rw-r--r--Documentation/sysctl/kernel.txt18
-rw-r--r--Documentation/x86/x86_64/mm.txt3
-rw-r--r--arch/Kconfig7
-rw-r--r--arch/arm64/kernel/process.c22
-rw-r--r--arch/x86/Kconfig1
-rw-r--r--arch/x86/entry/calling.h14
-rw-r--r--arch/x86/entry/entry_32.S7
-rw-r--r--arch/x86/entry/entry_64.S3
-rw-r--r--arch/x86/entry/entry_64_compat.S5
-rw-r--r--drivers/misc/lkdtm/Makefile2
-rw-r--r--drivers/misc/lkdtm/core.c1
-rw-r--r--drivers/misc/lkdtm/lkdtm.h3
-rw-r--r--drivers/misc/lkdtm/stackleak.c73
-rw-r--r--fs/proc/base.c18
-rw-r--r--include/linux/sched.h5
-rw-r--r--include/linux/stackleak.h35
-rw-r--r--kernel/Makefile4
-rw-r--r--kernel/fork.c3
-rw-r--r--kernel/stackleak.c132
-rw-r--r--kernel/sysctl.c15
-rw-r--r--scripts/Makefile.gcc-plugins10
-rw-r--r--scripts/gcc-plugins/Kconfig51
-rw-r--r--scripts/gcc-plugins/stackleak_plugin.c427
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.
302Memory poisoning 302Memory poisoning
303---------------- 303----------------
304 304
305When releasing memory, it is best to poison the contents (clear stack on 305When releasing memory, it is best to poison the contents, to avoid reuse
306syscall return, wipe heap memory on a free), to avoid reuse attacks that 306attacks that rely on the old contents of memory. E.g., clear stack on a
307rely on the old contents of memory. This frustrates many uninitialized 307syscall return (``CONFIG_GCC_PLUGIN_STACKLEAK``), wipe heap memory on a
308variable attacks, stack content exposures, heap content exposures, and 308free. This frustrates many uninitialized variable attacks, stack content
309use-after-free attacks. 309exposures, heap content exposures, and use-after-free attacks.
310 310
311Destination tracking 311Destination 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
991stack_erasing
992
993This parameter can be used to control kernel stack erasing at the end
994of syscalls for kernels built with CONFIG_GCC_PLUGIN_STACKLEAK.
995
996That erasing reduces the information which kernel stack leak bugs
997can reveal and blocks some uninitialized stack variable attacks.
998The tradeoff is the performance impact: on a single CPU system kernel
999compilation 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
990tainted: 1008tainted:
991 1009
992Non-zero if the kernel has been tainted. Numeric values, which can be 1010Non-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.
146Be very careful vs. KASLR when changing anything here. The KASLR address 146Be very careful vs. KASLR when changing anything here. The KASLR address
147range must not overlap with anything except the KASAN shadow area, which is 147range must not overlap with anything except the KASAN shadow area, which is
148correct as KASAN disables KASLR. 148correct as KASAN disables KASLR.
149
150For both 4- and 5-level layouts, the STACKLEAK_POISON value in the last 2MB
151hole: 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
432config 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
432config HAVE_STACKPROTECTOR 439config 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
502void __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}
520EXPORT_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
1000restore_all: 1007restore_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 */
263sysret32_from_system_call: 263sysret32_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
8lkdtm-$(CONFIG_LKDTM) += refcount.o 8lkdtm-$(CONFIG_LKDTM) += refcount.o
9lkdtm-$(CONFIG_LKDTM) += rodata_objcopy.o 9lkdtm-$(CONFIG_LKDTM) += rodata_objcopy.o
10lkdtm-$(CONFIG_LKDTM) += usercopy.o 10lkdtm-$(CONFIG_LKDTM) += usercopy.o
11lkdtm-$(CONFIG_LKDTM) += stackleak.o
11 12
13KASAN_SANITIZE_stackleak.o := n
12KCOV_INSTRUMENT_rodata.o := n 14KCOV_INSTRUMENT_rodata.o := n
13 15
14OBJCOPYFLAGS := 16OBJCOPYFLAGS :=
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);
84void lkdtm_USERCOPY_KERNEL(void); 84void lkdtm_USERCOPY_KERNEL(void);
85void lkdtm_USERCOPY_KERNEL_DS(void); 85void lkdtm_USERCOPY_KERNEL_DS(void);
86 86
87/* lkdtm_stackleak.c */
88void 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
14void 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
2909static 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
3011static int proc_tgid_base_readdir(struct file *file, struct dir_context *ctx) 3029static 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
18static 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
27int 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 */
32static 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
117obj-$(CONFIG_ZONE_DEVICE) += memremap.o 117obj-$(CONFIG_ZONE_DEVICE) += memremap.o
118obj-$(CONFIG_RSEQ) += rseq.o 118obj-$(CONFIG_RSEQ) += rseq.o
119 119
120obj-$(CONFIG_GCC_PLUGIN_STACKLEAK) += stackleak.o
121KASAN_SANITIZE_stackleak.o := n
122KCOV_INSTRUMENT_stackleak.o := n
123
120$(obj)/configs.o: $(obj)/config_data.h 124$(obj)/configs.o: $(obj)/config_data.h
121 125
122targets += config_data.gz 126targets += 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
19static DEFINE_STATIC_KEY_FALSE(stack_erasing_bypass);
20
21int 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
50asmlinkage 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
105void __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}
132EXPORT_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) \
26gcc-plugin-cflags-$(CONFIG_GCC_PLUGIN_RANDSTRUCT_PERFORMANCE) \ 26gcc-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
29gcc-plugin-$(CONFIG_GCC_PLUGIN_STACKLEAK) += stackleak_plugin.so
30gcc-plugin-cflags-$(CONFIG_GCC_PLUGIN_STACKLEAK) \
31 += -DSTACKLEAK_PLUGIN
32gcc-plugin-cflags-$(CONFIG_GCC_PLUGIN_STACKLEAK) \
33 += -fplugin-arg-stackleak_plugin-track-min-size=$(CONFIG_STACKLEAK_TRACK_MIN_SIZE)
34ifdef CONFIG_GCC_PLUGIN_STACKLEAK
35 DISABLE_STACKLEAK_PLUGIN += -fplugin-arg-stackleak_plugin-disable
36endif
37export 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.
31GCC_PLUGINS_CFLAGS := $(strip $(addprefix -fplugin=$(objtree)/scripts/gcc-plugins/, $(gcc-plugin-y)) $(gcc-plugin-cflags-y)) 41GCC_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
142config 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
161config 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
173config 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
185config 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
142endif 193endif
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
34static int track_frame_size = -1;
35static 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 */
41static GTY(()) tree track_function_decl;
42
43static 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
49static 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
76static 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 */
94static 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
177static 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 */
191static 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
265static 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() */
288static 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 */
309static 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
321static 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 = &gt_ggc_mx_tree_node,
350 .pchw = &gt_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 *)&gt_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}