UPSTREAM: 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> (cherry picked from commit 9beccca0984022a844850e32f0d7dd80d4a225de) Bug: 283954062 Change-Id: I71ed23533124b071bd6bf5ab91b2af3bbf03b42b Signed-off-by: Sami Tolvanen <samitolvanen@google.com>
This commit is contained in:
parent
e8d9375e53
commit
d44a32aa2c
2
Makefile
2
Makefile
@ -993,8 +993,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
|
||||||
|
|
||||||
|
@ -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
|
||||||
|
@ -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 */
|
||||||
|
|
||||||
|
14
kernel/scs.c
14
kernel/scs.c
@ -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),
|
||||||
|
Loading…
Reference in New Issue
Block a user