// SPDX-License-Identifier: GPL-2.0 /* * Clang Control Flow Integrity (CFI) support. * * Copyright (C) 2023 Google LLC */ #include #include /* * Returns the target address and the expected type when regs->epc points * to a compiler-generated CFI trap. */ static bool decode_cfi_insn(struct pt_regs *regs, unsigned long *target, u32 *type) { unsigned long *regs_ptr = (unsigned long *)regs; int rs1_num; u32 insn; *target = *type = 0; /* * The compiler generates the following instruction sequence * for indirect call checks: * *   lw t1, -4() * lui t2, * addiw t2, t2, * beq t1, t2, .Ltmp1 * ebreak ; <- regs->epc * .Ltmp1: * jalr * * We can read the expected type and the target address from the * registers passed to the beq/jalr instructions. */ if (get_kernel_nofault(insn, (void *)regs->epc - 4)) return false; if (!riscv_insn_is_beq(insn)) return false; *type = (u32)regs_ptr[RV_EXTRACT_RS1_REG(insn)]; if (get_kernel_nofault(insn, (void *)regs->epc) || get_kernel_nofault(insn, (void *)regs->epc + GET_INSN_LENGTH(insn))) return false; if (riscv_insn_is_jalr(insn)) rs1_num = RV_EXTRACT_RS1_REG(insn); else if (riscv_insn_is_c_jalr(insn)) rs1_num = RVC_EXTRACT_C2_RS1_REG(insn); else return false; *target = regs_ptr[rs1_num]; return true; } /* * Checks if the ebreak trap is because of a CFI failure, and handles the trap * if needed. Returns a bug_trap_type value similarly to report_bug. */ enum bug_trap_type handle_cfi_failure(struct pt_regs *regs) { unsigned long target; u32 type; if (!is_cfi_trap(regs->epc)) return BUG_TRAP_TYPE_NONE; if (!decode_cfi_insn(regs, &target, &type)) return report_cfi_failure_noaddr(regs, regs->epc); return report_cfi_failure(regs, regs->epc, &target, type); } #ifdef CONFIG_CFI_CLANG struct bpf_insn; /* Must match bpf_func_t / DEFINE_BPF_PROG_RUN() */ extern unsigned int __bpf_prog_runX(const void *ctx, const struct bpf_insn *insn); /* * Force a reference to the external symbol so the compiler generates * __kcfi_typid. */ __ADDRESSABLE(__bpf_prog_runX); /* u32 __ro_after_init cfi_bpf_hash = __kcfi_typeid___bpf_prog_runX; */ asm ( " .pushsection .data..ro_after_init,\"aw\",@progbits \n" " .type cfi_bpf_hash,@object \n" " .globl cfi_bpf_hash \n" " .p2align 2, 0x0 \n" "cfi_bpf_hash: \n" " .word __kcfi_typeid___bpf_prog_runX \n" " .size cfi_bpf_hash, 4 \n" " .popsection \n" ); /* Must match bpf_callback_t */ extern u64 __bpf_callback_fn(u64, u64, u64, u64, u64); __ADDRESSABLE(__bpf_callback_fn); /* u32 __ro_after_init cfi_bpf_subprog_hash = __kcfi_typeid___bpf_callback_fn; */ asm ( " .pushsection .data..ro_after_init,\"aw\",@progbits \n" " .type cfi_bpf_subprog_hash,@object \n" " .globl cfi_bpf_subprog_hash \n" " .p2align 2, 0x0 \n" "cfi_bpf_subprog_hash: \n" " .word __kcfi_typeid___bpf_callback_fn \n" " .size cfi_bpf_subprog_hash, 4 \n" " .popsection \n" ); u32 cfi_get_func_hash(void *func) { u32 hash; if (get_kernel_nofault(hash, func - cfi_get_offset())) return 0; return hash; } #endif