linux/kernel/cfi.c
Sami Tolvanen f1befc82ad cfi: Move BPF CFI types and helpers to generic code
Instead of duplicating the same code for each architecture, move
the CFI type hash variables for BPF function types and related
helper functions to generic CFI code, and allow architectures to
override the function definitions if needed.

Signed-off-by: Sami Tolvanen <samitolvanen@google.com>
Link: https://lore.kernel.org/r/20250801001004.1859976-7-samitolvanen@google.com
Signed-off-by: Alexei Starovoitov <ast@kernel.org>
2025-07-31 18:23:53 -07:00

115 lines
2.8 KiB
C

// 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_TRAPS
static 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 */