From: Richard Patel <ripatel@wii.dev>
To: x86@kernel.org
Cc: Rick Edgecombe <rick.p.edgecombe@intel.com>,
Yu-cheng Yu <yu-cheng.yu@intel.com>,
Dave Hansen <dave.hansen@linux.intel.com>,
Thomas Gleixner <tglx@kernel.org>, Ingo Molnar <mingo@redhat.com>,
Borislav Petkov <bp@alien8.de>, "H. Peter Anvin" <hpa@zytor.com>,
Andy Lutomirski <luto@kernel.org>, Kees Cook <kees@kernel.org>,
Peter Zijlstra <peterz@infradead.org>,
Shuah Khan <shuah@kernel.org>,
linux-kselftest@vger.kernel.org, linux-kernel@vger.kernel.org
Subject: [PATCH 3/7] x86: signal handler support for IBT
Date: Sun, 17 May 2026 13:30:20 -0500 [thread overview]
Message-ID: <20260517183024.16292-4-ripatel@wii.dev> (raw)
In-Reply-To: <20260517183024.16292-1-ripatel@wii.dev>
Addresses an edge case in usermode IBT with signal handlers.
When entering and exiting signals, the WAIT_FOR_ENDBR CPU state
must be restored. WAIT_FOR_ENDBR is a flag that raises a CET
violation if the next instruction is not endbr64.
If a thread enters a signal handler immediately after executing an
indirect jump (before reaching an endbr64), the WAIT_FOR_ENDBR
would otherwise mistakenly cause a CET violation on the signal
handler's first instruction.
Worse, an attacker could circumvent IBT by triggering a signal
before a vulnerable jump, call rt_sigreturn in that signal, and
then return to the original indirect jump target without endbr64
checking.
Unless FRED is enabled, the WAIT_FOR_ENDBR flag is backed up
from the U_CET MSR. Due to XSAVE, this MSR might be stale in
kernel-mode.
A gap in 32-bit signal handling is resolved in a follow-up commit.
Based-on-patch-by: Yu-cheng Yu <yu-cheng.yu@intel.com>
Link: https://lore.kernel.org/lkml/20210820182245.1188-4-yu-cheng.yu@intel.com/
Signed-off-by: Richard Patel <ripatel@wii.dev>
---
arch/x86/include/asm/ibt.h | 14 +++++
arch/x86/include/asm/processor.h | 5 ++
arch/x86/include/uapi/asm/ucontext.h | 5 ++
arch/x86/kernel/Makefile | 1 +
arch/x86/kernel/ibt.c | 88 ++++++++++++++++++++++++++++
arch/x86/kernel/signal_64.c | 6 ++
6 files changed, 119 insertions(+)
create mode 100644 arch/x86/kernel/ibt.c
diff --git a/arch/x86/include/asm/ibt.h b/arch/x86/include/asm/ibt.h
index 5e45d6424722..3fe464bf83e7 100644
--- a/arch/x86/include/asm/ibt.h
+++ b/arch/x86/include/asm/ibt.h
@@ -114,4 +114,18 @@ static inline void ibt_restore(u64 save) { }
#define ENDBR_INSN_SIZE (4*HAS_KERNEL_IBT)
+#ifndef __ASSEMBLER__
+
+struct pt_regs;
+
+#ifdef CONFIG_X86_USER_IBT
+bool user_ibt_pop_wait_endbr(struct pt_regs *regs);
+void user_ibt_restore_wait_endbr(struct pt_regs *regs, bool wait_endbr);
+#else
+static inline bool user_ibt_pop_wait_endbr(struct pt_regs *regs) { return false; }
+static inline void user_ibt_restore_wait_endbr(struct pt_regs *regs, bool wait_endbr) {}
+#endif /* CONFIG_X86_USER_IBT */
+
+#endif /* __ASSEMBLER__ */
+
#endif /* _ASM_X86_IBT_H */
diff --git a/arch/x86/include/asm/processor.h b/arch/x86/include/asm/processor.h
index 10b5355b323e..6ce8f7b6607c 100644
--- a/arch/x86/include/asm/processor.h
+++ b/arch/x86/include/asm/processor.h
@@ -504,6 +504,11 @@ struct thread_struct {
unsigned int iopl_warn:1;
+#ifdef CONFIG_X86_USER_IBT
+ unsigned int ibt:1;
+ unsigned int ibt_locked:1;
+#endif
+
/*
* Protection Keys Register for Userspace. Loaded immediately on
* context switch. Store it in thread_struct to avoid a lookup in
diff --git a/arch/x86/include/uapi/asm/ucontext.h b/arch/x86/include/uapi/asm/ucontext.h
index 5657b7a49f03..0271f5e8aa14 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 status WAIT_FOR_ENDBR.
+ */
+#define UC_WAIT_ENDBR 0x8
+
#include <asm-generic/ucontext.h>
#endif /* _ASM_X86_UCONTEXT_H */
diff --git a/arch/x86/kernel/Makefile b/arch/x86/kernel/Makefile
index 47a32f583930..05c87f014552 100644
--- a/arch/x86/kernel/Makefile
+++ b/arch/x86/kernel/Makefile
@@ -169,6 +169,7 @@ obj-$(CONFIG_CALL_THUNKS) += callthunks.o
obj-$(CONFIG_X86_CET) += cet.o
obj-$(CONFIG_X86_USER_SHADOW_STACK) += shstk.o
+obj-$(CONFIG_X86_USER_IBT) += ibt.o
###
# 64 bit specific files
diff --git a/arch/x86/kernel/ibt.c b/arch/x86/kernel/ibt.c
new file mode 100644
index 000000000000..596b0629106d
--- /dev/null
+++ b/arch/x86/kernel/ibt.c
@@ -0,0 +1,88 @@
+// SPDX-License-Identifier: GPL-2.0
+
+#include <linux/types.h>
+#include <asm/msr.h>
+#include <asm/fpu/xstate.h>
+
+static bool user_ibt_enabled(struct task_struct *task)
+{
+ return task->thread.ibt;
+}
+
+bool user_ibt_pop_wait_endbr(struct pt_regs *regs)
+{
+ struct fpu *fpu = x86_task_fpu(current);
+ u64 msrval = 0;
+
+ if (!user_ibt_enabled(current))
+ return 0;
+
+#ifdef CONFIG_X86_FRED
+ if (cpu_feature_enabled(X86_FEATURE_FRED)) {
+ msrval = regs->fred_cs.wfe;
+ regs->fred_cs.wfe = 0;
+ return !!msrval;
+ }
+#endif
+
+ fpregs_lock();
+
+ if (!test_thread_flag(TIF_NEED_FPU_LOAD)) {
+ if (!rdmsrq_safe(MSR_IA32_U_CET, &msrval))
+ wrmsrq(MSR_IA32_U_CET, msrval & ~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(&fpu->fpstate->regs.xsave,
+ XFEATURE_CET_USER);
+ if (cet) {
+ msrval = cet->user_cet;
+ cet->user_cet = msrval & ~CET_WAIT_ENDBR;
+ }
+ }
+
+ fpregs_unlock();
+
+ return !!(msrval & CET_WAIT_ENDBR);
+}
+
+void
+user_ibt_restore_wait_endbr(struct pt_regs *regs, bool wait_endbr)
+{
+ struct fpu *fpu = x86_task_fpu(current);
+ u64 msrval = 0;
+
+ if (!user_ibt_enabled(current))
+ return;
+
+#ifdef CONFIG_X86_FRED
+ if (cpu_feature_enabled(X86_FEATURE_FRED)) {
+ regs->fred_cs.wfe = wait_endbr;
+ return;
+ }
+#endif
+
+ if (!wait_endbr)
+ return;
+
+ fpregs_lock();
+
+ if (!test_thread_flag(TIF_NEED_FPU_LOAD)) {
+ if (!rdmsrq_safe(MSR_IA32_U_CET, &msrval))
+ wrmsrq(MSR_IA32_U_CET, msrval | CET_WAIT_ENDBR);
+ } else {
+ struct cet_user_state *cet;
+
+ cet = get_xsave_addr(&fpu->fpstate->regs.xsave,
+ XFEATURE_CET_USER);
+ if (cet)
+ cet->user_cet |= CET_WAIT_ENDBR;
+ }
+
+ fpregs_unlock();
+}
diff --git a/arch/x86/kernel/signal_64.c b/arch/x86/kernel/signal_64.c
index d483b585c6c6..9f1861540d27 100644
--- a/arch/x86/kernel/signal_64.c
+++ b/arch/x86/kernel/signal_64.c
@@ -14,6 +14,7 @@
#include <asm/ucontext.h>
#include <asm/fpu/signal.h>
+#include <asm/ibt.h>
#include <asm/sighandling.h>
#include <asm/syscall.h>
@@ -92,6 +93,8 @@ static bool restore_sigcontext(struct pt_regs *regs,
if (unlikely(!(uc_flags & UC_STRICT_RESTORE_SS) && user_64bit_mode(regs)))
force_valid_ss(regs);
+ user_ibt_restore_wait_endbr(regs, uc_flags & UC_WAIT_ENDBR);
+
return fpu__restore_sig((void __user *)sc.fpstate, 0);
}
@@ -158,6 +161,9 @@ static unsigned long frame_uc_flags(struct pt_regs *regs)
if (likely(user_64bit_mode(regs)))
flags |= UC_STRICT_RESTORE_SS;
+ if (user_ibt_pop_wait_endbr(regs))
+ flags |= UC_WAIT_ENDBR;
+
return flags;
}
--
2.47.3
next prev parent reply other threads:[~2026-05-17 18:35 UTC|newest]
Thread overview: 22+ messages / expand[flat|nested] mbox.gz Atom feed top
2026-05-17 18:30 [PATCH 0/7] Usermode Indirect Branch Tracking Richard Patel
2026-05-17 18:30 ` [PATCH 1/7] x86: add userspace IBT config option Richard Patel
2026-05-17 18:30 ` [PATCH 2/7] x86: shstk: don't clobber IBT bits in U_CET MSR Richard Patel
2026-05-17 18:30 ` Richard Patel [this message]
2026-05-17 18:30 ` [PATCH 4/7] x86: ban 32-bit sigreturn when user IBT enabled Richard Patel
2026-05-18 20:22 ` H. Peter Anvin
2026-05-19 0:14 ` Richard Patel
2026-05-24 21:53 ` Richard Patel
2026-05-25 11:05 ` David Laight
2026-05-17 18:30 ` [PATCH 5/7] x86: expose user IBT via PR_CFI_BRANCH_LANDING_PADS Richard Patel
2026-05-18 6:46 ` Richard Patel
2026-05-17 18:30 ` [PATCH 6/7] x86/entry/vdso: build with IBT support Richard Patel
2026-05-17 18:30 ` [PATCH 7/7] selftests/x86: test usermode IBT Richard Patel
2026-05-18 7:36 ` [PATCH 0/7] Usermode Indirect Branch Tracking Peter Zijlstra
2026-05-18 16:25 ` Richard Patel
2026-05-18 19:31 ` Peter Zijlstra
2026-05-19 9:33 ` David Laight
2026-05-19 9:40 ` Peter Zijlstra
2026-05-19 13:14 ` Richard Patel
2026-05-19 13:28 ` David Laight
2026-05-19 14:18 ` Richard Patel
2026-05-19 14:42 ` Peter Zijlstra
Reply instructions:
You may reply publicly to this message via plain-text email
using any one of the following methods:
* Save the following mbox file, import it into your mail client,
and reply-to-all from there: mbox
Avoid top-posting and favor interleaved quoting:
https://en.wikipedia.org/wiki/Posting_style#Interleaved_style
* Reply using the --to, --cc, and --in-reply-to
switches of git-send-email(1):
git send-email \
--in-reply-to=20260517183024.16292-4-ripatel@wii.dev \
--to=ripatel@wii.dev \
--cc=bp@alien8.de \
--cc=dave.hansen@linux.intel.com \
--cc=hpa@zytor.com \
--cc=kees@kernel.org \
--cc=linux-kernel@vger.kernel.org \
--cc=linux-kselftest@vger.kernel.org \
--cc=luto@kernel.org \
--cc=mingo@redhat.com \
--cc=peterz@infradead.org \
--cc=rick.p.edgecombe@intel.com \
--cc=shuah@kernel.org \
--cc=tglx@kernel.org \
--cc=x86@kernel.org \
--cc=yu-cheng.yu@intel.com \
/path/to/YOUR_REPLY
https://kernel.org/pub/software/scm/git/docs/git-send-email.html
* If your mail client supports setting the In-Reply-To header
via mailto: links, try the mailto: link
Be sure your reply has a Subject: header at the top and a blank line
before the message body.
This is an external index of several public inboxes,
see mirroring instructions on how to clone and mirror
all data and code used by this external index.