Skip to content

Commit d08b9f0

Browse files
samitolvanenwilldeacon
authored andcommitted
scs: Add support for Clang's 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 Note that security guarantees in the kernel differ from the ones documented for user space. The kernel must store addresses of shadow stacks in memory, which means an attacker capable reading and writing arbitrary memory may be able to locate them and hijack control flow by modifying the stacks. Signed-off-by: Sami Tolvanen <[email protected]> Reviewed-by: Kees Cook <[email protected]> Reviewed-by: Miguel Ojeda <[email protected]> [will: Numerous cosmetic changes] Signed-off-by: Will Deacon <[email protected]>
1 parent 6a8b55e commit d08b9f0

File tree

10 files changed

+191
-0
lines changed

10 files changed

+191
-0
lines changed

Makefile

Lines changed: 6 additions & 0 deletions
Original file line numberDiff line numberDiff line change
@@ -866,6 +866,12 @@ ifdef CONFIG_LIVEPATCH
866866
KBUILD_CFLAGS += $(call cc-option, -flive-patching=inline-clone)
867867
endif
868868

869+
ifdef CONFIG_SHADOW_CALL_STACK
870+
CC_FLAGS_SCS := -fsanitize=shadow-call-stack
871+
KBUILD_CFLAGS += $(CC_FLAGS_SCS)
872+
export CC_FLAGS_SCS
873+
endif
874+
869875
# arch Makefile may override CC so keep this after arch Makefile is included
870876
NOSTDINC_FLAGS += -nostdinc -isystem $(shell $(CC) -print-file-name=include)
871877

arch/Kconfig

Lines changed: 24 additions & 0 deletions
Original file line numberDiff line numberDiff line change
@@ -533,6 +533,30 @@ config STACKPROTECTOR_STRONG
533533
about 20% of all kernel functions, which increases the kernel code
534534
size by about 2%.
535535

536+
config ARCH_SUPPORTS_SHADOW_CALL_STACK
537+
bool
538+
help
539+
An architecture should select this if it supports Clang's Shadow
540+
Call Stack, has asm/scs.h, and implements runtime support for shadow
541+
stack switching.
542+
543+
config SHADOW_CALL_STACK
544+
bool "Clang Shadow Call Stack"
545+
depends on CC_IS_CLANG && ARCH_SUPPORTS_SHADOW_CALL_STACK
546+
help
547+
This option enables Clang's Shadow Call Stack, which uses a
548+
shadow stack to protect function return addresses from being
549+
overwritten by an attacker. More information can be found in
550+
Clang's documentation:
551+
552+
https://clang.llvm.org/docs/ShadowCallStack.html
553+
554+
Note that security guarantees in the kernel differ from the
555+
ones documented for user space. The kernel must store addresses
556+
of shadow stacks in memory, which means an attacker capable of
557+
reading and writing arbitrary memory may be able to locate them
558+
and hijack control flow by modifying the stacks.
559+
536560
config HAVE_ARCH_WITHIN_STACK_FRAMES
537561
bool
538562
help

include/linux/compiler-clang.h

Lines changed: 4 additions & 0 deletions
Original file line numberDiff line numberDiff line change
@@ -42,3 +42,7 @@
4242
* compilers, like ICC.
4343
*/
4444
#define barrier() __asm__ __volatile__("" : : : "memory")
45+
46+
#if __has_feature(shadow_call_stack)
47+
# define __noscs __attribute__((__no_sanitize__("shadow-call-stack")))
48+
#endif

include/linux/compiler_types.h

