mirror of
https://github.com/torvalds/linux.git
synced 2026-06-07 14:04:54 +02:00
Revert "FROMLIST: add support for Clang's Shadow Call Stack (SCS)"
Shadow call stack is now merged upstream for 5.9, so revert the patches
cherry-picked into android-mainline in preparation for pulling the upstream
version instead.
This reverts commit 44a2290faf.
Signed-off-by: Will Deacon <willdeacon@google.com>
Change-Id: Id9119f4b5f2cd20508a9747790ac113c14ca5ced
This commit is contained in:
parent
712917c604
commit
294257f516
6
Makefile
6
Makefile
|
|
@ -876,12 +876,6 @@ 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
|
||||
|
||||
ifdef CONFIG_LTO_CLANG
|
||||
ifdef CONFIG_THINLTO
|
||||
CC_FLAGS_LTO_CLANG := -flto=thin $(call cc-option, -fsplit-lto-unit)
|
||||
|
|
|
|||
33
arch/Kconfig
33
arch/Kconfig
|
|
@ -533,39 +533,6 @@ 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 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
|
||||
|
||||
Note that security guarantees in the kernel differ from the ones
|
||||
documented for user space. The kernel must store addresses of shadow
|
||||
stacks used by other tasks and interrupt handlers in memory, which
|
||||
means an attacker capable reading and writing arbitrary memory may
|
||||
be able to locate them and hijack control flow by modifying shadow
|
||||
stacks that are not currently in use.
|
||||
|
||||
config SHADOW_CALL_STACK_VMAP
|
||||
bool "Use virtually mapped shadow call stacks"
|
||||
depends on SHADOW_CALL_STACK
|
||||
help
|
||||
Use virtually mapped shadow call stacks. Selecting this option
|
||||
provides better stack exhaustion protection, but increases per-thread
|
||||
memory consumption as a full page is allocated for each shadow stack.
|
||||
|
||||
config LTO
|
||||
bool
|
||||
|
||||
|
|
|
|||
|
|
@ -43,11 +43,6 @@
|
|||
*/
|
||||
#define barrier() __asm__ __volatile__("" : : : "memory")
|
||||
|
||||
#if __has_feature(shadow_call_stack)
|
||||
# define __noscs __attribute__((__no_sanitize__("shadow-call-stack")))
|
||||
#else
|
||||
# define __noscs
|
||||
#endif
|
||||
|
||||
#ifdef CONFIG_LTO_CLANG
|
||||
#ifdef CONFIG_FTRACE_MCOUNT_RECORD
|
||||
|
|
|
|||
|
|
@ -197,10 +197,6 @@ struct ftrace_likely_data {
|
|||
# define randomized_struct_fields_end
|
||||
#endif
|
||||
|
||||
#ifndef __noscs
|
||||
# define __noscs
|
||||
#endif
|
||||
|
||||
#ifndef __norecordmcount
|
||||
# define __norecordmcount
|
||||
#endif
|
||||
|
|
|
|||
|
|
@ -1,57 +0,0 @@
|
|||
/* 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/sched.h>
|
||||
#include <asm/page.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 1024UL
|
||||
#define GFP_SCS (GFP_KERNEL | __GFP_ZERO)
|
||||
|
||||
/*
|
||||
* A random number outside the kernel's virtual address space to mark the
|
||||
* end of the shadow stack.
|
||||
*/
|
||||
#define SCS_END_MAGIC 0xaf0194819b1635f6UL
|
||||
|
||||
#define task_scs(tsk) (task_thread_info(tsk)->shadow_call_stack)
|
||||
|
||||
static inline void task_set_scs(struct task_struct *tsk, void *s)
|
||||
{
|
||||
task_scs(tsk) = s;
|
||||
}
|
||||
|
||||
extern void scs_init(void);
|
||||
extern void scs_task_reset(struct task_struct *tsk);
|
||||
extern int scs_prepare(struct task_struct *tsk, int node);
|
||||
extern bool scs_corrupted(struct task_struct *tsk);
|
||||
extern void scs_release(struct task_struct *tsk);
|
||||
|
||||
#else /* CONFIG_SHADOW_CALL_STACK */
|
||||
|
||||
#define task_scs(tsk) NULL
|
||||
|
||||
static inline void task_set_scs(struct task_struct *tsk, void *s) {}
|
||||
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 */
|
||||
|
|
@ -11,7 +11,6 @@
|
|||
#include <linux/mm.h>
|
||||
#include <linux/audit.h>
|
||||
#include <linux/numa.h>
|
||||
#include <linux/scs.h>
|
||||
|
||||
#include <asm/pgtable.h>
|
||||
#include <linux/uaccess.h>
|
||||
|
|
@ -191,13 +190,6 @@ struct task_struct init_task
|
|||
};
|
||||
EXPORT_SYMBOL(init_task);
|
||||
|
||||
#ifdef CONFIG_SHADOW_CALL_STACK
|
||||
unsigned long init_shadow_call_stack[SCS_SIZE / sizeof(long)] __init_task_data
|
||||
__aligned(SCS_SIZE) = {
|
||||
[(SCS_SIZE / sizeof(long)) - 1] = SCS_END_MAGIC
|
||||
};
|
||||
#endif
|
||||
|
||||
/*
|
||||
* Initial thread structure. Alignment of this is handled by a special
|
||||
* linker map entry.
|
||||
|
|
|
|||
|
|
@ -106,7 +106,6 @@ obj-$(CONFIG_TRACEPOINTS) += trace/
|
|||
obj-$(CONFIG_IRQ_WORK) += irq_work.o
|
||||
obj-$(CONFIG_CPU_PM) += cpu_pm.o
|
||||
obj-$(CONFIG_BPF) += bpf/
|
||||
obj-$(CONFIG_SHADOW_CALL_STACK) += scs.o
|
||||
obj-$(CONFIG_CFI_CLANG) += cfi.o
|
||||
|
||||
obj-$(CONFIG_PERF_EVENTS) += events/
|
||||
|
|
|
|||
|
|
@ -93,7 +93,6 @@
|
|||
#include <linux/livepatch.h>
|
||||
#include <linux/thread_info.h>
|
||||
#include <linux/stackleak.h>
|
||||
#include <linux/scs.h>
|
||||
#include <linux/kasan.h>
|
||||
#include <linux/cpufreq_times.h>
|
||||
|
||||
|
|
@ -459,7 +458,6 @@ void put_task_stack(struct task_struct *tsk)
|
|||
void free_task(struct task_struct *tsk)
|
||||
{
|
||||
cpufreq_task_times_exit(tsk);
|
||||
scs_release(tsk);
|
||||
|
||||
#ifndef CONFIG_THREAD_INFO_IN_TASK
|
||||
/*
|
||||
|
|
@ -845,8 +843,6 @@ void __init fork_init(void)
|
|||
NULL, free_vm_stack_cache);
|
||||
#endif
|
||||
|
||||
scs_init();
|
||||
|
||||
lockdep_init_task(&init_task);
|
||||
uprobes_init();
|
||||
}
|
||||
|
|
@ -906,10 +902,6 @@ static struct task_struct *dup_task_struct(struct task_struct *orig, int node)
|
|||
if (err)
|
||||
goto free_stack;
|
||||
|
||||
err = scs_prepare(tsk, node);
|
||||
if (err)
|
||||
goto free_stack;
|
||||
|
||||
#ifdef CONFIG_SECCOMP
|
||||
/*
|
||||
* We must handle setting up seccomp filters once we're under
|
||||
|
|
|
|||
|
|
@ -11,7 +11,6 @@
|
|||
#include <linux/nospec.h>
|
||||
|
||||
#include <linux/kcov.h>
|
||||
#include <linux/scs.h>
|
||||
|
||||
#include <asm/switch_to.h>
|
||||
#include <asm/tlb.h>
|
||||
|
|
@ -6075,7 +6074,6 @@ void init_idle(struct task_struct *idle, int cpu)
|
|||
idle->se.exec_start = sched_clock();
|
||||
idle->flags |= PF_IDLE;
|
||||
|
||||
scs_task_reset(idle);
|
||||
kasan_unpoison_task_stack(idle);
|
||||
|
||||
#ifdef CONFIG_SMP
|
||||
|
|
|
|||
187
kernel/scs.c
187
kernel/scs.c
|
|
@ -1,187 +0,0 @@
|
|||
// SPDX-License-Identifier: GPL-2.0
|
||||
/*
|
||||
* Shadow Call Stack support.
|
||||
*
|
||||
* Copyright (C) 2019 Google LLC
|
||||
*/
|
||||
|
||||
#include <linux/cpuhotplug.h>
|
||||
#include <linux/kasan.h>
|
||||
#include <linux/mm.h>
|
||||
#include <linux/mmzone.h>
|
||||
#include <linux/scs.h>
|
||||
#include <linux/slab.h>
|
||||
#include <linux/vmalloc.h>
|
||||
#include <asm/scs.h>
|
||||
|
||||
static inline void *__scs_base(struct task_struct *tsk)
|
||||
{
|
||||
/*
|
||||
* To minimize risk the of exposure, architectures may clear a
|
||||
* task's thread_info::shadow_call_stack while that task is
|
||||
* running, and only save/restore the active shadow call stack
|
||||
* pointer when the usual register may be clobbered (e.g. across
|
||||
* context switches).
|
||||
*
|
||||
* The shadow call stack is aligned to SCS_SIZE, and grows
|
||||
* upwards, so we can mask out the low bits to extract the base
|
||||
* when the task is not running.
|
||||
*/
|
||||
return (void *)((unsigned long)task_scs(tsk) & ~(SCS_SIZE - 1));
|
||||
}
|
||||
|
||||
static inline unsigned long *scs_magic(void *s)
|
||||
{
|
||||
return (unsigned long *)(s + SCS_SIZE) - 1;
|
||||
}
|
||||
|
||||
static inline void scs_set_magic(void *s)
|
||||
{
|
||||
*scs_magic(s) = SCS_END_MAGIC;
|
||||
}
|
||||
|
||||
#ifdef CONFIG_SHADOW_CALL_STACK_VMAP
|
||||
|
||||
/* Matches NR_CACHED_STACKS for VMAP_STACK */
|
||||
#define NR_CACHED_SCS 2
|
||||
static DEFINE_PER_CPU(void *, scs_cache[NR_CACHED_SCS]);
|
||||
|
||||
static void *scs_alloc(int node)
|
||||
{
|
||||
int i;
|
||||
void *s;
|
||||
|
||||
for (i = 0; i < NR_CACHED_SCS; i++) {
|
||||
s = this_cpu_xchg(scs_cache[i], NULL);
|
||||
if (s) {
|
||||
memset(s, 0, SCS_SIZE);
|
||||
goto out;
|
||||
}
|
||||
}
|
||||
|
||||
/*
|
||||
* We allocate a full page for the shadow stack, which should be
|
||||
* more than we need. Check the assumption nevertheless.
|
||||
*/
|
||||
BUILD_BUG_ON(SCS_SIZE > PAGE_SIZE);
|
||||
|
||||
s = __vmalloc_node_range(PAGE_SIZE, SCS_SIZE,
|
||||
VMALLOC_START, VMALLOC_END,
|
||||
GFP_SCS, PAGE_KERNEL, 0,
|
||||
node, __builtin_return_address(0));
|
||||
|
||||
out:
|
||||
if (s)
|
||||
scs_set_magic(s);
|
||||
/* TODO: poison for KASAN, unpoison in scs_free */
|
||||
|
||||
return s;
|
||||
}
|
||||
|
||||
static void scs_free(void *s)
|
||||
{
|
||||
int i;
|
||||
|
||||
for (i = 0; i < NR_CACHED_SCS; i++)
|
||||
if (this_cpu_cmpxchg(scs_cache[i], 0, s) == NULL)
|
||||
return;
|
||||
|
||||
vfree_atomic(s);
|
||||
}
|
||||
|
||||
static int scs_cleanup(unsigned int cpu)
|
||||
{
|
||||
int i;
|
||||
void **cache = per_cpu_ptr(scs_cache, cpu);
|
||||
|
||||
for (i = 0; i < NR_CACHED_SCS; i++) {
|
||||
vfree(cache[i]);
|
||||
cache[i] = NULL;
|
||||
}
|
||||
|
||||
return 0;
|
||||
}
|
||||
|
||||
void __init scs_init(void)
|
||||
{
|
||||
WARN_ON(cpuhp_setup_state(CPUHP_BP_PREPARE_DYN, "scs:scs_cache", NULL,
|
||||
scs_cleanup) < 0);
|
||||
}
|
||||
|
||||
#else /* !CONFIG_SHADOW_CALL_STACK_VMAP */
|
||||
|
||||
static struct kmem_cache *scs_cache;
|
||||
|
||||
static inline void *scs_alloc(int node)
|
||||
{
|
||||
void *s;
|
||||
|
||||
s = kmem_cache_alloc_node(scs_cache, GFP_SCS, node);
|
||||
if (s) {
|
||||
scs_set_magic(s);
|
||||
/*
|
||||
* Poison the allocation to catch unintentional accesses to
|
||||
* the shadow stack when KASAN is enabled.
|
||||
*/
|
||||
kasan_poison_object_data(scs_cache, s);
|
||||
}
|
||||
|
||||
return s;
|
||||
}
|
||||
|
||||
static inline void scs_free(void *s)
|
||||
{
|
||||
kasan_unpoison_object_data(scs_cache, s);
|
||||
kmem_cache_free(scs_cache, s);
|
||||
}
|
||||
|
||||
void __init scs_init(void)
|
||||
{
|
||||
scs_cache = kmem_cache_create("scs_cache", SCS_SIZE, SCS_SIZE,
|
||||
0, NULL);
|
||||
WARN_ON(!scs_cache);
|
||||
}
|
||||
|
||||
#endif /* CONFIG_SHADOW_CALL_STACK_VMAP */
|
||||
|
||||
void scs_task_reset(struct task_struct *tsk)
|
||||
{
|
||||
/*
|
||||
* Reset the shadow stack to the base address in case the task
|
||||
* is reused.
|
||||
*/
|
||||
task_set_scs(tsk, __scs_base(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);
|
||||
return 0;
|
||||
}
|
||||
|
||||
bool scs_corrupted(struct task_struct *tsk)
|
||||
{
|
||||
unsigned long *magic = scs_magic(__scs_base(tsk));
|
||||
|
||||
return READ_ONCE_NOCHECK(*magic) != SCS_END_MAGIC;
|
||||
}
|
||||
|
||||
void scs_release(struct task_struct *tsk)
|
||||
{
|
||||
void *s;
|
||||
|
||||
s = __scs_base(tsk);
|
||||
if (!s)
|
||||
return;
|
||||
|
||||
WARN_ON(scs_corrupted(tsk));
|
||||
|
||||
task_set_scs(tsk, NULL);
|
||||
scs_free(s);
|
||||
}
|
||||
Loading…
Reference in New Issue
Block a user