Commit e1d486445af3 for kernel

commit e1d486445af3c392628532229f7ce5f5cf7891b6
Author: Puranjay Mohan <puranjay@kernel.org>
Date:   Fri Apr 17 07:33:52 2026 -0700

    bpf, arm32: Reject BPF-to-BPF calls and callbacks in the JIT

    The ARM32 BPF JIT does not support BPF-to-BPF function calls
    (BPF_PSEUDO_CALL) or callbacks (BPF_PSEUDO_FUNC), but it does
    not reject them either.

    When a program with subprograms is loaded (e.g. libxdp's XDP
    dispatcher uses __noinline__ subprograms, or any program using
    callbacks like bpf_loop or bpf_for_each_map_elem), the verifier
    invokes bpf_jit_subprogs() which calls bpf_int_jit_compile()
    for each subprogram.

    For BPF_PSEUDO_CALL, since ARM32 does not reject it, the JIT
    silently emits code using the wrong address computation:

        func = __bpf_call_base + imm

    where imm is a pc-relative subprogram offset, producing a bogus
    function pointer.

    For BPF_PSEUDO_FUNC, the ldimm64 handler ignores src_reg and
    loads the immediate as a normal 64-bit value without error.

    In both cases, build_body() reports success and a JIT image is
    allocated. ARM32 lacks the jit_data/extra_pass mechanism needed
    for the second JIT pass in bpf_jit_subprogs(). On the second
    pass, bpf_int_jit_compile() performs a full fresh compilation,
    allocating a new JIT binary and overwriting prog->bpf_func. The
    first allocation is never freed. bpf_jit_subprogs() then detects
    the function pointer changed and aborts with -ENOTSUPP, but the
    original JIT binary has already been leaked. Each program
    load/unload cycle leaks one JIT binary allocation, as reported
    by kmemleak:

        unreferenced object 0xbf0a1000 (size 4096):
          backtrace:
            bpf_jit_binary_alloc+0x64/0xfc
            bpf_int_jit_compile+0x14c/0x348
            bpf_jit_subprogs+0x4fc/0xa60

    Fix this by rejecting both BPF_PSEUDO_CALL in the BPF_CALL
    handler and BPF_PSEUDO_FUNC in the BPF_LD_IMM64 handler, falling
    through to the existing 'notyet' path. This causes build_body()
    to fail before any JIT binary is allocated, so
    bpf_int_jit_compile() returns the original program unjitted.
    bpf_jit_subprogs() then sees !prog->jited and cleanly falls
    back to the interpreter with no leak.

    Acked-by: Daniel Borkmann <daniel@iogearbox.net>
    Fixes: 1c2a088a6626 ("bpf: x64: add JIT support for multi-function programs")
    Reported-by: Jonas Rebmann <jre@pengutronix.de>
    Closes: https://lore.kernel.org/bpf/b63e9174-7a3d-4e22-8294-16df07a4af89@pengutronix.de
    Tested-by: Jonas Rebmann <jre@pengutronix.de>
    Signed-off-by: Puranjay Mohan <puranjay@kernel.org>
    Reviewed-by: Emil Tsalapatis <emil@etsalapatis.com>
    Link: https://lore.kernel.org/r/20260417143353.838911-1-puranjay@kernel.org
    Signed-off-by: Alexei Starovoitov <ast@kernel.org>

diff --git a/arch/arm/net/bpf_jit_32.c b/arch/arm/net/bpf_jit_32.c
index 1628b6fc70a4..9ede81afbc50 100644
--- a/arch/arm/net/bpf_jit_32.c
+++ b/arch/arm/net/bpf_jit_32.c
@@ -1852,6 +1852,9 @@ static int build_insn(const struct bpf_insn *insn, struct jit_ctx *ctx)
 	{
 		u64 val = (u32)imm | (u64)insn[1].imm << 32;

+		if (insn->src_reg == BPF_PSEUDO_FUNC)
+			goto notyet;
+
 		emit_a32_mov_i64(dst, val, ctx);

 		return 1;
@@ -2055,6 +2058,9 @@ static int build_insn(const struct bpf_insn *insn, struct jit_ctx *ctx)
 		const s8 *r5 = bpf2a32[BPF_REG_5];
 		const u32 func = (u32)__bpf_call_base + (u32)imm;

+		if (insn->src_reg == BPF_PSEUDO_CALL)
+			goto notyet;
+
 		emit_a32_mov_r64(true, r0, r1, ctx);
 		emit_a32_mov_r64(true, r1, r2, ctx);
 		emit_push_r64(r5, ctx);