[PATCH 8/8] x86/bhi: Add support for clearing branch history at syscall entry

Yuxuan Luo yuxuan.luo at canonical.com
Tue Aug 6 22:50:33 UTC 2024


From: Pawan Gupta <pawan.kumar.gupta at linux.intel.com>

Branch History Injection (BHI) attacks may allow a malicious application to
influence indirect branch prediction in kernel by poisoning the branch
history. eIBRS isolates indirect branch targets in ring0.  The BHB can
still influence the choice of indirect branch predictor entry, and although
branch predictor entries are isolated between modes when eIBRS is enabled,
the BHB itself is not isolated between modes.

Alder Lake and new processors supports a hardware control BHI_DIS_S to
mitigate BHI.  For older processors Intel has released a software sequence
to clear the branch history on parts that don't support BHI_DIS_S. Add
support to execute the software sequence at syscall entry and VMexit to
overwrite the branch history.

For now, branch history is not cleared at interrupt entry, as malicious
applications are not believed to have sufficient control over the
registers, since previous register state is cleared at interrupt
entry. Researchers continue to poke at this area and it may become
necessary to clear at interrupt entry as well in the future.

This mitigation is only defined here. It is enabled later.

Signed-off-by: Pawan Gupta <pawan.kumar.gupta at linux.intel.com>
Co-developed-by: Daniel Sneddon <daniel.sneddon at linux.intel.com>
Signed-off-by: Daniel Sneddon <daniel.sneddon at linux.intel.com>
Signed-off-by: Thomas Gleixner <tglx at linutronix.de>
Reviewed-by: Alexandre Chartre <alexandre.chartre at oracle.com>
Reviewed-by: Josh Poimboeuf <jpoimboe at kernel.org>

(cherry picked from commit 7390db8aea0d64e9deb28b8e1ce716f5020c7ee5)
[yuxuan.luo:
Backporting this commit again so that the fixes for CVE-2024-25744 will
not make Jammy vulnerable to CVE-2024-2201 Native BHI again.
]
CVE-2024-25744
Signed-off-by: Yuxuan Luo <yuxuan.luo at canonical.com>
---
 arch/x86/entry/common.c              |  4 ++--
 arch/x86/entry/entry_64_compat.S     | 16 ++++++++++++++++
 arch/x86/include/asm/nospec-branch.h |  4 ++++
 arch/x86/include/asm/syscall.h       |  1 +
 4 files changed, 23 insertions(+), 2 deletions(-)

diff --git a/arch/x86/entry/common.c b/arch/x86/entry/common.c
index d1594b4acf485..045e6615bf3b6 100644
--- a/arch/x86/entry/common.c
+++ b/arch/x86/entry/common.c
@@ -141,7 +141,7 @@ static __always_inline bool int80_is_external(void)
 }
 
 /**
- * int80_emulation - 32-bit legacy syscall entry
+ * do_int80_emulation - 32-bit legacy syscall C entry from asm
  *
  * This entry point can be used by 32-bit and 64-bit programs to perform
  * 32-bit system calls.  Instances of INT $0x80 can be found inline in
@@ -159,7 +159,7 @@ static __always_inline bool int80_is_external(void)
  *   eax:				system call number
  *   ebx, ecx, edx, esi, edi, ebp:	arg1 - arg 6
  */
-DEFINE_IDTENTRY_RAW(int80_emulation)
+__visible noinstr void do_int80_emulation(struct pt_regs *regs)
 {
 	int nr;
 
diff --git a/arch/x86/entry/entry_64_compat.S b/arch/x86/entry/entry_64_compat.S
index 118df23c28d45..f5d0744241ce8 100644
--- a/arch/x86/entry/entry_64_compat.S
+++ b/arch/x86/entry/entry_64_compat.S
@@ -114,6 +114,8 @@ SYM_INNER_LABEL(entry_SYSENTER_compat_after_hwframe, SYM_L_GLOBAL)
 
 	cld
 
+	CLEAR_BRANCH_HISTORY
+
 	/*
 	 * SYSENTER doesn't filter flags, so we need to clear NT and AC
 	 * ourselves.  To save a few cycles, we can check whether
@@ -330,3 +332,17 @@ sysret32_from_system_call:
 	CLEAR_CPU_BUFFERS
 	sysretl
 SYM_CODE_END(entry_SYSCALL_compat)
+
+/*
+ * int 0x80 is used by 32 bit mode as a system call entry. Normally idt entries
+ * point to C routines, however since this is a system call interface the branch
+ * history needs to be scrubbed to protect against BHI attacks, and that
+ * scrubbing needs to take place in assembly code prior to entering any C
+ * routines.
+ */
+SYM_CODE_START(int80_emulation)
+	ANNOTATE_NOENDBR
+	UNWIND_HINT_FUNC
+	CLEAR_BRANCH_HISTORY
+	jmp do_int80_emulation
+SYM_CODE_END(int80_emulation)
diff --git a/arch/x86/include/asm/nospec-branch.h b/arch/x86/include/asm/nospec-branch.h
index de97843bdeb80..da464ccdf2269 100644
--- a/arch/x86/include/asm/nospec-branch.h
+++ b/arch/x86/include/asm/nospec-branch.h
@@ -241,6 +241,10 @@ extern void srso_alias_untrain_ret(void);
 extern void entry_untrain_ret(void);
 extern void entry_ibpb(void);
 
+#ifdef CONFIG_X86_64
+extern void clear_bhb_loop(void);
+#endif
+
 extern void (*x86_return_thunk)(void);
 
 #ifdef CONFIG_X86_64
diff --git a/arch/x86/include/asm/syscall.h b/arch/x86/include/asm/syscall.h
index e873e95ff6bfc..250a01782d6a5 100644
--- a/arch/x86/include/asm/syscall.h
+++ b/arch/x86/include/asm/syscall.h
@@ -158,6 +158,7 @@ static inline int syscall_get_arch(struct task_struct *task)
 }
 
 void do_syscall_64(struct pt_regs *regs, int nr);
+void do_int80_emulation(struct pt_regs *regs);
 
 #endif	/* CONFIG_X86_32 */
 
-- 
2.34.1




More information about the kernel-team mailing list