]> www.infradead.org Git - users/hch/misc.git/commitdiff
x86/ibt: Handle FineIBT in handle_cfi_failure()
authorPeter Zijlstra <peterz@infradead.org>
Thu, 13 Feb 2025 11:45:47 +0000 (12:45 +0100)
committerPeter Zijlstra <peterz@infradead.org>
Fri, 14 Feb 2025 09:32:07 +0000 (10:32 +0100)
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
arch/x86/include/asm/cfi.h
arch/x86/kernel/alternative.c
arch/x86/kernel/cfi.c

index 31d19c815f992ca6951833851cb60aa250119e46..7dd5ab239c87bdc48292cea07eb356db31bbc7c8 100644 (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)
 {
index e285933506e991bf20a8c8d5dca0a26e92a0a63f..247ee5ffbff4734bdc31fa21f1f3f56fec422e76 100644 (file)
@@ -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,
index e6bf78fac1462209be7f7115dc3f588bdd3a9c12..f6905bef0af844e6716aa74db32f0d29f67c40bc 100644 (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);
+
+               break;
 
-       if (!decode_cfi_insn(regs, &target, &type))
-               return report_cfi_failure_noaddr(regs, regs->ip);
+       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);
 }