Message ID | 20250502-v5_user_cfi_series-v15-5-914966471885@rivosinc.com |
---|---|
State | Superseded |
Headers | show |
Series | riscv control-flow integrity for usermode | expand |
[Ah, I missed v13 and v14, feel free to Cc me on next versions.] 2025-05-02T16:30:36-07:00, Deepak Gupta <debug@rivosinc.com>: > diff --git a/arch/riscv/kernel/entry.S b/arch/riscv/kernel/entry.S > @@ -91,6 +91,32 @@ > +.macro save_userssp tmp, status > + ALTERNATIVE("nops(4)", > + __stringify( \ > + andi \tmp, \status, SR_SPP; \ > + bnez \tmp, skip_ssp_save; \ > + csrrw \tmp, CSR_SSP, x0; \ > + REG_S \tmp, TASK_TI_USER_SSP(tp); \ > + skip_ssp_save:), > + 0, > + RISCV_ISA_EXT_ZICFISS, > + CONFIG_RISCV_USER_CFI) > +.endm > + > +.macro restore_userssp tmp > + ALTERNATIVE("nops(2)", > + __stringify( \ > + REG_L \tmp, TASK_TI_USER_SSP(tp); \ > + csrw CSR_SSP, \tmp), > + 0, > + RISCV_ISA_EXT_ZICFISS, > + CONFIG_RISCV_USER_CFI) > +.endm Do we need to emit the nops when CONFIG_RISCV_USER_CFI isn't selected? (Why not put #ifdef CONFIG_RISCV_USER_CFI around the ALTERNATIVES?) Thanks.
On Thu, May 15, 2025 at 10:48:35AM +0200, Radim Krčmář wrote: >2025-05-15T09:28:25+02:00, Alexandre Ghiti <alex@ghiti.fr>: >> On 06/05/2025 12:10, Radim Krčmář wrote: >>> 2025-05-02T16:30:36-07:00, Deepak Gupta <debug@rivosinc.com>: >>>> diff --git a/arch/riscv/kernel/entry.S b/arch/riscv/kernel/entry.S >>>> @@ -91,6 +91,32 @@ >>>> +.macro restore_userssp tmp >>>> + ALTERNATIVE("nops(2)", >>>> + __stringify( \ >>>> + REG_L \tmp, TASK_TI_USER_SSP(tp); \ >>>> + csrw CSR_SSP, \tmp), >>>> + 0, >>>> + RISCV_ISA_EXT_ZICFISS, >>>> + CONFIG_RISCV_USER_CFI) >>>> +.endm >>> Do we need to emit the nops when CONFIG_RISCV_USER_CFI isn't selected? >>> >>> (Why not put #ifdef CONFIG_RISCV_USER_CFI around the ALTERNATIVES?) >> >> The alternatives are used to create a generic kernel that contains the >> code for a large number of extensions and only enable it at runtime >> depending on the platform capabilities. This way distros can ship a >> single kernel that works on all platforms. > >Yup, and if a kernel is compiled without CONFIG_RISCV_USER_CFI, the nops >will only enlarge the binary and potentially slow down execution. >In other words, why we don't do something like this > > (!CONFIG_RISCV_USER_CFI ? "" : > (RISCV_ISA_EXT_ZICFISS ? __stringify(...) : "nops(x)")) > >instead of the current > > (CONFIG_RISCV_USER_CFI && > RISCV_ISA_EXT_ZICFISS ? __stringify(...) : "nops(x)") > >It could be a new preprocessor macro in case we wanted to make it nice, >but it's probably not a common case, so an ifdef could work as well. > >Do we just generally not care about such minor optimizations? On its own just for this series, I am not sure if I would call it even a minor optimization. But sure, it may (or may not) have noticeable effect if someone were to go around and muck with ALTERNATIVES macro and emit `old_c` only if config were selected. That should be a patch set on its own with data providing benefits from it. > >(If we wanted to go an extra mile, we could also keep the nops when both > CONFIG_RISCV_USER_CFI and RISCV_ISA_EXT_ZICFISS are present, but > command line riscv_nousercfi disabled backward cfi.) > >Thanks.
diff --git a/arch/riscv/include/asm/processor.h b/arch/riscv/include/asm/processor.h index e3aba3336e63..d851bb5c6da0 100644 --- a/arch/riscv/include/asm/processor.h +++ b/arch/riscv/include/asm/processor.h @@ -14,6 +14,7 @@ #include <asm/ptrace.h> #include <asm/hwcap.h> +#include <asm/usercfi.h> #define arch_get_mmap_end(addr, len, flags) \ ({ \ diff --git a/arch/riscv/include/asm/thread_info.h b/arch/riscv/include/asm/thread_info.h index f5916a70879a..e066f41176ca 100644 --- a/arch/riscv/include/asm/thread_info.h +++ b/arch/riscv/include/asm/thread_info.h @@ -73,6 +73,9 @@ struct thread_info { */ unsigned long a0, a1, a2; #endif +#ifdef CONFIG_RISCV_USER_CFI + struct cfi_state user_cfi_state; +#endif }; #ifdef CONFIG_SHADOW_CALL_STACK diff --git a/arch/riscv/include/asm/usercfi.h b/arch/riscv/include/asm/usercfi.h new file mode 100644 index 000000000000..94b214c295c0 --- /dev/null +++ b/arch/riscv/include/asm/usercfi.h @@ -0,0 +1,23 @@ +/* SPDX-License-Identifier: GPL-2.0 + * Copyright (C) 2024 Rivos, Inc. + * Deepak Gupta <debug@rivosinc.com> + */ +#ifndef _ASM_RISCV_USERCFI_H +#define _ASM_RISCV_USERCFI_H + +#ifndef __ASSEMBLY__ +#include <linux/types.h> + +#ifdef CONFIG_RISCV_USER_CFI +struct cfi_state { + unsigned long ubcfi_en : 1; /* Enable for backward cfi. */ + unsigned long user_shdw_stk; /* Current user shadow stack pointer */ + unsigned long shdw_stk_base; /* Base address of shadow stack */ + unsigned long shdw_stk_size; /* size of shadow stack */ +}; + +#endif /* CONFIG_RISCV_USER_CFI */ + +#endif /* __ASSEMBLY__ */ + +#endif /* _ASM_RISCV_USERCFI_H */ diff --git a/arch/riscv/kernel/asm-offsets.c b/arch/riscv/kernel/asm-offsets.c index 16490755304e..f33945432f8f 100644 --- a/arch/riscv/kernel/asm-offsets.c +++ b/arch/riscv/kernel/asm-offsets.c @@ -49,6 +49,10 @@ void asm_offsets(void) #endif OFFSET(TASK_TI_CPU_NUM, task_struct, thread_info.cpu); +#ifdef CONFIG_RISCV_USER_CFI + OFFSET(TASK_TI_CFI_STATE, task_struct, thread_info.user_cfi_state); + OFFSET(TASK_TI_USER_SSP, task_struct, thread_info.user_cfi_state.user_shdw_stk); +#endif OFFSET(TASK_THREAD_F0, task_struct, thread.fstate.f[0]); OFFSET(TASK_THREAD_F1, task_struct, thread.fstate.f[1]); OFFSET(TASK_THREAD_F2, task_struct, thread.fstate.f[2]); diff --git a/arch/riscv/kernel/entry.S b/arch/riscv/kernel/entry.S index 33a5a9f2a0d4..c4bfe2085c41 100644 --- a/arch/riscv/kernel/entry.S +++ b/arch/riscv/kernel/entry.S @@ -91,6 +91,32 @@ REG_L a0, TASK_TI_A0(tp) .endm +/* + * If previous mode was U, capture shadow stack pointer and save it away + * Zero CSR_SSP at the same time for sanitization. + */ +.macro save_userssp tmp, status + ALTERNATIVE("nops(4)", + __stringify( \ + andi \tmp, \status, SR_SPP; \ + bnez \tmp, skip_ssp_save; \ + csrrw \tmp, CSR_SSP, x0; \ + REG_S \tmp, TASK_TI_USER_SSP(tp); \ + skip_ssp_save:), + 0, + RISCV_ISA_EXT_ZICFISS, + CONFIG_RISCV_USER_CFI) +.endm + +.macro restore_userssp tmp + ALTERNATIVE("nops(2)", + __stringify( \ + REG_L \tmp, TASK_TI_USER_SSP(tp); \ + csrw CSR_SSP, \tmp), + 0, + RISCV_ISA_EXT_ZICFISS, + CONFIG_RISCV_USER_CFI) +.endm SYM_CODE_START(handle_exception) /* @@ -147,6 +173,7 @@ SYM_CODE_START(handle_exception) REG_L s0, TASK_TI_USER_SP(tp) csrrc s1, CSR_STATUS, t0 + save_userssp s2, s1 csrr s2, CSR_EPC csrr s3, CSR_TVAL csrr s4, CSR_CAUSE @@ -236,6 +263,7 @@ SYM_CODE_START_NOALIGN(ret_from_exception) * structures again. */ csrw CSR_SCRATCH, tp + restore_userssp s3 1: #ifdef CONFIG_RISCV_ISA_V_PREEMPTIVE move a0, sp