Skip to content

Commit 156ff4a

Browse files
author
Peter Zijlstra
committed
x86/ibt: Base IBT bits
Add Kconfig, Makefile and basic instruction support for x86 IBT. (Ab)use __DISABLE_EXPORTS to disable IBT since it's already employed to mark compressed and purgatory. Additionally mark realmode with it as well to avoid inserting ENDBR instructions there. While ENDBR is technically a NOP, inserting them was causing some grief due to code growth. There's also a problem with using __noendbr in code compiled without -fcf-protection=branch. Signed-off-by: Peter Zijlstra (Intel) <[email protected]> Acked-by: Josh Poimboeuf <[email protected]> Link: https://lore.kernel.org/r/[email protected]
1 parent 5cff208 commit 156ff4a

File tree

3 files changed

+121
-2
lines changed

3 files changed

+121
-2
lines changed

arch/x86/Kconfig

Lines changed: 20 additions & 0 deletions
Original file line numberDiff line numberDiff line change
@@ -1861,6 +1861,26 @@ config X86_UMIP
18611861
specific cases in protected and virtual-8086 modes. Emulated
18621862
results are dummy.
18631863

1864+
config CC_HAS_IBT
1865+
# GCC >= 9 and binutils >= 2.29
1866+
# Retpoline check to work around https://gcc.gnu.org/bugzilla/show_bug.cgi?id=93654
1867+
# Clang/LLVM >= 14
1868+
# fentry check to work around https://reviews.llvm.org/D111108
1869+
def_bool ((CC_IS_GCC && $(cc-option, -fcf-protection=branch -mindirect-branch-register)) || \
1870+
(CC_IS_CLANG && $(success,echo "void a(void) {}" | $(CC) -Werror $(CLANG_FLAGS) -fcf-protection=branch -mfentry -pg -x c - -c -o /dev/null))) && \
1871+
$(as-instr,endbr64)
1872+
1873+
config X86_KERNEL_IBT
1874+
prompt "Indirect Branch Tracking"
1875+
bool
1876+
depends on X86_64 && CC_HAS_IBT
1877+
help
1878+
Build the kernel with support for Indirect Branch Tracking, a
1879+
hardware support course-grain forward-edge Control Flow Integrity
1880+
protection. It enforces that all indirect calls must land on
1881+
an ENDBR instruction, as such, the compiler will instrument the
1882+
code with them to make this happen.
1883+
18641884
config X86_INTEL_MEMORY_PROTECTION_KEYS
18651885
prompt "Memory Protection Keys"
18661886
def_bool y

arch/x86/Makefile

Lines changed: 14 additions & 2 deletions
Original file line numberDiff line numberDiff line change
@@ -36,7 +36,7 @@ endif
3636

3737
# How to compile the 16-bit code. Note we always compile for -march=i386;
3838
# that way we can complain to the user if the CPU is insufficient.
39-
REALMODE_CFLAGS := -m16 -g -Os -DDISABLE_BRANCH_PROFILING \
39+
REALMODE_CFLAGS := -m16 -g -Os -DDISABLE_BRANCH_PROFILING -D__DISABLE_EXPORTS \
4040
-Wall -Wstrict-prototypes -march=i386 -mregparm=3 \
4141
-fno-strict-aliasing -fomit-frame-pointer -fno-pic \
4242
-mno-mmx -mno-sse $(call cc-option,-fcf-protection=none)
@@ -62,8 +62,20 @@ export BITS
6262
#
6363
KBUILD_CFLAGS += -mno-sse -mno-mmx -mno-sse2 -mno-3dnow -mno-avx
6464

