Linux Kernel Selftest development
 help / color / mirror / Atom feed
* [PATCH bpf 0/2] bpf, arm64: fix the exception callback's frame pointer
@ 2026-09-04  7:02 Donggeun Yoo
  2026-09-04  7:02 ` [PATCH bpf 1/2] bpf, arm64: set up the frame pointer for the exception callback Donggeun Yoo
  2026-09-04  7:02 ` [PATCH bpf 2/2] selftests/bpf: cover the exception callback using its own BPF stack Donggeun Yoo
  0 siblings, 2 replies; 5+ messages in thread
From: Donggeun Yoo @ 2026-09-04  7:02 UTC (permalink / raw)
  To: Alexei Starovoitov, Andrii Nakryiko, Catalin Marinas,
	Daniel Borkmann, Eduard Zingerman, Emil Tsalapatis, Ihor Solodrai,
	Jiri Olsa, Kumar Kartikeya Dwivedi, Mark Rutland,
	Martin KaFai Lau, Puranjay Mohan, Shuah Khan, Song Liu,
	Will Deacon, Xu Kuohai, Yonghong Song
  Cc: bpf, linux-arm-kernel, linux-kernel, linux-kselftest, Xu Kuohai,
	Donggeun Yoo

The arm64 JIT does not set BPF_REG_FP in the prologue of an exception
callback, so the callback runs with whatever x25 held when bpf_throw()
was called. A callback that materializes the register, for instance to
pass the address of a local variable to a helper, then works on the
frame of the subprogram that threw.

Patch 1 sets ctx->fp_used on that path, the same fix commit b114fcee766d
("bpf, arm64: Fix fp initialization for exception boundary") made for
the exception boundary. Patch 2 adds a selftest that reaches the case.

Tested on aarch64 under QEMU with vmtest.sh. Without patch 1 the new
test panics the kernel, because the address handed to the helper lands
on the helper's own saved return address:

  pc : 0x1234
  lr : 0x1234
  Call trace:
   0x1234 (P)
   bpf_test_run+0x188/0x3e0
   bpf_prog_test_run_skb+0x47c/0x998
   __sys_bpf+0xbdc/0xdd8
  Kernel panic - not syncing: Oops: Fatal exception in interrupt

With patch 1 applied the whole group passes:

  #116/11  exceptions/exception_throw_subprog_stack_cb:OK
  #116     exceptions:OK
  Summary: 1/118 PASSED, 0 SKIPPED, 0/0 FAILED

Not tested on other architectures.

Donggeun Yoo (2):
  bpf, arm64: set up the frame pointer for the exception callback
  selftests/bpf: cover the exception callback using its own BPF stack

 arch/arm64/net/bpf_jit_comp.c                 |  2 ++
 .../selftests/bpf/prog_tests/exceptions.c     |  1 +
 .../testing/selftests/bpf/progs/exceptions.c  | 29 +++++++++++++++++++
 3 files changed, 32 insertions(+)

-- 
2.53.0


^ permalink raw reply	[flat|nested] 5+ messages in thread

* [PATCH bpf 1/2] bpf, arm64: set up the frame pointer for the exception callback
  2026-09-04  7:02 [PATCH bpf 0/2] bpf, arm64: fix the exception callback's frame pointer Donggeun Yoo
@ 2026-09-04  7:02 ` Donggeun Yoo
  2026-09-04  7:02 ` [PATCH bpf 2/2] selftests/bpf: cover the exception callback using its own BPF stack Donggeun Yoo
  1 sibling, 0 replies; 5+ messages in thread
From: Donggeun Yoo @ 2026-09-04  7:02 UTC (permalink / raw)
  To: Alexei Starovoitov, Andrii Nakryiko, Catalin Marinas,
	Daniel Borkmann, Eduard Zingerman, Emil Tsalapatis, Ihor Solodrai,
	Jiri Olsa, Kumar Kartikeya Dwivedi, Mark Rutland,
	Martin KaFai Lau, Puranjay Mohan, Shuah Khan, Song Liu,
	Will Deacon, Xu Kuohai, Yonghong Song
  Cc: bpf, linux-arm-kernel, linux-kernel, linux-kselftest, Xu Kuohai,
	Donggeun Yoo

