From f344424f3ce6165e8bcafa3d8ca2b72da9471da3 Mon Sep 17 00:00:00 2001 From: Sami Tolvanen Date: Wed, 2 May 2018 10:44:59 -0700 Subject: [PATCH] ANDROID: add support for clang Shadow Call Stack (SCS) This change adds generic support for clang's Shadow Call Stack, which uses a shadow stack to protect return addresses from being overwritten by an attacker. Details are available here: https://clang.llvm.org/docs/ShadowCallStack.html Bug: 112277034 Change-Id: Idd553b7c978b0673ab533a68980fb9a654f4510c Signed-off-by: Sami Tolvanen --- Makefile | 12 ++++++ arch/Kconfig | 31 +++++++++++++ include/linux/compiler-clang.h | 2 + include/linux/compiler.h | 4 ++ include/linux/scs.h | 66 ++++++++++++++++++++++++++++ init/init_task.c | 6 +++ init/main.c | 3 ++ kernel/Makefile | 1 + kernel/fork.c | 7 +++ kernel/scs.c | 79 ++++++++++++++++++++++++++++++++++ 10 files changed, 211 insertions(+) create mode 100644 include/linux/scs.h create mode 100644 kernel/scs.c diff --git a/Makefile b/Makefile index 68511603c7c6..4ec155eb22f4 100644 --- a/Makefile +++ b/Makefile @@ -711,6 +711,13 @@ DISABLE_LTO += $(DISABLE_CFI) export DISABLE_CFI endif +ifdef CONFIG_SHADOW_CALL_STACK +scs-flags := -fsanitize=shadow-call-stack +KBUILD_CFLAGS += $(scs-flags) +DISABLE_SCS := -fno-sanitize=shadow-call-stack +export DISABLE_SCS +endif + ifdef CONFIG_CC_OPTIMIZE_FOR_SIZE KBUILD_CFLAGS += $(call cc-option,-Oz,-Os) KBUILD_CFLAGS += $(call cc-disable-warning,maybe-uninitialized,) @@ -1200,6 +1207,11 @@ ifdef cfi-flags ifeq ($(call cc-option, $(cfi-flags)),) @echo Cannot use CONFIG_CFI: $(cfi-flags) not supported by compiler >&2 && exit 1 endif +endif +ifdef scs-flags + ifeq ($(call cc-option, $(scs-flags)),) + @echo Cannot use CONFIG_SHADOW_CALL_STACK: $(scs-flags) not supported by compiler >&2 && exit 1 + endif endif @: diff --git a/arch/Kconfig b/arch/Kconfig index ad306a9d3fb2..10c8b80db9a3 100644 --- a/arch/Kconfig +++ b/arch/Kconfig @@ -574,6 +574,37 @@ config CFI_CLANG_SHADOW If you select this option, the kernel builds a fast look-up table of CFI check functions in loaded modules to reduce overhead. +config ARCH_SUPPORTS_SHADOW_CALL_STACK + bool + help + An architecture should select this if it supports clang's Shadow + Call Stack, has asm/scs.h, and implements runtime support for shadow + stack switching. + +choice + prompt "Return-oriented programming (ROP) protection" + default ROP_PROTECTION_NONE + help + This option controls kernel protections against return-oriented + programming (ROP) attacks, which involve overwriting function return + addresses. + +config ROP_PROTECTION_NONE + bool "None" + +config SHADOW_CALL_STACK + bool "clang Shadow Call Stack (EXPERIMENTAL)" + depends on ARCH_SUPPORTS_SHADOW_CALL_STACK + 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 from clang's + documentation: + + https://clang.llvm.org/docs/ShadowCallStack.html + +endchoice + config HAVE_ARCH_WITHIN_STACK_FRAMES bool help diff --git a/include/linux/compiler-clang.h b/include/linux/compiler-clang.h index 03dc578df8a2..058dd5d4314f 100644 --- a/include/linux/compiler-clang.h +++ b/include/linux/compiler-clang.h @@ -33,6 +33,8 @@ #define __nocfi __attribute__((no_sanitize("cfi"))) #endif +#define __noscs __attribute__((no_sanitize("shadow-call-stack"))) + /* all clang versions usable with the kernel support KASAN ABI version 5 */ #define KASAN_ABI_VERSION 5 diff --git a/include/linux/compiler.h b/include/linux/compiler.h index 7385c7f1b60d..8b1b8371d2b6 100644 --- a/include/linux/compiler.h +++ b/include/linux/compiler.h @@ -459,6 +459,10 @@ static __always_inline void __write_once_size(volatile void *p, void *res, int s #define __nocfi #endif +#ifndef __noscs +#define __noscs +#endif + /* * Assume alignment of return value. */ diff --git a/include/linux/scs.h b/include/linux/scs.h new file mode 100644 index 000000000000..8af26adeed8d --- /dev/null +++ b/include/linux/scs.h @@ -0,0 +1,66 @@ +/* + * Shadow Call Stack support. + * + * Copyright (C) 2018 Google LLC + */ + +#ifndef _LINUX_SCS_H +#define _LINUX_SCS_H + +#ifdef CONFIG_SHADOW_CALL_STACK + +#include +#include +#include + +#define SCS_SIZE 512 +#define SCS_GFP (GFP_KERNEL | __GFP_ZERO) + +extern unsigned long init_shadow_call_stack[]; + +static inline void *task_scs(struct task_struct *tsk) +{ + return task_thread_info(tsk)->shadow_call_stack; +} + +static inline void task_set_scs(struct task_struct *tsk, void *s) +{ + task_thread_info(tsk)->shadow_call_stack = s; +} + +extern void scs_set_init_magic(struct task_struct *tsk); +extern void scs_task_init(struct task_struct *tsk); +extern int scs_prepare(struct task_struct *tsk, int node); +extern void scs_release(struct task_struct *tsk); + +#else /* CONFIG_SHADOW_CALL_STACK */ + +static inline void *task_scs(struct task_struct *tsk) +{ + return 0; +} + +static inline void task_set_scs(struct task_struct *tsk, void *s) +{ +} + +static inline void scs_set_init_magic(struct task_struct *tsk) +{ +} + +static inline void scs_task_init(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) +{ +} + +#endif /* CONFIG_SHADOW_CALL_STACK */ + +#endif /* _LINUX_SCS_H */ diff --git a/init/init_task.c b/init/init_task.c index 11f83be1fa79..7b200aa6e16e 100644 --- a/init/init_task.c +++ b/init/init_task.c @@ -7,6 +7,7 @@ #include #include #include +#include #include #include @@ -14,6 +15,11 @@ static struct signal_struct init_signals = INIT_SIGNALS(init_signals); static struct sighand_struct init_sighand = INIT_SIGHAND(init_sighand); +#ifdef CONFIG_SHADOW_CALL_STACK +unsigned long init_shadow_call_stack[SCS_SIZE / sizeof(long)] + __init_task_data __aligned(SCS_SIZE); +#endif + /* Initial task structure */ struct task_struct init_task = INIT_TASK(init_task); EXPORT_SYMBOL(init_task); diff --git a/init/main.c b/init/main.c index 263c0811e252..d08c744f8026 100644 --- a/init/main.c +++ b/init/main.c @@ -82,6 +82,7 @@ #include #include #include +#include #include #include @@ -484,6 +485,8 @@ asmlinkage __visible void __init start_kernel(void) char *after_dashes; set_task_stack_end_magic(&init_task); + scs_set_init_magic(&init_task); + smp_setup_processor_id(); debug_objects_early_init(); diff --git a/kernel/Makefile b/kernel/Makefile index f84f812ba75d..b2dbd7556469 100644 --- a/kernel/Makefile +++ b/kernel/Makefile @@ -106,6 +106,7 @@ obj-$(CONFIG_IRQ_WORK) += irq_work.o obj-$(CONFIG_CPU_PM) += cpu_pm.o obj-$(CONFIG_BPF) += bpf/ obj-$(CONFIG_CFI_CLANG) += cfi.o +obj-$(CONFIG_SHADOW_CALL_STACK) += scs.o obj-$(CONFIG_PERF_EVENTS) += events/ diff --git a/kernel/fork.c b/kernel/fork.c index c9e77e757499..067a32637b45 100644 --- a/kernel/fork.c +++ b/kernel/fork.c @@ -78,6 +78,7 @@ #include #include #include +#include #include #include @@ -343,6 +344,8 @@ void put_task_stack(struct task_struct *tsk) void free_task(struct task_struct *tsk) { + scs_release(tsk); + #ifndef CONFIG_THREAD_INFO_IN_TASK /* * The task is finally done with both the stack and thread_info, @@ -532,6 +535,7 @@ static struct task_struct *dup_task_struct(struct task_struct *orig, int node) clear_user_return_notifier(tsk); clear_tsk_need_resched(tsk); set_task_stack_end_magic(tsk); + scs_task_init(tsk); #ifdef CONFIG_CC_STACKPROTECTOR tsk->stack_canary = get_random_long(); @@ -1707,6 +1711,9 @@ static __latent_entropy struct task_struct *copy_process( retval = copy_thread_tls(clone_flags, stack_start, stack_size, p, tls); if (retval) goto bad_fork_cleanup_io; + retval = scs_prepare(p, node); + if (retval) + goto bad_fork_cleanup_thread; if (pid != &init_struct_pid) { pid = alloc_pid(p->nsproxy->pid_ns_for_children); diff --git a/kernel/scs.c b/kernel/scs.c new file mode 100644 index 000000000000..21e019eb46db --- /dev/null +++ b/kernel/scs.c @@ -0,0 +1,79 @@ +/* + * Shadow Call Stack support. + * + * Copyright (C) 2018 Google LLC + */ + +#include +#include +#include +#include + +#include + +#define SCS_END_MAGIC 0xaf0194819b1635f6UL + +static inline void *__scs_base(struct task_struct *tsk) +{ + return (void *)((uintptr_t)task_scs(tsk) & ~(SCS_SIZE - 1)); +} + +static inline void *scs_alloc(int node) +{ + return kmalloc(SCS_SIZE, SCS_GFP); +} + +static inline void scs_free(void *s) +{ + kfree(s); +} + +static inline unsigned long *scs_magic(struct task_struct *tsk) +{ + return (unsigned long *)(__scs_base(tsk) + SCS_SIZE - sizeof(long)); +} + +static inline void scs_set_magic(struct task_struct *tsk) +{ + *scs_magic(tsk) = SCS_END_MAGIC; +} + +void scs_task_init(struct task_struct *tsk) +{ + task_set_scs(tsk, NULL); +} + +void scs_set_init_magic(struct task_struct *tsk) +{ + scs_save(tsk); + scs_set_magic(tsk); + scs_load(tsk); +} + +int scs_prepare(struct task_struct *tsk, int node) +{ + void *s; + + s = scs_alloc(node); + if (!s) + return -ENOMEM; + + task_set_scs(tsk, s); + scs_set_magic(tsk); + + return 0; +} + +void scs_release(struct task_struct *tsk) +{ + void *s; + + s = __scs_base(tsk); + if (!s) + return; + + BUG_ON(*scs_magic(tsk) != SCS_END_MAGIC); + + scs_task_init(tsk); + scs_free(s); +}