Commit c9b859c4 authored by Deepak Gupta's avatar Deepak Gupta Committed by Paul Walmsley
Browse files

riscv: add kernel command line option to opt out of user CFI



Add a kernel command line option to disable part or all
of user CFI.  User backward CFI and forward CFI can be controlled
independently.  The kernel command line parameter "riscv_nousercfi" can
take the following values:
 - "all" : Disable forward and backward cfi both
 - "bcfi" : Disable backward cfi
 - "fcfi" : Disable forward cfi

Signed-off-by: default avatarDeepak Gupta <debug@rivosinc.com>
Tested-by: Andreas Korb <andreas.korb@aisec.fraunhofer.de> # QEMU, custom CVA6
Tested-by: default avatarValentin Haudiquet <valentin.haudiquet@canonical.com>
Link: https://patch.msgid.link/20251112-v5_user_cfi_series-v23-21-b55691eacf4f@rivosinc.com


[pjw@kernel.org: fixed warnings from checkpatch; cleaned up patch description, doc, printk text]
Signed-off-by: default avatarPaul Walmsley <pjw@kernel.org>
parent 30c30990
Loading
Loading
Loading
Loading
+8 −0
Original line number Diff line number Diff line
@@ -6606,6 +6606,14 @@ Kernel parameters
			replacement properties are not found. See the Kconfig
			entry for RISCV_ISA_FALLBACK.

	riscv_nousercfi=
		all	Disable user CFI ABI to userspace even if cpu extension
			are available.
		bcfi	Disable user backward CFI ABI to userspace even if
			the shadow stack extension is available.
		fcfi	Disable user forward CFI ABI to userspace even if the
			landing pad extension is available.

	ro		[KNL] Mount root device read-only on boot

	rodata=		[KNL,EARLY]
+9 −0
Original line number Diff line number Diff line
@@ -5,6 +5,10 @@
#ifndef _ASM_RISCV_USERCFI_H
#define _ASM_RISCV_USERCFI_H

#define CMDLINE_DISABLE_RISCV_USERCFI_FCFI	1
#define CMDLINE_DISABLE_RISCV_USERCFI_BCFI	2
#define CMDLINE_DISABLE_RISCV_USERCFI		3

#ifndef __ASSEMBLER__
#include <linux/types.h>
#include <linux/prctl.h>
@@ -13,6 +17,8 @@
struct task_struct;
struct kernel_clone_args;

extern unsigned long riscv_nousercfi;

