]> git.ipfire.org Git - thirdparty/kernel/stable.git/commitdiff
riscv: add kernel command line option to opt out of user CFI
authorDeepak Gupta <debug@rivosinc.com>
Mon, 26 Jan 2026 04:09:55 +0000 (21:09 -0700)
committerPaul Walmsley <pjw@kernel.org>
Thu, 29 Jan 2026 09:38:40 +0000 (02:38 -0700)
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: Deepak Gupta <debug@rivosinc.com>
Tested-by: Andreas Korb <andreas.korb@aisec.fraunhofer.de> # QEMU, custom CVA6
Tested-by: Valentin 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: Paul Walmsley <pjw@kernel.org>
Documentation/admin-guide/kernel-parameters.txt
arch/riscv/include/asm/usercfi.h
arch/riscv/kernel/cpufeature.c
arch/riscv/kernel/usercfi.c

index 1058f2a6d6a8c2e6a7153917b3c4d4312d57a8a0..1a355e701a802f30c2c67463dbacf8de73a54bd1 100644 (file)
@@ -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]
index ec4b8a53eb748572836c355647d58c91e094864b..7495baae1e3c2a21b5a481e6324b77749c139380 100644 (file)
@@ -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 */
index 6827a577f8709c30af6f656c99dbb46b08394ff1..1734f9a4c2fd70e8fc25bec120ff167f83c46b9e 100644 (file)
@@ -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;
index 7cec00ca1df485c1e3a36eba69afb88d0541a82e..1adba746f164e52d657d28c7a24a023f7e5c74d3 100644 (file)
@@ -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);