Commit d08b9f0c authored by Sami Tolvanen's avatar Sami Tolvanen Committed by Will Deacon
Browse files

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: default avatarSami Tolvanen <samitolvanen@google.com>
Reviewed-by: default avatarKees Cook <keescook@chromium.org>
Reviewed-by: default avatarMiguel Ojeda <miguel.ojeda.sandonis@gmail.com>
[will: Numerous cosmetic changes]
Signed-off-by: default avatarWill Deacon <will@kernel.org>
parent 6a8b55ed
Loading
Loading
Loading
Loading
+6 −0
Original line number Diff line number Diff line
@@ -866,6 +866,12 @@ ifdef CONFIG_LIVEPATCH
KBUILD_CFLAGS += $(call cc-option, -flive-patching=inline-clone)
endif

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

# arch Makefile may override CC so keep this after arch Makefile is included
NOSTDINC_FLAGS += -nostdinc -isystem $(shell $(CC) -print-file-name=include)

+24 −0
Original line number Diff line number Diff line
@@ -533,6 +533,30 @@ config STACKPROTECTOR_STRONG
	  about 20% of all kernel functions, which increases the kernel code
	  size by about 2%.

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.

config SHADOW_CALL_STACK
	bool "Clang Shadow Call Stack"
	depends on CC_IS_CLANG && 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 in
	  Clang's documentation:

	    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 of
	  reading and writing arbitrary memory may be able to locate them
	  and hijack control flow by modifying the stacks.

config HAVE_ARCH_WITHIN_STACK_FRAMES
	bool
	help
+4 −0
Original line number Diff line number Diff line
@@ -42,3 +42,7 @@
 * compilers, like ICC.
 */
#define barrier() __asm__ __volatile__("" : : : "memory")

#if __has_feature(shadow_call_stack)
# define __noscs	__attribute__((__no_sanitize__("shadow-call-stack")))
#endif
+4 −0
Original line number Diff line number Diff line
@@ -193,6 +193,10 @@ struct ftrace_likely_data {
# define randomized_struct_fields_end
#endif

#ifndef __noscs
# define __noscs
#endif

#ifndef asm_volatile_goto
#define asm_volatile_goto(x...) asm goto(x)
#endif

include/linux/scs.h

0 → 100644
+68 −0
Original line number Diff line number Diff line
/* SPDX-License-Identifier: GPL-2.0 */
/*
 * Shadow Call Stack support.
 *
 * Copyright (C) 2019 Google LLC
 */

#ifndef _LINUX_SCS_H
#define _LINUX_SCS_H

#include <linux/gfp.h>
#include <linux/poison.h>
#include <linux/sched.h>
#include <linux/sizes.h>

#ifdef CONFIG_SHADOW_CALL_STACK

/*
 * In testing, 1 KiB shadow stack size (i.e. 128 stack frames on a 64-bit
 * architecture) provided ~40% safety margin on stack usage while keeping
 * memory allocation overhead reasonable.
 */
#define SCS_SIZE		SZ_1K
#define GFP_SCS			(GFP_KERNEL | __GFP_ZERO)

/* An illegal pointer value to mark the end of the shadow stack. */
#define SCS_END_MAGIC		(0x5f6UL + POISON_POINTER_DELTA)

#define task_scs(tsk)		(task_thread_info(tsk)->scs_base)
#define task_scs_offset(tsk)	(task_thread_info(tsk)->scs_offset)

void scs_init(void);
int scs_prepare(struct task_struct *tsk, int node);
void scs_release(struct task_struct *tsk);

static inline void scs_task_reset(struct task_struct *tsk)
{
	/*
	 * Reset the shadow stack to the base address in case the task
	 * is reused.
	 */
	task_scs_offset(tsk) = 0;
}

static inline unsigned long *__scs_magic(void *s)
{
	return (unsigned long *)(s + SCS_SIZE) - 1;
}

static inline bool scs_corrupted(struct task_struct *tsk)
{
	unsigned long *magic = __scs_magic(task_scs(tsk));

	return (task_scs_offset(tsk) >= SCS_SIZE - 1 ||
		READ_ONCE_NOCHECK(*magic) != SCS_END_MAGIC);
}

#else /* CONFIG_SHADOW_CALL_STACK */

static inline void scs_init(void) {}
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 bool scs_corrupted(struct task_struct *tsk) { return false; }
static inline void scs_release(struct task_struct *tsk) {}

#endif /* CONFIG_SHADOW_CALL_STACK */

#endif /* _LINUX_SCS_H */
Loading