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);
|
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
|
#else
|
||||||
static inline enum bug_trap_type handle_cfi_failure(struct pt_regs *regs)
|
static inline enum bug_trap_type handle_cfi_failure(struct pt_regs *regs)
|
||||||
{
|
{
|
||||||
|
|||||||
@@ -1254,6 +1254,7 @@ asm( ".pushsection .rodata \n"
|
|||||||
" endbr64 \n"
|
" endbr64 \n"
|
||||||
" subl $0x12345678, %r10d \n"
|
" subl $0x12345678, %r10d \n"
|
||||||
" je fineibt_preamble_end \n"
|
" je fineibt_preamble_end \n"
|
||||||
|
"fineibt_preamble_ud2: \n"
|
||||||
" ud2 \n"
|
" ud2 \n"
|
||||||
" nop \n"
|
" nop \n"
|
||||||
"fineibt_preamble_end: \n"
|
"fineibt_preamble_end: \n"
|
||||||
@@ -1261,9 +1262,11 @@ asm( ".pushsection .rodata \n"
|
|||||||
);
|
);
|
||||||
|
|
||||||
extern u8 fineibt_preamble_start[];
|
extern u8 fineibt_preamble_start[];
|
||||||
|
extern u8 fineibt_preamble_ud2[];
|
||||||
extern u8 fineibt_preamble_end[];
|
extern u8 fineibt_preamble_end[];
|
||||||
|
|
||||||
#define fineibt_preamble_size (fineibt_preamble_end - fineibt_preamble_start)
|
#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
|
#define fineibt_preamble_hash 7
|
||||||
|
|
||||||
asm( ".pushsection .rodata \n"
|
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
|
#else
|
||||||
|
|
||||||
static void __apply_fineibt(s32 *start_retpoline, s32 *end_retpoline,
|
static void __apply_fineibt(s32 *start_retpoline, s32 *end_retpoline,
|
||||||
|
|||||||
+18
-4
@@ -70,11 +70,25 @@ enum bug_trap_type handle_cfi_failure(struct pt_regs *regs)
|
|||||||
unsigned long target;
|
unsigned long target;
|
||||||
u32 type;
|
u32 type;
|
||||||
|
|
||||||
if (!is_cfi_trap(regs->ip))
|
switch (cfi_mode) {
|
||||||
return BUG_TRAP_TYPE_NONE;
|
case CFI_KCFI:
|
||||||
|
if (!is_cfi_trap(regs->ip))
|
||||||
|
return BUG_TRAP_TYPE_NONE;
|
||||||
|
|
||||||
if (!decode_cfi_insn(regs, &target, &type))
|
if (!decode_cfi_insn(regs, &target, &type))
|
||||||
return report_cfi_failure_noaddr(regs, regs->ip);
|
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);
|
return report_cfi_failure(regs, regs->ip, &target, type);
|
||||||
}
|
}
|
||||||
|
|||||||
Reference in New Issue
Block a user