Skip to content

Commit d1584d7

Browse files
samitolvanenpalmer-dabbelt
authored andcommitted
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>
1 parent e609b4f commit d1584d7

File tree

10 files changed

+99
-1
lines changed

10 files changed

+99
-1
lines changed

arch/riscv/Kconfig

Lines changed: 6 additions & 0 deletions
Original file line numberDiff line numberDiff line change
@@ -48,6 +48,7 @@ config RISCV
4848
select ARCH_SUPPORTS_HUGETLBFS if MMU
4949
select ARCH_SUPPORTS_PAGE_TABLE_CHECK if MMU
5050
select ARCH_SUPPORTS_PER_VMA_LOCK if MMU
51+
select ARCH_SUPPORTS_SHADOW_CALL_STACK if HAVE_SHADOW_CALL_STACK
5152
select ARCH_USE_MEMTEST
5253
select ARCH_USE_QUEUED_RWLOCKS
5354
select ARCH_USES_CFI_TRAPS if CFI_CLANG
@@ -174,6 +175,11 @@ config GCC_SUPPORTS_DYNAMIC_FTRACE
174175
def_bool CC_IS_GCC
175176
depends on $(cc-option,-fpatchable-function-entry=8)
176177

178+
config HAVE_SHADOW_CALL_STACK
179+
def_bool $(cc-option,-fsanitize=shadow-call-stack)
180+
# https://github.com/riscv-non-isa/riscv-elf-psabi-doc/commit/a484e843e6eeb51f0cb7b8819e50da6d2444d769
181+
depends on $(ld-option,--no-relax-gp)
182+
177183
config ARCH_MMAP_RND_BITS_MIN
178184
default 18 if 64BIT
179185
default 8

arch/riscv/Makefile

Lines changed: 4 additions & 0 deletions
Original file line numberDiff line numberDiff line change
@@ -55,6 +55,10 @@ endif
5555
endif
5656
endif
5757

58+
ifeq ($(CONFIG_SHADOW_CALL_STACK),y)
59+
KBUILD_LDFLAGS += --no-relax-gp
60+
endif
61+
5862
# ISA string setting
5963
riscv-march-$(CONFIG_ARCH_RV32I) := rv32ima
6064
riscv-march-$(CONFIG_ARCH_RV64I) := rv64ima

arch/riscv/include/asm/asm.h

Lines changed: 6 additions & 0 deletions
Original file line numberDiff line numberDiff line change
@@ -109,13 +109,19 @@
109109
REG_L \dst, 0(\dst)
110110
.endm
111111

112+
#ifdef CONFIG_SHADOW_CALL_STACK
113+
/* gp is used as the shadow call stack pointer instead */
114+
.macro load_global_pointer
115+
.endm
116+
#else
112117
/* load __global_pointer to gp */
113118
.macro load_global_pointer
114119
.option push
115120
.option norelax
116121
la gp, __global_pointer$
117122
.option pop
118123
.endm
124+
#endif /* CONFIG_SHADOW_CALL_STACK */
119125

120126
/* save all GPs except x1 ~ x5 */
121127
.macro save_from_x6_to_x31

arch/riscv/include/asm/scs.h

