diff mbox series

[v2,06/70] x86: Introduce support for CET-IBT

Message ID 20220214125127.17985-7-andrew.cooper3@citrix.com (mailing list archive)
State New, archived
Headers show
Series x86: Support for CET Indirect Branch Tracking | expand

Commit Message

Andrew Cooper Feb. 14, 2022, 12:50 p.m. UTC
CET Indirect Branch Tracking is a hardware feature designed to provide
forward-edge control flow integrity, protecting against jump/call oriented
programming.

IBT requires the placement of ENDBR{32,64} instructions at the target of every
indirect call/jmp, and every entrypoint.

However, the default -fcf-protection=branch places an ENDBR on every function
which far more than necessary, and reduces the quantity of protection
afforded.  Therefore, we use manual placement using the cf_check attribute.

Signed-off-by: Andrew Cooper <andrew.cooper3@citrix.com>
---
CC: Jan Beulich <JBeulich@suse.com>
CC: Roger Pau Monné <roger.pau@citrix.com>
CC: Wei Liu <wl@xen.org>

Clang/LLVM support for -mmanual-endbr is in progress:
  https://reviews.llvm.org/D118355

v2:
 * Correct CONFIG_HAS_CC_CET_IBT to CONFIG_XEN_IBT in some places
 * Move cf_check compatibility into tools/tests/x86_emulator/x86-emulate.h
---
 Config.mk                                   |  1 -
 tools/firmware/Makefile                     |  2 ++
 tools/libs/guest/xg_dom_decompress_unsafe.h |  2 ++
 tools/tests/x86_emulator/x86-emulate.h      |  2 ++
 xen/arch/x86/Kconfig                        | 17 +++++++++++++++++
 xen/arch/x86/arch.mk                        |  6 ++++++
 xen/arch/x86/include/asm/asm-defns.h        |  6 ++++++
 xen/arch/x86/include/asm/cpufeature.h       |  1 +
 xen/arch/x86/include/asm/cpufeatures.h      |  1 +
 xen/include/xen/compiler.h                  |  6 ++++++
 10 files changed, 43 insertions(+), 1 deletion(-)

Comments

Jan Beulich Feb. 15, 2022, 2:01 p.m. UTC | #1
On 14.02.2022 13:50, Andrew Cooper wrote:
> --- a/xen/arch/x86/Kconfig
> +++ b/xen/arch/x86/Kconfig
> @@ -39,6 +39,11 @@ config HAS_AS_CET_SS
>  	# binutils >= 2.29 or LLVM >= 6
>  	def_bool $(as-instr,wrssq %rax$(comma)0;setssbsy)
>  
> +config HAS_CC_CET_IBT
> +	# GCC >= 9 and binutils >= 2.29
> +	# Retpoline check to work around https://gcc.gnu.org/bugzilla/show_bug.cgi?id=93654
> +	def_bool $(cc-option,-fcf-protection=branch -mmanual-endbr -mindirect-branch=thunk-extern) && $(as-instr,endbr64)

At the top of asm-defns.h we have a number of similarly operand-less
instructions expressed via .macro expanding to .byte. I don't see why
we couldn't do so here as well, eliminating the need for the
$(as-instr ...). In fact ...

> --- a/xen/arch/x86/include/asm/asm-defns.h
> +++ b/xen/arch/x86/include/asm/asm-defns.h
> @@ -57,6 +57,12 @@
>      INDIRECT_BRANCH jmp \arg
>  .endm
>  
> +#ifdef CONFIG_XEN_IBT
> +# define ENDBR64 endbr64
> +#else
> +# define ENDBR64
> +#endif

... it could also be this macro which ends up conditionally empty,
but would then want expressing as an assembler macro. Albeit no, the
lower case form would probably still be needed to deal with compiler
emitted insns, as the compiler doesn't appear to make recognition of
the command line option dependent on the underlying assembler's
capabilities.

> --- a/xen/arch/x86/include/asm/cpufeatures.h
> +++ b/xen/arch/x86/include/asm/cpufeatures.h
> @@ -39,6 +39,7 @@ XEN_CPUFEATURE(SC_VERW_PV,        X86_SYNTH(23)) /* VERW used by Xen for PV */
>  XEN_CPUFEATURE(SC_VERW_HVM,       X86_SYNTH(24)) /* VERW used by Xen for HVM */
>  XEN_CPUFEATURE(SC_VERW_IDLE,      X86_SYNTH(25)) /* VERW used by Xen for idle */
>  XEN_CPUFEATURE(XEN_SHSTK,         X86_SYNTH(26)) /* Xen uses CET Shadow Stacks */
> +XEN_CPUFEATURE(XEN_IBT,           X86_SYNTH(27)) /* Xen uses CET Indirect Branch Tracking */

