2022-03-03 13:32:49

by Peter Zijlstra

[permalink] [raw]
Subject: [PATCH v3 04/39] 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]>
---
arch/x86/Kconfig | 20 ++++++++++++
arch/x86/Makefile | 7 +++-
arch/x86/include/asm/ibt.h | 74 +++++++++++++++++++++++++++++++++++++++++++++
3 files changed, 99 insertions(+), 2 deletions(-)

--- a/arch/x86/Kconfig
+++ b/arch/x86/Kconfig
@@ -1861,6 +1861,26 @@ config X86_UMIP
specific cases in protected and virtual-8086 modes. Emulated
results are dummy.

+config CC_HAS_IBT
+ # GCC >= 9 and binutils >= 2.29
+ # Retpoline check to work around https://gcc.gnu.org/bugzilla/show_bug.cgi?id=93654
+ # Clang/LLVM >= 14
+ # fentry check to work around https://reviews.llvm.org/D111108
+ def_bool ((CC_IS_GCC && $(cc-option, -fcf-protection=branch -mindirect-branch-register)) || \
+ (CC_IS_CLANG && $(cc-option, -fcf-protection=branch -mfentry))) && \
+ $(as-instr,endbr64)
+
+config X86_KERNEL_IBT
+ prompt "Indirect Branch Tracking"
+ bool
+ depends on X86_64 && CC_HAS_IBT
+ help
+ Build the kernel with support for Indirect Branch Tracking, a
+ hardware support course-grain forward-edge Control Flow Integrity
+ protection. It enforces that all indirect calls must land on
+ an ENDBR instruction, as such, the compiler will instrument the
+ code with them to make this happen.
+
config X86_INTEL_MEMORY_PROTECTION_KEYS
prompt "Memory Protection Keys"
def_bool y
--- a/arch/x86/Makefile
+++ b/arch/x86/Makefile
@@ -36,7 +36,7 @@ endif

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

-# Intel CET isn't enabled in the kernel
+ifeq ($(CONFIG_X86_KERNEL_IBT),y)
+KBUILD_CFLAGS += $(call cc-option,-fcf-protection=branch)
+else
KBUILD_CFLAGS += $(call cc-option,-fcf-protection=none)
+endif

ifeq ($(CONFIG_X86_32),y)
BITS := 32
--- /dev/null
+++ b/arch/x86/include/asm/ibt.h
@@ -0,0 +1,74 @@
+/* SPDX-License-Identifier: GPL-2.0 */
+#ifndef _ASM_X86_IBT_H
+#define _ASM_X86_IBT_H
+
+#include <linux/types.h>
+
+#if defined(CONFIG_X86_KERNEL_IBT) && !defined(__DISABLE_EXPORTS)
+
+#define HAS_KERNEL_IBT 1
+
+#ifndef __ASSEMBLY__
+
+#ifdef CONFIG_X86_64
+#define ASM_ENDBR "endbr64\n\t"
+#else
+#define ASM_ENDBR "endbr32\n\t"
+#endif
+
+#define __noendbr __attribute__((nocf_check))
+
+static inline __attribute_const__ u32 gen_endbr(void)
+{
+ u32 endbr;
+
+ /*
+ * Generate ENDBR64 in a way that is sure to not result in
+ * an ENDBR64 instruction as immediate.
+ */
+ asm ( "mov $~0xfa1e0ff3, %[endbr]\n\t"
+ "not %[endbr]\n\t"
+ : [endbr] "=&r" (endbr) );
+
+ return endbr;
+}
+
+static inline bool is_endbr(u32 val)
+{
+ val &= ~0x01000000U; /* ENDBR32 -> ENDBR64 */
+ return val == gen_endbr();
+}
+
+#else /* __ASSEMBLY__ */
+
+#ifdef CONFIG_X86_64
+#define ENDBR endbr64
+#else
+#define ENDBR endbr32
+#endif
+
+#endif /* __ASSEMBLY__ */
+
+#else /* !IBT */
+
+#define HAS_KERNEL_IBT 0
+
+#ifndef __ASSEMBLY__
+
+#define ASM_ENDBR
+
+#define __noendbr
+
+static inline bool is_endbr(u32 val) { return false; }
+
+#else /* __ASSEMBLY__ */
+
+#define ENDBR
+
+#endif /* __ASSEMBLY__ */
+
+#endif /* CONFIG_X86_KERNEL_IBT */
+
+#define ENDBR_INSN_SIZE (4*HAS_KERNEL_IBT)
+
+#endif /* _ASM_X86_IBT_H */



