Something went wrong. Try again.
Linux kernel mirror (for testing) git.kernel.org/pub/scm/linux/kernel/git/torvalds/linux.git
Something went wrong. Try again.
123456789101112131415161718192021222324252627282930313233343536373839404142434445464748495051525354555657585960616263646566676869707172737475767778798081828384858687888990919293949596979899100101102103104105106107108109110111112113114115116// SPDX-License-Identifier: GPL-2.0/* * Clang Control Flow Integrity (CFI) error handling. * * Copyright (C) 2022 Google LLC */
#include <linux/bpf.h>#include <linux/cfi_types.h>#include <linux/cfi.h>
bool cfi_warn __ro_after_init = IS_ENABLED(CONFIG_CFI_PERMISSIVE);
enum bug_trap_type report_cfi_failure(struct pt_regs *regs, unsigned long addr, unsigned long *target, u32 type){ if (target) pr_err("CFI failure at %pS (target: %pS; expected type: 0x%08x)\n", (void *)addr, (void *)*target, type); else pr_err("CFI failure at %pS (no target information)\n", (void *)addr);
if (cfi_warn) { __warn(NULL, 0, (void *)addr, 0, regs, NULL); return BUG_TRAP_TYPE_WARN; }
return BUG_TRAP_TYPE_BUG;}
/* * Declare two non-existent functions with types that match bpf_func_t and * bpf_callback_t pointers, and use DEFINE_CFI_TYPE to define type hash * variables for each function type. The cfi_bpf_* variables are used by * arch-specific BPF JIT implementations to ensure indirectly callable JIT * code has matching CFI type hashes. */extern typeof(*(bpf_func_t)0) __bpf_prog_runX;DEFINE_CFI_TYPE(cfi_bpf_hash, __bpf_prog_runX);
extern typeof(*(bpf_callback_t)0) __bpf_callback_fn;DEFINE_CFI_TYPE(cfi_bpf_subprog_hash, __bpf_callback_fn);
#ifdef CONFIG_ARCH_USES_CFI_TRAPSstatic inline unsigned long trap_address(s32 *p){ return (unsigned long)((long)p + (long)*p);}
static bool is_trap(unsigned long addr, s32 *start, s32 *end){ s32 *p;
for (p = start; p < end; ++p) { if (trap_address(p) == addr) return true; }
return false;}
#ifdef CONFIG_MODULES/* Populates `kcfi_trap(_end)?` fields in `struct module`. */void module_cfi_finalize(const Elf_Ehdr *hdr, const Elf_Shdr *sechdrs, struct module *mod){ char *secstrings; unsigned int i;
mod->kcfi_traps = NULL; mod->kcfi_traps_end = NULL;
secstrings = (char *)hdr + sechdrs[hdr->e_shstrndx].sh_offset;
for (i = 1; i < hdr->e_shnum; i++) { if (strcmp(secstrings + sechdrs[i].sh_name, "__kcfi_traps")) continue;
mod->kcfi_traps = (s32 *)sechdrs[i].sh_addr; mod->kcfi_traps_end = (s32 *)(sechdrs[i].sh_addr + sechdrs[i].sh_size); break; }}
static bool is_module_cfi_trap(unsigned long addr){ struct module *mod; bool found = false;
guard(rcu)(); mod = __module_address(addr); if (mod) found = is_trap(addr, mod->kcfi_traps, mod->kcfi_traps_end);
return found;}#else /* CONFIG_MODULES */static inline bool is_module_cfi_trap(unsigned long addr){ return false;}#endif /* CONFIG_MODULES */
extern s32 __start___kcfi_traps[];extern s32 __stop___kcfi_traps[];
bool is_cfi_trap(unsigned long addr){ if (is_trap(addr, __start___kcfi_traps, __stop___kcfi_traps)) return true;
return is_module_cfi_trap(addr);}#endif /* CONFIG_ARCH_USES_CFI_TRAPS */