When an architecture implements bpf_jit_inlines_helper_call(), such as LoongArch, ARM64, and RISC-V, the BPF verifier skips rewriting the helper call offset (insn->imm) during the bpf_do_misc_fixups() phase if the helper is expected to be inlined by the JIT compiler. As a result, insn->imm remains as the raw helper enum ID. However, if JIT is disabled at runtime (net.core.bpf_jit_enable=0) or if the JIT compilation later dynamically fails (e.g., due to OOM during bpf_jit_alloc_exec()), the core BPF subsystem falls back to the BPF interpreter. When the fallback interpreter executes (__bpf_call_base + insn->imm) with the unpatched raw helper ID, it jumps into an unaligned invalid address space, triggering a fatal instruction alignment fault (ADEF) or illegal memory access kernel panic. This issue impacts all architectures that support helper inlining. Introduce a late fixup phase in __bpf_prog_select_runtime() to fix it. When the JIT compilation is skipped or fails (!fp->jited), just traverse the BPF instructions and rewrite the call offsets for the inlined helpers right before transferring control to the interpreter, ensuring a safe fallback path. 1. Test case (test_panic.c): #include #include SEC("kprobe/sys_getpid") int test_panic(void *ctx) { struct task_struct *task; task = (struct task_struct *)bpf_get_current_task(); if (task) bpf_printk("Task address: %p\n", task); return 0; } char LICENSE[] SEC("license") = "GPL"; 2. Reproduction steps on LoongArch: $ clang -target bpf -O2 -g -c test_panic.c -o test_panic.o $ sudo sysctl -w net.core.bpf_jit_enable=0 $ sudo bpftool prog load test_panic.o /sys/fs/bpf/test_panic autoattach $ sudo cat /sys/kernel/debug/tracing/trace_pipe 3. Panic information on LoongArch: Kernel ade access[#1]: ... ra: 9000000000486e50 ___bpf_prog_run+0x1370/0x36b0 ERA: 9000000000485383 __bpf_prog_ret0_warn+0x13/0x20 ... ESTAT: 00080000 [ADEF] (IS= ECode=8 EsubCode=0) Fixes: 2ddec2c80b44 ("riscv, bpf: inline bpf_get_smp_processor_id()") Signed-off-by: Tiezhu Yang --- kernel/bpf/core.c | 33 +++++++++++++++++++++++++++++++++ 1 file changed, 33 insertions(+) diff --git a/kernel/bpf/core.c b/kernel/bpf/core.c index 649cce41e13f..de0c46b495b4 100644 --- a/kernel/bpf/core.c +++ b/kernel/bpf/core.c @@ -2608,6 +2608,30 @@ static struct bpf_prog *bpf_prog_jit_compile(struct bpf_verifier_env *env, struc return prog; } +/* + * Rewrite the helper call offset for inlined helpers when fallback to + * the interpreter happens due to JIT compilation failure or JIT disabled. + */ +static void bpf_fixup_fallback_inline_helpers(struct bpf_verifier_env *env, struct bpf_prog *fp) +{ + struct bpf_insn *insn = fp->insnsi; + const struct bpf_func_proto *fn; + int i; + + if (!env || !env->ops->get_func_proto) + return; + + for (i = 0; i < fp->len; i++, insn++) { + if (insn->code == (BPF_JMP | BPF_CALL) && insn->src_reg == 0) { + if (bpf_jit_inlines_helper_call(insn->imm)) { + fn = env->ops->get_func_proto(insn->imm, fp); + if (fn && fn->func) + insn->imm = fn->func - __bpf_call_base; + } + } + } +} + struct bpf_prog *__bpf_prog_select_runtime(struct bpf_verifier_env *env, struct bpf_prog *fp, int *err) { @@ -2639,6 +2663,15 @@ struct bpf_prog *__bpf_prog_select_runtime(struct bpf_verifier_env *env, struct fp = bpf_prog_jit_compile(env, fp); bpf_prog_jit_attempt_done(fp); + + /* + * If JIT compilation failed or is disabled (!fp->jited), we are + * about to fall back to the interpreter path. Fix up the call + * offsets to prevent unaligned memory access panic. + */ + if (!fp->jited) + bpf_fixup_fallback_inline_helpers(env, fp); + if (!fp->jited && jit_needed) { *err = -ENOTSUPP; return fp; -- 2.42.0