summaryrefslogtreecommitdiffstats
path: root/scripts
diff options
context:
space:
mode:
authorArd Biesheuvel <ard.biesheuvel@linaro.org>2018-12-06 09:32:57 +0100
committerKees Cook <keescook@chromium.org>2018-12-12 13:20:07 -0800
commit189af4657186da08a2e79fb8e906cfd82b2ccddc (patch)
treeaef1d1ec51997c0baf5f415ad71a7929ac049746 /scripts
parentccda4af0f4b92f7b4c308d3acc262f4a7e3affad (diff)
downloadlinux-stable-189af4657186da08a2e79fb8e906cfd82b2ccddc.tar.gz
linux-stable-189af4657186da08a2e79fb8e906cfd82b2ccddc.tar.bz2
linux-stable-189af4657186da08a2e79fb8e906cfd82b2ccddc.zip
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. Cc: Russell King <linux@armlinux.org.uk> Cc: Kees Cook <keescook@chromium.org> Cc: Emese Revfy <re.emese@gmail.com> Cc: Arnd Bergmann <arnd@arndb.de> Cc: Laura Abbott <labbott@redhat.com> Cc: kernel-hardening@lists.openwall.com Acked-by: Nicolas Pitre <nico@linaro.org> Signed-off-by: Ard Biesheuvel <ard.biesheuvel@linaro.org> Signed-off-by: Kees Cook <keescook@chromium.org>
Diffstat (limited to 'scripts')
-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
3 files changed, 113 insertions, 0 deletions
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
endif
export DISABLE_STACKLEAK_PLUGIN
+gcc-plugin-$(CONFIG_GCC_PLUGIN_ARM_SSP_PER_TASK) += arm_ssp_per_task_plugin.so
+ifdef CONFIG_GCC_PLUGIN_ARM_SSP_PER_TASK
+ DISABLE_ARM_SSP_PER_TASK_PLUGIN += -fplugin-arg-arm_ssp_per_task_plugin-disable
+endif
+export DISABLE_ARM_SSP_PER_TASK_PLUGIN
+
# All the plugin CFLAGS are collected here in case a build target needs to
# filter them out of the KBUILD_CFLAGS.
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 0d5c799688f0..d45f7f36b859 100644
--- a/scripts/gcc-plugins/Kconfig
+++ b/scripts/gcc-plugins/Kconfig
@@ -190,4 +190,8 @@ config STACKLEAK_RUNTIME_DISABLE
runtime to control kernel stack erasing for kernels built with
CONFIG_GCC_PLUGIN_STACKLEAK.
+config GCC_PLUGIN_ARM_SSP_PER_TASK
+ bool
+ depends on GCC_PLUGINS && ARM
+
endif
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 @@
+// SPDX-License-Identifier: GPL-2.0
+
+#include "gcc-common.h"
+
+__visible int plugin_is_GPL_compatible;
+
+static unsigned int sp_mask, canary_offset;
+
+static unsigned int arm_pertask_ssp_rtl_execute(void)
+{
+ rtx_insn *insn;
+
+ for (insn = get_insns(); insn; insn = NEXT_INSN(insn)) {
+ const char *sym;
+ rtx body;
+ rtx masked_sp;
+
+ /*
+ * Find a SET insn involving a SYMBOL_REF to __stack_chk_guard
+ */
+ if (!INSN_P(insn))
+ continue;
+ body = PATTERN(insn);
+ if (GET_CODE(body) != SET ||
+ GET_CODE(SET_SRC(body)) != SYMBOL_REF)
+ continue;
+ sym = XSTR(SET_SRC(body), 0);
+ if (strcmp(sym, "__stack_chk_guard"))
+ continue;
+
+ /*
+ * Replace the source of the SET insn with an expression that
+ * produces the address of the copy of the stack canary value
+ * stored in struct thread_info
+ */
+ masked_sp = gen_reg_rtx(Pmode);
+
+ emit_insn_before(gen_rtx_SET(masked_sp,
+ gen_rtx_AND(Pmode,
+ stack_pointer_rtx,
+ GEN_INT(sp_mask))),
+ insn);
+
+ SET_SRC(body) = gen_rtx_PLUS(Pmode, masked_sp,
+ GEN_INT(canary_offset));
+ }
+ return 0;
+}
+
+#define PASS_NAME arm_pertask_ssp_rtl
+
+#define NO_GATE
+#include "gcc-generate-rtl-pass.h"
+
+__visible int plugin_init(struct plugin_name_args *plugin_info,
+ struct plugin_gcc_version *version)
+{
+ const char * const plugin_name = plugin_info->base_name;
+ const int argc = plugin_info->argc;
+ const struct plugin_argument *argv = plugin_info->argv;
+ int tso = 0;
+ int i;
+
+ if (!plugin_default_version_check(version, &gcc_version)) {
+ error(G_("incompatible gcc/plugin versions"));
+ return 1;
+ }
+
+ for (i = 0; i < argc; ++i) {
+ if (!strcmp(argv[i].key, "disable"))
+ return 0;
+
+ /* all remaining options require a value */
+ if (!argv[i].value) {
+ error(G_("no value supplied for option '-fplugin-arg-%s-%s'"),
+ plugin_name, argv[i].key);
+ return 1;
+ }
+
+ if (!strcmp(argv[i].key, "tso")) {
+ tso = atoi(argv[i].value);
+ continue;
+ }
+
+ if (!strcmp(argv[i].key, "offset")) {
+ canary_offset = atoi(argv[i].value);
+ continue;
+ }
+ error(G_("unknown option '-fplugin-arg-%s-%s'"),
+ plugin_name, argv[i].key);
+ return 1;
+ }
+
+ /* create the mask that produces the base of the stack */
+ sp_mask = ~((1U << (12 + tso)) - 1);
+
+ PASS_INFO(arm_pertask_ssp_rtl, "expand", 1, PASS_POS_INSERT_AFTER);
+
+ register_callback(plugin_info->base_name, PLUGIN_PASS_MANAGER_SETUP,
+ NULL, &arm_pertask_ssp_rtl_pass_info);
+
+ return 0;
+}