at v6.18 2.8 kB view raw
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/bpf.h> 9#include <linux/cfi_types.h> 10#include <linux/cfi.h> 11 12bool cfi_warn __ro_after_init = IS_ENABLED(CONFIG_CFI_PERMISSIVE); 13 14enum bug_trap_type report_cfi_failure(struct pt_regs *regs, unsigned long addr, 15 unsigned long *target, u32 type) 16{ 17 if (target) 18 pr_err("CFI failure at %pS (target: %pS; expected type: 0x%08x)\n", 19 (void *)addr, (void *)*target, type); 20 else 21 pr_err("CFI failure at %pS (no target information)\n", 22 (void *)addr); 23 24 if (cfi_warn) { 25 __warn(NULL, 0, (void *)addr, 0, regs, NULL); 26 return BUG_TRAP_TYPE_WARN; 27 } 28 29 return BUG_TRAP_TYPE_BUG; 30} 31 32/* 33 * Declare two non-existent functions with types that match bpf_func_t and 34 * bpf_callback_t pointers, and use DEFINE_CFI_TYPE to define type hash 35 * variables for each function type. The cfi_bpf_* variables are used by 36 * arch-specific BPF JIT implementations to ensure indirectly callable JIT 37 * code has matching CFI type hashes. 38 */ 39extern typeof(*(bpf_func_t)0) __bpf_prog_runX; 40DEFINE_CFI_TYPE(cfi_bpf_hash, __bpf_prog_runX); 41 42extern typeof(*(bpf_callback_t)0) __bpf_callback_fn; 43DEFINE_CFI_TYPE(cfi_bpf_subprog_hash, __bpf_callback_fn); 44 45#ifdef CONFIG_ARCH_USES_CFI_TRAPS 46static inline unsigned long trap_address(s32 *p) 47{ 48 return (unsigned long)((long)p + (long)*p); 49} 50 51static bool is_trap(unsigned long addr, s32 *start, s32 *end) 52{ 53 s32 *p; 54 55 for (p = start; p < end; ++p) { 56 if (trap_address(p) == addr) 57 return true; 58 } 59 60 return false; 61} 62 63#ifdef CONFIG_MODULES 64/* Populates `kcfi_trap(_end)?` fields in `struct module`. */ 65void module_cfi_finalize(const Elf_Ehdr *hdr, const Elf_Shdr *sechdrs, 66 struct module *mod) 67{ 68 char *secstrings; 69 unsigned int i; 70 71 mod->kcfi_traps = NULL; 72 mod->kcfi_traps_end = NULL; 73 74 secstrings = (char *)hdr + sechdrs[hdr->e_shstrndx].sh_offset; 75 76 for (i = 1; i < hdr->e_shnum; i++) { 77 if (strcmp(secstrings + sechdrs[i].sh_name, "__kcfi_traps")) 78 continue; 79 80 mod->kcfi_traps = (s32 *)sechdrs[i].sh_addr; 81 mod->kcfi_traps_end = (s32 *)(sechdrs[i].sh_addr + sechdrs[i].sh_size); 82 break; 83 } 84} 85 86static bool is_module_cfi_trap(unsigned long addr) 87{ 88 struct module *mod; 89 bool found = false; 90 91 guard(rcu)(); 92 mod = __module_address(addr); 93 if (mod) 94 found = is_trap(addr, mod->kcfi_traps, mod->kcfi_traps_end); 95 96 return found; 97} 98#else /* CONFIG_MODULES */ 99static inline bool is_module_cfi_trap(unsigned long addr) 100{ 101 return false; 102} 103#endif /* CONFIG_MODULES */ 104 105extern s32 __start___kcfi_traps[]; 106extern s32 __stop___kcfi_traps[]; 107 108bool is_cfi_trap(unsigned long addr) 109{ 110 if (is_trap(addr, __start___kcfi_traps, __stop___kcfi_traps)) 111 return true; 112 113 return is_module_cfi_trap(addr); 114} 115#endif /* CONFIG_ARCH_USES_CFI_TRAPS */