mirror-linux/arch/riscv/kernel/cfi.c

102 lines
2.4 KiB
C
Raw Blame History

This file contains invisible Unicode characters!

This file contains invisible Unicode characters that may be processed differently from what appears below. If your use case is intentional and legitimate, you can safely ignore this warning. Use the Escape button to reveal hidden characters.

// SPDX-License-Identifier: GPL-2.0
/*
* Clang Control Flow Integrity (CFI) support.
*
* Copyright (C) 2023 Google LLC
*/
#include <linux/cfi_types.h>
#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);
DEFINE_CFI_TYPE(cfi_bpf_hash, __bpf_prog_runX);
/* Must match bpf_callback_t */
extern u64 __bpf_callback_fn(u64, u64, u64, u64, u64);
DEFINE_CFI_TYPE(cfi_bpf_subprog_hash, __bpf_callback_fn);
u32 cfi_get_func_hash(void *func)
{
u32 hash;
if (get_kernel_nofault(hash, func - cfi_get_offset()))
return 0;
return hash;
}
#endif