#ifdef CONFIG_RISCV_USER_CFI
struct cfi_state {
	unsigned long ubcfi_en : 1; /* Enable for backward cfi. */
@@ -83,6 +89,9 @@ void set_indir_lp_lock(struct task_struct *task);

#endif /* CONFIG_RISCV_USER_CFI */

bool is_user_shstk_enabled(void);
bool is_user_lpad_enabled(void);

#endif /* __ASSEMBLER__ */

#endif /* _ASM_RISCV_USERCFI_H */
+5 −2
Original line number Diff line number Diff line
@@ -28,6 +28,7 @@
#include <asm/vector.h>
#include <asm/vendor_extensions.h>
#include <asm/vendor_extensions/thead.h>
#include <asm/usercfi.h>

#define NUM_ALPHA_EXTS ('z' - 'a' + 1)

@@ -299,7 +300,8 @@ static int riscv_ext_svadu_validate(const struct riscv_isa_ext_data *data,
static int riscv_cfilp_validate(const struct riscv_isa_ext_data *data,
				const unsigned long *isa_bitmap)
{
	if (!IS_ENABLED(CONFIG_RISCV_USER_CFI))
	if (!IS_ENABLED(CONFIG_RISCV_USER_CFI) ||
	    (riscv_nousercfi & CMDLINE_DISABLE_RISCV_USERCFI_FCFI))
		return -EINVAL;

	return 0;
@@ -308,7 +310,8 @@ static int riscv_cfilp_validate(const struct riscv_isa_ext_data *data,
static int riscv_cfiss_validate(const struct riscv_isa_ext_data *data,
				const unsigned long *isa_bitmap)
{
	if (!IS_ENABLED(CONFIG_RISCV_USER_CFI))
	if (!IS_ENABLED(CONFIG_RISCV_USER_CFI) ||
	    (riscv_nousercfi & CMDLINE_DISABLE_RISCV_USERCFI_BCFI))
		return -EINVAL;

	return 0;
+47 −11
Original line number Diff line number Diff line
@@ -17,6 +17,8 @@
#include <asm/csr.h>
#include <asm/usercfi.h>

unsigned long riscv_nousercfi __read_mostly;

#define SHSTK_ENTRY_SIZE sizeof(void *)

bool is_shstk_enabled(struct task_struct *task)
@@ -59,7 +61,7 @@ unsigned long get_active_shstk(struct task_struct *task)

void set_shstk_status(struct task_struct *task, bool enable)
{
	if (!cpu_supports_shadow_stack())
	if (!is_user_shstk_enabled())
		return;

	task->thread_info.user_cfi_state.ubcfi_en = enable ? 1 : 0;
@@ -89,7 +91,7 @@ bool is_indir_lp_locked(struct task_struct *task)

void set_indir_lp_status(struct task_struct *task, bool enable)
{
	if (!cpu_supports_indirect_br_lp_instr())
	if (!is_user_lpad_enabled())
		return;

	task->thread_info.user_cfi_state.ufcfi_en = enable ? 1 : 0;
@@ -257,7 +259,7 @@ SYSCALL_DEFINE3(map_shadow_stack, unsigned long, addr, unsigned long, size, unsi
	bool set_tok = flags & SHADOW_STACK_SET_TOKEN;
	unsigned long aligned_size = 0;

	if (!cpu_supports_shadow_stack())
	if (!is_user_shstk_enabled())
		return -EOPNOTSUPP;

	/* Anything other than set token should result in invalid param */
@@ -304,7 +306,7 @@ unsigned long shstk_alloc_thread_stack(struct task_struct *tsk,
	unsigned long addr, size;

	/* If shadow stack is not supported, return 0 */
	if (!cpu_supports_shadow_stack())
	if (!is_user_shstk_enabled())
		return 0;

	/*
@@ -350,7 +352,7 @@ void shstk_release(struct task_struct *tsk)
{
	unsigned long base = 0, size = 0;
	/* If shadow stack is not supported or not enabled, nothing to release */
	if (!cpu_supports_shadow_stack() || !is_shstk_enabled(tsk))
	if (!is_user_shstk_enabled() || !is_shstk_enabled(tsk))
		return;

	/*
@@ -379,7 +381,7 @@ int arch_get_shadow_stack_status(struct task_struct *t, unsigned long __user *st
{
	unsigned long bcfi_status = 0;

	if (!cpu_supports_shadow_stack())
	if (!is_user_shstk_enabled())
		return -EINVAL;

	/* this means shadow stack is enabled on the task */
@@ -393,7 +395,7 @@ int arch_set_shadow_stack_status(struct task_struct *t, unsigned long status)
	unsigned long size = 0, addr = 0;
	bool enable_shstk = false;

	if (!cpu_supports_shadow_stack())
	if (!is_user_shstk_enabled())
		return -EINVAL;

	/* Reject unknown flags */
@@ -446,7 +448,7 @@ 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() ||
	if (!is_user_shstk_enabled() ||
	    !is_shstk_enabled(task) || arg != 0)
		return -EINVAL;

@@ -459,7 +461,7 @@ int arch_get_indir_br_lp_status(struct task_struct *t, unsigned long __user *sta
{
	unsigned long fcfi_status = 0;

	if (!cpu_supports_indirect_br_lp_instr())
	if (!is_user_lpad_enabled())
		return -EINVAL;

	/* indirect branch tracking is enabled on the task or not */
@@ -472,7 +474,7 @@ int arch_set_indir_br_lp_status(struct task_struct *t, unsigned long status)
{
	bool enable_indir_lp = false;

	if (!cpu_supports_indirect_br_lp_instr())
	if (!is_user_lpad_enabled())
		return -EINVAL;

	/* indirect branch tracking is locked and further can't be modified by user */
@@ -496,7 +498,7 @@ int arch_lock_indir_br_lp_status(struct task_struct *task,
	 * If indirect branch tracking is not supported or not enabled on task,
	 * nothing to lock here
	 */
	if (!cpu_supports_indirect_br_lp_instr() ||
	if (!is_user_lpad_enabled() ||
	    !is_indir_lp_enabled(task) || arg != 0)
		return -EINVAL;

@@ -504,3 +506,37 @@ int arch_lock_indir_br_lp_status(struct task_struct *task,

	return 0;
}

bool is_user_shstk_enabled(void)
{
	return (cpu_supports_shadow_stack() &&
		!(riscv_nousercfi & CMDLINE_DISABLE_RISCV_USERCFI_BCFI));
}

bool is_user_lpad_enabled(void)
{
	return (cpu_supports_indirect_br_lp_instr() &&
		!(riscv_nousercfi & CMDLINE_DISABLE_RISCV_USERCFI_FCFI));
}

static int __init setup_global_riscv_enable(char *str)
{
	if (strcmp(str, "all") == 0)
		riscv_nousercfi = CMDLINE_DISABLE_RISCV_USERCFI;

	if (strcmp(str, "fcfi") == 0)
		riscv_nousercfi |= CMDLINE_DISABLE_RISCV_USERCFI_FCFI;

	if (strcmp(str, "bcfi") == 0)
		riscv_nousercfi |= CMDLINE_DISABLE_RISCV_USERCFI_BCFI;

	if (riscv_nousercfi)
		pr_info("RISC-V user CFI disabled via cmdline - shadow stack status : %s, landing pad status : %s\n",
			(riscv_nousercfi & CMDLINE_DISABLE_RISCV_USERCFI_BCFI) ? "disabled" :
			"enabled", (riscv_nousercfi & CMDLINE_DISABLE_RISCV_USERCFI_FCFI) ?
			"disabled" : "enabled");

	return 1;
}

__setup("riscv_nousercfi=", setup_global_riscv_enable);