Lines changed: 4 additions & 0 deletions
Original file line numberDiff line numberDiff line change
@@ -193,6 +193,10 @@ struct ftrace_likely_data {
193193
# define randomized_struct_fields_end
194194
#endif
195195

196+
#ifndef __noscs
197+
# define __noscs
198+
#endif
199+
196200
#ifndef asm_volatile_goto
197201
#define asm_volatile_goto(x...) asm goto(x)
198202
#endif

include/linux/scs.h

Lines changed: 68 additions & 0 deletions
Original file line numberDiff line numberDiff line change
@@ -0,0 +1,68 @@
1+
/* SPDX-License-Identifier: GPL-2.0 */
2+
/*
3+
* Shadow Call Stack support.
4+
*
5+
* Copyright (C) 2019 Google LLC
6+
*/
7+
8+
#ifndef _LINUX_SCS_H
9+
#define _LINUX_SCS_H
10+
11+
#include <linux/gfp.h>
12+
#include <linux/poison.h>
13+
#include <linux/sched.h>
14+
#include <linux/sizes.h>
15+
16+
#ifdef CONFIG_SHADOW_CALL_STACK
17+
18+
/*
19+
* In testing, 1 KiB shadow stack size (i.e. 128 stack frames on a 64-bit
20+
* architecture) provided ~40% safety margin on stack usage while keeping
21+
* memory allocation overhead reasonable.
22+
*/
23+
#define SCS_SIZE SZ_1K
24+
#define GFP_SCS (GFP_KERNEL | __GFP_ZERO)
25+
26+
/* An illegal pointer value to mark the end of the shadow stack. */
27+
#define SCS_END_MAGIC (0x5f6UL + POISON_POINTER_DELTA)
28+
29+
#define task_scs(tsk) (task_thread_info(tsk)->scs_base)
30+
#define task_scs_offset(tsk) (task_thread_info(tsk)->scs_offset)
31+
32+
void scs_init(void);
33+
int scs_prepare(struct task_struct *tsk, int node);
34+
void scs_release(struct task_struct *tsk);
35+
36+
static inline void scs_task_reset(struct task_struct *tsk)
37+
{
38+
/*
39+
* Reset the shadow stack to the base address in case the task
40+
* is reused.
41+
*/
42+
task_scs_offset(tsk) = 0;
43+
}
44+
45+
static inline unsigned long *__scs_magic(void *s)
46+
{
47+
return (unsigned long *)(s + SCS_SIZE) - 1;
48+
}
49+
50+
static inline bool scs_corrupted(struct task_struct *tsk)
51+
{
52+
unsigned long *magic = __scs_magic(task_scs(tsk));
53+
54+
return (task_scs_offset(tsk) >= SCS_SIZE - 1 ||
55+
READ_ONCE_NOCHECK(*magic) != SCS_END_MAGIC);
56+
}
57+
58+
#else /* CONFIG_SHADOW_CALL_STACK */
59+
60+
static inline void scs_init(void) {}
61+
static inline void scs_task_reset(struct task_struct *tsk) {}
62+
static inline int scs_prepare(struct task_struct *tsk, int node) { return 0; }
63+
static inline bool scs_corrupted(struct task_struct *tsk) { return false; }
64+
static inline void scs_release(struct task_struct *tsk) {}
65+
66+
#endif /* CONFIG_SHADOW_CALL_STACK */
67+
68+
#endif /* _LINUX_SCS_H */

init/init_task.c

Lines changed: 8 additions & 0 deletions
Original file line numberDiff line numberDiff line change
@@ -11,6 +11,7 @@
1111
#include <linux/mm.h>
1212
#include <linux/audit.h>
1313
#include <linux/numa.h>
14+
#include <linux/scs.h>
1415

1516
#include <asm/pgtable.h>
1617
#include <linux/uaccess.h>
@@ -50,6 +51,13 @@ static struct sighand_struct init_sighand = {
5051
.signalfd_wqh = __WAIT_QUEUE_HEAD_INITIALIZER(init_sighand.signalfd_wqh),
5152
};
5253

54+
#ifdef CONFIG_SHADOW_CALL_STACK
55+
unsigned long init_shadow_call_stack[SCS_SIZE / sizeof(long)]
56+
__init_task_data = {
57+
[(SCS_SIZE / sizeof(long)) - 1] = SCS_END_MAGIC
58+
};
59+
#endif
60+
5361
/*
5462
* Set up the first task table, touch at your own risk!. Base=0,
5563
* limit=0x1fffff (=2MB)

kernel/Makefile

Lines changed: 1 addition & 0 deletions
Original file line numberDiff line numberDiff line change
@@ -103,6 +103,7 @@ obj-$(CONFIG_TRACEPOINTS) += trace/
103103
obj-$(CONFIG_IRQ_WORK) += irq_work.o
104104
obj-$(CONFIG_CPU_PM) += cpu_pm.o
105105
obj-$(CONFIG_BPF) += bpf/
106+
obj-$(CONFIG_SHADOW_CALL_STACK) += scs.o
106107

107108
obj-$(CONFIG_PERF_EVENTS) += events/
108109

kernel/fork.c

Lines changed: 9 additions & 0 deletions
Original file line numberDiff line numberDiff line change
@@ -94,6 +94,7 @@
9494
#include <linux/thread_info.h>
9595
#include <linux/stackleak.h>
9696
#include <linux/kasan.h>
97+
#include <linux/scs.h>
9798

9899
#include <asm/pgtable.h>
99100
#include <asm/pgalloc.h>
@@ -456,6 +457,8 @@ void put_task_stack(struct task_struct *tsk)
456457

457458
void free_task(struct task_struct *tsk)
458459
{
460+
scs_release(tsk);
461+
459462
#ifndef CONFIG_THREAD_INFO_IN_TASK
460463
/*
461464
* The task is finally done with both the stack and thread_info,
@@ -840,6 +843,8 @@ void __init fork_init(void)
840843
NULL, free_vm_stack_cache);
841844
#endif
842845

846+
scs_init();
847+
843848
lockdep_init_task(&init_task);
844849
uprobes_init();
845850
}
@@ -899,6 +904,10 @@ static struct task_struct *dup_task_struct(struct task_struct *orig, int node)
899904
if (err)
900905
goto free_stack;
901906

907+
err = scs_prepare(tsk, node);
908+
if (err)
909+
goto free_stack;
910+
902911
#ifdef CONFIG_SECCOMP
903912
/*
904913
* We must handle setting up seccomp filters once we're under

kernel/sched/core.c

Lines changed: 2 additions & 0 deletions
Original file line numberDiff line numberDiff line change
@@ -11,6 +11,7 @@
1111
#include <linux/nospec.h>
1212

1313
#include <linux/kcov.h>
14+
#include <linux/scs.h>
1415

1516
#include <asm/switch_to.h>
1617
#include <asm/tlb.h>
@@ -6040,6 +6041,7 @@ void init_idle(struct task_struct *idle, int cpu)
60406041
idle->se.exec_start = sched_clock();
60416042
idle->flags |= PF_IDLE;
60426043

6044+
scs_task_reset(idle);
60436045
kasan_unpoison_task_stack(idle);
60446046

60456047
#ifdef CONFIG_SMP

kernel/scs.c

Lines changed: 65 additions & 0 deletions
Original file line numberDiff line numberDiff line change
@@ -0,0 +1,65 @@
1+
// SPDX-License-Identifier: GPL-2.0
2+
/*
3+
* Shadow Call Stack support.
4+
*
5+
* Copyright (C) 2019 Google LLC
6+
*/
7+
8+
#include <linux/kasan.h>
9+
#include <linux/scs.h>
10+
#include <linux/slab.h>
11+
#include <asm/scs.h>
12+
13+
static struct kmem_cache *scs_cache;
14+
15+
static void *scs_alloc(int node)
16+
{
17+
void *s;
18+
19+
s = kmem_cache_alloc_node(scs_cache, GFP_SCS, node);
20+
if (s) {
21+
*__scs_magic(s) = SCS_END_MAGIC;
22+
/*
23+
* Poison the allocation to catch unintentional accesses to
24+
* the shadow stack when KASAN is enabled.
25+
*/
26+
kasan_poison_object_data(scs_cache, s);
27+
}
28+
29+
return s;
30+
}
31+
32+
static void scs_free(void *s)
33+
{
34+
kasan_unpoison_object_data(scs_cache, s);
35+
kmem_cache_free(scs_cache, s);
36+
}
37+
38+
void __init scs_init(void)
39+
{
40+
scs_cache = kmem_cache_create("scs_cache", SCS_SIZE, 0, 0, NULL);
41+
}
42+
43+
int scs_prepare(struct task_struct *tsk, int node)
44+
{
45+
void *s = scs_alloc(node);
46+
47+
if (!s)
48+
return -ENOMEM;
49+
50+
task_scs(tsk) = s;
51+
task_scs_offset(tsk) = 0;
52+
53+
return 0;
54+
}
55+
56+
void scs_release(struct task_struct *tsk)
57+
{
58+
void *s = task_scs(tsk);
59+
60+
if (!s)
61+
return;
62+
63+
WARN(scs_corrupted(tsk), "corrupted shadow stack detected when freeing task\n");
64+
scs_free(s);
65+
}

0 commit comments

Comments
 (0)