@@ -517,6 +517,10 @@ config GCC_PLUGIN_RANDSTRUCT_PERFORMANCE
in structures. This reduces the performance hit of RANDSTRUCT
at the cost of weakened randomization.
+config GCC_PLUGIN_ARM64_SSP_PER_TASK
+ bool
+ depends on GCC_PLUGINS
+
config HAVE_CC_STACKPROTECTOR
bool
help
@@ -35,6 +35,8 @@ ifdef CONFIG_GCC_PLUGINS
gcc-plugin-cflags-$(CONFIG_GCC_PLUGIN_RANDSTRUCT) += -DRANDSTRUCT_PLUGIN
gcc-plugin-cflags-$(CONFIG_GCC_PLUGIN_RANDSTRUCT_PERFORMANCE) += -fplugin-arg-randomize_layout_plugin-performance-mode
+ gcc-plugin-$(CONFIG_GCC_PLUGIN_ARM64_SSP_PER_TASK) += arm64_ssp_per_task_plugin.so
+
GCC_PLUGINS_CFLAGS := $(strip $(addprefix -fplugin=$(objtree)/scripts/gcc-plugins/, $(gcc-plugin-y)) $(gcc-plugin-cflags-y))
export PLUGINCC GCC_PLUGINS_CFLAGS GCC_PLUGIN GCC_PLUGIN_SUBDIR
new file mode 100644
@@ -0,0 +1,121 @@
+/* SPDX-License-Identifier: GPL-2.0 */
+/*
+ * Copyright 2018 Ard Biesheuvel <ard.biesheuvel@linaro.org>
+ */
+
+#include "gcc-common.h"
+
+__visible int plugin_is_GPL_compatible;
+
+static GTY(()) tree stack_chk_guard_decl;
+
+static void init_stack_chk_guard_decl(void)
+{
+ tree t;
+ rtx x;
+
+ t = build_decl (UNKNOWN_LOCATION,
+ VAR_DECL,
+ get_identifier ("__stack_chk_guard_tsk_offset"),
+ ptr_type_node);
+ TREE_STATIC (t) = 1;
+ TREE_PUBLIC (t) = 1;
+ DECL_EXTERNAL (t) = 1;
+ TREE_USED (t) = 1;
+ TREE_THIS_VOLATILE (t) = 1;
+ DECL_ARTIFICIAL (t) = 1;
+ DECL_IGNORED_P (t) = 1;
+
+ /* Do not share RTL as the declaration is visible outside of
+ current function. */
+ x = DECL_RTL (t);
+ RTX_FLAG (x, used) = 1;
+
+ stack_chk_guard_decl = t;
+}
+
+static tree arm64_pertask_ssp_stack_protect_guard(void)
+{
+ if (stack_chk_guard_decl == NULL);
+ init_stack_chk_guard_decl();
+ return stack_chk_guard_decl;
+}
+
+static bool arm64_pertask_ssp_rtl_gate(void)
+{
+ return true;
+}
+
+static unsigned int arm64_pertask_ssp_rtl_execute(void)
+{
+ rtx_insn *insn, *next;
+ int regno;
+
+ for (insn = get_insns(); insn; insn = next) {
+ const char *sym;
+ rtx body;
+
+ //
+ // Find a SET insn involving a HIGH SYMBOL_REF to
+ // __stack_chk_guard_tsk_offset
+ //
+ next = NEXT_INSN(insn);
+ if (!INSN_P(insn))
+ continue;
+ body = PATTERN(insn);
+ if (GET_CODE(body) != SET ||
+ GET_CODE(SET_SRC(body)) != HIGH ||
+ GET_CODE(XEXP(SET_SRC (body), 0)) != SYMBOL_REF)
+ continue;
+ if (!REG_P(SET_DEST(body)))
+ continue;
+ sym = XSTR(XEXP(SET_SRC (body), 0), 0);
+ if (strcmp(sym, "__stack_chk_guard_tsk_offset"))
+ continue;
+ regno = REGNO(SET_DEST(body));
+
+ //
+ // We have found the ADRP assignment of the relative address
+ // of __stack_chk_guard_tsk_offset. Replace it with an asm
+ // expression returning the value of sp_el0 into the same
+ // register.
+ //
+ SET_SRC(body) = gen_rtx_ASM_OPERANDS(Pmode,
+ "mrs %0, sp_el0",
+ "=r",
+ regno,
+ rtvec_alloc (0),
+ rtvec_alloc (0),
+ rtvec_alloc (0),
+ UNKNOWN_LOCATION);
+ }
+ return 0;
+}
+
+#define PASS_NAME arm64_pertask_ssp_rtl
+#define TODO_FLAGS_FINISH TODO_dump_func
+#include "gcc-generate-rtl-pass.h"
+
+static void arm64_pertask_ssp_start_unit(void *gcc_data, void *user_data)
+{
+ targetm.stack_protect_guard = arm64_pertask_ssp_stack_protect_guard;
+}
+
+__visible int plugin_init(struct plugin_name_args *plugin_info,
+ struct plugin_gcc_version *version)
+{
+ if (!plugin_default_version_check(version, &gcc_version)) {
+ error(G_("incompatible gcc/plugin versions"));
+ return 1;
+ }
+
+ PASS_INFO(arm64_pertask_ssp_rtl, "final", 1, PASS_POS_INSERT_BEFORE);
+
+ register_callback(plugin_info->base_name, PLUGIN_START_UNIT,
+ arm64_pertask_ssp_start_unit, NULL);
+
+ register_callback(plugin_info->base_name, PLUGIN_PASS_MANAGER_SETUP,
+ NULL, &arm64_pertask_ssp_rtl_pass_info);
+
+ return 0;
+}
By default, GCC for AArch64 uses a global variable __stack_chk_guard which is shared between all CPUs, and hence between all tasks, which means the stack canary value only changes between reboots. This plugin renames the symbol to __stack_chk_guard_tsk_offset, and adds an RTL pass to replace the first instruction in each adrp/add pair referring to the symbol with a load of sp_el0, which holds the address of 'current'. In a subsequent patch, we will tweak the kernel build to expose the offset of the stack_canary field in task_struct via the symbol, so that the resulting reference resolves to the task's unique canary value directly. Signed-off-by: Ard Biesheuvel <ard.biesheuvel@linaro.org> --- arch/Kconfig | 4 + scripts/Makefile.gcc-plugins | 2 + scripts/gcc-plugins/arm64_ssp_per_task_plugin.c | 121 ++++++++++++++++++++ 3 files changed, 127 insertions(+)