Is a feature flag actually warranted here, rather than a single
global boolean? You don't key any alternatives patching to this
bit, unlike was the case for XEN_SHSTK. And the only consumer is
cpu_has_xen_ibt, expanding to the boot CPU's instance of the bit.

Jan
Andrew Cooper Feb. 16, 2022, 9:54 p.m. UTC | #2
On 15/02/2022 14:01, Jan Beulich wrote:
> On 14.02.2022 13:50, Andrew Cooper wrote:
>> --- a/xen/arch/x86/Kconfig
>> +++ b/xen/arch/x86/Kconfig
>> @@ -39,6 +39,11 @@ config HAS_AS_CET_SS
>>  	# binutils >= 2.29 or LLVM >= 6
>>  	def_bool $(as-instr,wrssq %rax$(comma)0;setssbsy)
>>  
>> +config HAS_CC_CET_IBT
>> +	# GCC >= 9 and binutils >= 2.29
>> +	# Retpoline check to work around https://gcc.gnu.org/bugzilla/show_bug.cgi?id=93654
>> +	def_bool $(cc-option,-fcf-protection=branch -mmanual-endbr -mindirect-branch=thunk-extern) && $(as-instr,endbr64)
> At the top of asm-defns.h we have a number of similarly operand-less
> instructions expressed via .macro expanding to .byte. I don't see why
> we couldn't do so here as well, eliminating the need for the
> $(as-instr ...). In fact ...
>
>> --- a/xen/arch/x86/include/asm/asm-defns.h
>> +++ b/xen/arch/x86/include/asm/asm-defns.h
>> @@ -57,6 +57,12 @@
>>      INDIRECT_BRANCH jmp \arg
>>  .endm
>>  
>> +#ifdef CONFIG_XEN_IBT
>> +# define ENDBR64 endbr64
>> +#else
>> +# define ENDBR64
>> +#endif
> ... it could also be this macro which ends up conditionally empty,
> but would then want expressing as an assembler macro. Albeit no, the
> lower case form would probably still be needed to deal with compiler
> emitted insns, as the compiler doesn't appear to make recognition of
> the command line option dependent on the underlying assembler's
> capabilities.

$(as-instr) isn't only for endbr64.  It also for the notrack prefix,
which GCC does emit for any function pointer call laundered through void
* even when everything was otherwise cf_check.

It's another area where treating the cf_check-ness as type-checking
falls down, and created some very weird build failures until I figured
out that Juergen's "Don't use the hypercall table for calling compat
hypercalls" really did need to be a prerequisite.

CET-IBT toolchain support is 3 years old already, and I don't think
there is any value attempting to support a developer mixing a new GCC
and ancient binutils.


>> --- a/xen/arch/x86/include/asm/cpufeatures.h
>> +++ b/xen/arch/x86/include/asm/cpufeatures.h
>> @@ -39,6 +39,7 @@ XEN_CPUFEATURE(SC_VERW_PV,        X86_SYNTH(23)) /* VERW used by Xen for PV */
>>  XEN_CPUFEATURE(SC_VERW_HVM,       X86_SYNTH(24)) /* VERW used by Xen for HVM */
>>  XEN_CPUFEATURE(SC_VERW_IDLE,      X86_SYNTH(25)) /* VERW used by Xen for idle */
>>  XEN_CPUFEATURE(XEN_SHSTK,         X86_SYNTH(26)) /* Xen uses CET Shadow Stacks */
>> +XEN_CPUFEATURE(XEN_IBT,           X86_SYNTH(27)) /* Xen uses CET Indirect Branch Tracking */
> Is a feature flag actually warranted here, rather than a single
> global boolean? You don't key any alternatives patching to this
> bit, unlike was the case for XEN_SHSTK. And the only consumer is
> cpu_has_xen_ibt, expanding to the boot CPU's instance of the bit.

These are just bits.  They long predate alternatives finding a
convenient use for the form, and are 8 times more compact than a global
boolean, with better locality of reference too.

