lkml.org 
[lkml]   [2023]   [Jan]   [19]   [last100]   RSS Feed
Views: [wrap][no wrap]   [headers]  [forward] 
 
Messages in this thread
    Patch in this message
    /
    From
    Subject[PATCH v5 30/39] x86/shstk: Handle signals for shadow stack
    Date
    From: Yu-cheng Yu <yu-cheng.yu@intel.com>

    When a signal is handled normally the context is pushed to the stack
    before handling it. For shadow stacks, since the shadow stack only track's
    return addresses, there isn't any state that needs to be pushed. However,
    there are still a few things that need to be done. These things are
    userspace visible and which will be kernel ABI for shadow stacks.

    One is to make sure the restorer address is written to shadow stack, since
    the signal handler (if not changing ucontext) returns to the restorer, and
    the restorer calls sigreturn. So add the restorer on the shadow stack
    before handling the signal, so there is not a conflict when the signal
    handler returns to the restorer.

    The other thing to do is to place some type of checkable token on the
    thread's shadow stack before handling the signal and check it during
    sigreturn. This is an extra layer of protection to hamper attackers
    calling sigreturn manually as in SROP-like attacks.

    For this token we can use the shadow stack data format defined earlier.
    Have the data pushed be the previous SSP. In the future the sigreturn
    might want to return back to a different stack. Storing the SSP (instead
    of a restore offset or something) allows for future functionality that
    may want to restore to a different stack.

    So, when handling a signal push
    - the SSP pointing in the shadow stack data format
    - the restorer address below the restore token.

    In sigreturn, verify SSP is stored in the data format and pop the shadow
    stack.

    Reviewed-by: Kees Cook <keescook@chromium.org>
    Tested-by: Pengfei Xu <pengfei.xu@intel.com>
    Tested-by: John Allen <john.allen@amd.com>
    Signed-off-by: Yu-cheng Yu <yu-cheng.yu@intel.com>
    Co-developed-by: Rick Edgecombe <rick.p.edgecombe@intel.com>
    Signed-off-by: Rick Edgecombe <rick.p.edgecombe@intel.com>
    Cc: Andy Lutomirski <luto@kernel.org>
    Cc: Cyrill Gorcunov <gorcunov@gmail.com>
    Cc: Florian Weimer <fweimer@redhat.com>
    Cc: H. Peter Anvin <hpa@zytor.com>
    Cc: Kees Cook <keescook@chromium.org>
    ---

    v3:
    - Drop shstk_setup_rstor_token() (Kees)
    - Drop x32 signal support, since x32 support is dropped

    v2:
    - Switch to new shstk signal format

    v1:
    - Use xsave helpers.
    - Expand commit log.

    Yu-cheng v27:
    - Eliminate saving shadow stack pointer to signal context.

    arch/x86/include/asm/shstk.h | 5 ++
    arch/x86/kernel/shstk.c | 98 ++++++++++++++++++++++++++++++++++++
    arch/x86/kernel/signal.c | 1 +
    arch/x86/kernel/signal_64.c | 6 +++
    4 files changed, 110 insertions(+)

    diff --git a/arch/x86/include/asm/shstk.h b/arch/x86/include/asm/shstk.h
    index 172a69052770..746c040f7cb6 100644
    --- a/arch/x86/include/asm/shstk.h
    +++ b/arch/x86/include/asm/shstk.h
    @@ -6,6 +6,7 @@
    #include <linux/types.h>

    struct task_struct;
    +struct ksignal;

    #ifdef CONFIG_X86_USER_SHADOW_STACK
    struct thread_shstk {
    @@ -19,6 +20,8 @@ int shstk_alloc_thread_stack(struct task_struct *p, unsigned long clone_flags,
    unsigned long stack_size,
    unsigned long *shstk_addr);
    void shstk_free(struct task_struct *p);
    +int setup_signal_shadow_stack(struct ksignal *ksig);
    +int restore_signal_shadow_stack(void);
    #else
    static inline long shstk_prctl(struct task_struct *task, int option,
    unsigned long features) { return -EINVAL; }
    @@ -28,6 +31,8 @@ static inline int shstk_alloc_thread_stack(struct task_struct *p,
    unsigned long stack_size,
    unsigned long *shstk_addr) { return 0; }
    static inline void shstk_free(struct task_struct *p) {}
    +static inline int setup_signal_shadow_stack(struct ksignal *ksig) { return 0; }
    +static inline int restore_signal_shadow_stack(void) { return 0; }
    #endif /* CONFIG_X86_USER_SHADOW_STACK */

    #endif /* __ASSEMBLY__ */
    diff --git a/arch/x86/kernel/shstk.c b/arch/x86/kernel/shstk.c
    index 3e470917eb0b..56e7ca8e42cc 100644
    --- a/arch/x86/kernel/shstk.c
    +++ b/arch/x86/kernel/shstk.c
    @@ -233,6 +233,104 @@ static int get_shstk_data(unsigned long *data, unsigned long __user *addr)
    return 0;
    }

    +static int shstk_push_sigframe(unsigned long *ssp)
    +{
    + unsigned long target_ssp = *ssp;
    +
    + /* Token must be aligned */
    + if (!IS_ALIGNED(*ssp, 8))
    + return -EINVAL;
    +
    + if (!IS_ALIGNED(target_ssp, 8))
    + return -EINVAL;
    +
    + *ssp -= SS_FRAME_SIZE;
    + if (put_shstk_data((void *__user)*ssp, target_ssp))
    + return -EFAULT;
    +
    + return 0;
    +}
    +
    +static int shstk_pop_sigframe(unsigned long *ssp)
    +{
    + unsigned long token_addr;
    + int err;
    +
    + err = get_shstk_data(&token_addr, (unsigned long __user *)*ssp);
    + if (unlikely(err))
    + return err;
    +
    + /* Restore SSP aligned? */
    + if (unlikely(!IS_ALIGNED(token_addr, 8)))
    + return -EINVAL;
    +
    + /* SSP in userspace? */
    + if (unlikely(token_addr >= TASK_SIZE_MAX))
    + return -EINVAL;
    +
    + *ssp = token_addr;
    +
    + return 0;
    +}
    +
    +int setup_signal_shadow_stack(struct ksignal *ksig)
    +{
    + void __user *restorer = ksig->ka.sa.sa_restorer;
    + unsigned long ssp;
    + int err;
    +
    + if (!cpu_feature_enabled(X86_FEATURE_USER_SHSTK) ||
    + !features_enabled(ARCH_SHSTK_SHSTK))
    + return 0;
    +
    + if (!restorer)
    + return -EINVAL;
    +
    + ssp = get_user_shstk_addr();
    + if (unlikely(!ssp))
    + return -EINVAL;
    +
    + err = shstk_push_sigframe(&ssp);
    + if (unlikely(err))
    + return err;
    +
    + /* Push restorer address */
    + ssp -= SS_FRAME_SIZE;
    + err = write_user_shstk_64((u64 __user *)ssp, (u64)restorer);
    + if (unlikely(err))
    + return -EFAULT;
    +
    + fpregs_lock_and_load();
    + wrmsrl(MSR_IA32_PL3_SSP, ssp);
    + fpregs_unlock();
    +
    + return 0;
    +}
    +
    +int restore_signal_shadow_stack(void)
    +{
    + unsigned long ssp;
    + int err;
    +
    + if (!cpu_feature_enabled(X86_FEATURE_USER_SHSTK) ||
    + !features_enabled(ARCH_SHSTK_SHSTK))
    + return 0;
    +
    + ssp = get_user_shstk_addr();
    + if (unlikely(!ssp))
    + return -EINVAL;
    +
    + err = shstk_pop_sigframe(&ssp);
    + if (unlikely(err))
    + return err;
    +
    + fpregs_lock_and_load();
    + wrmsrl(MSR_IA32_PL3_SSP, ssp);
    + fpregs_unlock();
    +
    + return 0;
    +}
    +
    void shstk_free(struct task_struct *tsk)
    {
    struct thread_shstk *shstk = &tsk->thread.shstk;
    diff --git a/arch/x86/kernel/signal.c b/arch/x86/kernel/signal.c
    index 004cb30b7419..356253e85ce9 100644
    --- a/arch/x86/kernel/signal.c
    +++ b/arch/x86/kernel/signal.c
    @@ -40,6 +40,7 @@
    #include <asm/syscall.h>
    #include <asm/sigframe.h>
    #include <asm/signal.h>
    +#include <asm/shstk.h>

    static inline int is_ia32_compat_frame(struct ksignal *ksig)
    {
    diff --git a/arch/x86/kernel/signal_64.c b/arch/x86/kernel/signal_64.c
    index 0e808c72bf7e..cacf2ede6217 100644
    --- a/arch/x86/kernel/signal_64.c
    +++ b/arch/x86/kernel/signal_64.c
    @@ -175,6 +175,9 @@ int x64_setup_rt_frame(struct ksignal *ksig, struct pt_regs *regs)
    frame = get_sigframe(ksig, regs, sizeof(struct rt_sigframe), &fp);
    uc_flags = frame_uc_flags(regs);

    + if (setup_signal_shadow_stack(ksig))
    + return -EFAULT;
    +
    if (!user_access_begin(frame, sizeof(*frame)))
    return -EFAULT;

    @@ -260,6 +263,9 @@ SYSCALL_DEFINE0(rt_sigreturn)
    if (!restore_sigcontext(regs, &frame->uc.uc_mcontext, uc_flags))
    goto badframe;

    + if (restore_signal_shadow_stack())
    + goto badframe;
    +
    if (restore_altstack(&frame->uc.uc_stack))
    goto badframe;

    --
    2.17.1
    \
     
     \ /
      Last update: 2023-03-26 23:48    [W:6.315 / U:0.152 seconds]
    ©2003-2020 Jasper Spaans|hosted at Digital Ocean and TransIP|Read the blog|Advertise on this site