scs: add support for dynamic shadow call stacks
authorArd Biesheuvel <ardb@kernel.org>
Thu, 27 Oct 2022 15:59:07 +0000 (17:59 +0200)
committerWill Deacon <will@kernel.org>
Wed, 9 Nov 2022 18:06:35 +0000 (18:06 +0000)
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>
Makefile
arch/Kconfig
include/linux/scs.h
kernel/scs.c

index ac2ec990422d9a01753aa48c43e07f79b25fb01a..69b40b629f3c8ee187dc780b9e3c4523ea201cc0 100644 (file)
--- a/Makefile
+++ b/Makefile
@@ -966,8 +966,10 @@ LDFLAGS_vmlinux += --gc-sections
 endif
 
 ifdef CONFIG_SHADOW_CALL_STACK
+ifndef CONFIG_DYNAMIC_SCS
 CC_FLAGS_SCS   := -fsanitize=shadow-call-stack
 KBUILD_CFLAGS  += $(CC_FLAGS_SCS)
+endif
 export CC_FLAGS_SCS
 endif
 
index 8f138e580d1ae1f141dd0b4d3060d86e16f0287c..072a1b39e3afd0d112bc00d807910cf1057107e9 100644 (file)
@@ -651,6 +651,13 @@ config SHADOW_CALL_STACK
          reading and writing arbitrary memory may be able to locate them
          and hijack control flow by modifying the stacks.
 
+config DYNAMIC_SCS
+       bool
+       help
+         Set by the arch code if it relies on code patching to insert the
+         shadow call stack push and pop instructions rather than on the
+         compiler.
+
 config LTO
        bool
        help
index 18122d9e17ff5c35f22d97fc4c093942837c5632..4ab5bdc898cfe55db1d53c155d6562a6ffacd9b8 100644 (file)
@@ -53,6 +53,22 @@ static inline bool task_scs_end_corrupted(struct task_struct *tsk)
        return sz >= SCS_SIZE - 1 || READ_ONCE_NOCHECK(*magic) != SCS_END_MAGIC;
 }
 
+DECLARE_STATIC_KEY_FALSE(dynamic_scs_enabled);
+
+static inline bool scs_is_dynamic(void)
+{
+       if (!IS_ENABLED(CONFIG_DYNAMIC_SCS))
+               return false;
+       return static_branch_likely(&dynamic_scs_enabled);
+}
+
+static inline bool scs_is_enabled(void)
+{
+       if (!IS_ENABLED(CONFIG_DYNAMIC_SCS))
+               return true;
+       return scs_is_dynamic();
+}
+
 #else /* CONFIG_SHADOW_CALL_STACK */
 
 static inline void *scs_alloc(int node) { return NULL; }
@@ -62,6 +78,8 @@ static inline void scs_task_reset(struct task_struct *tsk) {}
 static inline int scs_prepare(struct task_struct *tsk, int node) { return 0; }
 static inline void scs_release(struct task_struct *tsk) {}
 static inline bool task_scs_end_corrupted(struct task_struct *tsk) { return false; }
+static inline bool scs_is_enabled(void) { return false; }
+static inline bool scs_is_dynamic(void) { return false; }
 
 #endif /* CONFIG_SHADOW_CALL_STACK */
 
index b7e1b096d90601bfb2dc6a2a3646c2928d22dc1d..d7809affe740471eac9a9723f7df7e92353aa0a7 100644 (file)
 #include <linux/vmalloc.h>
 #include <linux/vmstat.h>
 
+#ifdef CONFIG_DYNAMIC_SCS
+DEFINE_STATIC_KEY_FALSE(dynamic_scs_enabled);
+#endif
+
 static void __scs_account(void *s, int account)
 {
        struct page *scs_page = vmalloc_to_page(s);
@@ -101,14 +105,20 @@ static int scs_cleanup(unsigned int cpu)
 
 void __init scs_init(void)
 {
+       if (!scs_is_enabled())
+               return;
        cpuhp_setup_state(CPUHP_BP_PREPARE_DYN, "scs:scs_cache", NULL,
                          scs_cleanup);
 }
 
 int scs_prepare(struct task_struct *tsk, int node)
 {
-       void *s = scs_alloc(node);
+       void *s;
 
+       if (!scs_is_enabled())
+               return 0;
+
+       s = scs_alloc(node);
        if (!s)
                return -ENOMEM;
 
@@ -148,7 +158,7 @@ void scs_release(struct task_struct *tsk)
 {
        void *s = task_scs(tsk);
 
-       if (!s)
+       if (!scs_is_enabled() || !s)
                return;
 
        WARN(task_scs_end_corrupted(tsk),