x86/ibt: Handle FineIBT in handle_cfi_failure()

[ Upstream commit 882b86fd4e0d49bf91148dbadcdbece19ded40e6 ]

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: Sami Tolvanen <samitolvanen@google.com>
Signed-off-by: Peter Zijlstra (Intel) <peterz@infradead.org>
Reviewed-by: Sami Tolvanen <samitolvanen@google.com>
Link: https://lkml.kernel.org/r/20250214092619.GB21726@noisy.programming.kicks-ass.net
Signed-off-by: Sasha Levin <sashal@kernel.org>
This commit is contained in:
Peter Zijlstra
2025-02-13 12:45:47 +01:00
committed by Greg Kroah-Hartman
parent b870651021
commit eb6fd16b4f
3 changed files with 59 additions and 4 deletions

View File

@@ -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)
{

View File

@@ -1254,6 +1254,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"
@@ -1261,9 +1262,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"
@@ -1568,6 +1571,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,

View File

@@ -70,11 +70,25 @@ enum bug_trap_type handle_cfi_failure(struct pt_regs *regs)
unsigned long target;
u32 type;
if (!is_cfi_trap(regs->ip))
return BUG_TRAP_TYPE_NONE;
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);
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);
}