aboutsummaryrefslogtreecommitdiffstats
diff options
context:
space:
mode:
-rw-r--r--arch/arm/Kconfig15
-rw-r--r--arch/arm/Makefile12
-rw-r--r--arch/arm/boot/compressed/Makefile1
-rw-r--r--arch/arm/include/asm/stackprotector.h12
-rw-r--r--arch/arm/include/asm/thread_info.h3
-rw-r--r--arch/arm/kernel/asm-offsets.c4
-rw-r--r--arch/arm/kernel/process.c6
-rw-r--r--scripts/Makefile.gcc-plugins6
-rw-r--r--scripts/gcc-plugins/Kconfig4
-rw-r--r--scripts/gcc-plugins/arm_ssp_per_task_plugin.c103
10 files changed, 163 insertions, 3 deletions
diff --git a/arch/arm/Kconfig b/arch/arm/Kconfig
index 91be74d8df65..5c0305585a0a 100644
--- a/arch/arm/Kconfig
+++ b/arch/arm/Kconfig
@@ -1810,6 +1810,21 @@ config XEN
1810 help 1810 help
1811 Say Y if you want to run Linux in a Virtual Machine on Xen on ARM. 1811 Say Y if you want to run Linux in a Virtual Machine on Xen on ARM.
1812 1812
1813config STACKPROTECTOR_PER_TASK
1814 bool "Use a unique stack canary value for each task"
1815 depends on GCC_PLUGINS && STACKPROTECTOR && SMP && !XIP_DEFLATED_DATA
1816 select GCC_PLUGIN_ARM_SSP_PER_TASK
1817 default y
1818 help
1819 Due to the fact that GCC uses an ordinary symbol reference from
1820 which to load the value of the stack canary, this value can only
1821 change at reboot time on SMP systems, and all tasks running in the
1822 kernel's address space are forced to use the same canary value for
1823 the entire duration that the system is up.
1824
1825 Enable this option to switch to a different method that uses a
1826 different canary value for each task.
1827
1813endmenu 1828endmenu
1814 1829
1815menu "Boot options" 1830menu "Boot options"
diff --git a/arch/arm/Makefile b/arch/arm/Makefile
index 05a91d8b89f3..0436002d5091 100644
--- a/arch/arm/Makefile
+++ b/arch/arm/Makefile
@@ -303,6 +303,18 @@ else
303KBUILD_IMAGE := $(boot)/zImage 303KBUILD_IMAGE := $(boot)/zImage
304endif 304endif
305 305
306ifeq ($(CONFIG_STACKPROTECTOR_PER_TASK),y)
307prepare: stack_protector_prepare
308stack_protector_prepare: prepare0
309 $(eval KBUILD_CFLAGS += \
310 -fplugin-arg-arm_ssp_per_task_plugin-tso=$(shell \
311 awk '{if ($$2 == "THREAD_SZ_ORDER") print $$3;}'\
312 include/generated/asm-offsets.h) \
313 -fplugin-arg-arm_ssp_per_task_plugin-offset=$(shell \
314 awk '{if ($$2 == "TI_STACK_CANARY") print $$3;}'\
315 include/generated/asm-offsets.h))
316endif
317
306all: $(notdir $(KBUILD_IMAGE)) 318all: $(notdir $(KBUILD_IMAGE))
307 319
308 320
diff --git a/arch/arm/boot/compressed/Makefile b/arch/arm/boot/compressed/Makefile
index 1f5a5ffe7fcf..01bf2585a0fa 100644
--- a/arch/arm/boot/compressed/Makefile
+++ b/arch/arm/boot/compressed/Makefile
@@ -101,6 +101,7 @@ clean-files += piggy_data lib1funcs.S ashldi3.S bswapsdi2.S \
101 $(libfdt) $(libfdt_hdrs) hyp-stub.S 101 $(libfdt) $(libfdt_hdrs) hyp-stub.S
102 102
103KBUILD_CFLAGS += -DDISABLE_BRANCH_PROFILING 103KBUILD_CFLAGS += -DDISABLE_BRANCH_PROFILING
104KBUILD_CFLAGS += $(DISABLE_ARM_SSP_PER_TASK_PLUGIN)
104 105
105ifeq ($(CONFIG_FUNCTION_TRACER),y) 106ifeq ($(CONFIG_FUNCTION_TRACER),y)
106ORIG_CFLAGS := $(KBUILD_CFLAGS) 107ORIG_CFLAGS := $(KBUILD_CFLAGS)
diff --git a/arch/arm/include/asm/stackprotector.h b/arch/arm/include/asm/stackprotector.h
index ef5f7b69443e..72a20c3a0a90 100644
--- a/arch/arm/include/asm/stackprotector.h
+++ b/arch/arm/include/asm/stackprotector.h
@@ -6,8 +6,10 @@
6 * the stack frame and verifying that it hasn't been overwritten when 6 * the stack frame and verifying that it hasn't been overwritten when
7 * returning from the function. The pattern is called stack canary 7 * returning from the function. The pattern is called stack canary
8 * and gcc expects it to be defined by a global variable called 8 * and gcc expects it to be defined by a global variable called
9 * "__stack_chk_guard" on ARM. This unfortunately means that on SMP 9 * "__stack_chk_guard" on ARM. This prevents SMP systems from using a
10 * we cannot have a different canary value per task. 10 * different value for each task unless we enable a GCC plugin that
11 * replaces these symbol references with references to each task's own
12 * value.
11 */ 13 */
12 14
13#ifndef _ASM_STACKPROTECTOR_H 15#ifndef _ASM_STACKPROTECTOR_H
@@ -16,6 +18,8 @@
16#include <linux/random.h> 18#include <linux/random.h>
17#include <linux/version.h> 19#include <linux/version.h>
18 20
21#include <asm/thread_info.h>
22
19extern unsigned long __stack_chk_guard; 23extern unsigned long __stack_chk_guard;
20 24
21/* 25/*
@@ -33,7 +37,11 @@ static __always_inline void boot_init_stack_canary(void)
33 canary ^= LINUX_VERSION_CODE; 37 canary ^= LINUX_VERSION_CODE;
34 38
35 current->stack_canary = canary; 39 current->stack_canary = canary;
40#ifndef CONFIG_STACKPROTECTOR_PER_TASK
36 __stack_chk_guard = current->stack_canary; 41 __stack_chk_guard = current->stack_canary;
42#else
43 current_thread_info()->stack_canary = current->stack_canary;
44#endif
37} 45}
38 46
39#endif /* _ASM_STACKPROTECTOR_H */ 47#endif /* _ASM_STACKPROTECTOR_H */
diff --git a/arch/arm/include/asm/thread_info.h b/arch/arm/include/asm/thread_info.h
index 8f55dc520a3e..286eb61c632b 100644
--- a/arch/arm/include/asm/thread_info.h
+++ b/arch/arm/include/asm/thread_info.h
@@ -53,6 +53,9 @@ struct thread_info {
53 struct task_struct *task; /* main task structure */ 53 struct task_struct *task; /* main task structure */
54 __u32 cpu; /* cpu */ 54 __u32 cpu; /* cpu */
55 __u32 cpu_domain; /* cpu domain */ 55 __u32 cpu_domain; /* cpu domain */
56#ifdef CONFIG_STACKPROTECTOR_PER_TASK
57 unsigned long stack_canary;
58#endif
56 struct cpu_context_save cpu_context; /* cpu context */ 59 struct cpu_context_save cpu_context; /* cpu context */
57 __u32 syscall; /* syscall number */ 60 __u32 syscall; /* syscall number */
58 __u8 used_cp[16]; /* thread used copro */ 61 __u8 used_cp[16]; /* thread used copro */
diff --git a/arch/arm/kernel/asm-offsets.c b/arch/arm/kernel/asm-offsets.c
index 3968d6c22455..28b27104ac0c 100644
--- a/arch/arm/kernel/asm-offsets.c
+++ b/arch/arm/kernel/asm-offsets.c
@@ -79,6 +79,10 @@ int main(void)
79#ifdef CONFIG_CRUNCH 79#ifdef CONFIG_CRUNCH
80 DEFINE(TI_CRUNCH_STATE, offsetof(struct thread_info, crunchstate)); 80 DEFINE(TI_CRUNCH_STATE, offsetof(struct thread_info, crunchstate));
81#endif 81#endif
82#ifdef CONFIG_STACKPROTECTOR_PER_TASK
83 DEFINE(TI_STACK_CANARY, offsetof(struct thread_info, stack_canary));
84#endif
85 DEFINE(THREAD_SZ_ORDER, THREAD_SIZE_ORDER);
82 BLANK(); 86 BLANK();
83 DEFINE(S_R0, offsetof(struct pt_regs, ARM_r0)); 87 DEFINE(S_R0, offsetof(struct pt_regs, ARM_r0));
84 DEFINE(S_R1, offsetof(struct pt_regs, ARM_r1)); 88 DEFINE(S_R1, offsetof(struct pt_regs, ARM_r1));
diff --git a/arch/arm/kernel/process.c b/arch/arm/kernel/process.c
index 82ab015bf42b..16601d1442d1 100644
--- a/arch/arm/kernel/process.c
+++ b/arch/arm/kernel/process.c
@@ -39,7 +39,7 @@
39#include <asm/tls.h> 39#include <asm/tls.h>
40#include <asm/vdso.h> 40#include <asm/vdso.h>
41 41
42#ifdef CONFIG_STACKPROTECTOR 42#if defined(CONFIG_STACKPROTECTOR) && !defined(CONFIG_STACKPROTECTOR_PER_TASK)
43#include <linux/stackprotector.h> 43#include <linux/stackprotector.h>
44unsigned long __stack_chk_guard __read_mostly; 44unsigned long __stack_chk_guard __read_mostly;
45EXPORT_SYMBOL(__stack_chk_guard); 45EXPORT_SYMBOL(__stack_chk_guard);
@@ -267,6 +267,10 @@ copy_thread(unsigned long clone_flags, unsigned long stack_start,
267 267
268 thread_notify(THREAD_NOTIFY_COPY, thread); 268 thread_notify(THREAD_NOTIFY_COPY, thread);
269 269
270#ifdef CONFIG_STACKPROTECTOR_PER_TASK
271 thread->stack_canary = p->stack_canary;
272#endif
273
270 return 0; 274 return 0;
271} 275}
272 276
diff --git a/scripts/Makefile.gcc-plugins b/scripts/Makefile.gcc-plugins
index 46c5c6809806..048179d8c07f 100644
--- a/scripts/Makefile.gcc-plugins
+++ b/scripts/Makefile.gcc-plugins
@@ -36,6 +36,12 @@ ifdef CONFIG_GCC_PLUGIN_STACKLEAK
36endif 36endif
37export DISABLE_STACKLEAK_PLUGIN 37export DISABLE_STACKLEAK_PLUGIN
38 38
39gcc-plugin-$(CONFIG_GCC_PLUGIN_ARM_SSP_PER_TASK) += arm_ssp_per_task_plugin.so
40ifdef CONFIG_GCC_PLUGIN_ARM_SSP_PER_TASK
41 DISABLE_ARM_SSP_PER_TASK_PLUGIN += -fplugin-arg-arm_ssp_per_task_plugin-disable
42endif
43export DISABLE_ARM_SSP_PER_TASK_PLUGIN
44
39# All the plugin CFLAGS are collected here in case a build target needs to 45# All the plugin CFLAGS are collected here in case a build target needs to
40# filter them out of the KBUILD_CFLAGS. 46# filter them out of the KBUILD_CFLAGS.
41GCC_PLUGINS_CFLAGS := $(strip $(addprefix -fplugin=$(objtree)/scripts/gcc-plugins/, $(gcc-plugin-y)) $(gcc-plugin-cflags-y)) 47GCC_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 0d5c799688f0..d45f7f36b859 100644
--- a/scripts/gcc-plugins/Kconfig
+++ b/scripts/gcc-plugins/Kconfig
@@ -190,4 +190,8 @@ config STACKLEAK_RUNTIME_DISABLE
190 runtime to control kernel stack erasing for kernels built with 190 runtime to control kernel stack erasing for kernels built with
191 CONFIG_GCC_PLUGIN_STACKLEAK. 191 CONFIG_GCC_PLUGIN_STACKLEAK.
192 192
193config GCC_PLUGIN_ARM_SSP_PER_TASK
194 bool
195 depends on GCC_PLUGINS && ARM
196
193endif 197endif
diff --git a/scripts/gcc-plugins/arm_ssp_per_task_plugin.c b/scripts/gcc-plugins/arm_ssp_per_task_plugin.c
new file mode 100644
index 000000000000..de70b8470971
--- /dev/null
+++ b/scripts/gcc-plugins/arm_ssp_per_task_plugin.c
@@ -0,0 +1,103 @@
1// SPDX-License-Identifier: GPL-2.0
2
3#include "gcc-common.h"
4
5__visible int plugin_is_GPL_compatible;
6
7static unsigned int sp_mask, canary_offset;
8
9static unsigned int arm_pertask_ssp_rtl_execute(void)
10{
11 rtx_insn *insn;
12
13 for (insn = get_insns(); insn; insn = NEXT_INSN(insn)) {
14 const char *sym;
15 rtx body;
16 rtx masked_sp;
17
18 /*
19 * Find a SET insn involving a SYMBOL_REF to __stack_chk_guard
20 */
21 if (!INSN_P(insn))
22 continue;
23 body = PATTERN(insn);
24 if (GET_CODE(body) != SET ||
25 GET_CODE(SET_SRC(body)) != SYMBOL_REF)
26 continue;
27 sym = XSTR(SET_SRC(body), 0);
28 if (strcmp(sym, "__stack_chk_guard"))
29 continue;
30
31 /*
32 * Replace the source of the SET insn with an expression that
33 * produces the address of the copy of the stack canary value
34 * stored in struct thread_info
35 */
36 masked_sp = gen_reg_rtx(Pmode);
37
38 emit_insn_before(gen_rtx_SET(masked_sp,
39 gen_rtx_AND(Pmode,
40 stack_pointer_rtx,
41 GEN_INT(sp_mask))),
42 insn);
43
44 SET_SRC(body) = gen_rtx_PLUS(Pmode, masked_sp,
45 GEN_INT(canary_offset));
46 }
47 return 0;
48}
49
50#define PASS_NAME arm_pertask_ssp_rtl
51
52#define NO_GATE
53#include "gcc-generate-rtl-pass.h"
54
55__visible int plugin_init(struct plugin_name_args *plugin_info,
56 struct plugin_gcc_version *version)
57{
58 const char * const plugin_name = plugin_info->base_name;
59 const int argc = plugin_info->argc;
60 const struct plugin_argument *argv = plugin_info->argv;
61 int tso = 0;
62 int i;
63
64 if (!plugin_default_version_check(version, &gcc_version)) {
65 error(G_("incompatible gcc/plugin versions"));
66 return 1;
67 }
68
69 for (i = 0; i < argc; ++i) {
70 if (!strcmp(argv[i].key, "disable"))
71 return 0;
72
73 /* all remaining options require a value */
74 if (!argv[i].value) {
75 error(G_("no value supplied for option '-fplugin-arg-%s-%s'"),
76 plugin_name, argv[i].key);
77 return 1;
78 }
79
80 if (!strcmp(argv[i].key, "tso")) {
81 tso = atoi(argv[i].value);
82 continue;
83 }
84
85 if (!strcmp(argv[i].key, "offset")) {
86 canary_offset = atoi(argv[i].value);
87 continue;
88 }
89 error(G_("unknown option '-fplugin-arg-%s-%s'"),
90 plugin_name, argv[i].key);
91 return 1;
92 }
93
94 /* create the mask that produces the base of the stack */
95 sp_mask = ~((1U << (12 + tso)) - 1);
96
97 PASS_INFO(arm_pertask_ssp_rtl, "expand", 1, PASS_POS_INSERT_AFTER);
98
99 register_callback(plugin_info->base_name, PLUGIN_PASS_MANAGER_SETUP,
100 NULL, &arm_pertask_ssp_rtl_pass_info);
101
102 return 0;
103}