A program acting as exception boundary saves all callee-saved registers,
so build_prologue() takes the exception_cb path and never calls
push_callee_regs(). That is the only place find_used_callee_regs() runs,
and with it the only place ctx->fp_used is set, so the callback prologue
does not emit the

  mov x25, sp

that points BPF_REG_FP at the frame the callback runs on. x25 keeps
whatever it held when bpf_throw() was called. If the throw came from a
subprogram that uses its own BPF stack, that is the subprogram's frame
pointer, and since the subprogram never returns it never restores x25
either.

Stack accesses through BPF_REG_FP are rewritten to be stack pointer
relative, so those still land in the callback's own frame. Materializing
the register does not: a callback that passes the address of a local
variable to a helper hands over an address in the dead subprogram's
frame. That address is below the callback's stack pointer by then, and
the helper's own call chain covers it, so the helper can write over its
own return address. 0x1234 below is the value the helper was asked to
store:

  pc : 0x1234
  lr : 0x1234
  Call trace:
   0x1234 (P)
   bpf_test_run+0x188/0x3e0
   bpf_prog_test_run_skb+0x47c/0x998
   __sys_bpf+0xbdc/0xdd8
  Kernel panic - not syncing: Oops: Fatal exception in interrupt

Set ctx->fp_used on the exception callback path so that the existing code
further down sets x25 from the stack pointer. The epilogue restores it
from the main program's save area along with the other callee-saved
registers, as it already does. x86 sets the frame pointer for the
callback from the argument it is passed, and powerpc computes it from
the stack pointer.

Fixes: 5d4fa9ec5643 ("bpf, arm64: Avoid blindly saving/restoring all callee-saved registers")
Signed-off-by: Donggeun Yoo <donggeunyoo.kernel@gmail.com>
---
 arch/arm64/net/bpf_jit_comp.c | 2 ++
 1 file changed, 2 insertions(+)

diff --git a/arch/arm64/net/bpf_jit_comp.c b/arch/arm64/net/bpf_jit_comp.c
index c18e005a41db..c5f55d6161fe 100644
--- a/arch/arm64/net/bpf_jit_comp.c
+++ b/arch/arm64/net/bpf_jit_comp.c
@@ -600,6 +600,8 @@ static int build_prologue(struct jit_ctx *ctx, bool ebpf_from_cbpf)
 		 * 12 registers are on the stack
 		 */
 		emit(A64_SUB_I(1, A64_SP, A64_FP, 96), ctx);
+		/* The callback may use its own BPF stack, set up fp for it. */
+		ctx->fp_used = true;
 	}
 
 	/* Stack must be multiples of 16B */
-- 
2.53.0


^ permalink raw reply related	[flat|nested] 5+ messages in thread

