Linux kernel mirror (for testing) git.kernel.org/pub/scm/linux/kernel/git/torvalds/linux.git
kernel os linux

scs: add support for dynamic shadow call stacks

In order to allow arches to use code patching to conditionally emit the
shadow stack pushes and pops, rather than always taking the performance
hit even on CPUs that implement alternatives such as stack pointer
authentication on arm64, add a Kconfig symbol that can be set by the
arch to omit the SCS codegen itself, without otherwise affecting how
support code for SCS and compiler options (for register reservation, for
instance) are emitted.

Also, add a static key and some plumbing to omit the allocation of
shadow call stack for dynamic SCS configurations if SCS is disabled at
runtime.

Signed-off-by: Ard Biesheuvel <ardb@kernel.org>
Reviewed-by: Nick Desaulniers <ndesaulniers@google.com>
Reviewed-by: Kees Cook <keescook@chromium.org>
Reviewed-by: Sami Tolvanen <samitolvanen@google.com>
Tested-by: Sami Tolvanen <samitolvanen@google.com>
Link: https://lore.kernel.org/r/20221027155908.1940624-3-ardb@kernel.org
Signed-off-by: Will Deacon <will@kernel.org>

authored by

Ard Biesheuvel and committed by
Will Deacon
9beccca0 68c76ad4

+39 -2
+2
Makefile
··· 966 966 endif 967 967 968 968 ifdef CONFIG_SHADOW_CALL_STACK 969 + ifndef CONFIG_DYNAMIC_SCS 969 970 CC_FLAGS_SCS := -fsanitize=shadow-call-stack 970 971 KBUILD_CFLAGS += $(CC_FLAGS_SCS) 972 + endif 971 973 export CC_FLAGS_SCS 972 974 endif 973 975
+7
arch/Kconfig
··· 651 651 reading and writing arbitrary memory may be able to locate them 652 652 and hijack control flow by modifying the stacks. 653 653 654 + config DYNAMIC_SCS 655 + bool 656 + help 657 + Set by the arch code if it relies on code patching to insert the 658 + shadow call stack push and pop instructions rather than on the 659 + compiler. 660 + 654 661 config LTO 655 662 bool 656 663 help
+18
include/linux/scs.h
··· 53 53 return sz >= SCS_SIZE - 1 || READ_ONCE_NOCHECK(*magic) != SCS_END_MAGIC; 54 54 } 55 55 56 + DECLARE_STATIC_KEY_FALSE(dynamic_scs_enabled); 57 + 58 + static inline bool scs_is_dynamic(void) 59 + { 60 + if (!IS_ENABLED(CONFIG_DYNAMIC_SCS)) 61 + return false; 62 + return static_branch_likely(&dynamic_scs_enabled); 63 + } 64 + 65 + static inline bool scs_is_enabled(void) 66 + { 67 + if (!IS_ENABLED(CONFIG_DYNAMIC_SCS)) 68 + return true; 69 + return scs_is_dynamic(); 70 + } 71 + 56 72 #else /* CONFIG_SHADOW_CALL_STACK */ 57 73 58 74 static inline void *scs_alloc(int node) { return NULL; } ··· 78 62 static inline int scs_prepare(struct task_struct *tsk, int node) { return 0; } 79 63 static inline void scs_release(struct task_struct *tsk) {} 80 64 static inline bool task_scs_end_corrupted(struct task_struct *tsk) { return false; } 65 + static inline bool scs_is_enabled(void) { return false; } 66 + static inline bool scs_is_dynamic(void) { return false; } 81 67 82 68 #endif /* CONFIG_SHADOW_CALL_STACK */ 83 69
+12 -2
kernel/scs.c
··· 12 12 #include <linux/vmalloc.h> 13 13 #include <linux/vmstat.h> 14 14 15 + #ifdef CONFIG_DYNAMIC_SCS 16 + DEFINE_STATIC_KEY_FALSE(dynamic_scs_enabled); 17 + #endif 18 + 15 19 static void __scs_account(void *s, int account) 16 20 { 17 21 struct page *scs_page = vmalloc_to_page(s); ··· 105 101 106 102 void __init scs_init(void) 107 103 { 104 + if (!scs_is_enabled()) 105 + return; 108 106 cpuhp_setup_state(CPUHP_BP_PREPARE_DYN, "scs:scs_cache", NULL, 109 107 scs_cleanup); 110 108 } 111 109 112 110 int scs_prepare(struct task_struct *tsk, int node) 113 111 { 114 - void *s = scs_alloc(node); 112 + void *s; 115 113 114 + if (!scs_is_enabled()) 115 + return 0; 116 + 117 + s = scs_alloc(node); 116 118 if (!s) 117 119 return -ENOMEM; 118 120 ··· 158 148 { 159 149 void *s = task_scs(tsk); 160 150 161 - if (!s) 151 + if (!scs_is_enabled() || !s) 162 152 return; 163 153 164 154 WARN(task_scs_end_corrupted(tsk),