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:
committed by
Greg Kroah-Hartman
parent
b870651021
commit
eb6fd16b4f
@@ -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)
|
||||
{
|
||||
|
||||
@@ -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,
|
||||
|
||||
@@ -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);
|
||||
}
|
||||
|
||||
Reference in New Issue
Block a user