Linux kernel mirror (for testing) git.kernel.org/pub/scm/linux/kernel/git/torvalds/linux.git
kernel os linux

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: 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

+59 -4
+11
arch/x86/include/asm/cfi.h
··· 126 126 127 127 extern u32 cfi_get_func_hash(void *func); 128 128 129 + #ifdef CONFIG_FINEIBT 130 + extern bool decode_fineibt_insn(struct pt_regs *regs, unsigned long *target, u32 *type); 131 + #else 132 + static inline bool 133 + decode_fineibt_insn(struct pt_regs *regs, unsigned long *target, u32 *type) 134 + { 135 + return false; 136 + } 137 + 138 + #endif 139 + 129 140 #else 130 141 static inline enum bug_trap_type handle_cfi_failure(struct pt_regs *regs) 131 142 {
+30
arch/x86/kernel/alternative.c
··· 1065 1065 " endbr64 \n" 1066 1066 " subl $0x12345678, %r10d \n" 1067 1067 " je fineibt_preamble_end \n" 1068 + "fineibt_preamble_ud2: \n" 1068 1069 " ud2 \n" 1069 1070 " nop \n" 1070 1071 "fineibt_preamble_end: \n" ··· 1073 1072 ); 1074 1073 1075 1074 extern u8 fineibt_preamble_start[]; 1075 + extern u8 fineibt_preamble_ud2[]; 1076 1076 extern u8 fineibt_preamble_end[]; 1077 1077 1078 1078 #define fineibt_preamble_size (fineibt_preamble_end - fineibt_preamble_start) 1079 + #define fineibt_preamble_ud2 (fineibt_preamble_ud2 - fineibt_preamble_start) 1079 1080 #define fineibt_preamble_hash 7 1080 1081 1081 1082 asm( ".pushsection .rodata \n" ··· 1411 1408 default: 1412 1409 break; 1413 1410 } 1411 + } 1412 + 1413 + /* 1414 + * regs->ip points to a UD2 instruction, return true and fill out target and 1415 + * type when this UD2 is from a FineIBT preamble. 1416 + * 1417 + * We check the preamble by checking for the ENDBR instruction relative to the 1418 + * UD2 instruction. 1419 + */ 1420 + bool decode_fineibt_insn(struct pt_regs *regs, unsigned long *target, u32 *type) 1421 + { 1422 + unsigned long addr = regs->ip - fineibt_preamble_ud2; 1423 + u32 endbr, hash; 1424 + 1425 + __get_kernel_nofault(&endbr, addr, u32, Efault); 1426 + if (endbr != gen_endbr()) 1427 + return false; 1428 + 1429 + *target = addr + fineibt_preamble_size; 1430 + 1431 + __get_kernel_nofault(&hash, addr + fineibt_preamble_hash, u32, Efault); 1432 + *type = (u32)regs->r10 + hash; 1433 + 1434 + return true; 1435 + 1436 + Efault: 1437 + return false; 1414 1438 } 1415 1439 1416 1440 #else
+18 -4
arch/x86/kernel/cfi.c
··· 70 70 unsigned long target; 71 71 u32 type; 72 72 73 - if (!is_cfi_trap(regs->ip)) 74 - return BUG_TRAP_TYPE_NONE; 73 + switch (cfi_mode) { 74 + case CFI_KCFI: 75 + if (!is_cfi_trap(regs->ip)) 76 + return BUG_TRAP_TYPE_NONE; 75 77 76 - if (!decode_cfi_insn(regs, &target, &type)) 77 - return report_cfi_failure_noaddr(regs, regs->ip); 78 + if (!decode_cfi_insn(regs, &target, &type)) 79 + return report_cfi_failure_noaddr(regs, regs->ip); 80 + 81 + break; 82 + 83 + case CFI_FINEIBT: 84 + if (!decode_fineibt_insn(regs, &target, &type)) 85 + return BUG_TRAP_TYPE_NONE; 86 + 87 + break; 88 + 89 + default: 90 + return BUG_TRAP_TYPE_NONE; 91 + } 78 92 79 93 return report_cfi_failure(regs, regs->ip, &target, type); 80 94 }