| // SPDX-License-Identifier: GPL-2.0 |
| /* |
| * Clang Control Flow Integrity (CFI) support. |
| * |
| * Copyright (C) 2023 Google LLC |
| */ |
| #include <linux/cfi.h> |
| #include <asm/insn.h> |
| |
| /* |
| * 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(<reg>) |
| * lui t2, <hi20> |
| * addiw t2, t2, <lo12> |
| * beq t1, t2, .Ltmp1 |
| * ebreak ; <- regs->epc |
| * .Ltmp1: |
| * jalr <reg> |
| * |
| * 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 |