* [PATCH bpf-next] bpf, arm32: Reject BPF_PSEUDO_CALL in the JIT
@ 2026-04-17 10:30 Puranjay Mohan
2026-04-17 11:17 ` bot+bpf-ci
2026-04-17 11:21 ` Daniel Borkmann
0 siblings, 2 replies; 4+ messages in thread
From: Puranjay Mohan @ 2026-04-17 10:30 UTC (permalink / raw)
To: bpf, linux-arm-kernel
Cc: Puranjay Mohan, Jonas Rebmann, Alexei Starovoitov,
Daniel Borkmann, Andrii Nakryiko, Martin KaFai Lau,
Eduard Zingerman, Kumar Kartikeya Dwivedi, Song Liu, Russell King,
kernel
The ARM32 BPF JIT does not support BPF-to-BPF function calls
(subprogram calls). When insn->src_reg == BPF_PSEUDO_CALL, the
imm field contains a pc-relative offset to another BPF function,
not a helper function index.
When a program containing BPF-to-BPF calls is loaded, the verifier
invokes bpf_jit_subprogs() which calls bpf_int_jit_compile() for each
subprogram. Since ARM32 does not reject BPF_PSEUDO_CALL, the JIT
silently emits code for the call using the wrong address computation:
func = __bpf_call_base + imm
where imm is actually a pc-relative subprogram offset, producing
a bogus function pointer. Because build_body() reports success,
bpf_jit_binary_alloc() is reached and a JIT image is allocated.
ARM32 also 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 BPF_PSEUDO_CALL early in build_insn(),
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.
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>
---
arch/arm/net/bpf_jit_32.c | 2 ++
1 file changed, 2 insertions(+)
diff --git a/arch/arm/net/bpf_jit_32.c b/arch/arm/net/bpf_jit_32.c
index deeb8f292454..91fef10e88bc 100644
--- a/arch/arm/net/bpf_jit_32.c
+++ b/arch/arm/net/bpf_jit_32.c
@@ -2047,6 +2047,8 @@ static int build_insn(const struct bpf_insn *insn, struct jit_ctx *ctx)
/* function call */
case BPF_JMP | BPF_CALL:
{
+ if (insn->src_reg == BPF_PSEUDO_CALL)
+ goto notyet;
const s8 *r0 = bpf2a32[BPF_REG_0];
const s8 *r1 = bpf2a32[BPF_REG_1];
const s8 *r2 = bpf2a32[BPF_REG_2];
base-commit: 1f5ffc672165ff851063a5fd044b727ab2517ae3
--
2.52.0
^ permalink raw reply related [flat|nested] 4+ messages in thread* Re: [PATCH bpf-next] bpf, arm32: Reject BPF_PSEUDO_CALL in the JIT
2026-04-17 10:30 [PATCH bpf-next] bpf, arm32: Reject BPF_PSEUDO_CALL in the JIT Puranjay Mohan
@ 2026-04-17 11:17 ` bot+bpf-ci
2026-04-17 14:16 ` Puranjay Mohan
2026-04-17 11:21 ` Daniel Borkmann
1 sibling, 1 reply; 4+ messages in thread
From: bot+bpf-ci @ 2026-04-17 11:17 UTC (permalink / raw)
To: puranjay, bpf, linux-arm-kernel
Cc: puranjay, jre, ast, daniel, andrii, martin.lau, eddyz87, memxor,
song, linux, kernel, ast, andrii, daniel, martin.lau, eddyz87,
yonghong.song, clm, ihor.solodrai
[-- Attachment #1: Type: text/plain, Size: 1409 bytes --]
> Fixes: 1c2a088a6626 ("bpf: x64: add JIT support for multi-function programs")
Is this the correct Fixes tag? That commit only modified x64 JIT code
and BPF core infrastructure for BPF-to-BPF calls - it never touched
arch/arm/net/bpf_jit_32.c.
The actual bug was introduced by commit 39c13c204bb1 ("arm: eBPF JIT
compiler") from August 2017, which added the ARM32 eBPF JIT with
BPF_CALL handling but failed to reject BPF_PSEUDO_CALL. The code has
been incorrectly computing function addresses as 'func = __bpf_call_base
+ imm' for BPF_PSEUDO_CALL since the ARM32 JIT was first introduced.
Suggested: Fixes: 39c13c204bb1 ("arm: eBPF JIT compiler")
> diff --git a/arch/arm/net/bpf_jit_32.c b/arch/arm/net/bpf_jit_32.c
> index deeb8f292454..91fef10e88bc 100644
> --- a/arch/arm/net/bpf_jit_32.c
> +++ b/arch/arm/net/bpf_jit_32.c
> @@ -2047,6 +2047,8 @@ static int build_insn(const struct bpf_insn *insn, struct jit_ctx *ctx)
> /* function call */
> case BPF_JMP | BPF_CALL:
> {
> + if (insn->src_reg == BPF_PSEUDO_CALL)
> + goto notyet;
> const s8 *r0 = bpf2a32[BPF_REG_0];
> const s8 *r1 = bpf2a32[BPF_REG_1];
> const s8 *r2 = bpf2a32[BPF_REG_2];
---
AI reviewed your patch. Please fix the bug or email reply why it's not a bug.
See: https://github.com/kernel-patches/vmtest/blob/master/ci/claude/README.md
CI run summary: https://github.com/kernel-patches/bpf/actions/runs/24561211195
^ permalink raw reply [flat|nested] 4+ messages in thread* Re: [PATCH bpf-next] bpf, arm32: Reject BPF_PSEUDO_CALL in the JIT
2026-04-17 11:17 ` bot+bpf-ci
@ 2026-04-17 14:16 ` Puranjay Mohan
0 siblings, 0 replies; 4+ messages in thread
From: Puranjay Mohan @ 2026-04-17 14:16 UTC (permalink / raw)
To: bot+bpf-ci
Cc: bpf, linux-arm-kernel, jre, ast, daniel, andrii, martin.lau,
eddyz87, memxor, song, linux, kernel, martin.lau, yonghong.song,
clm, ihor.solodrai
On Fri, Apr 17, 2026 at 12:17 PM <bot+bpf-ci@kernel.org> wrote:
>
> > Fixes: 1c2a088a6626 ("bpf: x64: add JIT support for multi-function programs")
>
> Is this the correct Fixes tag? That commit only modified x64 JIT code
> and BPF core infrastructure for BPF-to-BPF calls - it never touched
> arch/arm/net/bpf_jit_32.c.
>
> The actual bug was introduced by commit 39c13c204bb1 ("arm: eBPF JIT
> compiler") from August 2017, which added the ARM32 eBPF JIT with
> BPF_CALL handling but failed to reject BPF_PSEUDO_CALL. The code has
> been incorrectly computing function addresses as 'func = __bpf_call_base
> + imm' for BPF_PSEUDO_CALL since the ARM32 JIT was first introduced.
>
> Suggested: Fixes: 39c13c204bb1 ("arm: eBPF JIT compiler")
I knew Claude would say this!
39c13c204bb1 ("arm: eBPF JIT compiler") came before BPF_PSEUDO_CALL
was introduced, so the bug was introduced when jit_subprogs() was
added.
The fixes tag in the patch is correct!
^ permalink raw reply [flat|nested] 4+ messages in thread
* Re: [PATCH bpf-next] bpf, arm32: Reject BPF_PSEUDO_CALL in the JIT
2026-04-17 10:30 [PATCH bpf-next] bpf, arm32: Reject BPF_PSEUDO_CALL in the JIT Puranjay Mohan
2026-04-17 11:17 ` bot+bpf-ci
@ 2026-04-17 11:21 ` Daniel Borkmann
1 sibling, 0 replies; 4+ messages in thread
From: Daniel Borkmann @ 2026-04-17 11:21 UTC (permalink / raw)
To: Puranjay Mohan, bpf, linux-arm-kernel
Cc: Jonas Rebmann, Alexei Starovoitov, Andrii Nakryiko,
Martin KaFai Lau, Eduard Zingerman, Kumar Kartikeya Dwivedi,
Song Liu, Russell King, kernel
On 4/17/26 12:30 PM, Puranjay Mohan wrote:
> The ARM32 BPF JIT does not support BPF-to-BPF function calls
> (subprogram calls). When insn->src_reg == BPF_PSEUDO_CALL, the
> imm field contains a pc-relative offset to another BPF function,
> not a helper function index.
>
> When a program containing BPF-to-BPF calls is loaded, the verifier
> invokes bpf_jit_subprogs() which calls bpf_int_jit_compile() for each
> subprogram. Since ARM32 does not reject BPF_PSEUDO_CALL, the JIT
> silently emits code for the call using the wrong address computation:
>
> func = __bpf_call_base + imm
>
> where imm is actually a pc-relative subprogram offset, producing
> a bogus function pointer. Because build_body() reports success,
> bpf_jit_binary_alloc() is reached and a JIT image is allocated.
>
> ARM32 also 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 BPF_PSEUDO_CALL early in build_insn(),
> 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.
>
> 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>
Acked-by: Daniel Borkmann <daniel@iogearbox.net>
^ permalink raw reply [flat|nested] 4+ messages in thread
end of thread, other threads:[~2026-04-17 14:16 UTC | newest]
Thread overview: 4+ messages (download: mbox.gz follow: Atom feed
-- links below jump to the message on this page --
2026-04-17 10:30 [PATCH bpf-next] bpf, arm32: Reject BPF_PSEUDO_CALL in the JIT Puranjay Mohan
2026-04-17 11:17 ` bot+bpf-ci
2026-04-17 14:16 ` Puranjay Mohan
2026-04-17 11:21 ` Daniel Borkmann
This is a public inbox, see mirroring instructions
for how to clone and mirror all data and code used for this inbox