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 | 14 +++++ arch/x86/include/asm/processor.h | 5 ++ arch/x86/kernel/Makefile | 1 + arch/x86/kernel/ibt.c | 98 ++++++++++++++++++++++++++++++++ arch/x86/kernel/process_64.c | 2 + 5 files changed, 120 insertions(+) create mode 100644 arch/x86/kernel/ibt.c diff --git a/arch/x86/include/asm/ibt.h b/arch/x86/include/asm/ibt.h index 5e45d6424722..586e5fadf844 100644 --- a/arch/x86/include/asm/ibt.h +++ b/arch/x86/include/asm/ibt.h @@ -114,4 +114,18 @@ static inline void ibt_restore(u64 save) { } #define ENDBR_INSN_SIZE (4*HAS_KERNEL_IBT) +#ifndef __ASSEMBLER__ + +#include <linux/prctl.h> + +#define PR_CFI_SUPPORTED_STATUS_MASK (PR_CFI_ENABLE | PR_CFI_DISABLE | PR_CFI_LOCK) + +#ifdef CONFIG_X86_USER_IBT +void reset_thread_ibt(void); +#else +static inline void reset_thread_ibt(void) {} +#endif /* CONFIG_X86_USER_IBT */ + +#endif /* __ASSEMBLER__ */ + #endif /* _ASM_X86_IBT_H */ diff --git a/arch/x86/include/asm/processor.h b/arch/x86/include/asm/processor.h index 67dd932305db..7fbf10410973 100644 --- a/arch/x86/include/asm/processor.h +++ b/arch/x86/include/asm/processor.h @@ -504,6 +504,11 @@ struct thread_struct { unsigned int iopl_warn:1; +#ifdef CONFIG_X86_USER_IBT + unsigned int ibt:1; + unsigned int ibt_locked:1; +#endif + /* * Protection Keys Register for Userspace. Loaded immediately on * context switch. Store it in thread_struct to avoid a lookup in diff --git a/arch/x86/kernel/Makefile b/arch/x86/kernel/Makefile index 47a32f583930..05c87f014552 100644 --- a/arch/x86/kernel/Makefile +++ b/arch/x86/kernel/Makefile @@ -169,6 +169,7 @@ obj-$(CONFIG_CALL_THUNKS) += callthunks.o obj-$(CONFIG_X86_CET) += cet.o obj-$(CONFIG_X86_USER_SHADOW_STACK) += shstk.o +obj-$(CONFIG_X86_USER_IBT) += ibt.o ### # 64 bit specific files diff --git a/arch/x86/kernel/ibt.c b/arch/x86/kernel/ibt.c new file mode 100644 index 000000000000..682414fde5a4 --- /dev/null +++ b/arch/x86/kernel/ibt.c @@ -0,0 +1,98 @@ +// SPDX-License-Identifier: GPL-2.0 + +#include <linux/types.h> +#include <linux/cpu.h> +#include <linux/prctl.h> +#include <asm/msr.h> + +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)) + 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)) + return -EINVAL; + + if (t != current) + return -EINVAL; + + if (state & ~PR_CFI_SUPPORTED_STATUS_MASK) + 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)) + return -EINVAL; + + user_ibt_set_lock(task, true); + + return 0; +} + +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

