1 // SPDX-License-Identifier: GPL-2.0 2 /* 3 * Clang Control Flow Integrity (CFI) error handling. 4 * 5 * Copyright (C) 2022 Google LLC 6 */ 7 8 #include <linux/cfi.h> 9 10 bool cfi_warn __ro_after_init = IS_ENABLED(CONFIG_CFI_PERMISSIVE); 11 12 enum bug_trap_type report_cfi_failure(struct pt_regs *regs, unsigned long addr, 13 unsigned long *target, u32 type) 14 { 15 if (target) 16 pr_err("CFI failure at %pS (target: %pS; expected type: 0x%08x)\n", 17 (void *)addr, (void *)*target, type); 18 else 19 pr_err("CFI failure at %pS (no target information)\n", 20 (void *)addr); 21 22 if (cfi_warn) { 23 __warn(NULL, 0, (void *)addr, 0, regs, NULL); 24 return BUG_TRAP_TYPE_WARN; 25 } 26 27 return BUG_TRAP_TYPE_BUG; 28 } 29 30 #ifdef CONFIG_ARCH_USES_CFI_TRAPS 31 static inline unsigned long trap_address(s32 *p) 32 { 33 return (unsigned long)((long)p + (long)*p); 34 } 35 36 static bool is_trap(unsigned long addr, s32 *start, s32 *end) 37 { 38 s32 *p; 39 40 for (p = start; p < end; ++p) { 41 if (trap_address(p) == addr) 42 return true; 43 } 44 45 return false; 46 } 47 48 #ifdef CONFIG_MODULES 49 /* Populates `kcfi_trap(_end)?` fields in `struct module`. */ 50 void module_cfi_finalize(const Elf_Ehdr *hdr, const Elf_Shdr *sechdrs, 51 struct module *mod) 52 { 53 char *secstrings; 54 unsigned int i; 55 56 mod->kcfi_traps = NULL; 57 mod->kcfi_traps_end = NULL; 58 59 secstrings = (char *)hdr + sechdrs[hdr->e_shstrndx].sh_offset; 60 61 for (i = 1; i < hdr->e_shnum; i++) { 62 if (strcmp(secstrings + sechdrs[i].sh_name, "__kcfi_traps")) 63 continue; 64 65 mod->kcfi_traps = (s32 *)sechdrs[i].sh_addr; 66 mod->kcfi_traps_end = (s32 *)(sechdrs[i].sh_addr + sechdrs[i].sh_size); 67 break; 68 } 69 } 70 71 static bool is_module_cfi_trap(unsigned long addr) 72 { 73 struct module *mod; 74 bool found = false; 75 76 guard(rcu)(); 77 mod = __module_address(addr); 78 if (mod) 79 found = is_trap(addr, mod->kcfi_traps, mod->kcfi_traps_end); 80 81 return found; 82 } 83 #else /* CONFIG_MODULES */ 84 static inline bool is_module_cfi_trap(unsigned long addr) 85 { 86 return false; 87 } 88 #endif /* CONFIG_MODULES */ 89 90 extern s32 __start___kcfi_traps[]; 91 extern s32 __stop___kcfi_traps[]; 92 93 bool is_cfi_trap(unsigned long addr) 94 { 95 if (is_trap(addr, __start___kcfi_traps, __stop___kcfi_traps)) 96 return true; 97 98 return is_module_cfi_trap(addr); 99 } 100 #endif /* CONFIG_ARCH_USES_CFI_TRAPS */ 101