~Andrew
Jan Beulich Feb. 17, 2022, 11:32 a.m. UTC | #3
On 16.02.2022 22:54, Andrew Cooper wrote:
> On 15/02/2022 14:01, Jan Beulich wrote:
>> On 14.02.2022 13:50, Andrew Cooper wrote:
>>> --- a/xen/arch/x86/Kconfig
>>> +++ b/xen/arch/x86/Kconfig
>>> @@ -39,6 +39,11 @@ config HAS_AS_CET_SS
>>>  	# binutils >= 2.29 or LLVM >= 6
>>>  	def_bool $(as-instr,wrssq %rax$(comma)0;setssbsy)
>>>  
>>> +config HAS_CC_CET_IBT
>>> +	# GCC >= 9 and binutils >= 2.29
>>> +	# Retpoline check to work around https://gcc.gnu.org/bugzilla/show_bug.cgi?id=93654
>>> +	def_bool $(cc-option,-fcf-protection=branch -mmanual-endbr -mindirect-branch=thunk-extern) && $(as-instr,endbr64)
>> At the top of asm-defns.h we have a number of similarly operand-less
>> instructions expressed via .macro expanding to .byte. I don't see why
>> we couldn't do so here as well, eliminating the need for the
>> $(as-instr ...). In fact ...
>>
>>> --- a/xen/arch/x86/include/asm/asm-defns.h
>>> +++ b/xen/arch/x86/include/asm/asm-defns.h
>>> @@ -57,6 +57,12 @@
>>>      INDIRECT_BRANCH jmp \arg
>>>  .endm
>>>  
>>> +#ifdef CONFIG_XEN_IBT
>>> +# define ENDBR64 endbr64
>>> +#else
>>> +# define ENDBR64
>>> +#endif
>> ... it could also be this macro which ends up conditionally empty,
>> but would then want expressing as an assembler macro. Albeit no, the
>> lower case form would probably still be needed to deal with compiler
>> emitted insns, as the compiler doesn't appear to make recognition of
>> the command line option dependent on the underlying assembler's
>> capabilities.
> 
> $(as-instr) isn't only for endbr64.  It also for the notrack prefix,
> which GCC does emit for any function pointer call laundered through void
> * even when everything was otherwise cf_check.
> 
> It's another area where treating the cf_check-ness as type-checking
> falls down, and created some very weird build failures until I figured
> out that Juergen's "Don't use the hypercall table for calling compat
> hypercalls" really did need to be a prerequisite.

Oh, I see. I can certainly accept this as a reason, but half a sentence
mentioning this would be nice in the description.

>>> --- a/xen/arch/x86/include/asm/cpufeatures.h
>>> +++ b/xen/arch/x86/include/asm/cpufeatures.h
>>> @@ -39,6 +39,7 @@ XEN_CPUFEATURE(SC_VERW_PV,        X86_SYNTH(23)) /* VERW used by Xen for PV */
>>>  XEN_CPUFEATURE(SC_VERW_HVM,       X86_SYNTH(24)) /* VERW used by Xen for HVM */
>>>  XEN_CPUFEATURE(SC_VERW_IDLE,      X86_SYNTH(25)) /* VERW used by Xen for idle */
>>>  XEN_CPUFEATURE(XEN_SHSTK,         X86_SYNTH(26)) /* Xen uses CET Shadow Stacks */
>>> +XEN_CPUFEATURE(XEN_IBT,           X86_SYNTH(27)) /* Xen uses CET Indirect Branch Tracking */
>> Is a feature flag actually warranted here, rather than a single
>> global boolean? You don't key any alternatives patching to this
>> bit, unlike was the case for XEN_SHSTK. And the only consumer is
>> cpu_has_xen_ibt, expanding to the boot CPU's instance of the bit.
> 
> These are just bits.  They long predate alternatives finding a
> convenient use for the form, and are 8 times more compact than a global
> boolean, with better locality of reference too.

Well, I disagree (and we were here before, so I think you could have
predicted such a comment coming back). We should never have cloned this
directly from Linux. It's only bits, but with enough CPUs it sums up.
We shouldn't duplicate data when we need only a single instance (and
when no other infrastructure, like alternative patching, depends on it).

Last time you put me in a situation like this one, I told myself to not
ack such changes anymore, but here I am again - in the interest of not
being blamed for blocking this series:
Acked-by: Jan Beulich <jbeulich@suse.com>

Jan
diff mbox series

Patch

diff --git a/Config.mk b/Config.mk
index 95c053212ec3..f56f7dc33468 100644
--- a/Config.mk
+++ b/Config.mk
@@ -190,7 +190,6 @@  APPEND_CFLAGS += $(foreach i, $(APPEND_INCLUDES), -I$(i))
 
 EMBEDDED_EXTRA_CFLAGS := -nopie -fno-stack-protector -fno-stack-protector-all
 EMBEDDED_EXTRA_CFLAGS += -fno-exceptions -fno-asynchronous-unwind-tables
