bpf, riscv64/cfi: Support kCFI + BPF on riscv64
The riscv BPF JIT doesn't emit proper kCFI prologues for BPF programs and struct_ops trampolines when CONFIG_CFI_CLANG is enabled. This causes CFI failures when calling BPF programs and can even crash the kernel due to invalid memory accesses. Example crash: root@rv-selftester:~/bpf# ./test_progs -a dummy_st_ops Unable to handle kernel paging request at virtual address ffffffff78204ffc Oops [#1] Modules linked in: bpf_testmod(OE) [....] CPU: 3 PID: 356 Comm: test_progs Tainted: P OE 6.8.0-rc1 #1 Hardware name: riscv-virtio,qemu (DT) epc : bpf_struct_ops_test_run+0x28c/0x5fc ra : bpf_struct_ops_test_run+0x26c/0x5fc epc : ffffffff82958010 ra : ffffffff82957ff0 sp : ff200000007abc80 gp : ffffffff868d6218 tp : ff6000008d87b840 t0 : 000000000000000f t1 : 0000000000000000 t2 : 000000002005793e s0 : ff200000007abcf0 s1 : ff6000008a90fee0 a0 : 0000000000000000 a1 : 0000000000000000 a2 : 0000000000000000 a3 : 0000000000000000 a4 : 0000000000000000 a5 : ffffffff868dba26 a6 : 0000000000000001 a7 : 0000000052464e43 s2 : 00007ffffc0a95f0 s3 : ff6000008a90fe80 s4 : ff60000084c24c00 s5 : ffffffff78205000 s6 : ff60000088750648 s7 : ff20000000035008 s8 : fffffffffffffff4 s9 : ffffffff86200610 s10: 0000000000000000 s11: 0000000000000000 t3 : ffffffff8483dc30 t4 : ffffffff8483dc10 t5 : ffffffff8483dbf0 t6 : ffffffff8483dbd0 status: 0000000200000120 badaddr: ffffffff78204ffc cause: 000000000000000d [<ffffffff82958010>] bpf_struct_ops_test_run+0x28c/0x5fc [<ffffffff805083ee>] bpf_prog_test_run+0x170/0x548 [<ffffffff805029c8>] __sys_bpf+0x2d2/0x378 [<ffffffff804ff570>] __riscv_sys_bpf+0x5c/0x120 [<ffffffff8000e8fe>] syscall_handler+0x62/0xe4 [<ffffffff83362df6>] do_trap_ecall_u+0xc6/0x27c [<ffffffff833822c4>] ret_from_exception+0x0/0x64 Code: b603 0109 b683 0189 b703 0209 8493 0609 157d 8d65 (a303) ffca ---[ end trace 0000000000000000 ]--- Kernel panic - not syncing: Fatal exception SMP: stopping secondary CPUs Implement proper kCFI prologues for the BPF programs and callbacks and drop __nocfi for riscv64. Fix the trampoline generation code to emit kCFI prologue when a struct_ops trampoline is being prepared. Signed-off-by: Puranjay Mohan <puranjay12@gmail.com> Signed-off-by: Daniel Borkmann <daniel@iogearbox.net> Signed-off-by: Andrii Nakryiko <andrii@kernel.org> Acked-by: Björn Töpel <bjorn@kernel.org> Link: https://lore.kernel.org/bpf/20240303170207.82201-2-puranjay12@gmail.com
This commit is contained in:
parent
516fca5a75
commit
e63985ecd2
@ -13,11 +13,28 @@ struct pt_regs;
|
||||
|
||||
#ifdef CONFIG_CFI_CLANG
|
||||
enum bug_trap_type handle_cfi_failure(struct pt_regs *regs);
|
||||
#define __bpfcall
|
||||
static inline int cfi_get_offset(void)
|
||||
{
|
||||
return 4;
|
||||
}
|
||||
|
||||
#define cfi_get_offset cfi_get_offset
|
||||
extern u32 cfi_bpf_hash;
|
||||
extern u32 cfi_bpf_subprog_hash;
|
||||
extern u32 cfi_get_func_hash(void *func);
|
||||
#else
|
||||
static inline enum bug_trap_type handle_cfi_failure(struct pt_regs *regs)
|
||||
{
|
||||
return BUG_TRAP_TYPE_NONE;
|
||||
}
|
||||
|
||||
#define cfi_bpf_hash 0U
|
||||
#define cfi_bpf_subprog_hash 0U
|
||||
static inline u32 cfi_get_func_hash(void *func)
|
||||
{
|
||||
return 0;
|
||||
}
|
||||
#endif /* CONFIG_CFI_CLANG */
|
||||
|
||||
#endif /* _ASM_RISCV_CFI_H */
|
||||
|
@ -75,3 +75,56 @@ enum bug_trap_type handle_cfi_failure(struct pt_regs *regs)
|
||||
|
||||
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
|
||||
|
@ -1223,7 +1223,7 @@ out_be:
|
||||
|
||||
#endif /* __riscv_xlen == 64 */
|
||||
|
||||
void bpf_jit_build_prologue(struct rv_jit_context *ctx);
|
||||
void bpf_jit_build_prologue(struct rv_jit_context *ctx, bool is_subprog);
|
||||
void bpf_jit_build_epilogue(struct rv_jit_context *ctx);
|
||||
|
||||
int bpf_jit_emit_insn(const struct bpf_insn *insn, struct rv_jit_context *ctx,
|
||||
|
@ -1301,7 +1301,7 @@ notsupported:
|
||||
return 0;
|
||||
}
|
||||
|
||||
void bpf_jit_build_prologue(struct rv_jit_context *ctx)
|
||||
void bpf_jit_build_prologue(struct rv_jit_context *ctx, bool is_subprog)
|
||||
{
|
||||
const s8 *fp = bpf2rv32[BPF_REG_FP];
|
||||
const s8 *r1 = bpf2rv32[BPF_REG_1];
|
||||
|
@ -11,6 +11,7 @@
|
||||
#include <linux/memory.h>
|
||||
#include <linux/stop_machine.h>
|
||||
#include <asm/patch.h>
|
||||
#include <asm/cfi.h>
|
||||
#include "bpf_jit.h"
|
||||
|
||||
#define RV_FENTRY_NINSNS 2
|
||||
@ -455,6 +456,12 @@ static int emit_call(u64 addr, bool fixed_addr, struct rv_jit_context *ctx)
|
||||
return emit_jump_and_link(RV_REG_RA, off, fixed_addr, ctx);
|
||||
}
|
||||
|
||||
static inline void emit_kcfi(u32 hash, struct rv_jit_context *ctx)
|
||||
{
|
||||
if (IS_ENABLED(CONFIG_CFI_CLANG))
|
||||
emit(hash, ctx);
|
||||
}
|
||||
|
||||
static void emit_atomic(u8 rd, u8 rs, s16 off, s32 imm, bool is64,
|
||||
struct rv_jit_context *ctx)
|
||||
{
|
||||
@ -869,6 +876,8 @@ static int __arch_prepare_bpf_trampoline(struct bpf_tramp_image *im,
|
||||
emit_sd(RV_REG_SP, stack_size - 16, RV_REG_FP, ctx);
|
||||
emit_addi(RV_REG_FP, RV_REG_SP, stack_size, ctx);
|
||||
} else {
|
||||
/* emit kcfi hash */
|
||||
emit_kcfi(cfi_get_func_hash(func_addr), ctx);
|
||||
/* For the trampoline called directly, just handle
|
||||
* the frame of trampoline.
|
||||
*/
|
||||
@ -1711,7 +1720,7 @@ int bpf_jit_emit_insn(const struct bpf_insn *insn, struct rv_jit_context *ctx,
|
||||
return 0;
|
||||
}
|
||||
|
||||
void bpf_jit_build_prologue(struct rv_jit_context *ctx)
|
||||
void bpf_jit_build_prologue(struct rv_jit_context *ctx, bool is_subprog)
|
||||
{
|
||||
int i, stack_adjust = 0, store_offset, bpf_stack_adjust;
|
||||
|
||||
@ -1740,6 +1749,9 @@ void bpf_jit_build_prologue(struct rv_jit_context *ctx)
|
||||
|
||||
store_offset = stack_adjust - 8;
|
||||
|
||||
/* emit kcfi type preamble immediately before the first insn */
|
||||
emit_kcfi(is_subprog ? cfi_bpf_subprog_hash : cfi_bpf_hash, ctx);
|
||||
|
||||
/* nops reserved for auipc+jalr pair */
|
||||
for (i = 0; i < RV_FENTRY_NINSNS; i++)
|
||||
emit(rv_nop(), ctx);
|
||||
|
@ -10,6 +10,7 @@
|
||||
#include <linux/filter.h>
|
||||
#include <linux/memory.h>
|
||||
#include <asm/patch.h>
|
||||
#include <asm/cfi.h>
|
||||
#include "bpf_jit.h"
|
||||
|
||||
/* Number of iterations to try until offsets converge. */
|
||||
@ -100,7 +101,7 @@ struct bpf_prog *bpf_int_jit_compile(struct bpf_prog *prog)
|
||||
pass++;
|
||||
ctx->ninsns = 0;
|
||||
|
||||
bpf_jit_build_prologue(ctx);
|
||||
bpf_jit_build_prologue(ctx, bpf_is_subprog(prog));
|
||||
ctx->prologue_len = ctx->ninsns;
|
||||
|
||||
if (build_body(ctx, extra_pass, ctx->offset)) {
|
||||
@ -160,7 +161,7 @@ skip_init_ctx:
|
||||
ctx->ninsns = 0;
|
||||
ctx->nexentries = 0;
|
||||
|
||||
bpf_jit_build_prologue(ctx);
|
||||
bpf_jit_build_prologue(ctx, bpf_is_subprog(prog));
|
||||
if (build_body(ctx, extra_pass, NULL)) {
|
||||
prog = orig_prog;
|
||||
goto out_free_hdr;
|
||||
@ -170,9 +171,9 @@ skip_init_ctx:
|
||||
if (bpf_jit_enable > 1)
|
||||
bpf_jit_dump(prog->len, prog_size, pass, ctx->insns);
|
||||
|
||||
prog->bpf_func = (void *)ctx->ro_insns;
|
||||
prog->bpf_func = (void *)ctx->ro_insns + cfi_get_offset();
|
||||
prog->jited = 1;
|
||||
prog->jited_len = prog_size;
|
||||
prog->jited_len = prog_size - cfi_get_offset();
|
||||
|
||||
if (!prog->is_func || extra_pass) {
|
||||
if (WARN_ON(bpf_jit_binary_pack_finalize(prog, jit_data->ro_header,
|
||||
|
Loading…
Reference in New Issue
Block a user