2022-03-07 13:22:36

by Joao Moreira

[permalink] [raw]
Subject: Re: [PATCH v3 04/39] x86/ibt: Base IBT bits

>
> Joao will be submitting GCC and Clang bugreports on this shortly.
>
Reported both on GCC and Clang:

https://gcc.gnu.org/bugzilla/show_bug.cgi?id=104816
https://github.com/llvm/llvm-project/issues/54247

I'm not sure if something should be reported to ClangBuiltLinux, since
the kernel will be built with -fno-jump-tables anyways.

Tks,
Joao

2022-03-07 22:37:37

by Nick Desaulniers

[permalink] [raw]
Subject: Re: [PATCH v3 04/39] x86/ibt: Base IBT bits

On Mon, Mar 7, 2022 at 4:25 AM Joao Moreira <[email protected]> wrote:
>
> >
> > Joao will be submitting GCC and Clang bugreports on this shortly.
> >
> Reported both on GCC and Clang:
>
> https://gcc.gnu.org/bugzilla/show_bug.cgi?id=104816
> https://github.com/llvm/llvm-project/issues/54247
>
> I'm not sure if something should be reported to ClangBuiltLinux, since
> the kernel will be built with -fno-jump-tables anyways.

As long as a bug gets filed; that's what's more important to me.
Doesn't matter if we track it upstream in LLVM's issue tracker or
downstream. Having the record of it in either is what's important to
me. Thanks for filing!
--
Thanks,
~Nick Desaulniers

2022-03-07 23:16:26

by Peter Zijlstra

[permalink] [raw]
Subject: Re: [PATCH v3 04/39] x86/ibt: Base IBT bits

On Thu, Mar 03, 2022 at 12:23:25PM +0100, Peter Zijlstra wrote:

> --- a/arch/x86/Makefile
> +++ b/arch/x86/Makefile
> @@ -62,8 +62,11 @@ export BITS
> #
> KBUILD_CFLAGS += -mno-sse -mno-mmx -mno-sse2 -mno-3dnow -mno-avx
>
> -# Intel CET isn't enabled in the kernel
> +ifeq ($(CONFIG_X86_KERNEL_IBT),y)
> +KBUILD_CFLAGS += $(call cc-option,-fcf-protection=branch)
> +else
> KBUILD_CFLAGS += $(call cc-option,-fcf-protection=none)
> +endif
>
> ifeq ($(CONFIG_X86_32),y)
> BITS := 32

Joao reported that RETPOLINE=n builds explode; turns out the compilers
default to using NOTRACK prefixes for jump-tables and we explicitly do
not enable that security compromise for the kernel.

Since the compilers don't have explicit control over NOTRACK generation,
blanket disable jump-tables when using IBT without RETPOLINE.

Joao will be submitting GCC and Clang bugreports on this shortly.

diff --git a/arch/x86/Makefile b/arch/x86/Makefile
index d38c18f4bd53..f80a425e7d29 100644
--- a/arch/x86/Makefile
+++ b/arch/x86/Makefile
@@ -63,7 +63,9 @@ export BITS
KBUILD_CFLAGS += -mno-sse -mno-mmx -mno-sse2 -mno-3dnow -mno-avx

ifeq ($(CONFIG_X86_KERNEL_IBT),y)
-KBUILD_CFLAGS += $(call cc-option,-fcf-protection=branch)
+# Explicitly disable jump-tables, also implied by RETPOLINE=y, for kernel IBT
+# to avoid NOTRACK prefixes.
+KBUILD_CFLAGS += $(call cc-option,-fcf-protection=branch -fno-jump-tables)
else
KBUILD_CFLAGS += $(call cc-option,-fcf-protection=none)
endif