aboutsummaryrefslogtreecommitdiffstats
path: root/kernel
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 /kernel
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
Diffstat (limited to 'kernel')
-rw-r--r--kernel/Makefile4
-rw-r--r--kernel/fork.c3
-rw-r--r--kernel/stackleak.c132
-rw-r--r--kernel/sysctl.c15
4 files changed, 153 insertions, 1 deletions
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