Commit 9beccca0 authored by Ard Biesheuvel's avatar Ard Biesheuvel Committed by Will Deacon
Browse files

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: default avatarArd Biesheuvel <ardb@kernel.org>
Reviewed-by: default avatarNick Desaulniers <ndesaulniers@google.com>
Reviewed-by: default avatarKees Cook <keescook@chromium.org>
Reviewed-by: default avatarSami Tolvanen <samitolvanen@google.com>
Tested-by: default avatarSami Tolvanen <samitolvanen@google.com>
Link: https://lore.kernel.org/r/20221027155908.1940624-3-ardb@kernel.org


Signed-off-by: default avatarWill Deacon <will@kernel.org>
parent 68c76ad4
Loading
Loading
Loading
Loading
+2 −0
Original line number Original line Diff line number Diff line
@@ -966,8 +966,10 @@ LDFLAGS_vmlinux += --gc-sections
endif
endif


ifdef CONFIG_SHADOW_CALL_STACK
ifdef CONFIG_SHADOW_CALL_STACK
ifndef CONFIG_DYNAMIC_SCS
CC_FLAGS_SCS	:= -fsanitize=shadow-call-stack
CC_FLAGS_SCS	:= -fsanitize=shadow-call-stack
KBUILD_CFLAGS	+= $(CC_FLAGS_SCS)
KBUILD_CFLAGS	+= $(CC_FLAGS_SCS)
endif
export CC_FLAGS_SCS
export CC_FLAGS_SCS
endif
endif


+7 −0
Original line number Original line Diff line number Diff line
@@ -651,6 +651,13 @@ config SHADOW_CALL_STACK
	  reading and writing arbitrary memory may be able to locate them
	  reading and writing arbitrary memory may be able to locate them
	  and hijack control flow by modifying the stacks.
	  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
config LTO
	bool
	bool
	help
	help
+18 −0
Original line number Original line Diff line number Diff line
@@ -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;
	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 */
#else /* CONFIG_SHADOW_CALL_STACK */


static inline void *scs_alloc(int node) { return NULL; }
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 int scs_prepare(struct task_struct *tsk, int node) { return 0; }
static inline void scs_release(struct task_struct *tsk) {}
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 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 */
#endif /* CONFIG_SHADOW_CALL_STACK */


+12 −2
Original line number Original line Diff line number Diff line
@@ -12,6 +12,10 @@
#include <linux/vmalloc.h>
#include <linux/vmalloc.h>
#include <linux/vmstat.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)
static void __scs_account(void *s, int account)
{
{
	struct page *scs_page = vmalloc_to_page(s);
	struct page *scs_page = vmalloc_to_page(s);
@@ -101,14 +105,20 @@ static int scs_cleanup(unsigned int cpu)


void __init scs_init(void)
void __init scs_init(void)
{
{
	if (!scs_is_enabled())
		return;
	cpuhp_setup_state(CPUHP_BP_PREPARE_DYN, "scs:scs_cache", NULL,
	cpuhp_setup_state(CPUHP_BP_PREPARE_DYN, "scs:scs_cache", NULL,
			  scs_cleanup);
			  scs_cleanup);
}
}


int scs_prepare(struct task_struct *tsk, int node)
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)
	if (!s)
		return -ENOMEM;
		return -ENOMEM;


@@ -148,7 +158,7 @@ void scs_release(struct task_struct *tsk)
{
{
	void *s = task_scs(tsk);
	void *s = task_scs(tsk);


	if (!s)
	if (!scs_is_enabled() || !s)
		return;
		return;


	WARN(task_scs_end_corrupted(tsk),
	WARN(task_scs_end_corrupted(tsk),