[RFC PATCH 09/21] arm64: Add CFI error handling

Sami Tolvanen samitolvanen at google.com
Fri Apr 29 13:36:32 PDT 2022


With -fsanitize=kcfi, CFI always traps. Add arm64 support for handling
CFI failures and determining the target address.

Signed-off-by: Sami Tolvanen <samitolvanen at google.com>
---
 arch/arm64/include/asm/brk-imm.h |  2 ++
 arch/arm64/include/asm/insn.h    |  1 +
 arch/arm64/kernel/traps.c        | 57 ++++++++++++++++++++++++++++++++
 3 files changed, 60 insertions(+)

diff --git a/arch/arm64/include/asm/brk-imm.h b/arch/arm64/include/asm/brk-imm.h
index ec7720dbe2c8..3a50b70b4404 100644
--- a/arch/arm64/include/asm/brk-imm.h
+++ b/arch/arm64/include/asm/brk-imm.h
@@ -16,6 +16,7 @@
  * 0x400: for dynamic BRK instruction
  * 0x401: for compile time BRK instruction
  * 0x800: kernel-mode BUG() and WARN() traps
+ * 0x801: Control-Flow Integrity traps
  * 0x9xx: tag-based KASAN trap (allowed values 0x900 - 0x9ff)
  */
 #define KPROBES_BRK_IMM			0x004
@@ -25,6 +26,7 @@
 #define KGDB_DYN_DBG_BRK_IMM		0x400
 #define KGDB_COMPILED_DBG_BRK_IMM	0x401
 #define BUG_BRK_IMM			0x800
+#define CFI_BRK_IMM			0x801
 #define KASAN_BRK_IMM			0x900
 #define KASAN_BRK_MASK			0x0ff
 
diff --git a/arch/arm64/include/asm/insn.h b/arch/arm64/include/asm/insn.h
index 1e5760d567ae..12225bdfa776 100644
--- a/arch/arm64/include/asm/insn.h
+++ b/arch/arm64/include/asm/insn.h
@@ -334,6 +334,7 @@ __AARCH64_INSN_FUNCS(store_pre,	0x3FE00C00, 0x38000C00)
 __AARCH64_INSN_FUNCS(load_pre,	0x3FE00C00, 0x38400C00)
 __AARCH64_INSN_FUNCS(store_post,	0x3FE00C00, 0x38000400)
 __AARCH64_INSN_FUNCS(load_post,	0x3FE00C00, 0x38400400)
+__AARCH64_INSN_FUNCS(ldur,	0x3FE00C00, 0x38400000)
 __AARCH64_INSN_FUNCS(str_reg,	0x3FE0EC00, 0x38206800)
 __AARCH64_INSN_FUNCS(ldadd,	0x3F20FC00, 0x38200000)
 __AARCH64_INSN_FUNCS(ldclr,	0x3F20FC00, 0x38201000)
diff --git a/arch/arm64/kernel/traps.c b/arch/arm64/kernel/traps.c
index 0529fd57567e..b524411ba663 100644
--- a/arch/arm64/kernel/traps.c
+++ b/arch/arm64/kernel/traps.c
@@ -26,6 +26,7 @@
 #include <linux/syscalls.h>
 #include <linux/mm_types.h>
 #include <linux/kasan.h>
+#include <linux/cfi.h>
 
 #include <asm/atomic.h>
 #include <asm/bug.h>
@@ -990,6 +991,55 @@ static struct break_hook bug_break_hook = {
 	.imm = BUG_BRK_IMM,
 };
 
+#ifdef CONFIG_CFI_CLANG
+void *arch_get_cfi_target(unsigned long addr, struct pt_regs *regs)
+{
+	/* The expected CFI check instruction sequence:
+	 *   ldur    wA, [xN, #-4]
+	 *   movk    wB, #nnnnn
+	 *   movk    wB, #nnnnn, lsl #16
+	 *   cmp     wA, wB
+	 *   b.eq    .Ltmp1
+	 *   brk     #0x801		; <- addr
+	 *   .Ltmp1:
+	 *
+	 * Therefore, the target address is in the xN register, which we can
+	 * decode from the ldur instruction.
+	 */
+	u32 insn, rn;
+	void *p = (void *)(addr - 5 * AARCH64_INSN_SIZE);
+
+	if (aarch64_insn_read(p, &insn) || !aarch64_insn_is_ldur(insn))
+		return NULL;
+
+	rn = aarch64_insn_decode_register(AARCH64_INSN_REGTYPE_RN, insn);
+	return (void *)regs->regs[rn];
+}
+
+static int cfi_handler(struct pt_regs *regs, unsigned int esr)
+{
+	switch (report_cfi(regs->pc, regs)) {
+	case BUG_TRAP_TYPE_BUG:
+		die("Oops - CFI", regs, 0);
+		break;
+
+	case BUG_TRAP_TYPE_WARN:
+		break;
+
+	default:
+		return DBG_HOOK_ERROR;
+	}
+
+	arm64_skip_faulting_instruction(regs, AARCH64_INSN_SIZE);
+	return DBG_HOOK_HANDLED;
+}
+
+static struct break_hook cfi_break_hook = {
+	.fn = cfi_handler,
+	.imm = CFI_BRK_IMM,
+};
+#endif /* CONFIG_CFI_CLANG */
+
 static int reserved_fault_handler(struct pt_regs *regs, unsigned int esr)
 {
 	pr_err("%s generated an invalid instruction at %pS!\n",
@@ -1063,6 +1113,10 @@ int __init early_brk64(unsigned long addr, unsigned int esr,
 
 	if ((comment & ~KASAN_BRK_MASK) == KASAN_BRK_IMM)
 		return kasan_handler(regs, esr) != DBG_HOOK_HANDLED;
+#endif
+#ifdef CONFIG_CFI_CLANG
+	if ((esr & ESR_ELx_BRK64_ISS_COMMENT_MASK) == CFI_BRK_IMM)
+		return cfi_handler(regs, esr) != DBG_HOOK_HANDLED;
 #endif
 	return bug_handler(regs, esr) != DBG_HOOK_HANDLED;
 }
@@ -1070,6 +1124,9 @@ int __init early_brk64(unsigned long addr, unsigned int esr,
 void __init trap_init(void)
 {
 	register_kernel_break_hook(&bug_break_hook);
+#ifdef CONFIG_CFI_CLANG
+	register_kernel_break_hook(&cfi_break_hook);
+#endif
 	register_kernel_break_hook(&fault_break_hook);
 #ifdef CONFIG_KASAN_SW_TAGS
 	register_kernel_break_hook(&kasan_break_hook);
-- 
2.36.0.464.gb9c8b46e94-goog




More information about the linux-arm-kernel mailing list