WRUSS is a new kernel-mode instruction but writes directly to user shadow stack memory. This is used to construct a return address on the shadow stack for the signal handler. This instruction can fault if the user shadow stack is invalid shadow stack memory. In that case, the kernel does fixup. Signed-off-by: Yu-cheng Yu <yu-cheng.yu@xxxxxxxxx> --- arch/x86/include/asm/special_insns.h | 37 ++++++++++++++++++++++++++++ arch/x86/mm/extable.c | 11 +++++++++ arch/x86/mm/fault.c | 9 +++++++ 3 files changed, 57 insertions(+) diff --git a/arch/x86/include/asm/special_insns.h b/arch/x86/include/asm/special_insns.h index 317fc59b512c..9f609e802c5c 100644 --- a/arch/x86/include/asm/special_insns.h +++ b/arch/x86/include/asm/special_insns.h @@ -237,6 +237,43 @@ static inline void clwb(volatile void *__p) : [pax] "a" (p)); } +#ifdef CONFIG_X86_INTEL_CET + +#if defined(CONFIG_IA32_EMULATION) || defined(CONFIG_X86_X32) +static inline int write_user_shstk_32(unsigned long addr, unsigned int val) +{ + int err = 0; + + asm volatile("1: wrussd %1, (%0)\n" + "2:\n" + _ASM_EXTABLE_HANDLE(1b, 2b, ex_handler_wruss) + : + : "r" (addr), "r" (val)); + + return err; +} +#else +static inline int write_user_shstk_32(unsigned long addr, unsigned int val) +{ + BUG(); + return 0; +} +#endif + +static inline int write_user_shstk_64(unsigned long addr, unsigned long val) +{ + int err = 0; + + asm volatile("1: wrussq %1, (%0)\n" + "2:\n" + _ASM_EXTABLE_HANDLE(1b, 2b, ex_handler_wruss) + : + : "r" (addr), "r" (val)); + + return err; +} +#endif /* CONFIG_X86_INTEL_CET */ + #define nop() asm volatile ("nop") diff --git a/arch/x86/mm/extable.c b/arch/x86/mm/extable.c index 45f5d6cf65ae..e06ff851b671 100644 --- a/arch/x86/mm/extable.c +++ b/arch/x86/mm/extable.c @@ -157,6 +157,17 @@ __visible bool ex_handler_clear_fs(const struct exception_table_entry *fixup, } EXPORT_SYMBOL(ex_handler_clear_fs); +#ifdef CONFIG_X86_INTEL_CET +__visible bool ex_handler_wruss(const struct exception_table_entry *fixup, + struct pt_regs *regs, int trapnr) +{ + regs->ip = ex_fixup_addr(fixup); + regs->ax = -1; + return true; +} +EXPORT_SYMBOL(ex_handler_wruss); +#endif + __visible bool ex_has_fault_handler(unsigned long ip) { const struct exception_table_entry *e; diff --git a/arch/x86/mm/fault.c b/arch/x86/mm/fault.c index 3842353fb4a3..10dbb5c9aaef 100644 --- a/arch/x86/mm/fault.c +++ b/arch/x86/mm/fault.c @@ -1305,6 +1305,15 @@ __do_page_fault(struct pt_regs *regs, unsigned long error_code, error_code |= X86_PF_USER; flags |= FAULT_FLAG_USER; } else { + /* + * WRUSS is a kernel instrcution and but writes + * to user shadow stack. When a fault occurs, + * both X86_PF_USER and X86_PF_SHSTK are set. + * Clear X86_PF_USER here. + */ + if ((error_code & (X86_PF_USER | X86_PF_SHSTK)) == + (X86_PF_USER | X86_PF_SHSTK)) + error_code &= ~X86_PF_USER; if (regs->flags & X86_EFLAGS_IF) local_irq_enable(); } -- 2.17.1