Begin reporting arena page faults and the faulting address to BPF program's stderr, for now limited to x86, but arm64 support should be easy to add. Signed-off-by: Kumar Kartikeya Dwivedi <memxor@xxxxxxxxx> --- arch/x86/net/bpf_jit_comp.c | 21 ++++++++++++++++++--- include/linux/bpf.h | 1 + kernel/bpf/arena.c | 14 ++++++++++++++ 3 files changed, 33 insertions(+), 3 deletions(-) diff --git a/arch/x86/net/bpf_jit_comp.c b/arch/x86/net/bpf_jit_comp.c index 9e5fe2ba858f..434a6eae4621 100644 --- a/arch/x86/net/bpf_jit_comp.c +++ b/arch/x86/net/bpf_jit_comp.c @@ -1384,15 +1384,27 @@ static int emit_atomic_ld_st_index(u8 **pprog, u32 atomic_op, u32 size, } #define DONT_CLEAR 1 +#define ARENA_FAULT (1 << 8) bool ex_handler_bpf(const struct exception_table_entry *x, struct pt_regs *regs) { - u32 reg = x->fixup >> 8; + u32 arena_reg = (x->fixup >> 8) & 0xff; + bool is_arena = !!arena_reg; + u32 reg = x->fixup >> 16; + unsigned long addr; + + /* Read here, if src_reg is dst_reg for load, we'll write 0 to it. */ + if (is_arena) + addr = *(unsigned long *)((void *)regs + arena_reg); /* jump over faulting load and clear dest register */ if (reg != DONT_CLEAR) *(unsigned long *)((void *)regs + reg) = 0; regs->ip += x->fixup & 0xff; + + if (is_arena) + bpf_prog_report_arena_violation(reg == DONT_CLEAR, addr); + return true; } @@ -2043,7 +2055,10 @@ st: if (is_imm8(insn->off)) ex->data = EX_TYPE_BPF; ex->fixup = (prog - start_of_ldx) | - ((BPF_CLASS(insn->code) == BPF_LDX ? reg2pt_regs[dst_reg] : DONT_CLEAR) << 8); + ((BPF_CLASS(insn->code) == BPF_LDX ? reg2pt_regs[dst_reg] : DONT_CLEAR) << 16) + | ((BPF_CLASS(insn->code) == BPF_LDX ? reg2pt_regs[src_reg] : reg2pt_regs[dst_reg])<< 8); + /* Ensure src_reg offset fits in 1 byte. */ + BUILD_BUG_ON(sizeof(struct pt_regs) > U8_MAX); } break; @@ -2161,7 +2176,7 @@ st: if (is_imm8(insn->off)) * End result: x86 insn "mov rbx, qword ptr [rax+0x14]" * of 4 bytes will be ignored and rbx will be zero inited. */ - ex->fixup = (prog - start_of_ldx) | (reg2pt_regs[dst_reg] << 8); + ex->fixup = (prog - start_of_ldx) | (reg2pt_regs[dst_reg] << 16); } break; diff --git a/include/linux/bpf.h b/include/linux/bpf.h index cf057327798c..9fae22dde926 100644 --- a/include/linux/bpf.h +++ b/include/linux/bpf.h @@ -3593,6 +3593,7 @@ __printf(2, 3) int bpf_prog_stderr_printk(struct bpf_prog *prog, const char *fmt, ...); struct bpf_prog *bpf_prog_find_from_stack(void); void bpf_prog_stderr_dump_stack(struct bpf_prog *prog); +void bpf_prog_report_arena_violation(bool write, unsigned long addr); #ifdef CONFIG_BPF_LSM void bpf_cgroup_atype_get(u32 attach_btf_id, int cgroup_atype); diff --git a/kernel/bpf/arena.c b/kernel/bpf/arena.c index 0d56cea71602..593a93195cdc 100644 --- a/kernel/bpf/arena.c +++ b/kernel/bpf/arena.c @@ -590,3 +590,17 @@ static int __init kfunc_init(void) return register_btf_kfunc_id_set(BPF_PROG_TYPE_UNSPEC, &common_kfunc_set); } late_initcall(kfunc_init); + +void bpf_prog_report_arena_violation(bool write, unsigned long addr) +{ + struct bpf_prog *prog; + + prog = bpf_prog_find_from_stack(); + if (!prog) + return; + if (atomic_long_fetch_add(1, &prog->aux->error_count) >= BPF_PROG_ERROR_COUNT_MAX) + return; + bpf_prog_stderr_printk(prog, "ERROR: Arena %s access at unmapped address 0x%lx\n", + write ? "WRITE" : "READ", addr); + bpf_prog_stderr_dump_stack(prog); +} -- 2.47.1