Message ID | 20180921150351.20898-20-yu-cheng.yu@intel.com (mailing list archive) |
---|---|
State | New, archived |
Headers | show |
Series | Control Flow Enforcement: Shadow Stack | expand |
On Fri, Sep 21, 2018 at 08:03:43AM -0700, Yu-cheng Yu wrote: > 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. "a fixup" > > Signed-off-by: Yu-cheng Yu <yu-cheng.yu@intel.com> > --- > arch/x86/include/asm/special_insns.h | 32 ++++++++++++++++++++++++++++ > arch/x86/mm/fault.c | 9 ++++++++ > 2 files changed, 41 insertions(+) > > diff --git a/arch/x86/include/asm/special_insns.h b/arch/x86/include/asm/special_insns.h > index 317fc59b512c..c04e68ef47da 100644 > --- a/arch/x86/include/asm/special_insns.h > +++ b/arch/x86/include/asm/special_insns.h > @@ -237,6 +237,38 @@ 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) > +{ > + asm_volatile_goto("1: wrussd %1, (%0)\n" > + _ASM_EXTABLE(1b, %l[fail]) > + :: "r" (addr), "r" (val) > + :: fail); > + return 0; > +fail: > + return -1; Should it... > +} > +#else > +static inline int write_user_shstk_32(unsigned long addr, unsigned int val) > +{ > + WARN_ONCE(1, "write_user_shstk_32 used but not supported.\n"); "is/was used" > + return -EFAULT; > +} > +#endif > + > +static inline int write_user_shstk_64(unsigned long addr, unsigned long val) > +{ > + asm_volatile_goto("1: wrussq %1, (%0)\n" > + _ASM_EXTABLE(1b, %l[fail]) > + :: "r" (addr), "r" (val) > + :: fail); > + return 0; > +fail: > + return -1; ...and it be -EPERM, if -EFAULT was returned earlier for write_user_shstk_32? > +} > +#endif /* CONFIG_X86_INTEL_CET */ > + > #define nop() asm volatile ("nop") > > > diff --git a/arch/x86/mm/fault.c b/arch/x86/mm/fault.c > index 7c3877a982f4..4d4ac57a4ba2 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 "WRUSS is a kernel instruction 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 >
diff --git a/arch/x86/include/asm/special_insns.h b/arch/x86/include/asm/special_insns.h index 317fc59b512c..c04e68ef47da 100644 --- a/arch/x86/include/asm/special_insns.h +++ b/arch/x86/include/asm/special_insns.h @@ -237,6 +237,38 @@ 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) +{ + asm_volatile_goto("1: wrussd %1, (%0)\n" + _ASM_EXTABLE(1b, %l[fail]) + :: "r" (addr), "r" (val) + :: fail); + return 0; +fail: + return -1; +} +#else +static inline int write_user_shstk_32(unsigned long addr, unsigned int val) +{ + WARN_ONCE(1, "write_user_shstk_32 used but not supported.\n"); + return -EFAULT; +} +#endif + +static inline int write_user_shstk_64(unsigned long addr, unsigned long val) +{ + asm_volatile_goto("1: wrussq %1, (%0)\n" + _ASM_EXTABLE(1b, %l[fail]) + :: "r" (addr), "r" (val) + :: fail); + return 0; +fail: + return -1; +} +#endif /* CONFIG_X86_INTEL_CET */ + #define nop() asm volatile ("nop") diff --git a/arch/x86/mm/fault.c b/arch/x86/mm/fault.c index 7c3877a982f4..4d4ac57a4ba2 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(); }
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@intel.com> --- arch/x86/include/asm/special_insns.h | 32 ++++++++++++++++++++++++++++ arch/x86/mm/fault.c | 9 ++++++++ 2 files changed, 41 insertions(+)