Commit 882b86fd authored by Peter Zijlstra's avatar Peter Zijlstra
Browse files

x86/ibt: Handle FineIBT in handle_cfi_failure()



Sami reminded me that FineIBT failure does not hook into the regular
CFI failure case, and as such CFI_PERMISSIVE does not work.

Reported-by: default avatarSami Tolvanen <samitolvanen@google.com>
Signed-off-by: default avatarPeter Zijlstra (Intel) <peterz@infradead.org>
Reviewed-by: default avatarSami Tolvanen <samitolvanen@google.com>
Link: https://lkml.kernel.org/r/20250214092619.GB21726@noisy.programming.kicks-ass.net
parent 306859de
Loading
Loading
Loading
Loading
+11 −0
Original line number Diff line number Diff line
@@ -126,6 +126,17 @@ static inline int cfi_get_offset(void)

extern u32 cfi_get_func_hash(void *func);

#ifdef CONFIG_FINEIBT
extern bool decode_fineibt_insn(struct pt_regs *regs, unsigned long *target, u32 *type);
#else
static inline bool
decode_fineibt_insn(struct pt_regs *regs, unsigned long *target, u32 *type)
{
	return false;
}

#endif

#else
static inline enum bug_trap_type handle_cfi_failure(struct pt_regs *regs)
{
+30 −0
Original line number Diff line number Diff line
@@ -1065,6 +1065,7 @@ asm( ".pushsection .rodata \n"
	"	endbr64				\n"
	"	subl	$0x12345678, %r10d	\n"
	"	je	fineibt_preamble_end	\n"
	"fineibt_preamble_ud2:			\n"
	"	ud2				\n"
	"	nop				\n"
	"fineibt_preamble_end:			\n"
@@ -1072,9 +1073,11 @@ asm( ".pushsection .rodata \n"
);

extern u8 fineibt_preamble_start[];
extern u8 fineibt_preamble_ud2[];
extern u8 fineibt_preamble_end[];

#define fineibt_preamble_size (fineibt_preamble_end - fineibt_preamble_start)
#define fineibt_preamble_ud2  (fineibt_preamble_ud2 - fineibt_preamble_start)
#define fineibt_preamble_hash 7

asm(	".pushsection .rodata			\n"
@@ -1410,6 +1413,33 @@ static void poison_cfi(void *addr)
	}
}

/*
 * regs->ip points to a UD2 instruction, return true and fill out target and
 * type when this UD2 is from a FineIBT preamble.
 *
 * We check the preamble by checking for the ENDBR instruction relative to the
 * UD2 instruction.
 */
bool decode_fineibt_insn(struct pt_regs *regs, unsigned long *target, u32 *type)
{
	unsigned long addr = regs->ip - fineibt_preamble_ud2;
	u32 endbr, hash;

	__get_kernel_nofault(&endbr, addr, u32, Efault);
	if (endbr != gen_endbr())
		return false;

	*target = addr + fineibt_preamble_size;

	__get_kernel_nofault(&hash, addr + fineibt_preamble_hash, u32, Efault);
	*type = (u32)regs->r10 + hash;

	return true;

Efault:
	return false;
}

#else

static void __apply_fineibt(s32 *start_retpoline, s32 *end_retpoline,
+18 −4
Original line number Diff line number Diff line
@@ -70,12 +70,26 @@ enum bug_trap_type handle_cfi_failure(struct pt_regs *regs)
	unsigned long target;
	u32 type;

	switch (cfi_mode) {
	case CFI_KCFI:
		if (!is_cfi_trap(regs->ip))
			return BUG_TRAP_TYPE_NONE;

		if (!decode_cfi_insn(regs, &target, &type))
			return report_cfi_failure_noaddr(regs, regs->ip);

		break;

	case CFI_FINEIBT:
		if (!decode_fineibt_insn(regs, &target, &type))
			return BUG_TRAP_TYPE_NONE;

		break;

	default:
		return BUG_TRAP_TYPE_NONE;
	}

	return report_cfi_failure(regs, regs->ip, &target, type);
}