Lines changed: 47 additions & 0 deletions
Original file line numberDiff line numberDiff line change
@@ -0,0 +1,47 @@
1+
/* SPDX-License-Identifier: GPL-2.0 */
2+
#ifndef _ASM_SCS_H
3+
#define _ASM_SCS_H
4+
5+
#ifdef __ASSEMBLY__
6+
#include <asm/asm-offsets.h>
7+
8+
#ifdef CONFIG_SHADOW_CALL_STACK
9+
10+
/* Load init_shadow_call_stack to gp. */
11+
.macro scs_load_init_stack
12+
la gp, init_shadow_call_stack
13+
XIP_FIXUP_OFFSET gp
14+
.endm
15+
16+
/* Load task_scs_sp(current) to gp. */
17+
.macro scs_load_current
18+
REG_L gp, TASK_TI_SCS_SP(tp)
19+
.endm
20+
21+
/* Load task_scs_sp(current) to gp, but only if tp has changed. */
22+
.macro scs_load_current_if_task_changed prev
23+
beq \prev, tp, _skip_scs
24+
scs_load_current
25+
_skip_scs:
26+
.endm
27+
28+
/* Save gp to task_scs_sp(current). */
29+
.macro scs_save_current
30+
REG_S gp, TASK_TI_SCS_SP(tp)
31+
.endm
32+
33+
#else /* CONFIG_SHADOW_CALL_STACK */
34+
35+
.macro scs_load_init_stack
36+
.endm
37+
.macro scs_load_current
38+
.endm
39+
.macro scs_load_current_if_task_changed prev
40+
.endm
41+
.macro scs_save_current
42+
.endm
43+
44+
#endif /* CONFIG_SHADOW_CALL_STACK */
45+
#endif /* __ASSEMBLY__ */
46+
47+
#endif /* _ASM_SCS_H */

arch/riscv/include/asm/thread_info.h

Lines changed: 13 additions & 0 deletions
Original file line numberDiff line numberDiff line change
@@ -57,8 +57,20 @@ struct thread_info {
5757
long user_sp; /* User stack pointer */
5858
int cpu;
5959
unsigned long syscall_work; /* SYSCALL_WORK_ flags */
60+
#ifdef CONFIG_SHADOW_CALL_STACK
61+
void *scs_base;
62+
void *scs_sp;
63+
#endif
6064
};
6165

66+
#ifdef CONFIG_SHADOW_CALL_STACK
67+
#define INIT_SCS \
68+
.scs_base = init_shadow_call_stack, \
69+
.scs_sp = init_shadow_call_stack,
70+
#else
71+
#define INIT_SCS
72+
#endif
73+
6274
/*
6375
* macros/functions for gaining access to the thread information structure
6476
*
@@ -68,6 +80,7 @@ struct thread_info {
6880
{ \
6981
.flags = 0, \
7082
.preempt_count = INIT_PREEMPT_COUNT, \
83+
INIT_SCS \
7184
}
7285

7386
void arch_release_task_struct(struct task_struct *tsk);

arch/riscv/kernel/asm-offsets.c

Lines changed: 3 additions & 0 deletions
Original file line numberDiff line numberDiff line change
@@ -39,6 +39,9 @@ void asm_offsets(void)
3939
OFFSET(TASK_TI_PREEMPT_COUNT, task_struct, thread_info.preempt_count);
4040
OFFSET(TASK_TI_KERNEL_SP, task_struct, thread_info.kernel_sp);
4141
OFFSET(TASK_TI_USER_SP, task_struct, thread_info.user_sp);
42+
#ifdef CONFIG_SHADOW_CALL_STACK
43+
OFFSET(TASK_TI_SCS_SP, task_struct, thread_info.scs_sp);
44+
#endif
4245

4346
OFFSET(TASK_TI_CPU_NUM, task_struct, thread_info.cpu);
4447
OFFSET(TASK_THREAD_F0, task_struct, thread.fstate.f[0]);

arch/riscv/kernel/entry.S

Lines changed: 11 additions & 0 deletions
Original file line numberDiff line numberDiff line change
@@ -9,6 +9,7 @@
99

1010
#include <asm/asm.h>
1111
#include <asm/csr.h>
12+
#include <asm/scs.h>
1213
#include <asm/unistd.h>
1314
#include <asm/page.h>
1415
#include <asm/thread_info.h>
@@ -77,6 +78,9 @@ _save_context:
7778
/* Load the global pointer */
7879
load_global_pointer
7980

81+
/* Load the kernel shadow call stack pointer if coming from userspace */
82+
scs_load_current_if_task_changed s5
83+
8084
move a0, sp /* pt_regs */
8185
la ra, ret_from_exception
8286

@@ -123,6 +127,9 @@ SYM_CODE_START_NOALIGN(ret_from_exception)
123127
addi s0, sp, PT_SIZE_ON_STACK
124128
REG_S s0, TASK_TI_KERNEL_SP(tp)
125129