-EMBEDDED_EXTRA_CFLAGS += -fcf-protection=none
 
 XEN_EXTFILES_URL ?= http://xenbits.xen.org/xen-extfiles
 # All the files at that location were downloaded from elsewhere on
diff --git a/tools/firmware/Makefile b/tools/firmware/Makefile
index 345037b93b7f..53ed4f161edb 100644
--- a/tools/firmware/Makefile
+++ b/tools/firmware/Makefile
@@ -6,6 +6,8 @@  TARGET      := hvmloader/hvmloader
 INST_DIR := $(DESTDIR)$(XENFIRMWAREDIR)
 DEBG_DIR := $(DESTDIR)$(DEBUG_DIR)$(XENFIRMWAREDIR)
 
+EMBEDDED_EXTRA_CFLAGS += -fcf-protection=none
+
 SUBDIRS-y :=
 SUBDIRS-$(CONFIG_OVMF) += ovmf-dir
 SUBDIRS-$(CONFIG_SEABIOS) += seabios-dir
diff --git a/tools/libs/guest/xg_dom_decompress_unsafe.h b/tools/libs/guest/xg_dom_decompress_unsafe.h
index 4e0bf23aa587..3bce0cfefb88 100644
--- a/tools/libs/guest/xg_dom_decompress_unsafe.h
+++ b/tools/libs/guest/xg_dom_decompress_unsafe.h
@@ -8,6 +8,8 @@  typedef int decompress_fn(unsigned char *inbuf, unsigned int len,
                           void (*error)(const char *x));
 #endif
 
+#define cf_check
+
 int xc_dom_decompress_unsafe(
     decompress_fn fn, struct xc_dom_image *dom, void **blob, size_t *size)
     __attribute__((visibility("internal")));
diff --git a/tools/tests/x86_emulator/x86-emulate.h b/tools/tests/x86_emulator/x86-emulate.h
index 7f60ef9e89ba..c6819a417d05 100644
--- a/tools/tests/x86_emulator/x86-emulate.h
+++ b/tools/tests/x86_emulator/x86-emulate.h
@@ -54,6 +54,8 @@ 
 #define likely(x)   __builtin_expect(!!(x), true)
 #define unlikely(x) __builtin_expect(!!(x), false)
 
+#define cf_check
+
 #define container_of(ptr, type, member) ({             \
     typeof(((type *)0)->member) *mptr__ = (ptr);       \
     (type *)((char *)mptr__ - offsetof(type, member)); \
diff --git a/xen/arch/x86/Kconfig b/xen/arch/x86/Kconfig
index b4abfca46f6a..8b7ad0145b29 100644
--- a/xen/arch/x86/Kconfig
+++ b/xen/arch/x86/Kconfig
@@ -39,6 +39,11 @@  config HAS_AS_CET_SS
 	# binutils >= 2.29 or LLVM >= 6
 	def_bool $(as-instr,wrssq %rax$(comma)0;setssbsy)
 
+config HAS_CC_CET_IBT
+	# GCC >= 9 and binutils >= 2.29
+	# Retpoline check to work around https://gcc.gnu.org/bugzilla/show_bug.cgi?id=93654
+	def_bool $(cc-option,-fcf-protection=branch -mmanual-endbr -mindirect-branch=thunk-extern) && $(as-instr,endbr64)
+
 menu "Architecture Features"
 
 source "arch/Kconfig"
@@ -124,6 +129,18 @@  config XEN_SHSTK
 	  When CET-SS is active, 32bit PV guests cannot be used.  Backwards
 	  compatiblity can be provided via the PV Shim mechanism.
 
+config XEN_IBT
+	bool "Supervisor Indirect Branch Tracking"
+	depends on HAS_CC_CET_IBT
+	default y
+	help
+	  Control-flow Enforcement Technology (CET) is a set of features in
+	  hardware designed to combat Return-oriented Programming (ROP, also
+	  call/jump COP/JOP) attacks.  Indirect Branch Tracking is one CET
+	  feature designed to provide function pointer protection.
+
+	  This option arranges for Xen to use CET-IBT for its own protection.
+
 config SHADOW_PAGING
 	bool "Shadow Paging"
 	default !PV_SHIM_EXCLUSIVE
