ARM64: SCS: Add gcc plugin to support Shadow Call Stack
Subsystems (3)
PATCH v2 1/1 ARM64: SCS: Add gcc plugin to support Shadow Call Stack
This patch supports gcc-based SCS protection on ARM64 by adding a plugin.
For each function that x30 will be pushed onto the stack during execution, this plugin, similar to gcc's pac implementation, will normally:
1) insert "str x30, [x18], #8" at the beginning of function's prologue
2) insert "ldr x30, [x18, #-8]!" immediately before function's epilogue return/sibling calls
If pac is enabled, scs push/pop will be inserted between paciasp/autiasp.
At present, this patch has been successfully compiled(based on defconfig with kernel 5.14) in the following gcc versions(if plugin is supported) and startup normally with commands:
make ARCH=arm64 defconfig./scripts/config -e CONFIG_GCC_PLUGINS -e CONFIG_SHADOW_CALL_STACK \
-e CONFIG_GCC_PLUGIN_SHADOW_CALL_STACK
make ARCH=arm64 CROSS_COMPILE=aarch64-linux-gnu-
Tested gcc version:
* 6.3.1
* 7.3.1
* 7.5.0
* 8.2.1
* 9.2.0
* 10.3.1
---
V2:
- fix incorrect config dependency
- add support for DYNAMIC_FTRACE_WITH_REGS
- add support for ARM64_PTR_AUTH_KERNEL/ARM64_BTI_KERNEL
FYI:
This function can be used to test whether the shadow stack is effective:
//noinline void __noscs scs_test(void)
noinline void scs_test(void)
{
register unsigned long *sp asm("sp");
unsigned long * lr = sp + 1;
asm volatile("":::"x30");
*lr = 0;
}when compiled with:CONFIG_DYNAMIC_FTRACE_WITH_REGS=yCONFIG_ARM64_PTR_AUTH_KERNEL=yCONFIG_ARM64_BTI_KERNEL=y
ffff800010013b60 <scs_test>:
ffff800010013b60: d503245f bti c ffff800010013b64: d503201f nop ffff800010013b68: d503201f nop ffff800010013b6c: d503233f paciasp ffff800010013b70: f800865e str x30, [x18], #8 ffff800010013b74: a9bf7bfd stp x29, x30, [sp, #-16]! ffff800010013b78: 910003fd mov x29, sp ffff800010013b7c: 910003e0 mov x0, sp ffff800010013b80: f900041f str xzr, [x0, #8] ffff800010013b84: a8c17bfd ldp x29, x30, [sp], #16 ffff800010013b88: f85f8e5e ldr x30, [x18, #-8]! ffff800010013b8c: d50323bf autiasp ffff800010013b90: d65f03c0 ret
If SCS protection is enabled, this function will return normally. If the function has __noscs attribute (scs disabled), it will crash due to 0 address access.
--- Makefile | 20 ++- arch/Kconfig | 12 +- arch/arm64/Kconfig | 4 +- include/linux/compiler-gcc.h | 4 + scripts/Makefile.gcc-plugins | 7 + scripts/gcc-plugins/Kconfig | 9 + scripts/gcc-plugins/arm64_scs_plugin.c | 299 +++++++++++++++++++++++++++++++++ scripts/gcc-plugins/gcc-common.h | 4 + 8 files changed, 345 insertions(+), 14 deletions(-) create mode 100644 scripts/gcc-plugins/arm64_scs_plugin.c
Makefile +14 -6
@@ -923,12 +923,6 @@ KBUILD_CFLAGS_KERNEL += -ffunction-sections -fdata-sections LDFLAGS_vmlinux += --gc-sections endif -ifdef CONFIG_SHADOW_CALL_STACK -CC_FLAGS_SCS := -fsanitize=shadow-call-stack -KBUILD_CFLAGS += $(CC_FLAGS_SCS) -export CC_FLAGS_SCS -endif - ifdef CONFIG_LTO_CLANG ifdef CONFIG_LTO_CLANG_THIN CC_FLAGS_LTO := -flto=thin -fsplit-lto-unit
@@ -1034,6 +1028,20 @@ include-$(CONFIG_GCC_PLUGINS) += scripts/Makefile.gcc-plugins include $(addprefix $(srctree)/, $(include-y)) +ifdef CONFIG_SHADOW_CALL_STACK + +ifdef CONFIG_CC_IS_CLANG +CC_FLAGS_SCS := -fsanitize=shadow-call-stack +endif + +ifdef CONFIG_CC_IS_GCC +CC_FLAGS_SCS := $(ENABLE_SHADOW_CALL_STACK_PLUGIN) +endif + +KBUILD_CFLAGS += $(CC_FLAGS_SCS) +export CC_FLAGS_SCS +endif + # scripts/Makefile.gcc-plugins is intentionally included last. # Do not add $(call cc-option,...) below this line. When you build the kernel # from the clean source tree, the GCC plugins do not exist at this point.
arch/Kconfig +6 -6
@@ -593,14 +593,14 @@ config ARCH_SUPPORTS_SHADOW_CALL_STACK switching. config SHADOW_CALL_STACK - bool "Clang Shadow Call Stack" - depends on CC_IS_CLANG && ARCH_SUPPORTS_SHADOW_CALL_STACK + bool "Shadow Call Stack" + depends on ARCH_SUPPORTS_SHADOW_CALL_STACK depends on DYNAMIC_FTRACE_WITH_REGS || !FUNCTION_GRAPH_TRACER help - This option enables Clang's Shadow Call Stack, which uses a - shadow stack to protect function return addresses from being - overwritten by an attacker. More information can be found in - Clang's documentation: + This option enables Clang/GCC plugin's Shadow Call Stack, which + uses a shadow stack to protect function return addresses from + being overwritten by an attacker. More information can be found + in Clang's documentation: https://clang.llvm.org/docs/ShadowCallStack.html
arch/arm64/Kconfig +2 -2
@@ -81,7 +81,7 @@ config ARM64 select ARCH_SUPPORTS_DEBUG_PAGEALLOC select ARCH_SUPPORTS_HUGETLBFS select ARCH_SUPPORTS_MEMORY_FAILURE - select ARCH_SUPPORTS_SHADOW_CALL_STACK if CC_HAVE_SHADOW_CALL_STACK + select ARCH_SUPPORTS_SHADOW_CALL_STACK if (CC_HAVE_SHADOW_CALL_STACK || GCC_PLUGIN_SHADOW_CALL_STACK) select ARCH_SUPPORTS_LTO_CLANG if CPU_LITTLE_ENDIAN select ARCH_SUPPORTS_LTO_CLANG_THIN select ARCH_SUPPORTS_CFI_CLANG
@@ -1062,7 +1062,7 @@ config ARCH_HAS_FILTER_PGPROT # Supported by clang >= 7.0 config CC_HAVE_SHADOW_CALL_STACK - def_bool $(cc-option, -fsanitize=shadow-call-stack -ffixed-x18) + def_bool (CC_IS_CLANG && $(cc-option, -fsanitize=shadow-call-stack -ffixed-x18)) config PARAVIRT bool "Enable paravirtualization code"
include/linux/compiler-gcc.h +4 -0
@@ -50,6 +50,10 @@#define __latent_entropy __attribute__((latent_entropy))#endif +#if defined(SHADOW_CALL_STACK_PLUGIN ) && !defined(__CHECKER__) +#define __noscs __attribute__((no_shadow_call_stack)) +#endif +/* * calling noreturn functions, __builtin_unreachable() and __builtin_trap() * confuse the stack allocation in gcc, leading to overly large stack
scripts/Makefile.gcc-plugins +7 -0
@@ -46,6 +46,13 @@ ifdef CONFIG_GCC_PLUGIN_ARM_SSP_PER_TASK endif export DISABLE_ARM_SSP_PER_TASK_PLUGIN +gcc-plugin-$(CONFIG_GCC_PLUGIN_SHADOW_CALL_STACK) += arm64_scs_plugin.so +gcc-plugin-cflags-$(CONFIG_GCC_PLUGIN_SHADOW_CALL_STACK) \ + += -DSHADOW_CALL_STACK_PLUGIN +ifdef CONFIG_GCC_PLUGIN_SHADOW_CALL_STACK + ENABLE_SHADOW_CALL_STACK_PLUGIN += -fplugin-arg-arm64_scs_plugin-enable +endif + # 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))
scripts/gcc-plugins/Kconfig +9 -0
@@ -19,6 +19,15 @@ menuconfig GCC_PLUGINS if GCC_PLUGINS +config GCC_PLUGIN_SHADOW_CALL_STACK + bool "Plugin for ARM64 Shadow Call Stack" + depends on (!CC_HAVE_SHADOW_CALL_STACK) && ARM64 + help + This plugin is used to support kernel CONFIG_SHADOW_CALL_STACK + compiled by gcc. Its principle is basically the same as that of + CLANG. + For more information, please refer to "config SHADOW_CALL_STACK" + config GCC_PLUGIN_CYC_COMPLEXITY bool "Compute the cyclomatic complexity of a function" if EXPERT depends on !COMPILE_TEST # too noisy
scripts/gcc-plugins/arm64_scs_plugin.c new file +299 -0
299 lines not on this page · raw message
scripts/gcc-plugins/gcc-common.h +4 -0
@@ -27,6 +27,7 @@#include "except.h" #include "function.h" #include "toplev.h" +#include "insn-codes.h" #if BUILDING_GCC_VERSION >=5000 #include "expr.h" #endif
@@ -535,6 +536,9 @@ static inline void ipa_remove_stmt_references(symtab_node *referring_node, gimpl }#endif +/* RTL related */ +extern int recog (rtx ,rtx_insn *,int *); +#if BUILDING_GCC_VERSION <6000 #define get_inner_reference (exp, pbitsize, pbitpos, poffset, pmode, punsignedp, preversep, pvolatilep, keep_aligning) \ get_inner_reference(exp, pbitsize, pbitpos, poffset, pmode, punsignedp, pvolatilep, keep_aligning)
Signature · 1 line
2.7.4