130+
/* Save the kernel shadow call stack pointer */
131+
scs_save_current
132+
126133
/*
127134
* Save TP into the scratch register , so we can find the kernel data
128135
* structures again.
@@ -275,6 +282,8 @@ SYM_FUNC_START(__switch_to)
275282
REG_S s9, TASK_THREAD_S9_RA(a3)
276283
REG_S s10, TASK_THREAD_S10_RA(a3)
277284
REG_S s11, TASK_THREAD_S11_RA(a3)
285+
/* Save the kernel shadow call stack pointer */
286+
scs_save_current
278287
/* Restore context from next->thread */
279288
REG_L ra, TASK_THREAD_RA_RA(a4)
280289
REG_L sp, TASK_THREAD_SP_RA(a4)
@@ -292,6 +301,8 @@ SYM_FUNC_START(__switch_to)
292301
REG_L s11, TASK_THREAD_S11_RA(a4)
293302
/* The offset of thread_info in task_struct is zero. */
294303
move tp, a1
304+
/* Switch to the next shadow call stack */
305+
scs_load_current
295306
ret
296307
SYM_FUNC_END(__switch_to)
297308

arch/riscv/kernel/head.S

Lines changed: 4 additions & 0 deletions
Original file line numberDiff line numberDiff line change
@@ -14,6 +14,7 @@
1414
#include <asm/cpu_ops_sbi.h>
1515
#include <asm/hwcap.h>
1616
#include <asm/image.h>
17+
#include <asm/scs.h>
1718
#include <asm/xip_fixup.h>
1819
#include "efi-header.S"
1920

@@ -153,6 +154,7 @@ secondary_start_sbi:
153154
XIP_FIXUP_OFFSET a3
154155
add a3, a3, a1
155156
REG_L sp, (a3)
157+
scs_load_current
156158

157159
.Lsecondary_start_common:
158160

@@ -289,6 +291,7 @@ clear_bss_done:
289291
la sp, init_thread_union + THREAD_SIZE
290292
XIP_FIXUP_OFFSET sp
291293
addi sp, sp, -PT_SIZE_ON_STACK
294+
scs_load_init_stack
292295
#ifdef CONFIG_BUILTIN_DTB
293296
la a0, __dtb_start
294297
XIP_FIXUP_OFFSET a0
@@ -307,6 +310,7 @@ clear_bss_done:
307310
la tp, init_task
308311
la sp, init_thread_union + THREAD_SIZE
309312
addi sp, sp, -PT_SIZE_ON_STACK
313+
scs_load_current
310314

311315
#ifdef CONFIG_KASAN
312316
call kasan_early_init

arch/riscv/kernel/vdso/Makefile

Lines changed: 1 addition & 1 deletion
Original file line numberDiff line numberDiff line change
@@ -36,7 +36,7 @@ CPPFLAGS_vdso.lds += -DHAS_VGETTIMEOFDAY
3636
endif
3737

3838
# Disable -pg to prevent insert call site
39-
CFLAGS_REMOVE_vgettimeofday.o = $(CC_FLAGS_FTRACE)
39+
CFLAGS_REMOVE_vgettimeofday.o = $(CC_FLAGS_FTRACE) $(CC_FLAGS_SCS)
4040

4141
# Disable profiling and instrumentation for VDSO code
4242
GCOV_PROFILE := n

arch/riscv/purgatory/Makefile

Lines changed: 4 additions & 0 deletions
Original file line numberDiff line numberDiff line change
@@ -81,6 +81,10 @@ ifdef CONFIG_CFI_CLANG
8181
PURGATORY_CFLAGS_REMOVE += $(CC_FLAGS_CFI)
8282
endif
8383

84+
ifdef CONFIG_SHADOW_CALL_STACK
85+
PURGATORY_CFLAGS_REMOVE += $(CC_FLAGS_SCS)
86+
endif
87+
8488
CFLAGS_REMOVE_purgatory.o += $(PURGATORY_CFLAGS_REMOVE)
8589
CFLAGS_purgatory.o += $(PURGATORY_CFLAGS)
8690

0 commit comments

Comments
 (0)