Skip to content

Commit 8a9e22d

Browse files
deepak0414Paul Walmsley
authored andcommitted
riscv: Implement indirect branch tracking prctls
This patch adds a RISC-V implementation of the following prctls: PR_SET_INDIR_BR_LP_STATUS, PR_GET_INDIR_BR_LP_STATUS and PR_LOCK_INDIR_BR_LP_STATUS. Reviewed-by: Zong Li <zong.li@sifive.com> Signed-off-by: Deepak Gupta <debug@rivosinc.com> Tested-by: Andreas Korb <andreas.korb@aisec.fraunhofer.de> Tested-by: Valentin Haudiquet <valentin.haudiquet@canonical.com> Link: https://patch.msgid.link/20251112-v5_user_cfi_series-v23-14-b55691eacf4f@rivosinc.com [pjw@kernel.org: clean up patch description] Signed-off-by: Paul Walmsley <pjw@kernel.org>
1 parent 5ca243f commit 8a9e22d

4 files changed

Lines changed: 102 additions & 0 deletions

File tree

arch/riscv/include/asm/usercfi.h

Lines changed: 14 additions & 0 deletions
Original file line numberDiff line numberDiff line change
@@ -16,6 +16,8 @@ struct kernel_clone_args;
1616
struct cfi_state {
1717
unsigned long ubcfi_en : 1; /* Enable for backward cfi. */
1818
unsigned long ubcfi_locked : 1;
19+
unsigned long ufcfi_en : 1; /* Enable for forward cfi. Note that ELP goes in sstatus */
20+
unsigned long ufcfi_locked : 1;
1921
unsigned long user_shdw_stk; /* Current user shadow stack pointer */
2022
unsigned long shdw_stk_base; /* Base address of shadow stack */
2123
unsigned long shdw_stk_size; /* size of shadow stack */
@@ -32,6 +34,10 @@ bool is_shstk_locked(struct task_struct *task);
3234
bool is_shstk_allocated(struct task_struct *task);
3335
void set_shstk_lock(struct task_struct *task);
3436
void set_shstk_status(struct task_struct *task, bool enable);
37+
bool is_indir_lp_enabled(struct task_struct *task);
38+
bool is_indir_lp_locked(struct task_struct *task);
39+
void set_indir_lp_status(struct task_struct *task, bool enable);
40+
void set_indir_lp_lock(struct task_struct *task);
3541

3642
#define PR_SHADOW_STACK_SUPPORTED_STATUS_MASK (PR_SHADOW_STACK_ENABLE)
3743

@@ -57,6 +63,14 @@ void set_shstk_status(struct task_struct *task, bool enable);
5763

5864
#define set_shstk_status(task, enable) do {} while (0)
5965

66+
#define is_indir_lp_enabled(task) false
67+
68+
#define is_indir_lp_locked(task) false
69+
70+
#define set_indir_lp_status(task, enable) do {} while (0)
71+
72+
#define set_indir_lp_lock(task) do {} while (0)
73+
6074
#endif /* CONFIG_RISCV_USER_CFI */
6175

6276
#endif /* __ASSEMBLER__ */

arch/riscv/kernel/entry.S

Lines changed: 4 additions & 0 deletions
Original file line numberDiff line numberDiff line change
@@ -174,6 +174,10 @@ SYM_CODE_START(handle_exception)
174174
* or vector in kernel space.
175175
*/
176176
li t0, SR_SUM | SR_FS_VS
177+
#ifdef CONFIG_64BIT
178+
li t1, SR_ELP
179+
or t0, t0, t1
180+
#endif
177181

178182
REG_L s0, TASK_TI_USER_SP(tp)
179183
csrrc s1, CSR_STATUS, t0

arch/riscv/kernel/process.c

Lines changed: 5 additions & 0 deletions
Original file line numberDiff line numberDiff line change
@@ -163,6 +163,11 @@ void start_thread(struct pt_regs *regs, unsigned long pc,
163163
set_shstk_status(current, false);
164164
set_shstk_base(current, 0, 0);
165165
set_active_shstk(current, 0);
166+
/*
167+
* disable indirect branch tracking on exec.
168+
* libc will enable it later via prctl.
169+
*/
170+
set_indir_lp_status(current, false);
166171

167172
#ifdef CONFIG_64BIT
168173
regs->status &= ~SR_UXL;

arch/riscv/kernel/usercfi.c

Lines changed: 79 additions & 0 deletions
Original file line numberDiff line numberDiff line change
@@ -72,6 +72,35 @@ void set_shstk_lock(struct task_struct *task)
7272
task->thread_info.user_cfi_state.ubcfi_locked = 1;
7373
}
7474

75+
bool is_indir_lp_enabled(struct task_struct *task)
76+
{
77+
return task->thread_info.user_cfi_state.ufcfi_en;
78+
}
79+
80+
bool is_indir_lp_locked(struct task_struct *task)
81+
{
82+
return task->thread_info.user_cfi_state.ufcfi_locked;
83+
}
84+
85+
void set_indir_lp_status(struct task_struct *task, bool enable)
86+
{
87+
if (!cpu_supports_indirect_br_lp_instr())
88+
return;
89+
90+
task->thread_info.user_cfi_state.ufcfi_en = enable ? 1 : 0;
91+
92+
if (enable)
93+
task->thread.envcfg |= ENVCFG_LPE;
94+
else
95+
task->thread.envcfg &= ~ENVCFG_LPE;
96+
97+
csr_write(CSR_ENVCFG, task->thread.envcfg);
98+
}
99+
100+
void set_indir_lp_lock(struct task_struct *task)
101+
{
102+
task->thread_info.user_cfi_state.ufcfi_locked = 1;
103+
}
75104
/*
76105
* If size is 0, then to be compatible with regular stack we want it to be as big as
77106
* regular stack. Else PAGE_ALIGN it and return back
@@ -368,3 +397,53 @@ int arch_lock_shadow_stack_status(struct task_struct *task,
368397

369398
return 0;
370399
}
400+
401+
int arch_get_indir_br_lp_status(struct task_struct *t, unsigned long __user *status)
402+
{
403+
unsigned long fcfi_status = 0;
404+
405+
if (!cpu_supports_indirect_br_lp_instr())
406+
return -EINVAL;
407+
408+
/* indirect branch tracking is enabled on the task or not */
409+
fcfi_status |= (is_indir_lp_enabled(t) ? PR_INDIR_BR_LP_ENABLE : 0);
410+
411+
return copy_to_user(status, &fcfi_status, sizeof(fcfi_status)) ? -EFAULT : 0;
412+
}
413+
414+
int arch_set_indir_br_lp_status(struct task_struct *t, unsigned long status)
415+
{
416+
bool enable_indir_lp = false;
417+
418+
if (!cpu_supports_indirect_br_lp_instr())
419+
return -EINVAL;
420+
421+
/* indirect branch tracking is locked and further can't be modified by user */
422+
if (is_indir_lp_locked(t))
423+
return -EINVAL;
424+
425+
/* Reject unknown flags */
426+
if (status & ~PR_INDIR_BR_LP_ENABLE)
427+
return -EINVAL;
428+
429+
enable_indir_lp = (status & PR_INDIR_BR_LP_ENABLE);
430+
set_indir_lp_status(t, enable_indir_lp);
431+
432+
return 0;
433+
}
434+
435+
int arch_lock_indir_br_lp_status(struct task_struct *task,
436+
unsigned long arg)
437+
{
438+
/*
439+
* If indirect branch tracking is not supported or not enabled on task,
440+
* nothing to lock here
441+
*/
442+
if (!cpu_supports_indirect_br_lp_instr() ||
443+
!is_indir_lp_enabled(task) || arg != 0)
444+
return -EINVAL;
445+
446+
set_indir_lp_lock(task);
447+
448+
return 0;
449+
}

0 commit comments

Comments
 (0)