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

arm64: Add CFI error handling

With -fsanitize=kcfi, CFI always traps. Add arm64 support for handling CFI
failures. The registers containing the target address and the expected type
are encoded in the first ten bits of the ESR as follows:

- 0-4: n, where the register Xn contains the target address
- 5-9: m, where the register Wm contains the type hash

This produces the following oops on CFI failure (generated using lkdtm):

[ 21.885179] CFI failure at lkdtm_indirect_call+0x2c/0x44 [lkdtm]
(target: lkdtm_increment_int+0x0/0x1c [lkdtm]; expected type: 0x7e0c52a)
[ 21.886593] Internal error: Oops - CFI: 0 [#1] PREEMPT SMP
[ 21.891060] Modules linked in: lkdtm
[ 21.893363] CPU: 0 PID: 151 Comm: sh Not tainted
5.19.0-rc1-00021-g852f4e48dbab #1
[ 21.895560] Hardware name: linux,dummy-virt (DT)
[ 21.896543] pstate: 80400009 (Nzcv daif +PAN -UAO -TCO -DIT -SSBS BTYPE=--)
[ 21.897583] pc : lkdtm_indirect_call+0x2c/0x44 [lkdtm]
[ 21.898551] lr : lkdtm_CFI_FORWARD_PROTO+0x3c/0x6c [lkdtm]
[ 21.899520] sp : ffff8000083a3c50
[ 21.900191] x29: ffff8000083a3c50 x28: ffff0000027e0ec0 x27: 0000000000000000
[ 21.902453] x26: 0000000000000000 x25: ffffc2aa3d07e7b0 x24: 0000000000000002
[ 21.903736] x23: ffffc2aa3d079088 x22: ffffc2aa3d07e7b0 x21: ffff000003379000
[ 21.905062] x20: ffff8000083a3dc0 x19: 0000000000000012 x18: 0000000000000000
[ 21.906371] x17: 000000007e0c52a5 x16: 000000003ad55aca x15: ffffc2aa60d92138
[ 21.907662] x14: ffffffffffffffff x13: 2e2e2e2065707974 x12: 0000000000000018
[ 21.909775] x11: ffffc2aa62322b88 x10: ffffc2aa62322aa0 x9 : c7e305fb5195d200
[ 21.911898] x8 : ffffc2aa3d077e20 x7 : 6d20676e696c6c61 x6 : 43203a6d74646b6c
[ 21.913108] x5 : ffffc2aa6266c9df x4 : ffffc2aa6266c9e1 x3 : ffff8000083a3968
[ 21.914358] x2 : 80000000fffff122 x1 : 00000000fffff122 x0 : ffffc2aa3d07e8f8
[ 21.915827] Call trace:
[ 21.916375] lkdtm_indirect_call+0x2c/0x44 [lkdtm]
[ 21.918060] lkdtm_CFI_FORWARD_PROTO+0x3c/0x6c [lkdtm]
[ 21.919030] lkdtm_do_action+0x34/0x4c [lkdtm]
[ 21.919920] direct_entry+0x170/0x1ac [lkdtm]
[ 21.920772] full_proxy_write+0x84/0x104
[ 21.921759] vfs_write+0x188/0x3d8
[ 21.922387] ksys_write+0x78/0xe8
[ 21.922986] __arm64_sys_write+0x1c/0x2c
[ 21.923696] invoke_syscall+0x58/0x134
[ 21.924554] el0_svc_common+0xb4/0xf4
[ 21.925603] do_el0_svc+0x2c/0xb4
[ 21.926563] el0_svc+0x2c/0x7c
[ 21.927147] el0t_64_sync_handler+0x84/0xf0
[ 21.927985] el0t_64_sync+0x18c/0x190
[ 21.929133] Code: 728a54b1 72afc191 6b11021f 54000040 (d4304500)
[ 21.930690] ---[ end trace 0000000000000000 ]---
[ 21.930971] Kernel panic - not syncing: Oops - CFI: Fatal exception

Suggested-by: Mark Rutland <mark.rutland@arm.com>
Signed-off-by: Sami Tolvanen <samitolvanen@google.com>
Reviewed-by: Kees Cook <keescook@chromium.org>
Tested-by: Kees Cook <keescook@chromium.org>
Tested-by: Nathan Chancellor <nathan@kernel.org>
Acked-by: Peter Zijlstra (Intel) <peterz@infradead.org>
Tested-by: Peter Zijlstra (Intel) <peterz@infradead.org>
Signed-off-by: Kees Cook <keescook@chromium.org>
Link: https://lore.kernel.org/r/20220908215504.3686827-11-samitolvanen@google.com

authored by

Sami Tolvanen and committed by
Kees Cook
b26e484b c50d3285

+50 -3
+6
arch/arm64/include/asm/brk-imm.h
··· 17 17 * 0x401: for compile time BRK instruction 18 18 * 0x800: kernel-mode BUG() and WARN() traps 19 19 * 0x9xx: tag-based KASAN trap (allowed values 0x900 - 0x9ff) 20 + * 0x8xxx: Control-Flow Integrity traps 20 21 */ 21 22 #define KPROBES_BRK_IMM 0x004 22 23 #define UPROBES_BRK_IMM 0x005 ··· 28 27 #define BUG_BRK_IMM 0x800 29 28 #define KASAN_BRK_IMM 0x900 30 29 #define KASAN_BRK_MASK 0x0ff 30 + 31 + #define CFI_BRK_IMM_TARGET GENMASK(4, 0) 32 + #define CFI_BRK_IMM_TYPE GENMASK(9, 5) 33 + #define CFI_BRK_IMM_BASE 0x8000 34 + #define CFI_BRK_IMM_MASK (CFI_BRK_IMM_TARGET | CFI_BRK_IMM_TYPE) 31 35 32 36 #endif
+44 -3
arch/arm64/kernel/traps.c
··· 26 26 #include <linux/syscalls.h> 27 27 #include <linux/mm_types.h> 28 28 #include <linux/kasan.h> 29 + #include <linux/cfi.h> 29 30 30 31 #include <asm/atomic.h> 31 32 #include <asm/bug.h> ··· 992 991 .imm = BUG_BRK_IMM, 993 992 }; 994 993 994 + #ifdef CONFIG_CFI_CLANG 995 + static int cfi_handler(struct pt_regs *regs, unsigned long esr) 996 + { 997 + unsigned long target; 998 + u32 type; 999 + 1000 + target = pt_regs_read_reg(regs, FIELD_GET(CFI_BRK_IMM_TARGET, esr)); 1001 + type = (u32)pt_regs_read_reg(regs, FIELD_GET(CFI_BRK_IMM_TYPE, esr)); 1002 + 1003 + switch (report_cfi_failure(regs, regs->pc, &target, type)) { 1004 + case BUG_TRAP_TYPE_BUG: 1005 + die("Oops - CFI", regs, 0); 1006 + break; 1007 + 1008 + case BUG_TRAP_TYPE_WARN: 1009 + break; 1010 + 1011 + default: 1012 + return DBG_HOOK_ERROR; 1013 + } 1014 + 1015 + arm64_skip_faulting_instruction(regs, AARCH64_INSN_SIZE); 1016 + return DBG_HOOK_HANDLED; 1017 + } 1018 + 1019 + static struct break_hook cfi_break_hook = { 1020 + .fn = cfi_handler, 1021 + .imm = CFI_BRK_IMM_BASE, 1022 + .mask = CFI_BRK_IMM_MASK, 1023 + }; 1024 + #endif /* CONFIG_CFI_CLANG */ 1025 + 995 1026 static int reserved_fault_handler(struct pt_regs *regs, unsigned long esr) 996 1027 { 997 1028 pr_err("%s generated an invalid instruction at %pS!\n", ··· 1085 1052 }; 1086 1053 #endif 1087 1054 1055 + 1056 + #define esr_comment(esr) ((esr) & ESR_ELx_BRK64_ISS_COMMENT_MASK) 1057 + 1088 1058 /* 1089 1059 * Initial handler for AArch64 BRK exceptions 1090 1060 * This handler only used until debug_traps_init(). ··· 1095 1059 int __init early_brk64(unsigned long addr, unsigned long esr, 1096 1060 struct pt_regs *regs) 1097 1061 { 1062 + #ifdef CONFIG_CFI_CLANG 1063 + if ((esr_comment(esr) & ~CFI_BRK_IMM_MASK) == CFI_BRK_IMM_BASE) 1064 + return cfi_handler(regs, esr) != DBG_HOOK_HANDLED; 1065 + #endif 1098 1066 #ifdef CONFIG_KASAN_SW_TAGS 1099 - unsigned long comment = esr & ESR_ELx_BRK64_ISS_COMMENT_MASK; 1100 - 1101 - if ((comment & ~KASAN_BRK_MASK) == KASAN_BRK_IMM) 1067 + if ((esr_comment(esr) & ~KASAN_BRK_MASK) == KASAN_BRK_IMM) 1102 1068 return kasan_handler(regs, esr) != DBG_HOOK_HANDLED; 1103 1069 #endif 1104 1070 return bug_handler(regs, esr) != DBG_HOOK_HANDLED; ··· 1109 1071 void __init trap_init(void) 1110 1072 { 1111 1073 register_kernel_break_hook(&bug_break_hook); 1074 + #ifdef CONFIG_CFI_CLANG 1075 + register_kernel_break_hook(&cfi_break_hook); 1076 + #endif 1112 1077 register_kernel_break_hook(&fault_break_hook); 1113 1078 #ifdef CONFIG_KASAN_SW_TAGS 1114 1079 register_kernel_break_hook(&kasan_break_hook);