diff --git a/xen/arch/x86/arch.mk b/xen/arch/x86/arch.mk
index fa7cf3844362..8b88f0240e85 100644
--- a/xen/arch/x86/arch.mk
+++ b/xen/arch/x86/arch.mk
@@ -47,6 +47,12 @@  CFLAGS-$(CONFIG_INDIRECT_THUNK) += -mindirect-branch=thunk-extern
 CFLAGS-$(CONFIG_INDIRECT_THUNK) += -mindirect-branch-register
 CFLAGS-$(CONFIG_INDIRECT_THUNK) += -fno-jump-tables
 
+ifdef CONFIG_XEN_IBT
+CFLAGS += -fcf-protection=branch -mmanual-endbr
+else
+$(call cc-option-add,CFLAGS,CC,-fcf-protection=none)
+endif
+
 # If supported by the compiler, reduce stack alignment to 8 bytes. But allow
 # this to be overridden elsewhere.
 $(call cc-option-add,CFLAGS_stack_boundary,CC,-mpreferred-stack-boundary=3)
diff --git a/xen/arch/x86/include/asm/asm-defns.h b/xen/arch/x86/include/asm/asm-defns.h
index 505f39ad5f76..8bd9007731d5 100644
--- a/xen/arch/x86/include/asm/asm-defns.h
+++ b/xen/arch/x86/include/asm/asm-defns.h
@@ -57,6 +57,12 @@ 
     INDIRECT_BRANCH jmp \arg
 .endm
 
+#ifdef CONFIG_XEN_IBT
+# define ENDBR64 endbr64
+#else
+# define ENDBR64
+#endif
+
 .macro guest_access_mask_ptr ptr:req, scratch1:req, scratch2:req
 #if defined(CONFIG_SPECULATIVE_HARDEN_GUEST_ACCESS)
     /*
diff --git a/xen/arch/x86/include/asm/cpufeature.h b/xen/arch/x86/include/asm/cpufeature.h
index a0ab6d7d78ea..f2c6f255ace9 100644
--- a/xen/arch/x86/include/asm/cpufeature.h
+++ b/xen/arch/x86/include/asm/cpufeature.h
@@ -152,6 +152,7 @@ 
 #define cpu_has_nscb            boot_cpu_has(X86_FEATURE_NSCB)
 #define cpu_has_xen_lbr         boot_cpu_has(X86_FEATURE_XEN_LBR)
 #define cpu_has_xen_shstk       boot_cpu_has(X86_FEATURE_XEN_SHSTK)
+#define cpu_has_xen_ibt         boot_cpu_has(X86_FEATURE_XEN_IBT)
 
 #define cpu_has_msr_tsc_aux     (cpu_has_rdtscp || cpu_has_rdpid)
 
diff --git a/xen/arch/x86/include/asm/cpufeatures.h b/xen/arch/x86/include/asm/cpufeatures.h
index b10154fc44bb..7413febd7ad8 100644
--- a/xen/arch/x86/include/asm/cpufeatures.h
+++ b/xen/arch/x86/include/asm/cpufeatures.h
@@ -39,6 +39,7 @@  XEN_CPUFEATURE(SC_VERW_PV,        X86_SYNTH(23)) /* VERW used by Xen for PV */
 XEN_CPUFEATURE(SC_VERW_HVM,       X86_SYNTH(24)) /* VERW used by Xen for HVM */
 XEN_CPUFEATURE(SC_VERW_IDLE,      X86_SYNTH(25)) /* VERW used by Xen for idle */
 XEN_CPUFEATURE(XEN_SHSTK,         X86_SYNTH(26)) /* Xen uses CET Shadow Stacks */
+XEN_CPUFEATURE(XEN_IBT,           X86_SYNTH(27)) /* Xen uses CET Indirect Branch Tracking */
 
 /* Bug words follow the synthetic words. */
 #define X86_NR_BUG 1
diff --git a/xen/include/xen/compiler.h b/xen/include/xen/compiler.h
index 696c7eb89e4c..933aec09a92d 100644
--- a/xen/include/xen/compiler.h
+++ b/xen/include/xen/compiler.h
@@ -37,6 +37,12 @@ 
 # define nocall
 #endif
 
+#ifdef CONFIG_XEN_IBT
+# define cf_check     __attribute__((__cf_check__))
+#else
+# define cf_check
+#endif
+
 #if (!defined(__clang__) && (__GNUC__ == 4) && (__GNUC_MINOR__ < 5))
 #define unreachable() do {} while (1)
 #else