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 in the following gcc versions based on defconfig with kernel 5.14 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
- tested against DYNAMIC_FTRACE_WITH_REGS
- add support for sibling call
- add support for ARM64_PTR_AUTH_KERNEL/ARM64_BTI_KERNEL
FYI:
- The kernel compiled by Linaro GCC 7.3-2018.05 runs for more than 10 days, ltp> 24 hours without crash.
- 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
arch/Kconfig config ARCH_SUPPORTS_SHADOW_CALL_STACK · line 603
Quoted 9 lines · Dan Li
arch/Kconfig +4
-4- 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:
Perhaps it could be worded in a better way? It sounds like it is a custom plugin for Clang as well, e.g.:
This option enables Shadow Call Stack (in the case of GCC, as a plugin),
Cheers,
Miguel
Quoted 11 lines · Miguel Ojeda
arch/Kconfig config ARCH_SUPPORTS_SHADOW_CALL_STACK · line 603
Quoted 9 lines · Dan Li
arch/Kconfig +4
-4- 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:
Quoted 8 lines
Perhaps it could be worded in a better way? It sounds like it is a custom plugin for Clang as well, e.g.:
This option enables Shadow Call Stack (in the case of GCC, as a plugin),
Cheers,
Miguel
Thanks Miguel.
How about writing like this:
This option enables Shadow Call Stack (supported as a compiler option in the case of clang, supported as a plugin in the case of gcc), which uses a shadow stack to ...
Quoted 24 lines · Dan Li
Quoted 11 lines · Miguel Ojeda
arch/Kconfig config ARCH_SUPPORTS_SHADOW_CALL_STACK · line 603
Quoted 9 lines · Dan Li
arch/Kconfig +4
-4- 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:Quoted 8 lines
Perhaps it could be worded in a better way? It sounds like it is a custom plugin for Clang as well, e.g.:
This option enables Shadow Call Stack (in the case of GCC, as a plugin),
Cheers,
Miguel
Quoted 6 lines
Thanks Miguel.
How about writing like this:
This option enables Shadow Call Stack (supported as a compiler option in the case of clang, supported as a plugin in the case of gcc), which uses a shadow stack to ...
That sounds reasonable.
One question I have Dan, what is your sense for getting this feature landed upstream in GCC proper? It's neat to be able to use plugins to support older versions of a compiler than when the feature initially lands; I was curious if you're working to submit this into GCC as well? I didn't see anything in GCC's bug tracker for "shadow call stack," but maybe there's a different feature bug tracking this?
Signature · 2 lines
Thanks, ~Nick Desaulniers
Quoted 32 lines · Nick Desaulniers
Quoted 24 lines · Dan Li
Quoted 11 lines · Miguel Ojeda
arch/Kconfig config ARCH_SUPPORTS_SHADOW_CALL_STACK · line 603
Quoted 9 lines · Dan Li
arch/Kconfig +4
-4- 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:Quoted 8 lines
Perhaps it could be worded in a better way? It sounds like it is a custom plugin for Clang as well, e.g.:
This option enables Shadow Call Stack (in the case of GCC, as a plugin),
Cheers,
MiguelQuoted 6 lines
Thanks Miguel.
How about writing like this:
This option enables Shadow Call Stack (supported as a compiler option in the case of clang, supported as a plugin in the case of gcc), which uses a shadow stack to ...
Quoted 9 lines
That sounds reasonable.
One question I have Dan, what is your sense for getting this feature landed upstream in GCC proper? It's neat to be able to use plugins to support older versions of a compiler than when the feature initially lands; I was curious if you're working to submit this into GCC as well? I didn't see anything in GCC's bug tracker for "shadow call stack," but maybe there's a different feature bug tracking this?
Thanks Nick.
The original intention of this plugin is to provide the customer's (bsp) kernel with the ability to enable SCS protection under gcc. It's not always feasible to recompile gcc or convince customers to switch to a higher version, so I implemented this feature through a plugin. For bsp, this method could reduce its use cost.
Because my main area of concern is kernel security, I first submitted this plugin to the community.
I think [PATCH V2] can also be submitted to the gcc mainline. This is my next plan, and I still need some time to reorganize it into the gcc source code.