* [PATCH bpf 2/2] selftests/bpf: cover the exception callback using its own BPF stack
  2026-09-04  7:02 [PATCH bpf 0/2] bpf, arm64: fix the exception callback's frame pointer Donggeun Yoo
  2026-09-04  7:02 ` [PATCH bpf 1/2] bpf, arm64: set up the frame pointer for the exception callback Donggeun Yoo
@ 2026-09-04  7:02 ` Donggeun Yoo
  2026-09-04  8:09   ` bot+bpf-ci
  1 sibling, 1 reply; 5+ messages in thread
From: Donggeun Yoo @ 2026-09-04  7:02 UTC (permalink / raw)
  To: Alexei Starovoitov, Andrii Nakryiko, Catalin Marinas,
	Daniel Borkmann, Eduard Zingerman, Emil Tsalapatis, Ihor Solodrai,
	Jiri Olsa, Kumar Kartikeya Dwivedi, Mark Rutland,
	Martin KaFai Lau, Puranjay Mohan, Shuah Khan, Song Liu,
	Will Deacon, Xu Kuohai, Yonghong Song
  Cc: bpf, linux-arm-kernel, linux-kernel, linux-kselftest, Xu Kuohai,
	Donggeun Yoo

The existing exception tests do not reach a callback that materializes
BPF_REG_FP into a register. They either throw from the main program,
where BPF_REG_FP already holds the value the callback needs, or use a
callback whose only stack accesses are frame pointer relative, which the
arm64 JIT rewrites to be stack pointer relative.

Add a test that throws from a subprogram using its own BPF stack, with a
callback that hands the address of a local variable to
bpf_probe_read_kernel(). The helper and the callback have to name the
same slot for the value read back to be the one the helper stored.

Signed-off-by: Donggeun Yoo <donggeunyoo.kernel@gmail.com>
---
 .../selftests/bpf/prog_tests/exceptions.c     |  1 +
 .../testing/selftests/bpf/progs/exceptions.c  | 29 +++++++++++++++++++
 2 files changed, 30 insertions(+)

diff --git a/tools/testing/selftests/bpf/prog_tests/exceptions.c b/tools/testing/selftests/bpf/prog_tests/exceptions.c
index 3588d6f97fd4..639866ce09a9 100644
--- a/tools/testing/selftests/bpf/prog_tests/exceptions.c
+++ b/tools/testing/selftests/bpf/prog_tests/exceptions.c
@@ -55,6 +55,7 @@ static void test_exceptions_success(void)
 	RUN_SUCCESS(exception_ext, 0);
 	RUN_SUCCESS(exception_ext_mod_cb_runtime, 35);
 	RUN_SUCCESS(exception_throw_subprog, 1);
+	RUN_SUCCESS(exception_throw_subprog_stack_cb, 0x1234);
 	RUN_SUCCESS(exception_assert_nz_gfunc, 1);
 	RUN_SUCCESS(exception_assert_zero_gfunc, 1);
 	RUN_SUCCESS(exception_assert_neg_gfunc, 1);
diff --git a/tools/testing/selftests/bpf/progs/exceptions.c b/tools/testing/selftests/bpf/progs/exceptions.c
index c8d716fbd419..cac4e082139a 100644
--- a/tools/testing/selftests/bpf/progs/exceptions.c
+++ b/tools/testing/selftests/bpf/progs/exceptions.c
@@ -212,6 +212,35 @@ int exception_throw_subprog(struct __sk_buff *ctx)
 	return 0;
 }
 
+__u64 exception_cb_stack_src = 0x1234;
+
+/* The address handed to the helper has to be this callback's own stack
+ * slot, not one from a frame that is already gone.
+ */
+__noinline int exception_cb_stack(u64 cookie)
+{
+	volatile __u64 val = 0xdead;
+
+	bpf_probe_read_kernel((void *)&val, sizeof(val), &exception_cb_stack_src);
+	return val;
+}
+
+/* Throws from a subprogram that has a stack of its own. */
+__noinline static int throwing_subprog_stack(struct __sk_buff *ctx)
+{
+	volatile __u64 pad[4] = {};
+
+	bpf_throw(pad[0]);
+	return 0;
+}
+
+SEC("tc")
+__exception_cb(exception_cb_stack)
+int exception_throw_subprog_stack_cb(struct __sk_buff *ctx)
+{
+	return throwing_subprog_stack(ctx);
+}
+
 __noinline int assert_nz_gfunc(u64 c)
 {
 	volatile u64 cookie = c;
-- 
2.53.0


^ permalink raw reply related	[flat|nested] 5+ messages in thread

* Re: [PATCH bpf 2/2] selftests/bpf: cover the exception callback using its own BPF stack
  2026-09-04  7:02 ` [PATCH bpf 2/2] selftests/bpf: cover the exception callback using its own BPF stack Donggeun Yoo
@ 2026-09-04  8:09   ` bot+bpf-ci
  2026-09-04  9:27     ` Donggeun Yoo
  0 siblings, 1 reply; 5+ messages in thread
From: bot+bpf-ci @ 2026-09-04  8:09 UTC (permalink / raw)
  To: donggeunyoo.kernel, ast, andrii, catalin.marinas, daniel, eddyz87,
	emil, ihor.solodrai, jolsa, memxor, mark.rutland, martin.lau,
	puranjay, shuah, song, will, xukuohai, yonghong.song
  Cc: bpf, linux-arm-kernel, linux-kernel, linux-kselftest, xukuohai,
	donggeunyoo.kernel, ast, andrii, daniel, martin.lau, eddyz87,
	yonghong.song, mason, ihor.solodrai

