History

ARM: smp: add support for per-task stack canaries

On ARM, we currently only change the value of the stack canary when switching tasks if the kernel was built for UP. On SMP kernels, this is impossible since the stack canary value is obtained via a global symbol reference, which means a) all running tasks on all CPUs must use the same value b) we can only modify the value when no kernel stack frames are live on any CPU, which is effectively never. So instead, use a GCC plugin to add a RTL pass that replaces each reference to the address of the __stack_chk_guard symbol with an expression that produces the address of the 'stack_canary' field that is added to struct thread_info. This way, each task will use its own randomized value.

Ard Biesheuvel committed 7 years ago

Commit 189af461 parent ccda4af10 files · +163 −3Browse files

Showing the changes to scripts (3 of 10 files). Show all

Unified · Split

3 files
  1. scripts/Makefile.gcc-plugins +6 −0
  2. scripts/gcc-plugins/Kconfig +4 −0
  3. scripts/gcc-plugins/arm_ssp_per_task_plugin.c +103 −0
scripts/Makefile.gcc-plugins+6−0
@@ -36,6 +36,12 @@ ifdef CONFIG_GCC_PLUGIN_STACKLEAK · 35 unmodified lines
36endif36endif
37export DISABLE_STACKLEAK_PLUGIN37export 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 to45# 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))
scripts/gcc-plugins/Kconfig+4−0
@@ -190,4 +190,8 @@ config STACKLEAK_RUNTIME_DISABLE · 189 unmodified lines
190 runtime to control kernel stack erasing for kernels built with190 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
193endif197endif
scripts/gcc-plugins/arm_ssp_per_task_plugin.c+103−0
@@ -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}