Commit d1584d79 authored by Sami Tolvanen's avatar Sami Tolvanen Committed by Palmer Dabbelt

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: default avatarSami Tolvanen <samitolvanen@google.com>
Tested-by: default avatarNathan Chancellor <nathan@kernel.org>
Link: https://lore.kernel.org/r/20230927224757.1154247-12-samitolvanen@google.comSigned-off-by: default avatarPalmer Dabbelt <palmer@rivosinc.com>
parent e609b4f4
...@@ -48,6 +48,7 @@ config RISCV ...@@ -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 ...@@ -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 ...@@ -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 @@ ...@@ -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 @@ ...@@ -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
......
/* 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 { ...@@ -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 { ...@@ -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) ...@@ -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 @@ ...@@ -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: ...@@ -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) ...@@ -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) ...@@ -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) ...@@ -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 @@ ...@@ -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: ...@@ -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: ...@@ -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: ...@@ -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 ...@@ -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 ...@@ -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)
......
Markdown is supported
0%
or
You are about to add 0 people to the discussion. Proceed with caution.
Finish editing this message first!
Please register or to comment