lkml.org 
[lkml]   [2021]   [Jul]   [22]   [last100]   RSS Feed
Views: [wrap][no wrap]   [headers]  [forward] 
 
Messages in this thread
    Patch in this message
    /
    From
    Subject[PATCH v28 03/10] x86/cet/ibt: Handle signals for Indirect Branch Tracking
    Date
    IBT state machine tracks CALL/JMP instructions.  When a such instruction is
    executed and before arriving at an ENDBR, it is in WAIT_FOR_ENDBR state,
    which can be read from CET_WAIT_ENDBR bit of MSR_IA32_U_CET.

    Further details are described in Intel SDM Vol. 1, Sec. 18.3.

    In handling signals, WAIT_FOR_ENDBR state is saved/restored with a new
    UC_WAIT_ENDBR flag being introduced.

    A legacy IA32 signal frame does not have ucontext, and cannot be supported
    with a uc flag. Thus, IBT feature is not supported for ia32 app's, which
    is handled in a separate patch.

    Signed-off-by: Yu-cheng Yu <yu-cheng.yu@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>
    Link: https://lore.kernel.org/linux-api/f6e61dae-9805-c855-8873-7481ceb7ea79@intel.com/
    ---
    arch/x86/ia32/ia32_signal.c | 15 ++++++++--
    arch/x86/include/asm/cet.h | 4 +++
    arch/x86/include/uapi/asm/ucontext.h | 5 ++++
    arch/x86/kernel/ibt.c | 41 ++++++++++++++++++++++++++++
    arch/x86/kernel/signal.c | 6 ++++
    5 files changed, 68 insertions(+), 3 deletions(-)

    diff --git a/arch/x86/ia32/ia32_signal.c b/arch/x86/ia32/ia32_signal.c
    index d7a30bc98e66..77d0fa90cc19 100644
    --- a/arch/x86/ia32/ia32_signal.c
    +++ b/arch/x86/ia32/ia32_signal.c
    @@ -129,6 +129,7 @@ COMPAT_SYSCALL_DEFINE0(rt_sigreturn)
    {
    struct pt_regs *regs = current_pt_regs();
    struct rt_sigframe_ia32 __user *frame;
    + unsigned int uc_flags;
    sigset_t set;

    frame = (struct rt_sigframe_ia32 __user *)(regs->sp - 4);
    @@ -137,6 +138,11 @@ COMPAT_SYSCALL_DEFINE0(rt_sigreturn)
    goto badframe;
    if (__get_user(set.sig[0], (__u64 __user *)&frame->uc.uc_sigmask))
    goto badframe;
    + if (__get_user(uc_flags, &frame->uc.uc_flags))
    + goto badframe;
    +
    + if (uc_flags & UC_WAIT_ENDBR)
    + ibt_set_wait_endbr();

    set_current_blocked(&set);

    @@ -312,6 +318,7 @@ int ia32_setup_rt_frame(int sig, struct ksignal *ksig,
    compat_sigset_t *set, struct pt_regs *regs)
    {
    struct rt_sigframe_ia32 __user *frame;
    + unsigned int uc_flags = 0;
    void __user *restorer;
    void __user *fp = NULL;

    @@ -339,6 +346,9 @@ int ia32_setup_rt_frame(int sig, struct ksignal *ksig,
    if (setup_signal_shadow_stack(1, restorer))
    return -EFAULT;

    + if (ibt_get_clear_wait_endbr())
    + uc_flags |= UC_WAIT_ENDBR;
    +
    if (!user_access_begin(frame, sizeof(*frame)))
    return -EFAULT;

    @@ -348,9 +358,8 @@ int ia32_setup_rt_frame(int sig, struct ksignal *ksig,

    /* Create the ucontext. */
    if (static_cpu_has(X86_FEATURE_XSAVE))
    - unsafe_put_user(UC_FP_XSTATE, &frame->uc.uc_flags, Efault);
    - else
    - unsafe_put_user(0, &frame->uc.uc_flags, Efault);
    + uc_flags |= UC_FP_XSTATE;
    + unsafe_put_user(uc_flags, &frame->uc.uc_flags, Efault);
    unsafe_put_user(0, &frame->uc.uc_link, Efault);
    unsafe_compat_save_altstack(&frame->uc.uc_stack, regs->sp, Efault);

    diff --git a/arch/x86/include/asm/cet.h b/arch/x86/include/asm/cet.h
    index 3dfca29a7c0b..2618faf3fda5 100644
    --- a/arch/x86/include/asm/cet.h
    +++ b/arch/x86/include/asm/cet.h
    @@ -46,9 +46,13 @@ static inline int restore_signal_shadow_stack(void) { return 0; }
    #ifdef CONFIG_X86_IBT
    int ibt_setup(void);
    void ibt_disable(void);
    +int ibt_get_clear_wait_endbr(void);
    +int ibt_set_wait_endbr(void);
    #else
    static inline int ibt_setup(void) { return 0; }
    static inline void ibt_disable(void) {}
    +static inline int ibt_get_clear_wait_endbr(void) { return 0; }
    +static inline int ibt_set_wait_endbr(void) { return 0; }
    #endif

    #ifdef CONFIG_X86_SHADOW_STACK
    diff --git a/arch/x86/include/uapi/asm/ucontext.h b/arch/x86/include/uapi/asm/ucontext.h
    index 5657b7a49f03..905419de2cc7 100644
    --- a/arch/x86/include/uapi/asm/ucontext.h
    +++ b/arch/x86/include/uapi/asm/ucontext.h
    @@ -51,6 +51,11 @@
    #define UC_STRICT_RESTORE_SS 0x4
    #endif

    +/*
    + * Indicates IBT WAIT-ENDBR status.
    + */
    +#define UC_WAIT_ENDBR 0x08
    +
    #include <asm-generic/ucontext.h>

    #endif /* _ASM_X86_UCONTEXT_H */
    diff --git a/arch/x86/kernel/ibt.c b/arch/x86/kernel/ibt.c
    index 4ab7af33b274..5ab8632a1f7e 100644
    --- a/arch/x86/kernel/ibt.c
    +++ b/arch/x86/kernel/ibt.c
    @@ -56,3 +56,44 @@ void ibt_disable(void)
    ibt_set_clear_msr_bits(0, CET_ENDBR_EN);
    current->thread.shstk.ibt = 0;
    }
    +
    +int ibt_get_clear_wait_endbr(void)
    +{
    + u64 msr_val = 0;
    +
    + if (!current->thread.shstk.ibt)
    + return 0;
    +
    + fpregs_lock();
    +
    + if (!test_thread_flag(TIF_NEED_FPU_LOAD)) {
    + if (!rdmsrl_safe(MSR_IA32_U_CET, &msr_val))
    + wrmsrl(MSR_IA32_U_CET, msr_val & ~CET_WAIT_ENDBR);
    + } else {
    + struct cet_user_state *cet;
    +
    + /*
    + * If !TIF_NEED_FPU_LOAD and get_xsave_addr() returns zero,
    + * XFEATURE_CET_USER is in init state (cet is not active).
    + * Return zero status.
    + */
    + cet = get_xsave_addr(&current->thread.fpu.state.xsave,
    + XFEATURE_CET_USER);
    + if (cet) {
    + msr_val = cet->user_cet;
    + cet->user_cet = msr_val & ~CET_WAIT_ENDBR;
    + }
    + }
    +
    + fpregs_unlock();
    +
    + return msr_val & CET_WAIT_ENDBR;
    +}
    +
    +int ibt_set_wait_endbr(void)
    +{
    + if (!current->thread.shstk.ibt)
    + return 0;
    +
    + return ibt_set_clear_msr_bits(CET_WAIT_ENDBR, 0);
    +}
    diff --git a/arch/x86/kernel/signal.c b/arch/x86/kernel/signal.c
    index 661e46803b84..a1285650852e 100644
    --- a/arch/x86/kernel/signal.c
    +++ b/arch/x86/kernel/signal.c
    @@ -135,6 +135,9 @@ static int restore_sigcontext(struct pt_regs *regs,
    */
    if (unlikely(!(uc_flags & UC_STRICT_RESTORE_SS) && user_64bit_mode(regs)))
    force_valid_ss(regs);
    +
    + if (uc_flags & UC_WAIT_ENDBR)
    + ibt_set_wait_endbr();
    #endif

    return fpu__restore_sig((void __user *)sc.fpstate,
    @@ -455,6 +458,9 @@ static unsigned long frame_uc_flags(struct pt_regs *regs)
    if (likely(user_64bit_mode(regs)))
    flags |= UC_STRICT_RESTORE_SS;

    + if (ibt_get_clear_wait_endbr())
    + flags |= UC_WAIT_ENDBR;
    +
    return flags;
    }

    --
    2.21.0
    \
     
     \ /
      Last update: 2021-07-22 22:58    [W:4.704 / U:0.156 seconds]
    ©2003-2020 Jasper Spaans|hosted at Digital Ocean and TransIP|Read the blog|Advertise on this site