Commit ed53a0d9 authored by Peter Zijlstra's avatar Peter Zijlstra
Browse files

x86/alternative: Use .ibt_endbr_seal to seal indirect calls



Objtool's --ibt option generates .ibt_endbr_seal which lists
superfluous ENDBR instructions. That is those instructions for which
the function is never indirectly called.

Overwrite these ENDBR instructions with a NOP4 such that these
function can never be indirect called, reducing the number of viable
ENDBR targets in the kernel.

Signed-off-by: default avatarPeter Zijlstra (Intel) <peterz@infradead.org>
Acked-by: default avatarJosh Poimboeuf <jpoimboe@redhat.com>
Link: https://lore.kernel.org/r/20220308154319.822545231@infradead.org
parent 89bc853e
Loading
Loading
Loading
Loading
+4 −0
Original line number Diff line number Diff line
@@ -424,6 +424,10 @@ void __init check_bugs(void)
	os_check_bugs();
}

void apply_ibt_endbr(s32 *start, s32 *end)
{
}

void apply_retpolines(s32 *start, s32 *end)
{
}
+8 −1
Original line number Diff line number Diff line
@@ -1873,7 +1873,7 @@ config CC_HAS_IBT
config X86_KERNEL_IBT
	prompt "Indirect Branch Tracking"
	bool
	depends on X86_64 && CC_HAS_IBT
	depends on X86_64 && CC_HAS_IBT && STACK_VALIDATION
	help
	  Build the kernel with support for Indirect Branch Tracking, a
	  hardware support course-grain forward-edge Control Flow Integrity
@@ -1881,6 +1881,13 @@ config X86_KERNEL_IBT
	  an ENDBR instruction, as such, the compiler will instrument the
	  code with them to make this happen.

	  In addition to building the kernel with IBT, seal all functions that
	  are not indirect call targets, avoiding them ever becomming one.

	  This requires LTO like objtool runs and will slow down the build. It
	  does significantly reduce the number of ENDBR instructions in the
	  kernel image.

config X86_INTEL_MEMORY_PROTECTION_KEYS
	prompt "Memory Protection Keys"
	def_bool y
+1 −0
Original line number Diff line number Diff line
@@ -76,6 +76,7 @@ extern int alternatives_patched;
extern void alternative_instructions(void);
extern void apply_alternatives(struct alt_instr *start, struct alt_instr *end);
extern void apply_retpolines(s32 *start, s32 *end);
extern void apply_ibt_endbr(s32 *start, s32 *end);

struct module;

+12 −0
Original line number Diff line number Diff line
@@ -46,8 +46,20 @@ static inline __attribute_const__ u32 gen_endbr(void)
	return endbr;
}

static inline __attribute_const__ u32 gen_endbr_poison(void)
{
	/*
	 * 4 byte NOP that isn't NOP4 (in fact it is OSP NOP3), such that it
	 * will be unique to (former) ENDBR sites.
	 */
	return 0x001f0f66; /* osp nopl (%rax) */
}

static inline bool is_endbr(u32 val)
{
	if (val == gen_endbr_poison())
		return true;

	val &= ~0x01000000U; /* ENDBR32 -> ENDBR64 */
	return val == gen_endbr();
}
+39 −0
Original line number Diff line number Diff line
@@ -115,6 +115,7 @@ static void __init_or_module add_nops(void *insns, unsigned int len)
}

extern s32 __retpoline_sites[], __retpoline_sites_end[];
extern s32 __ibt_endbr_seal[], __ibt_endbr_seal_end[];
extern struct alt_instr __alt_instructions[], __alt_instructions_end[];
extern s32 __smp_locks[], __smp_locks_end[];
void text_poke_early(void *addr, const void *opcode, size_t len);
@@ -512,6 +513,42 @@ void __init_or_module noinline apply_retpolines(s32 *start, s32 *end) { }

#endif /* CONFIG_RETPOLINE && CONFIG_STACK_VALIDATION */

#ifdef CONFIG_X86_KERNEL_IBT

/*
 * Generated by: objtool --ibt
 */
void __init_or_module noinline apply_ibt_endbr(s32 *start, s32 *end)
{
	s32 *s;

	for (s = start; s < end; s++) {
		u32 endbr, poison = gen_endbr_poison();
		void *addr = (void *)s + *s;

		if (WARN_ON_ONCE(get_kernel_nofault(endbr, addr)))
			continue;

		if (WARN_ON_ONCE(!is_endbr(endbr)))
			continue;

		DPRINTK("ENDBR at: %pS (%px)", addr, addr);

		/*
		 * When we have IBT, the lack of ENDBR will trigger #CP
		 */
		DUMP_BYTES(((u8*)addr), 4, "%px: orig: ", addr);
		DUMP_BYTES(((u8*)&poison), 4, "%px: repl: ", addr);
		text_poke_early(addr, &poison, 4);
	}
}

#else

void __init_or_module noinline apply_ibt_endbr(s32 *start, s32 *end) { }

#endif /* CONFIG_X86_KERNEL_IBT */

#ifdef CONFIG_SMP
static void alternatives_smp_lock(const s32 *start, const s32 *end,
				  u8 *text, u8 *text_end)
@@ -830,6 +867,8 @@ void __init alternative_instructions(void)
	 */
	apply_alternatives(__alt_instructions, __alt_instructions_end);

	apply_ibt_endbr(__ibt_endbr_seal, __ibt_endbr_seal_end);

#ifdef CONFIG_SMP
	/* Patch to UP if other cpus not imminent. */
	if (!noreplace_smp && (num_present_cpus() == 1 || setup_max_cpus <= 1)) {
Loading