[PATCH v29 03/10] x86/cet/ibt: Handle signals for Indirect Branch Tracking

From: Yu-cheng Yu
Date: Fri Aug 20 2021 - 14:23:48 EST


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@xxxxxxxxx>
Cc: Andy Lutomirski <luto@xxxxxxxxxx>
Cc: Cyrill Gorcunov <gorcunov@xxxxxxxxx>
Cc: Florian Weimer <fweimer@xxxxxxxxxx>
Cc: H. Peter Anvin <hpa@xxxxxxxxx>
Cc: Kees Cook <keescook@xxxxxxxxxxxx>
Link: https://lore.kernel.org/linux-api/f6e61dae-9805-c855-8873-7481ceb7ea79@xxxxxxxxx/
---
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