2025-03-14T14:39:31-07:00, Deepak Gupta <debug@xxxxxxxxxxxx>:
diff --git a/arch/riscv/include/asm/usercfi.h b/arch/riscv/include/asm/usercfi.h
@@ -14,7 +15,8 @@ struct kernel_clone_args;
struct cfi_status {
unsigned long ubcfi_en : 1; /* Enable for backward cfi. */
- unsigned long rsvd : ((sizeof(unsigned long) * 8) - 1);
+ unsigned long ubcfi_locked : 1;
+ unsigned long rsvd : ((sizeof(unsigned long) * 8) - 2);
The rsvd field shouldn't be necessary as the container for the bitfield
is 'unsigned long' sized.
Why don't we use bools here, though?
It might produce a better binary and we're not hurting for struct size.
diff --git a/arch/riscv/kernel/usercfi.c b/arch/riscv/kernel/usercfi.c
@@ -24,6 +24,16 @@ bool is_shstk_enabled(struct task_struct *task)
+bool is_shstk_allocated(struct task_struct *task)
+{
+ return task->thread_info.user_cfi_state.shdw_stk_base ? true : false;
I think that the following is clearer:
return task->thread_info.user_cfi_state.shdw_stk_base
(Similar for all other implicit conversion ternaries.)
@@ -42,6 +52,26 @@ void set_active_shstk(struct task_struct *task, unsigned long shstk_addr)
+void set_shstk_status(struct task_struct *task, bool enable)
+{
+ if (!cpu_supports_shadow_stack())
+ return;
+
+ task->thread_info.user_cfi_state.ubcfi_en = enable ? 1 : 0;
+
+ if (enable)
+ task->thread.envcfg |= ENVCFG_SSE;
+ else
+ task->thread.envcfg &= ~ENVCFG_SSE;
+
+ csr_write(CSR_ENVCFG, task->thread.envcfg);
There is a new helper we could reuse for this:
envcfg_update_bits(task, ENVCFG_SSE, enable ? ENVCFG_SSE : 0);
+}
@@ -262,3 +292,83 @@ void shstk_release(struct task_struct *tsk)
+int arch_set_shadow_stack_status(struct task_struct *t, unsigned long status)
+{
+ /* Request is to enable shadow stack and shadow stack is not enabled already */
+ if (enable_shstk && !is_shstk_enabled(t)) {
+ /* shadow stack was allocated and enable request again
+ * no need to support such usecase and return EINVAL.
+ */
+ if (is_shstk_allocated(t))
+ return -EINVAL;
+
+ size = calc_shstk_size(0);
+ addr = allocate_shadow_stack(0, size, 0, false);
Why don't we use the userspace-allocated stack?
I'm completely missing the design idea here... Userspace has absolute
over the shadow stack pointer CSR, so we don't need to do much in Linux:
1. interface to set up page tables with -W- PTE and
2. interface to control senvcfg.SSE.
Userspace can do the rest.
+int arch_lock_shadow_stack_status(struct task_struct *task,
+ unsigned long arg)
+{
+ /* If shtstk not supported or not enabled on task, nothing to lock here */
+ if (!cpu_supports_shadow_stack() ||
+ !is_shstk_enabled(task) || arg != 0)
+ return -EINVAL;
The task might want to prevent shadow stack from being enabled?
Thanks.