[-- Attachment #1: Type: text/plain, Size: 1996 bytes --]

> diff --git a/tools/testing/selftests/bpf/progs/exceptions.c b/tools/testing/selftests/bpf/progs/exceptions.c
> index c8d716fbd4191..cac4e082139a7 100644
> --- a/tools/testing/selftests/bpf/progs/exceptions.c
> +++ b/tools/testing/selftests/bpf/progs/exceptions.c
> @@ -212,6 +212,35 @@ int exception_throw_subprog(struct __sk_buff *ctx)
>  	return 0;
>  }
>
> +__u64 exception_cb_stack_src = 0x1234;
> +
> +/* The address handed to the helper has to be this callback's own stack
> + * slot, not one from a frame that is already gone.
> + */
> +__noinline int exception_cb_stack(u64 cookie)
> +{
> +	volatile __u64 val = 0xdead;
> +
> +	bpf_probe_read_kernel((void *)&val, sizeof(val), &exception_cb_stack_src);
> +	return val;
> +}
> +
> +/* Throws from a subprogram that has a stack of its own. */
> +__noinline static int throwing_subprog_stack(struct __sk_buff *ctx)
> +{
> +	volatile __u64 pad[4] = {};

This isn't a bug, but would 'u64' fit the surrounding declarations in this
file a little better than '__u64' for the three new locals/globals?
Looking at the rest of progs/exceptions.c, every other scalar declaration
uses the short kernel spelling: 'static __noinline int static_func(u64 i)',
'__noinline int exception_cb_mod(u64 cookie)', 'volatile u64 cookie = c;',
and 'u64 time = bpf_ktime_get_ns();'. The new code at lines 215, 222, and
231 is the only place in the file that switches to the '__u64' spelling,
and it sits directly next to 'exception_cb_stack(u64 cookie)' which uses
the other form in the same signature.

> +
> +	bpf_throw(pad[0]);
> +	return 0;
> +}
> +
> +SEC("tc")
> +__exception_cb(exception_cb_stack)
> +int exception_throw_subprog_stack_cb(struct __sk_buff *ctx)
> +{
> +	return throwing_subprog_stack(ctx);
> +}
> +


---
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/33848479344

^ permalink raw reply	[flat|nested] 5+ messages in thread

* Re: [PATCH bpf 2/2] selftests/bpf: cover the exception callback using its own BPF stack
  2026-09-04  8:09   ` bot+bpf-ci
@ 2026-09-04  9:27     ` Donggeun Yoo
  0 siblings, 0 replies; 5+ messages in thread
From: Donggeun Yoo @ 2026-09-04  9:27 UTC (permalink / raw)
  To: bot+bpf-ci, ast, andrii, daniel, martin.lau, eddyz87, song,
	yonghong.song, jolsa, memxor, puranjay, xukuohai, catalin.marinas,
	will, mark.rutland, shuah, emil, ihor.solodrai
  Cc: bpf, linux-arm-kernel, linux-kernel, linux-kselftest, xukuohai,
	martin.lau, mason

On Fri, Sep 04, 2026 at 08:09:36AM +0000, bot+bpf-ci@kernel.org wrote:
> This isn't a bug, but would 'u64' fit the surrounding declarations in this
> file a little better than '__u64' for the three new locals/globals?

Right, the rest of exceptions.c uses u64. I'll switch the three new
declarations. Holding the respin for now in case more feedback comes in.

^ permalink raw reply	[flat|nested] 5+ messages in thread

end of thread, other threads:[~2026-09-04  9:27 UTC | newest]

Thread overview: 5+ messages (download: mbox.gz follow: Atom feed
-- links below jump to the message on this page --
2026-09-04  7:02 [PATCH bpf 0/2] bpf, arm64: fix the exception callback's frame pointer Donggeun Yoo
2026-09-04  7:02 ` [PATCH bpf 1/2] bpf, arm64: set up the frame pointer for the exception callback Donggeun Yoo
2026-09-04  7:02 ` [PATCH bpf 2/2] selftests/bpf: cover the exception callback using its own BPF stack Donggeun Yoo
2026-09-04  8:09   ` bot+bpf-ci
2026-09-04  9:27     ` Donggeun Yoo

This is a public inbox, see mirroring instructions
for how to clone and mirror all data and code used for this inbox