Allows userspace applications to enable IBT (forward-edge control flow integrity protection) using the portable PR_CFI prctl API.
The name 'branch landing pads' is RISC-V specific, but the mechanism is nearly identical in x86. This setting enables the following MSR_IA32_U_CET bits: - CET_ENDBR_EN (enforce endbr as indirect branch target) - CET_NOTRACK_EN (jump modifier to opt-out of IBT checking) Kernel-mode IBT (as part of CFI) bans notrack. A future prctl flag could be introduced to ban notrack in usermode too. Signed-off-by: Richard Patel <[email protected]> --- arch/x86/include/asm/ibt.h | 2 + arch/x86/kernel/ibt.c | 87 ++++++++++++++++++++++++++++++++++++ arch/x86/kernel/process_64.c | 2 + 3 files changed, 91 insertions(+) diff --git a/arch/x86/include/asm/ibt.h b/arch/x86/include/asm/ibt.h index 3fe464bf83e7..a67c9ceaaf9e 100644 --- a/arch/x86/include/asm/ibt.h +++ b/arch/x86/include/asm/ibt.h @@ -121,9 +121,11 @@ struct pt_regs; #ifdef CONFIG_X86_USER_IBT bool user_ibt_pop_wait_endbr(struct pt_regs *regs); void user_ibt_restore_wait_endbr(struct pt_regs *regs, bool wait_endbr); +void reset_thread_ibt(void); #else static inline bool user_ibt_pop_wait_endbr(struct pt_regs *regs) { return false; } static inline void user_ibt_restore_wait_endbr(struct pt_regs *regs, bool wait_endbr) {} +static inline void reset_thread_ibt(void) {} #endif /* CONFIG_X86_USER_IBT */ #endif /* __ASSEMBLER__ */ diff --git a/arch/x86/kernel/ibt.c b/arch/x86/kernel/ibt.c index 596b0629106d..343e6fd5dab0 100644 --- a/arch/x86/kernel/ibt.c +++ b/arch/x86/kernel/ibt.c @@ -1,6 +1,8 @@ // SPDX-License-Identifier: GPL-2.0 #include <linux/types.h> +#include <linux/cpu.h> +#include <linux/prctl.h> #include <asm/msr.h> #include <asm/fpu/xstate.h> @@ -9,6 +11,85 @@ static bool user_ibt_enabled(struct task_struct *task) return task->thread.ibt; } +static bool user_ibt_locked(struct task_struct *task) +{ + return task->thread.ibt_locked; +} + +static void user_ibt_set_lock(struct task_struct *task, bool lock) +{ + task->thread.ibt_locked = lock; +} + +static void user_ibt_set_enable(bool enable) +{ + u64 msrval; + + /* Already enabled */ + if (user_ibt_enabled(current) == enable) + return; + + current->thread.ibt = !!enable; + + fpregs_lock_and_load(); + rdmsrq(MSR_IA32_U_CET, msrval); + if (enable) + msrval |= CET_ENDBR_EN | CET_NO_TRACK_EN; + else + msrval &= ~(CET_ENDBR_EN | CET_NO_TRACK_EN); + msrval &= ~CET_WAIT_ENDBR; + wrmsrq(MSR_IA32_U_CET, msrval); + fpregs_unlock(); +} + +int arch_prctl_get_branch_landing_pad_state(struct task_struct *t, + unsigned long __user *state) +{ + unsigned long status = 0; + + if (!cpu_feature_enabled(X86_FEATURE_USER_IBT) || in_ia32_syscall()) + return -EINVAL; + + status = (user_ibt_enabled(t) ? PR_CFI_ENABLE : PR_CFI_DISABLE); + status |= (user_ibt_locked(t) ? PR_CFI_LOCK : 0); + + return copy_to_user(state, &status, sizeof(status)) ? -EFAULT : 0; +} + +int arch_prctl_set_branch_landing_pad_state(struct task_struct *t, unsigned long state) +{ + if (!cpu_feature_enabled(X86_FEATURE_USER_IBT) || in_ia32_syscall()) + return -EINVAL; + + if (t != current) + return -EINVAL; + + if (user_ibt_locked(t)) + return -EINVAL; + + if (!(state & (PR_CFI_ENABLE | PR_CFI_DISABLE))) + return -EINVAL; + + if (state & PR_CFI_ENABLE && state & PR_CFI_DISABLE) + return -EINVAL; + + user_ibt_set_enable(!!(state & PR_CFI_ENABLE)); + + return 0; +} + +int arch_prctl_lock_branch_landing_pad_state(struct task_struct *task) +{ + if (!cpu_feature_enabled(X86_FEATURE_USER_IBT) || + !user_ibt_enabled(task) || + in_ia32_syscall()) + return -EINVAL; + + user_ibt_set_lock(task, true); + + return 0; +} + bool user_ibt_pop_wait_endbr(struct pt_regs *regs) { struct fpu *fpu = x86_task_fpu(current); @@ -86,3 +167,9 @@ user_ibt_restore_wait_endbr(struct pt_regs *regs, bool wait_endbr) fpregs_unlock(); } + +void reset_thread_ibt(void) +{ + current->thread.ibt = false; + current->thread.ibt_locked = false; +} diff --git a/arch/x86/kernel/process_64.c b/arch/x86/kernel/process_64.c index b85e715ebb30..4b727cc7bccb 100644 --- a/arch/x86/kernel/process_64.c +++ b/arch/x86/kernel/process_64.c @@ -59,6 +59,7 @@ #include <asm/fsgsbase.h> #include <asm/fred.h> #include <asm/msr.h> +#include <asm/ibt.h> #ifdef CONFIG_IA32_EMULATION /* Not included via unistd.h */ #include <asm/unistd_32_ia32.h> @@ -540,6 +541,7 @@ start_thread_common(struct pt_regs *regs, unsigned long new_ip, } reset_thread_features(); + reset_thread_ibt(); loadsegment(fs, 0); loadsegment(es, _ds); -- 2.47.3

