With altcall, we convert indirect branches into direct ones. With that complete, none of the potential targets need an endbr64 instruction.
Furthermore, removing the endbr64 instructions is a security defence-in-depth improvement, because it limits the options available to an attacker who has managed to hijack a function pointer. Introduce new .init.{ro,}data.cf_clobber sections. Have _apply_alternatives() walk over this, looking for any pointers into .text, and clobber an endbr64 instruction if found. This is some minor structure (ab)use but it works alarmingly well. Signed-off-by: Andrew Cooper <andrew.coop...@citrix.com> --- CC: Jan Beulich <jbeul...@suse.com> CC: Roger Pau Monné <roger....@citrix.com> CC: Wei Liu <w...@xen.org> It would be nice for the printk() to say "optimised away %u of %u", but the latter number can only feasibly come from post-processing of xen-syms during the build. v2: * Drop hard tabs * Add __initconst_cf_clobber too * Change types to reduce casting --- xen/arch/x86/alternative.c | 38 ++++++++++++++++++++++++++++++++++++++ xen/arch/x86/xen.lds.S | 6 ++++++ xen/include/xen/init.h | 3 +++ 3 files changed, 47 insertions(+) diff --git a/xen/arch/x86/alternative.c b/xen/arch/x86/alternative.c index 65537fe1f0bd..dd4609070001 100644 --- a/xen/arch/x86/alternative.c +++ b/xen/arch/x86/alternative.c @@ -173,6 +173,9 @@ text_poke(void *addr, const void *opcode, size_t len) return memcpy(addr, opcode, len); } +extern void *const __initdata_cf_clobber_start[]; +extern void *const __initdata_cf_clobber_end[]; + /* * Replace instructions with better alternatives for this CPU type. * This runs before SMP is initialized to avoid SMP problems with @@ -330,6 +333,41 @@ static void init_or_livepatch _apply_alternatives(struct alt_instr *start, add_nops(buf + a->repl_len, total_len - a->repl_len); text_poke(orig, buf, total_len); } + + /* + * Clobber endbr64 instructions now that altcall has finished optimising + * all indirect branches to direct ones. + */ + if ( force && cpu_has_xen_ibt ) + { + void *const *val; + unsigned int clobbered = 0; + + /* + * This is some minor structure (ab)use. We walk the entire contents + * of .init.{ro,}data.cf_clobber as if it were an array of pointers. + * + * If the pointer points into .text, and at an endbr64 instruction, + * nop out the endbr64. This causes the pointer to no longer be a + * legal indirect branch target under CET-IBT. This is a + * defence-in-depth measure, to reduce the options available to an + * adversary who has managed to hijack a function pointer. + */ + for ( val = __initdata_cf_clobber_start; + val < __initdata_cf_clobber_end; + val++ ) + { + void *ptr = *val; + + if ( !is_kernel_text(ptr) || !is_endbr64(ptr) ) + continue; + + add_nops(ptr, 4); + clobbered++; + } + + printk("altcall: Optimised away %u endbr64 instructions\n", clobbered); + } } void init_or_livepatch apply_alternatives(struct alt_instr *start, diff --git a/xen/arch/x86/xen.lds.S b/xen/arch/x86/xen.lds.S index ca22e984f807..c399178ac123 100644 --- a/xen/arch/x86/xen.lds.S +++ b/xen/arch/x86/xen.lds.S @@ -221,6 +221,12 @@ SECTIONS *(.initcall1.init) __initcall_end = .; + . = ALIGN(POINTER_ALIGN); + __initdata_cf_clobber_start = .; + *(.init.data.cf_clobber) + *(.init.rodata.cf_clobber) + __initdata_cf_clobber_end = .; + *(.init.data) *(.init.data.rel) *(.init.data.rel.*) diff --git a/xen/include/xen/init.h b/xen/include/xen/init.h index bfe789e93f6b..0af0e234ec80 100644 --- a/xen/include/xen/init.h +++ b/xen/include/xen/init.h @@ -18,6 +18,9 @@ #define __init_call(lvl) __used_section(".initcall" lvl ".init") #define __exit_call __used_section(".exitcall.exit") +#define __initdata_cf_clobber __section(".init.data.cf_clobber") +#define __initconst_cf_clobber __section(".init.rodata.cf_clobber") + /* These macros are used to mark some functions or * initialized data (doesn't apply to uninitialized data) * as `initialization' functions. The kernel can take this -- 2.11.0