riscv: Implement Shadow Call Stack
Implement CONFIG_SHADOW_CALL_STACK for RISC-V. When enabled, the compiler injects instructions to all non-leaf C functions to store the return address to the shadow stack and unconditionally load it again before returning, which makes it harder to corrupt the return address through a stack overflow, for example. The active shadow call stack pointer is stored in the gp register, which makes SCS incompatible with gp relaxation. Use --no-relax-gp to ensure gp relaxation is disabled and disable global pointer loading. Add SCS pointers to struct thread_info, implement SCS initialization, and task switching Signed-off-by: Sami Tolvanen <samitolvanen@google.com> Tested-by: Nathan Chancellor <nathan@kernel.org> Link: https://lore.kernel.org/r/20230927224757.1154247-12-samitolvanen@google.com Signed-off-by: Palmer Dabbelt <palmer@rivosinc.com>
This commit is contained in:
parent
e609b4f425
commit
d1584d791a
10 changed files with 99 additions and 1 deletions
|
@ -48,6 +48,7 @@ config RISCV
|
||||||
select ARCH_SUPPORTS_HUGETLBFS if MMU
|
select ARCH_SUPPORTS_HUGETLBFS if MMU
|
||||||
select ARCH_SUPPORTS_PAGE_TABLE_CHECK if MMU
|
select ARCH_SUPPORTS_PAGE_TABLE_CHECK if MMU
|
||||||
select ARCH_SUPPORTS_PER_VMA_LOCK if MMU
|
select ARCH_SUPPORTS_PER_VMA_LOCK if MMU
|
||||||
|
select ARCH_SUPPORTS_SHADOW_CALL_STACK if HAVE_SHADOW_CALL_STACK
|
||||||
select ARCH_USE_MEMTEST
|
select ARCH_USE_MEMTEST
|
||||||
select ARCH_USE_QUEUED_RWLOCKS
|
select ARCH_USE_QUEUED_RWLOCKS
|
||||||
select ARCH_USES_CFI_TRAPS if CFI_CLANG
|
select ARCH_USES_CFI_TRAPS if CFI_CLANG
|
||||||
|
@ -174,6 +175,11 @@ config GCC_SUPPORTS_DYNAMIC_FTRACE
|
||||||
def_bool CC_IS_GCC
|
def_bool CC_IS_GCC
|
||||||
depends on $(cc-option,-fpatchable-function-entry=8)
|
depends on $(cc-option,-fpatchable-function-entry=8)
|
||||||
|
|
||||||
|
config HAVE_SHADOW_CALL_STACK
|
||||||
|
def_bool $(cc-option,-fsanitize=shadow-call-stack)
|
||||||
|
# https://github.com/riscv-non-isa/riscv-elf-psabi-doc/commit/a484e843e6eeb51f0cb7b8819e50da6d2444d769
|
||||||
|
depends on $(ld-option,--no-relax-gp)
|
||||||
|
|
||||||
config ARCH_MMAP_RND_BITS_MIN
|
config ARCH_MMAP_RND_BITS_MIN
|
||||||
default 18 if 64BIT
|
default 18 if 64BIT
|
||||||
default 8
|
default 8
|
||||||
|
|
|
@ -55,6 +55,10 @@ endif
|
||||||
endif
|
endif
|
||||||
endif
|
endif
|
||||||
|
|
||||||
|
ifeq ($(CONFIG_SHADOW_CALL_STACK),y)
|
||||||
|
KBUILD_LDFLAGS += --no-relax-gp
|
||||||
|
endif
|
||||||
|
|
||||||
# ISA string setting
|
# ISA string setting
|
||||||
riscv-march-$(CONFIG_ARCH_RV32I) := rv32ima
|
riscv-march-$(CONFIG_ARCH_RV32I) := rv32ima
|
||||||
riscv-march-$(CONFIG_ARCH_RV64I) := rv64ima
|
riscv-march-$(CONFIG_ARCH_RV64I) := rv64ima
|
||||||
|
|
|
@ -109,6 +109,11 @@
|
||||||
REG_L \dst, 0(\dst)
|
REG_L \dst, 0(\dst)
|
||||||
.endm
|
.endm
|
||||||
|
|
||||||
|
#ifdef CONFIG_SHADOW_CALL_STACK
|
||||||
|
/* gp is used as the shadow call stack pointer instead */
|
||||||
|
.macro load_global_pointer
|
||||||
|
.endm
|
||||||
|
#else
|
||||||
/* load __global_pointer to gp */
|
/* load __global_pointer to gp */
|
||||||
.macro load_global_pointer
|
.macro load_global_pointer
|
||||||
.option push
|
.option push
|
||||||
|
@ -116,6 +121,7 @@
|
||||||
la gp, __global_pointer$
|
la gp, __global_pointer$
|
||||||
.option pop
|
.option pop
|
||||||
.endm
|
.endm
|
||||||
|
#endif /* CONFIG_SHADOW_CALL_STACK */
|
||||||
|
|
||||||
/* save all GPs except x1 ~ x5 */
|
/* save all GPs except x1 ~ x5 */
|
||||||
.macro save_from_x6_to_x31
|
.macro save_from_x6_to_x31
|
||||||
|
|
47
arch/riscv/include/asm/scs.h
Normal file
47
arch/riscv/include/asm/scs.h
Normal file
|
@ -0,0 +1,47 @@
|
||||||
|
/* SPDX-License-Identifier: GPL-2.0 */
|
||||||
|
#ifndef _ASM_SCS_H
|
||||||
|
#define _ASM_SCS_H
|
||||||
|
|
||||||
|
#ifdef __ASSEMBLY__
|
||||||
|
#include <asm/asm-offsets.h>
|
||||||
|
|
||||||
|
#ifdef CONFIG_SHADOW_CALL_STACK
|
||||||
|
|
||||||
|
/* Load init_shadow_call_stack to gp. */
|
||||||
|
.macro scs_load_init_stack
|
||||||
|
la gp, init_shadow_call_stack
|
||||||
|
XIP_FIXUP_OFFSET gp
|
||||||
|
.endm
|
||||||
|
|
||||||
|
/* Load task_scs_sp(current) to gp. */
|
||||||
|
.macro scs_load_current
|
||||||
|
REG_L gp, TASK_TI_SCS_SP(tp)
|
||||||
|
.endm
|
||||||
|
|
||||||
|
/* Load task_scs_sp(current) to gp, but only if tp has changed. */
|
||||||
|
.macro scs_load_current_if_task_changed prev
|
||||||
|
beq \prev, tp, _skip_scs
|
||||||
|
scs_load_current
|
||||||
|
_skip_scs:
|
||||||
|
.endm
|
||||||
|
|
||||||
|
/* Save gp to task_scs_sp(current). */
|
||||||
|
.macro scs_save_current
|
||||||
|
REG_S gp, TASK_TI_SCS_SP(tp)
|
||||||
|
.endm
|
||||||
|
|
||||||
|
#else /* CONFIG_SHADOW_CALL_STACK */
|
||||||
|
|
||||||
|
.macro scs_load_init_stack
|
||||||
|
.endm
|
||||||
|
.macro scs_load_current
|
||||||
|
.endm
|
||||||
|
.macro scs_load_current_if_task_changed prev
|
||||||
|
.endm
|
||||||
|
.macro scs_save_current
|
||||||
|
.endm
|
||||||
|
|
||||||
|
#endif /* CONFIG_SHADOW_CALL_STACK */
|
||||||
|
#endif /* __ASSEMBLY__ */
|
||||||
|
|
||||||
|
#endif /* _ASM_SCS_H */
|
|
@ -57,8 +57,20 @@ struct thread_info {
|
||||||
long user_sp; /* User stack pointer */
|
long user_sp; /* User stack pointer */
|
||||||
int cpu;
|
int cpu;
|
||||||
unsigned long syscall_work; /* SYSCALL_WORK_ flags */
|
unsigned long syscall_work; /* SYSCALL_WORK_ flags */
|
||||||
|
#ifdef CONFIG_SHADOW_CALL_STACK
|
||||||
|
void *scs_base;
|
||||||
|
void *scs_sp;
|
||||||
|
#endif
|
||||||
};
|
};
|
||||||
|
|
||||||
|
#ifdef CONFIG_SHADOW_CALL_STACK
|
||||||
|
#define INIT_SCS \
|
||||||
|
.scs_base = init_shadow_call_stack, \
|
||||||
|
.scs_sp = init_shadow_call_stack,
|
||||||
|
#else
|
||||||
|
#define INIT_SCS
|
||||||
|
#endif
|
||||||
|
|
||||||
/*
|
/*
|
||||||
* macros/functions for gaining access to the thread information structure
|
* macros/functions for gaining access to the thread information structure
|
||||||
*
|
*
|
||||||
|
@ -68,6 +80,7 @@ struct thread_info {
|
||||||
{ \
|
{ \
|
||||||
.flags = 0, \
|
.flags = 0, \
|
||||||
.preempt_count = INIT_PREEMPT_COUNT, \
|
.preempt_count = INIT_PREEMPT_COUNT, \
|
||||||
|
INIT_SCS \
|
||||||
}
|
}
|
||||||
|
|
||||||
void arch_release_task_struct(struct task_struct *tsk);
|
void arch_release_task_struct(struct task_struct *tsk);
|
||||||
|
|
|
@ -39,6 +39,9 @@ void asm_offsets(void)
|
||||||
OFFSET(TASK_TI_PREEMPT_COUNT, task_struct, thread_info.preempt_count);
|
OFFSET(TASK_TI_PREEMPT_COUNT, task_struct, thread_info.preempt_count);
|
||||||
OFFSET(TASK_TI_KERNEL_SP, task_struct, thread_info.kernel_sp);
|
OFFSET(TASK_TI_KERNEL_SP, task_struct, thread_info.kernel_sp);
|
||||||
OFFSET(TASK_TI_USER_SP, task_struct, thread_info.user_sp);
|
OFFSET(TASK_TI_USER_SP, task_struct, thread_info.user_sp);
|
||||||
|
#ifdef CONFIG_SHADOW_CALL_STACK
|
||||||
|
OFFSET(TASK_TI_SCS_SP, task_struct, thread_info.scs_sp);
|
||||||
|
#endif
|
||||||
|
|
||||||
OFFSET(TASK_TI_CPU_NUM, task_struct, thread_info.cpu);
|
OFFSET(TASK_TI_CPU_NUM, task_struct, thread_info.cpu);
|
||||||
OFFSET(TASK_THREAD_F0, task_struct, thread.fstate.f[0]);
|
OFFSET(TASK_THREAD_F0, task_struct, thread.fstate.f[0]);
|
||||||
|
|
|
@ -9,6 +9,7 @@
|
||||||
|
|
||||||
#include <asm/asm.h>
|
#include <asm/asm.h>
|
||||||
#include <asm/csr.h>
|
#include <asm/csr.h>
|
||||||
|
#include <asm/scs.h>
|
||||||
#include <asm/unistd.h>
|
#include <asm/unistd.h>
|
||||||
#include <asm/page.h>
|
#include <asm/page.h>
|
||||||
#include <asm/thread_info.h>
|
#include <asm/thread_info.h>
|
||||||
|
@ -77,6 +78,9 @@ _save_context:
|
||||||
/* Load the global pointer */
|
/* Load the global pointer */
|
||||||
load_global_pointer
|
load_global_pointer
|
||||||
|
|
||||||
|
/* Load the kernel shadow call stack pointer if coming from userspace */
|
||||||
|
scs_load_current_if_task_changed s5
|
||||||
|
|
||||||
move a0, sp /* pt_regs */
|
move a0, sp /* pt_regs */
|
||||||
la ra, ret_from_exception
|
la ra, ret_from_exception
|
||||||
|
|
||||||
|
@ -123,6 +127,9 @@ SYM_CODE_START_NOALIGN(ret_from_exception)
|
||||||
addi s0, sp, PT_SIZE_ON_STACK
|
addi s0, sp, PT_SIZE_ON_STACK
|
||||||
REG_S s0, TASK_TI_KERNEL_SP(tp)
|
REG_S s0, TASK_TI_KERNEL_SP(tp)
|
||||||
|
|
||||||
|
/* Save the kernel shadow call stack pointer */
|
||||||
|
scs_save_current
|
||||||
|
|
||||||
/*
|
/*
|
||||||
* Save TP into the scratch register , so we can find the kernel data
|
* Save TP into the scratch register , so we can find the kernel data
|
||||||
* structures again.
|
* structures again.
|
||||||
|
@ -275,6 +282,8 @@ SYM_FUNC_START(__switch_to)
|
||||||
REG_S s9, TASK_THREAD_S9_RA(a3)
|
REG_S s9, TASK_THREAD_S9_RA(a3)
|
||||||
REG_S s10, TASK_THREAD_S10_RA(a3)
|
REG_S s10, TASK_THREAD_S10_RA(a3)
|
||||||
REG_S s11, TASK_THREAD_S11_RA(a3)
|
REG_S s11, TASK_THREAD_S11_RA(a3)
|
||||||
|
/* Save the kernel shadow call stack pointer */
|
||||||
|
scs_save_current
|
||||||
/* Restore context from next->thread */
|
/* Restore context from next->thread */
|
||||||
REG_L ra, TASK_THREAD_RA_RA(a4)
|
REG_L ra, TASK_THREAD_RA_RA(a4)
|
||||||
REG_L sp, TASK_THREAD_SP_RA(a4)
|
REG_L sp, TASK_THREAD_SP_RA(a4)
|
||||||
|
@ -292,6 +301,8 @@ SYM_FUNC_START(__switch_to)
|
||||||
REG_L s11, TASK_THREAD_S11_RA(a4)
|
REG_L s11, TASK_THREAD_S11_RA(a4)
|
||||||
/* The offset of thread_info in task_struct is zero. */
|
/* The offset of thread_info in task_struct is zero. */
|
||||||
move tp, a1
|
move tp, a1
|
||||||
|
/* Switch to the next shadow call stack */
|
||||||
|
scs_load_current
|
||||||
ret
|
ret
|
||||||
SYM_FUNC_END(__switch_to)
|
SYM_FUNC_END(__switch_to)
|
||||||
|
|
||||||
|
|
|
@ -14,6 +14,7 @@
|
||||||
#include <asm/cpu_ops_sbi.h>
|
#include <asm/cpu_ops_sbi.h>
|
||||||
#include <asm/hwcap.h>
|
#include <asm/hwcap.h>
|
||||||
#include <asm/image.h>
|
#include <asm/image.h>
|
||||||
|
#include <asm/scs.h>
|
||||||
#include <asm/xip_fixup.h>
|
#include <asm/xip_fixup.h>
|
||||||
#include "efi-header.S"
|
#include "efi-header.S"
|
||||||
|
|
||||||
|
@ -153,6 +154,7 @@ secondary_start_sbi:
|
||||||
XIP_FIXUP_OFFSET a3
|
XIP_FIXUP_OFFSET a3
|
||||||
add a3, a3, a1
|
add a3, a3, a1
|
||||||
REG_L sp, (a3)
|
REG_L sp, (a3)
|
||||||
|
scs_load_current
|
||||||
|
|
||||||
.Lsecondary_start_common:
|
.Lsecondary_start_common:
|
||||||
|
|
||||||
|
@ -289,6 +291,7 @@ clear_bss_done:
|
||||||
la sp, init_thread_union + THREAD_SIZE
|
la sp, init_thread_union + THREAD_SIZE
|
||||||
XIP_FIXUP_OFFSET sp
|
XIP_FIXUP_OFFSET sp
|
||||||
addi sp, sp, -PT_SIZE_ON_STACK
|
addi sp, sp, -PT_SIZE_ON_STACK
|
||||||
|
scs_load_init_stack
|
||||||
#ifdef CONFIG_BUILTIN_DTB
|
#ifdef CONFIG_BUILTIN_DTB
|
||||||
la a0, __dtb_start
|
la a0, __dtb_start
|
||||||
XIP_FIXUP_OFFSET a0
|
XIP_FIXUP_OFFSET a0
|
||||||
|
@ -307,6 +310,7 @@ clear_bss_done:
|
||||||
la tp, init_task
|
la tp, init_task
|
||||||
la sp, init_thread_union + THREAD_SIZE
|
la sp, init_thread_union + THREAD_SIZE
|
||||||
addi sp, sp, -PT_SIZE_ON_STACK
|
addi sp, sp, -PT_SIZE_ON_STACK
|
||||||
|
scs_load_current
|
||||||
|
|
||||||
#ifdef CONFIG_KASAN
|
#ifdef CONFIG_KASAN
|
||||||
call kasan_early_init
|
call kasan_early_init
|
||||||
|
|
|
@ -36,7 +36,7 @@ CPPFLAGS_vdso.lds += -DHAS_VGETTIMEOFDAY
|
||||||
endif
|
endif
|
||||||
|
|
||||||
# Disable -pg to prevent insert call site
|
# Disable -pg to prevent insert call site
|
||||||
CFLAGS_REMOVE_vgettimeofday.o = $(CC_FLAGS_FTRACE)
|
CFLAGS_REMOVE_vgettimeofday.o = $(CC_FLAGS_FTRACE) $(CC_FLAGS_SCS)
|
||||||
|
|
||||||
# Disable profiling and instrumentation for VDSO code
|
# Disable profiling and instrumentation for VDSO code
|
||||||
GCOV_PROFILE := n
|
GCOV_PROFILE := n
|
||||||
|
|
|
@ -81,6 +81,10 @@ ifdef CONFIG_CFI_CLANG
|
||||||
PURGATORY_CFLAGS_REMOVE += $(CC_FLAGS_CFI)
|
PURGATORY_CFLAGS_REMOVE += $(CC_FLAGS_CFI)
|
||||||
endif
|
endif
|
||||||
|
|
||||||
|
ifdef CONFIG_SHADOW_CALL_STACK
|
||||||
|
PURGATORY_CFLAGS_REMOVE += $(CC_FLAGS_SCS)
|
||||||
|
endif
|
||||||
|
|
||||||
CFLAGS_REMOVE_purgatory.o += $(PURGATORY_CFLAGS_REMOVE)
|
CFLAGS_REMOVE_purgatory.o += $(PURGATORY_CFLAGS_REMOVE)
|
||||||
CFLAGS_purgatory.o += $(PURGATORY_CFLAGS)
|
CFLAGS_purgatory.o += $(PURGATORY_CFLAGS)
|
||||||
|
|
||||||
|
|
Loading…
Add table
Reference in a new issue