When an architecture implements bpf_jit_inlines_helper_call(), such as LoongArch, ARM64, PowerPC, and RISC-V, the verifier skips rewriting the helper call offset (insn->imm) during the bpf_do_misc_fixups() phase, because 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), the core BPF subsystem falls back to the BPF interpreter. When the interpreter executes (__bpf_call_base + insn->imm) with the unpatched raw helper ID, it jumps into an unaligned invalid address space, triggering an instruction alignment fault or a memory access panic. To avoid modifying the BPF interpreter's complexity for JIT-specific paths, just introduce a 'jit_required' flag in struct bpf_prog. When a program contains helper calls that skip rewriting for JIT inlining, set this flag to 1. During runtime selection, if JIT compilation is not available, explicitly reject loading with -ENOTSUPP instead of falling back to the interpreter, safely preventing the kernel panic. Fixes: 2ddec2c80b44 ("riscv, bpf: inline bpf_get_smp_processor_id()") Suggested-by: Alexei Starovoitov Signed-off-by: Tiezhu Yang --- v6: - Place 'jit_required' as a standalone u8 field right before 'stats' to reuse the existing 4-byte struct hole (offset 52) based on pahole analysis, avoiding the u16->u32 bitfield conversion in v5 which unintentionally changed the layout/alignment of subsequent fields. v5: - Drop the fallback interpreter fixups as per maintainer's feedback. - Introduce 'jit_required' member in struct bpf_prog to flag programs that strictly rely on JIT. - Reject loading with -ENOTSUPP in __bpf_prog_select_runtime() if JIT fails or is disabled for these programs. - Upgrade 'jited' bitfield group type from u16 to u32 in struct bpf_prog to prevent 17-bit overflow warning. include/linux/bpf.h | 1 + kernel/bpf/core.c | 2 +- kernel/bpf/fixups.c | 4 +++- 3 files changed, 5 insertions(+), 2 deletions(-) diff --git a/include/linux/bpf.h b/include/linux/bpf.h index ba09795e0bfd..f5c283af3cf7 100644 --- a/include/linux/bpf.h +++ b/include/linux/bpf.h @@ -1889,6 +1889,7 @@ struct bpf_prog { u8 digest[SHA256_DIGEST_SIZE]; u8 tag[BPF_TAG_SIZE]; }; + u8 jit_required; /* program strictly requires JIT compiler */ struct bpf_prog_stats __percpu *stats; u8 __percpu *active; /* u8[BPF_NR_CONTEXTS] for recursion protection */ unsigned int (*bpf_func)(const void *ctx, diff --git a/kernel/bpf/core.c b/kernel/bpf/core.c index 649cce41e13f..a55754bcc593 100644 --- a/kernel/bpf/core.c +++ b/kernel/bpf/core.c @@ -2639,7 +2639,7 @@ 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 (!fp->jited && jit_needed) { + if (!fp->jited && (jit_needed || fp->jit_required)) { *err = -ENOTSUPP; return fp; } diff --git a/kernel/bpf/fixups.c b/kernel/bpf/fixups.c index 12a8a4eb757f..94e0457a0aa3 100644 --- a/kernel/bpf/fixups.c +++ b/kernel/bpf/fixups.c @@ -1841,8 +1841,10 @@ int bpf_do_misc_fixups(struct bpf_verifier_env *env) } /* Skip inlining the helper call if the JIT does it. */ - if (bpf_jit_inlines_helper_call(insn->imm)) + if (bpf_jit_inlines_helper_call(insn->imm)) { + prog->jit_required = 1; goto next_insn; + } if (insn->imm == BPF_FUNC_get_route_realm) prog->dst_needed = 1; -- 2.42.0