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