65-
# Intel CET isn't enabled in the kernel
65+
ifeq ($(CONFIG_X86_KERNEL_IBT),y)
66+
#
67+
# Kernel IBT has S_CET.NOTRACK_EN=0, as such the compilers must not generate
68+
# NOTRACK prefixes. Current generation compilers unconditionally employ NOTRACK
69+
# for jump-tables, as such, disable jump-tables for now.
70+
#
71+
# (jump-tables are implicitly disabled by RETPOLINE)
72+
#
73+
# https://gcc.gnu.org/bugzilla/show_bug.cgi?id=104816
74+
#
75+
KBUILD_CFLAGS += $(call cc-option,-fcf-protection=branch -fno-jump-tables)
76+
else
6677
KBUILD_CFLAGS += $(call cc-option,-fcf-protection=none)
78+
endif
6779

6880
ifeq ($(CONFIG_X86_32),y)
6981
BITS := 32

arch/x86/include/asm/ibt.h

Lines changed: 87 additions & 0 deletions
Original file line numberDiff line numberDiff line change
@@ -0,0 +1,87 @@
1+
/* SPDX-License-Identifier: GPL-2.0 */
2+
#ifndef _ASM_X86_IBT_H
3+
#define _ASM_X86_IBT_H
4+
5+
#include <linux/types.h>
6+
7+
/*
8+
* The rules for enabling IBT are:
9+
*
10+
* - CC_HAS_IBT: the toolchain supports it
11+
* - X86_KERNEL_IBT: it is selected in Kconfig
12+
* - !__DISABLE_EXPORTS: this is regular kernel code
13+
*
14+
* Esp. that latter one is a bit non-obvious, but some code like compressed,
15+
* purgatory, realmode etc.. is built with custom CFLAGS that do not include
16+
* -fcf-protection=branch and things will go *bang*.
17+
*
18+
* When all the above are satisfied, HAS_KERNEL_IBT will be 1, otherwise 0.
19+
*/
20+
#if defined(CONFIG_X86_KERNEL_IBT) && !defined(__DISABLE_EXPORTS)
21+
22+
#define HAS_KERNEL_IBT 1
23+
24+
#ifndef __ASSEMBLY__
25+
26+
#ifdef CONFIG_X86_64
27+
#define ASM_ENDBR "endbr64\n\t"
28+
#else
29+
#define ASM_ENDBR "endbr32\n\t"
30+
#endif
31+
32+
#define __noendbr __attribute__((nocf_check))
33+
34+
static inline __attribute_const__ u32 gen_endbr(void)
35+
{
36+
u32 endbr;
37+
38+
/*
39+
* Generate ENDBR64 in a way that is sure to not result in
40+
* an ENDBR64 instruction as immediate.
41+
*/
42+
asm ( "mov $~0xfa1e0ff3, %[endbr]\n\t"
43+
"not %[endbr]\n\t"
44+
: [endbr] "=&r" (endbr) );
45+
46+
return endbr;
47+
}
48+
49+
static inline bool is_endbr(u32 val)
50+
{
51+
val &= ~0x01000000U; /* ENDBR32 -> ENDBR64 */
52+
return val == gen_endbr();
53+
}
54+
55+
#else /* __ASSEMBLY__ */
56+
57+
#ifdef CONFIG_X86_64
58+
#define ENDBR endbr64
59+
#else
60+
#define ENDBR endbr32
61+
#endif
62+
63+
#endif /* __ASSEMBLY__ */
64+
65+
#else /* !IBT */
66+
67+
#define HAS_KERNEL_IBT 0
68+
69+
#ifndef __ASSEMBLY__
70+
71+
#define ASM_ENDBR
72+
73+
#define __noendbr
74+
75+
static inline bool is_endbr(u32 val) { return false; }
76+
77+
#else /* __ASSEMBLY__ */
78+
79+
#define ENDBR
80+
81+
#endif /* __ASSEMBLY__ */
82+
83+
#endif /* CONFIG_X86_KERNEL_IBT */
84+
85+
#define ENDBR_INSN_SIZE (4*HAS_KERNEL_IBT)
86+
87+
#endif /* _ASM_X86_IBT_H */

0 commit comments

Comments
 (0)