All of lore.kernel.org
 help / color / mirror / Atom feed
* [PATCH bpf-next v3 0/7] bpf: Follow-up fixes for stack argument support
@ 2026-05-15 22:50 Yonghong Song
  2026-05-15 22:50 ` [PATCH bpf-next v3 1/7] bpf: Validate outgoing stack args when btf_prepare_func_args fails Yonghong Song
                   ` (6 more replies)
  0 siblings, 7 replies; 8+ messages in thread
From: Yonghong Song @ 2026-05-15 22:50 UTC (permalink / raw)
  To: bpf
  Cc: Alexei Starovoitov, Andrii Nakryiko, Daniel Borkmann, kernel-team,
	Martin KaFai Lau

Commit cd59fa185a03 ("bpf: Support stack arguments for BPF functions and kfuncs")
added stack argument support for bpf functions and kfuncs. This patch set
is to fix various issues related to stack arguments, mainly include:
  - Validate outgoing stack args when btf_prepare_func_args fails
  - Fix arg_track_join log to use sa prefix for stack arg slots
  - Clean up redundant stack arg checks for non-JITed programs
  - Fix exception unwinding with outgoing stack arguments

Changelog:
  v2 -> v3:
    - v2: https://lore.kernel.org/bpf/20260515014958.1186132-1-yonghong.song@linux.dev/
    - Add additional fix (fix arg_track_join log, fix exception with outgoing stack
      arguments, some cleanup, etc.).
  v1 -> v2:
    - v1: https://lore.kernel.org/bpf/20260514184827.1619863-1-yonghong.song@linux.dev/
    - Remove 'Reported-by: Sashiko <sashiko-bot@kernel.org>'.
    - Add proper fix tag.

Yonghong Song (7):
  bpf: Validate outgoing stack args when btf_prepare_func_args fails
  selftests/bpf: Add test for stack arg read without caller write
  selftests/bpf: Log arg_track_join for stack arg slots in liveness
    analysis
  bpf: Fix arg_track_join log to use sa prefix for stack arg slots
  bpf: Clean up redundant stack arg checks for non-JITed programs
  bpf,x86: Fix exception unwinding with outgoing stack arguments
  selftests/bpf: Add exception tests with stack arguments

 arch/x86/net/bpf_jit_comp.c                   |   9 +-
 include/linux/bpf.h                           |   3 +-
 kernel/bpf/core.c                             |   2 +-
 kernel/bpf/fixups.c                           |   3 +-
 kernel/bpf/helpers.c                          |   2 +-
 kernel/bpf/liveness.c                         |   4 +-
 kernel/bpf/verifier.c                         |  10 +-
 .../selftests/bpf/prog_tests/exceptions.c     |   7 ++
 .../bpf/progs/btf__verifier_stack_arg_order.c |   8 ++
 .../testing/selftests/bpf/progs/exceptions.c  | 113 ++++++++++++++++++
 .../selftests/bpf/progs/verifier_stack_arg.c  |   8 +-
 .../bpf/progs/verifier_stack_arg_order.c      |  58 +++++++++
 12 files changed, 216 insertions(+), 11 deletions(-)

-- 
2.53.0-Meta


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

* [PATCH bpf-next v3 1/7] bpf: Validate outgoing stack args when btf_prepare_func_args fails
  2026-05-15 22:50 [PATCH bpf-next v3 0/7] bpf: Follow-up fixes for stack argument support Yonghong Song
@ 2026-05-15 22:50 ` Yonghong Song
  2026-05-15 22:50 ` [PATCH bpf-next v3 2/7] selftests/bpf: Add test for stack arg read without caller write Yonghong Song
                   ` (5 subsequent siblings)
  6 siblings, 0 replies; 8+ messages in thread
From: Yonghong Song @ 2026-05-15 22:50 UTC (permalink / raw)
  To: bpf
  Cc: Alexei Starovoitov, Andrii Nakryiko, Daniel Borkmann, kernel-team,
	Martin KaFai Lau

btf_prepare_func_args() sets sub->arg_cnt before validating arg types.
If validation fails (e.g. unsupported pointer type in a static subprog),
check_outgoing_stack_args() is skipped because btf_check_func_arg_match()
returns early. For static subprogs, check_func_call() ignores non-EFAULT
errors and proceeds with the call.

This causes the callee to read stack arg slots that the caller never
stored or not initialized, potentially dereferencing NULL caller->stack_arg_regs
or getting no-initialized value.

To fix the issue, when btf_prepare_func_args() fails and the subprog expects
stack args, call check_outgoing_stack_args() to verify the caller initialized
the slots. Return -EFAULT on failure so the error is not ignored.

Fixes: 3ab5bd317ee2 ("bpf: Set sub->arg_cnt earlier in btf_prepare_func_args()")
Signed-off-by: Yonghong Song <yonghong.song@linux.dev>
---
 kernel/bpf/verifier.c | 10 ++++++++--
 1 file changed, 8 insertions(+), 2 deletions(-)

diff --git a/kernel/bpf/verifier.c b/kernel/bpf/verifier.c
index 76a07f09ab64..8dd79b735a69 100644
--- a/kernel/bpf/verifier.c
+++ b/kernel/bpf/verifier.c
@@ -9118,11 +9118,17 @@ static int btf_check_func_arg_match(struct bpf_verifier_env *env, int subprog,
 	struct bpf_func_state *caller = cur_func(env);
 	struct bpf_verifier_log *log = &env->log;
 	u32 i;
-	int ret;
+	int ret, err;
 
 	ret = btf_prepare_func_args(env, subprog);
-	if (ret)
+	if (ret) {
+		if (bpf_in_stack_arg_cnt(sub) > 0) {
+			err = check_outgoing_stack_args(env, caller, sub->arg_cnt);
+			if (err)
+				return err;
+		}
 		return ret;
+	}
 
 	ret = check_outgoing_stack_args(env, caller, sub->arg_cnt);
 	if (ret)
-- 
2.53.0-Meta


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

* [PATCH bpf-next v3 2/7] selftests/bpf: Add test for stack arg read without caller write
  2026-05-15 22:50 [PATCH bpf-next v3 0/7] bpf: Follow-up fixes for stack argument support Yonghong Song
  2026-05-15 22:50 ` [PATCH bpf-next v3 1/7] bpf: Validate outgoing stack args when btf_prepare_func_args fails Yonghong Song
@ 2026-05-15 22:50 ` Yonghong Song
  2026-05-15 22:50 ` [PATCH bpf-next v3 3/7] selftests/bpf: Log arg_track_join for stack arg slots in liveness analysis Yonghong Song
                   ` (4 subsequent siblings)
  6 siblings, 0 replies; 8+ messages in thread
From: Yonghong Song @ 2026-05-15 22:50 UTC (permalink / raw)
  To: bpf
  Cc: Alexei Starovoitov, Andrii Nakryiko, Daniel Borkmann, kernel-team,
	Martin KaFai Lau

Add negative tests for the outgoing stack arg validation.
A static subprog with a 'long *' arg causes
btf_prepare_func_args() to fail after setting arg_cnt. The
validation ensures check_outgoing_stack_args() still runs.

Also update two existing tests (release_ref, stale_pkt_ptr) whose
expected error messages changed: invalidated stack arg slots are now
caught by check_outgoing_stack_args() at the call site instead of
at the callee's dereference.

Signed-off-by: Yonghong Song <yonghong.song@linux.dev>
---
 .../bpf/progs/btf__verifier_stack_arg_order.c |  8 +++
 .../selftests/bpf/progs/verifier_stack_arg.c  |  4 +-
 .../bpf/progs/verifier_stack_arg_order.c      | 58 +++++++++++++++++++
 3 files changed, 68 insertions(+), 2 deletions(-)

diff --git a/tools/testing/selftests/bpf/progs/btf__verifier_stack_arg_order.c b/tools/testing/selftests/bpf/progs/btf__verifier_stack_arg_order.c
index da34e8456b6c..99bc115f8380 100644
--- a/tools/testing/selftests/bpf/progs/btf__verifier_stack_arg_order.c
+++ b/tools/testing/selftests/bpf/progs/btf__verifier_stack_arg_order.c
@@ -21,6 +21,10 @@ int subprog_pruning_call_before_load_6args(int a, int b, int c, int d, int e, in
 	return a + b + c + d + e + f;
 }
 
+void subprog_bad_ptr_7args(long *a, int b, int c, int d, int e, int f, int g)
+{
+}
+
 #else
 
 int subprog_bad_order_6args(void)
@@ -38,4 +42,8 @@ int subprog_pruning_call_before_load_6args(void)
 	return 0;
 }
 
+void subprog_bad_ptr_7args(void)
+{
+}
+
 #endif
diff --git a/tools/testing/selftests/bpf/progs/verifier_stack_arg.c b/tools/testing/selftests/bpf/progs/verifier_stack_arg.c
index d43a9b42034c..d45339b83795 100644
--- a/tools/testing/selftests/bpf/progs/verifier_stack_arg.c
+++ b/tools/testing/selftests/bpf/progs/verifier_stack_arg.c
@@ -152,7 +152,7 @@ __naked void stack_arg_pruning_type_mismatch(void)
 SEC("tc")
 __description("stack_arg: release_reference invalidates stack arg slot")
 __failure
-__msg("R{{[0-9]}} !read_ok")
+__msg("callee expects 6 args, stack arg1 is not initialized")
 __naked void stack_arg_release_ref(void)
 {
 	asm volatile (
@@ -201,7 +201,7 @@ __naked void stack_arg_release_ref(void)
 SEC("tc")
 __description("stack_arg: pkt pointer in stack arg slot invalidated after pull_data")
 __failure
-__msg("R{{[0-9]}} !read_ok")
+__msg("callee expects 6 args, stack arg1 is not initialized")
 __naked void stack_arg_stale_pkt_ptr(void)
 {
 	asm volatile (
diff --git a/tools/testing/selftests/bpf/progs/verifier_stack_arg_order.c b/tools/testing/selftests/bpf/progs/verifier_stack_arg_order.c
index 1240cf8a40d6..c9fe4857da3f 100644
--- a/tools/testing/selftests/bpf/progs/verifier_stack_arg_order.c
+++ b/tools/testing/selftests/bpf/progs/verifier_stack_arg_order.c
@@ -112,6 +112,64 @@ __naked void stack_arg_pruning_load_after_call(void)
 	);
 }
 
+/*
+ * "bad_ptr": the first arg is 'long *', which is not a recognized pointer
+ * type for static subprogs (not ctx, dynptr, or tagged).  btf_prepare_func_args()
+ * sets arg_cnt = 7 / stack_arg_cnt = 2, then fails with -EINVAL.  The subprog
+ * is marked unreliable but the call still proceeds for static subprogs.
+ */
+__noinline __used __naked
+static void subprog_bad_ptr_7args(long *a, int b, int c, int d, int e, int f, int g)
+{
+	asm volatile (
+		"r0 = *(u64 *)(r11 + 8);"
+		"r1 = *(u64 *)(r11 + 16);"
+		"exit;"
+		::: __clobber_all
+	);
+}
+
+SEC("tc")
+__description("stack_arg: read without caller write")
+__failure
+__msg("callee expects 7 args, stack arg1 is not initialized")
+__btf_func_path("btf__verifier_stack_arg_order.bpf.o")
+__naked void stack_arg_read_without_write_1(void)
+{
+	asm volatile (
+		"r1 = 0;"
+		"r2 = 0;"
+		"r3 = 0;"
+		"r4 = 0;"
+		"r5 = 0;"
+		"call subprog_bad_ptr_7args;"
+		"exit;"
+		::: __clobber_all
+	);
+}
+
+SEC("tc")
+__description("stack_arg: read with not-initialized caller write")
+__failure
+__msg("R0 !read_ok")
+__btf_func_path("btf__verifier_stack_arg_order.bpf.o")
+__naked void stack_arg_read_without_write_2(void)
+{
+	asm volatile (
+		"r1 = 0;"
+		"r2 = 0;"
+		"r3 = 0;"
+		"r4 = 0;"
+		"r5 = 0;"
+		"*(u64 *)(r11 - 8) = 0;"
+		"*(u64 *)(r11 - 16) = 0;"
+		"call subprog_bad_ptr_7args;"
+		"call subprog_bad_ptr_7args;"
+		"exit;"
+		::: __clobber_all
+	);
+}
+
 #else
 
 SEC("socket")
-- 
2.53.0-Meta


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

* [PATCH bpf-next v3 3/7] selftests/bpf: Log arg_track_join for stack arg slots in liveness analysis
  2026-05-15 22:50 [PATCH bpf-next v3 0/7] bpf: Follow-up fixes for stack argument support Yonghong Song
  2026-05-15 22:50 ` [PATCH bpf-next v3 1/7] bpf: Validate outgoing stack args when btf_prepare_func_args fails Yonghong Song
  2026-05-15 22:50 ` [PATCH bpf-next v3 2/7] selftests/bpf: Add test for stack arg read without caller write Yonghong Song
@ 2026-05-15 22:50 ` Yonghong Song
  2026-05-15 22:50 ` [PATCH bpf-next v3 4/7] bpf: Fix arg_track_join log to use sa prefix for stack arg slots Yonghong Song
                   ` (3 subsequent siblings)
  6 siblings, 0 replies; 8+ messages in thread
From: Yonghong Song @ 2026-05-15 22:50 UTC (permalink / raw)
  To: bpf
  Cc: Alexei Starovoitov, Andrii Nakryiko, Daniel Borkmann, kernel-team,
	Martin KaFai Lau

Commit 2af4e792773f ("bpf: Extend liveness analysis to track stack argument slots")
added stack arg supports. For selftest
  verifier_stack_arg/stack_arg: pruning with different stack arg types
the following are two arg JOIN messages:
  arg JOIN insn 9 -> 10 r1: fp0-8 + _ => fp0-8|fp0+0
  arg JOIN insn 9 -> 10 r11: fp0-8 + _ => fp0-8|fp0+0

Here the "r11:" label for stack arg slot 0 is misleading since r11
is a special register (BPF_REG_PARAMS). The next patch corrects
this to "sa0:", properly representing the 'stack arg slot 0'.

Signed-off-by: Yonghong Song <yonghong.song@linux.dev>
---
 tools/testing/selftests/bpf/progs/verifier_stack_arg.c | 4 +++-
 1 file changed, 3 insertions(+), 1 deletion(-)

diff --git a/tools/testing/selftests/bpf/progs/verifier_stack_arg.c b/tools/testing/selftests/bpf/progs/verifier_stack_arg.c
index d45339b83795..df0c3438529e 100644
--- a/tools/testing/selftests/bpf/progs/verifier_stack_arg.c
+++ b/tools/testing/selftests/bpf/progs/verifier_stack_arg.c
@@ -114,8 +114,10 @@ __naked void stack_arg_gap_at_minus8(void)
 
 SEC("tc")
 __description("stack_arg: pruning with different stack arg types")
-__failure
+__failure __log_level(2)
 __flag(BPF_F_TEST_STATE_FREQ)
+__msg("arg JOIN insn 9 -> 10 r1: fp0-8 + _ => fp0-8|fp0+0")
+__msg("arg JOIN insn 9 -> 10 r11: fp0-8 + _ => fp0-8|fp0+0")
 __msg("R{{[0-9]}} invalid mem access 'scalar'")
 __naked void stack_arg_pruning_type_mismatch(void)
 {
-- 
2.53.0-Meta


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

* [PATCH bpf-next v3 4/7] bpf: Fix arg_track_join log to use sa prefix for stack arg slots
  2026-05-15 22:50 [PATCH bpf-next v3 0/7] bpf: Follow-up fixes for stack argument support Yonghong Song
                   ` (2 preceding siblings ...)
  2026-05-15 22:50 ` [PATCH bpf-next v3 3/7] selftests/bpf: Log arg_track_join for stack arg slots in liveness analysis Yonghong Song
@ 2026-05-15 22:50 ` Yonghong Song
  2026-05-15 22:51 ` [PATCH bpf-next v3 5/7] bpf: Clean up redundant stack arg checks for non-JITed programs Yonghong Song
                   ` (2 subsequent siblings)
  6 siblings, 0 replies; 8+ messages in thread
From: Yonghong Song @ 2026-05-15 22:50 UTC (permalink / raw)
  To: bpf
  Cc: Alexei Starovoitov, Andrii Nakryiko, Daniel Borkmann, kernel-team,
	Martin KaFai Lau

arg_track_join() logs state transitions at CFG merge points. For
stack arg slots (r >= MAX_BPF_REG), it printed "r11:", "r12:", etc.,
which is misleading since r11 is a special register (BPF_REG_PARAMS)
not meaningful to the user.

Fix it to print "sa0:", "sa1:", etc., matching the per-instruction
transition log in arg_track_log() which already uses the "sa" prefix.

Update the existing stack_arg_pruning_type_mismatch selftest to expect
the corrected format.

Fixes: 2af4e792773f ("bpf: Extend liveness analysis to track stack argument slots")
Signed-off-by: Yonghong Song <yonghong.song@linux.dev>
---
 kernel/bpf/liveness.c                                  | 4 +++-
 tools/testing/selftests/bpf/progs/verifier_stack_arg.c | 2 +-
 2 files changed, 4 insertions(+), 2 deletions(-)

diff --git a/kernel/bpf/liveness.c b/kernel/bpf/liveness.c
index 7f4a0e4c2c49..0aadfbae0acc 100644
--- a/kernel/bpf/liveness.c
+++ b/kernel/bpf/liveness.c
@@ -806,7 +806,9 @@ static bool arg_track_join(struct bpf_verifier_env *env, int idx, int target, in
 		return true;
 
 	verbose(env, "arg JOIN insn %d -> %d ", idx, target);
-	if (r >= 0)
+	if (r >= MAX_BPF_REG)
+		verbose(env, "sa%d: ", r - MAX_BPF_REG);
+	else if (r >= 0)
 		verbose(env, "r%d: ", r);
 	else
 		verbose(env, "fp%+d: ", r * 8);
diff --git a/tools/testing/selftests/bpf/progs/verifier_stack_arg.c b/tools/testing/selftests/bpf/progs/verifier_stack_arg.c
index df0c3438529e..7e0ce5db28a0 100644
--- a/tools/testing/selftests/bpf/progs/verifier_stack_arg.c
+++ b/tools/testing/selftests/bpf/progs/verifier_stack_arg.c
@@ -117,7 +117,7 @@ __description("stack_arg: pruning with different stack arg types")
 __failure __log_level(2)
 __flag(BPF_F_TEST_STATE_FREQ)
 __msg("arg JOIN insn 9 -> 10 r1: fp0-8 + _ => fp0-8|fp0+0")
-__msg("arg JOIN insn 9 -> 10 r11: fp0-8 + _ => fp0-8|fp0+0")
+__msg("arg JOIN insn 9 -> 10 sa0: fp0-8 + _ => fp0-8|fp0+0")
 __msg("R{{[0-9]}} invalid mem access 'scalar'")
 __naked void stack_arg_pruning_type_mismatch(void)
 {
-- 
2.53.0-Meta


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

* [PATCH bpf-next v3 5/7] bpf: Clean up redundant stack arg checks for non-JITed programs
  2026-05-15 22:50 [PATCH bpf-next v3 0/7] bpf: Follow-up fixes for stack argument support Yonghong Song
                   ` (3 preceding siblings ...)
  2026-05-15 22:50 ` [PATCH bpf-next v3 4/7] bpf: Fix arg_track_join log to use sa prefix for stack arg slots Yonghong Song
@ 2026-05-15 22:51 ` Yonghong Song
  2026-05-15 22:51 ` [PATCH bpf-next v3 6/7] bpf,x86: Fix exception unwinding with outgoing stack arguments Yonghong Song
  2026-05-15 22:51 ` [PATCH bpf-next v3 7/7] selftests/bpf: Add exception tests with " Yonghong Song
  6 siblings, 0 replies; 8+ messages in thread
From: Yonghong Song @ 2026-05-15 22:51 UTC (permalink / raw)
  To: bpf
  Cc: Alexei Starovoitov, Andrii Nakryiko, Daniel Borkmann, kernel-team,
	Martin KaFai Lau

Remove a redundant stack_arg_cnt check in __bpf_prog_select_runtime()
and start the stack arg loop from index 0 in bpf_fixup_call_args().
Both changes are no-ops that simplify the code:

In __bpf_prog_select_runtime(), the subprog_info[0].stack_arg_cnt
check is unreachable:
  - when there is only a main program (no bpf-to-bpf calls),
    subprog_info[0].stack_arg_cnt is always 0 because the main
    program's arg_cnt is forced to 1
  - when bpf-to-bpf calls use stack args and JIT succeeds,
    fp->bpf_func is set and this code is skipped
  - when JIT fails, bpf_fixup_call_args() rejects the program
    before we get to __bpf_prog_select_runtime().

In bpf_fixup_call_args(), starting the loop at i=1 skipped subprog 0,
which is safe since the main program always has arg_cnt=1 and thus
bpf_in_stack_arg_cnt() returns 0. Starting at i=0 removes the need
to reason about this invariant.

Signed-off-by: Yonghong Song <yonghong.song@linux.dev>
---
 kernel/bpf/core.c   | 2 +-
 kernel/bpf/fixups.c | 2 +-
 2 files changed, 2 insertions(+), 2 deletions(-)

diff --git a/kernel/bpf/core.c b/kernel/bpf/core.c
index 427a6d828e01..cdbe9fdf474f 100644
--- a/kernel/bpf/core.c
+++ b/kernel/bpf/core.c
@@ -2609,7 +2609,7 @@ struct bpf_prog *__bpf_prog_select_runtime(struct bpf_verifier_env *env, struct
 		goto finalize;
 
 	if (IS_ENABLED(CONFIG_BPF_JIT_ALWAYS_ON) ||
-	    bpf_prog_has_kfunc_call(fp) || (env && env->subprog_info[0].stack_arg_cnt))
+	    bpf_prog_has_kfunc_call(fp))
 		jit_needed = true;
 
 	if (!bpf_prog_select_interpreter(fp))
diff --git a/kernel/bpf/fixups.c b/kernel/bpf/fixups.c
index 19056016eed8..2cec4e8cd4a0 100644
--- a/kernel/bpf/fixups.c
+++ b/kernel/bpf/fixups.c
@@ -1407,7 +1407,7 @@ int bpf_fixup_call_args(struct bpf_verifier_env *env)
 		verbose(env, "calling kernel functions are not allowed in non-JITed programs\n");
 		return -EINVAL;
 	}
-	for (i = 1; i < env->subprog_cnt; i++) {
+	for (i = 0; i < env->subprog_cnt; i++) {
 		if (bpf_in_stack_arg_cnt(&env->subprog_info[i])) {
 			verbose(env, "stack args are not supported in non-JITed programs\n");
 			return -EINVAL;
-- 
2.53.0-Meta


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

* [PATCH bpf-next v3 6/7] bpf,x86: Fix exception unwinding with outgoing stack arguments
  2026-05-15 22:50 [PATCH bpf-next v3 0/7] bpf: Follow-up fixes for stack argument support Yonghong Song
                   ` (4 preceding siblings ...)
  2026-05-15 22:51 ` [PATCH bpf-next v3 5/7] bpf: Clean up redundant stack arg checks for non-JITed programs Yonghong Song
@ 2026-05-15 22:51 ` Yonghong Song
  2026-05-15 22:51 ` [PATCH bpf-next v3 7/7] selftests/bpf: Add exception tests with " Yonghong Song
  6 siblings, 0 replies; 8+ messages in thread
From: Yonghong Song @ 2026-05-15 22:51 UTC (permalink / raw)
  To: bpf
  Cc: Alexei Starovoitov, Andrii Nakryiko, Daniel Borkmann, kernel-team,
	Martin KaFai Lau

When a main program with exception_boundary has outgoing stack
arguments (e.g. from calling subprogs with >5 args), bpf_throw() fails
to correctly restore callee-saved registers, causing a kernel crash.

The x86 JIT allocates the outgoing stack arg area below the
callee-saved registers via 'sub rsp, outgoing_rsp' in the prologue.
When bpf_throw() unwinds, it captures the main program's sp (which
includes this outgoing area) and passes it to the exception callback.
The callback gets rsp and rbp, followed by pop_callee_regs, but rsp
points into the outgoing arg area rather than the callee-saved
registers, so the pops restore garbage values. Returning to the
kernel with corrupted callee-saved registers causes a crash.

Fix this by passing the main program's outgoing_rsp as the 4th
argument to the exception callback. The callback adjusts rsp with
'add rsp, rcx' before popping callee-saved registers, correctly
skipping the outgoing arg area. When outgoing_rsp is 0 (the common
case), this is a no-op.

Fixes: 324c3ca6eed6 ("bpf,x86: Implement JIT support for stack arguments")
Signed-off-by: Yonghong Song <yonghong.song@linux.dev>
---
 arch/x86/net/bpf_jit_comp.c | 9 ++++++++-
 include/linux/bpf.h         | 3 ++-
 kernel/bpf/fixups.c         | 1 +
 kernel/bpf/helpers.c        | 2 +-
 4 files changed, 12 insertions(+), 3 deletions(-)

diff --git a/arch/x86/net/bpf_jit_comp.c b/arch/x86/net/bpf_jit_comp.c
index ceefefb4da21..f4fdceedaad7 100644
--- a/arch/x86/net/bpf_jit_comp.c
+++ b/arch/x86/net/bpf_jit_comp.c
@@ -557,10 +557,15 @@ static void emit_prologue(u8 **pprog, u8 *ip, u32 stack_depth, bool ebpf_from_cb
 			/* Keep the same instruction layout. */
 			emit_nops(&prog, 3);     /* nop3 */
 	}
-	/* Exception callback receives FP as third parameter */
+	/*
+	 * Exception callback receives:
+	 *   rsi = main program's SP, rdx = main program's FP,
+	 *   rcx = main program's outgoing stack arg area size
+	 */
 	if (is_exception_cb) {
 		EMIT3(0x48, 0x89, 0xF4); /* mov rsp, rsi */
 		EMIT3(0x48, 0x89, 0xD5); /* mov rbp, rdx */
+		EMIT3(0x48, 0x01, 0xCC); /* add rsp, rcx */
 		/* The main frame must have exception_boundary as true, so we
 		 * first restore those callee-saved regs from stack, before
 		 * reusing the stack frame.
@@ -1789,6 +1794,8 @@ static int do_jit(struct bpf_verifier_env *env, struct bpf_prog *bpf_prog, int *
 	 * Arg 6 goes into r9 register, not on stack.
 	 */
 	outgoing_rsp = out_stack_arg_cnt > 1 ? (out_stack_arg_cnt - 1) * 8 : 0;
+	if (bpf_prog->aux->exception_boundary)
+		bpf_prog->aux->stack_arg_adjust = outgoing_rsp;
 	emit_sub_rsp(&prog, outgoing_rsp);
 
 	if (arena_vm_start)
diff --git a/include/linux/bpf.h b/include/linux/bpf.h
index 242f9597d9ab..2a1616c769a9 100644
--- a/include/linux/bpf.h
+++ b/include/linux/bpf.h
@@ -1735,7 +1735,8 @@ struct bpf_prog_aux {
 	int cgroup_atype; /* enum cgroup_bpf_attach_type */
 	struct bpf_map *cgroup_storage[MAX_BPF_CGROUP_STORAGE_TYPE];
 	char name[BPF_OBJ_NAME_LEN];
-	u64 (*bpf_exception_cb)(u64 cookie, u64 sp, u64 bp, u64, u64);
+	u64 (*bpf_exception_cb)(u64 cookie, u64 sp, u64 bp, u64 stack_arg_adjust, u64);
+	u16 stack_arg_adjust;
 #ifdef CONFIG_SECURITY
 	void *security;
 #endif
diff --git a/kernel/bpf/fixups.c b/kernel/bpf/fixups.c
index 2cec4e8cd4a0..52aaf2863648 100644
--- a/kernel/bpf/fixups.c
+++ b/kernel/bpf/fixups.c
@@ -1265,6 +1265,7 @@ static int jit_subprogs(struct bpf_verifier_env *env)
 	prog->aux->real_func_cnt = env->subprog_cnt;
 	prog->aux->bpf_exception_cb = (void *)func[env->exception_callback_subprog]->bpf_func;
 	prog->aux->exception_boundary = func[0]->aux->exception_boundary;
+	prog->aux->stack_arg_adjust = func[0]->aux->stack_arg_adjust;
 	bpf_prog_jit_attempt_done(prog);
 	return 0;
 out_free:
diff --git a/kernel/bpf/helpers.c b/kernel/bpf/helpers.c
index baa12b24bb64..53a0430f60b3 100644
--- a/kernel/bpf/helpers.c
+++ b/kernel/bpf/helpers.c
@@ -3301,7 +3301,7 @@ __bpf_kfunc void bpf_throw(u64 cookie)
 	 * which skips compiler generated instrumentation to do the same.
 	 */
 	kasan_unpoison_task_stack_below((void *)(long)ctx.sp);
-	ctx.aux->bpf_exception_cb(cookie, ctx.sp, ctx.bp, 0, 0);
+	ctx.aux->bpf_exception_cb(cookie, ctx.sp, ctx.bp, ctx.aux->stack_arg_adjust, 0);
 	WARN(1, "A call to BPF exception callback should never return\n");
 }
 
-- 
2.53.0-Meta


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

* [PATCH bpf-next v3 7/7] selftests/bpf: Add exception tests with stack arguments
  2026-05-15 22:50 [PATCH bpf-next v3 0/7] bpf: Follow-up fixes for stack argument support Yonghong Song
                   ` (5 preceding siblings ...)
  2026-05-15 22:51 ` [PATCH bpf-next v3 6/7] bpf,x86: Fix exception unwinding with outgoing stack arguments Yonghong Song
@ 2026-05-15 22:51 ` Yonghong Song
  6 siblings, 0 replies; 8+ messages in thread
From: Yonghong Song @ 2026-05-15 22:51 UTC (permalink / raw)
  To: bpf
  Cc: Alexei Starovoitov, Andrii Nakryiko, Daniel Borkmann, kernel-team,
	Martin KaFai Lau

Add tests to verify that bpf_throw() correctly unwinds the stack
when the program uses outgoing stack arguments (functions with >5
args). Without the preceding fix, these tests crash the kernel
due to corrupted callee-saved register restore.

Signed-off-by: Yonghong Song <yonghong.song@linux.dev>
---
 .../selftests/bpf/prog_tests/exceptions.c     |   7 ++
 .../testing/selftests/bpf/progs/exceptions.c  | 113 ++++++++++++++++++
 2 files changed, 120 insertions(+)

diff --git a/tools/testing/selftests/bpf/prog_tests/exceptions.c b/tools/testing/selftests/bpf/prog_tests/exceptions.c
index e8cbaf2a3e82..3588d6f97fd4 100644
--- a/tools/testing/selftests/bpf/prog_tests/exceptions.c
+++ b/tools/testing/selftests/bpf/prog_tests/exceptions.c
@@ -85,6 +85,13 @@ static void test_exceptions_success(void)
 	RUN_SUCCESS(exception_bad_assert_range_with, 10);
 	RUN_SUCCESS(exception_throw_from_void_global, 11);
 
+	if (skel->rodata->has_stack_arg) {
+		RUN_SUCCESS(exception_throw_stack_arg, 56);
+		RUN_SUCCESS(exception_throw_after_stack_arg, 56);
+		RUN_SUCCESS(exception_throw_subprog_stack_arg, 56);
+		RUN_SUCCESS(exception_throw_subprog_after_stack_arg, 56);
+	}
+
 #define RUN_EXT(load_ret, attach_err, expr, msg, after_link)			  \
 	{									  \
 		LIBBPF_OPTS(bpf_object_open_opts, o, .kernel_log_buf = log_buf,		 \
diff --git a/tools/testing/selftests/bpf/progs/exceptions.c b/tools/testing/selftests/bpf/progs/exceptions.c
index 4206f59d7b86..f236aadfe7e6 100644
--- a/tools/testing/selftests/bpf/progs/exceptions.c
+++ b/tools/testing/selftests/bpf/progs/exceptions.c
@@ -379,4 +379,117 @@ int exception_bad_assert_range_with(struct __sk_buff *ctx)
 	return 1;
 }
 
+#if defined(__TARGET_ARCH_x86) && defined(__BPF_FEATURE_STACK_ARGUMENT)
+
+const volatile bool has_stack_arg = true;
+
+long arg1 = 1, arg2 = 2, arg3 = 3, arg4 = 4, arg5 = 5;
+long arg6 = 6, arg7 = 7, arg8 = 8, arg9 = 9, arg10 = 10;
+
+__noinline static long throwing_many_args(long a, long b, long c, long d,
+					  long e, long f, long g, long h,
+					  long i, long j)
+{
+	bpf_throw(a + b + c + d + e + f + g + h + i + j);
+	return 0;
+}
+
+__noinline int exception_cb_sa(u64 cookie)
+{
+	return cookie + 1;
+}
+
+SEC("tc")
+__exception_cb(exception_cb_sa)
+int exception_throw_stack_arg(struct __sk_buff *ctx)
+{
+	throwing_many_args(arg1, arg2, arg3, arg4, arg5,
+			   arg6, arg7, arg8, arg9, arg10);
+	return 0;
+}
+
+__noinline static long no_throw_many_args(long a, long b, long c, long d,
+					  long e, long f, long g, long h,
+					  long i, long j)
+{
+	return a + b + c + d + e + f + g + h + i + j;
+}
+
+SEC("tc")
+__exception_cb(exception_cb_sa)
+int exception_throw_after_stack_arg(struct __sk_buff *ctx)
+{
+	long ret;
+
+	ret = no_throw_many_args(arg1, arg2, arg3, arg4, arg5,
+				 arg6, arg7, arg8, arg9, arg10);
+	if (ret > 0)
+		bpf_throw(ret);
+	return 0;
+}
+
+__noinline static long subprog_throw_sa(long val)
+{
+	throwing_many_args(val, val + 1, val + 2, val + 3, val + 4,
+			   val + 5, val + 6, val + 7, val + 8, val + 9);
+	return 0;
+}
+
+SEC("tc")
+__exception_cb(exception_cb_sa)
+int exception_throw_subprog_stack_arg(struct __sk_buff *ctx)
+{
+	subprog_throw_sa(arg1);
+	return 0;
+}
+
+__noinline static long subprog_throw_after_sa(long val)
+{
+	long ret;
+
+	ret = no_throw_many_args(val, val + 1, val + 2, val + 3, val + 4,
+				 val + 5, val + 6, val + 7, val + 8, val + 9);
+	if (ret > 0)
+		bpf_throw(ret);
+	return 0;
+}
+
+SEC("tc")
+__exception_cb(exception_cb_sa)
+int exception_throw_subprog_after_stack_arg(struct __sk_buff *ctx)
+{
+	subprog_throw_after_sa(arg1);
+	return 0;
+}
+
+#else
+
+const volatile bool has_stack_arg = false;
+
+SEC("tc")
+int exception_throw_stack_arg(struct __sk_buff *ctx)
+{
+	return 0;
+}
+
+SEC("tc")
+int exception_throw_after_stack_arg(struct __sk_buff *ctx)
+{
+	return 0;
+}
+
+SEC("tc")
+int exception_throw_subprog_stack_arg(struct __sk_buff *ctx)
+{
+	return 0;
+}
+
+SEC("tc")
+int exception_throw_subprog_after_stack_arg(struct __sk_buff *ctx)
+{
+	return 0;
+}
+
+#endif
+
 char _license[] SEC("license") = "GPL";
-- 
2.53.0-Meta


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

end of thread, other threads:[~2026-05-15 22:51 UTC | newest]

Thread overview: 8+ messages (download: mbox.gz follow: Atom feed
-- links below jump to the message on this page --
2026-05-15 22:50 [PATCH bpf-next v3 0/7] bpf: Follow-up fixes for stack argument support Yonghong Song
2026-05-15 22:50 ` [PATCH bpf-next v3 1/7] bpf: Validate outgoing stack args when btf_prepare_func_args fails Yonghong Song
2026-05-15 22:50 ` [PATCH bpf-next v3 2/7] selftests/bpf: Add test for stack arg read without caller write Yonghong Song
2026-05-15 22:50 ` [PATCH bpf-next v3 3/7] selftests/bpf: Log arg_track_join for stack arg slots in liveness analysis Yonghong Song
2026-05-15 22:50 ` [PATCH bpf-next v3 4/7] bpf: Fix arg_track_join log to use sa prefix for stack arg slots Yonghong Song
2026-05-15 22:51 ` [PATCH bpf-next v3 5/7] bpf: Clean up redundant stack arg checks for non-JITed programs Yonghong Song
2026-05-15 22:51 ` [PATCH bpf-next v3 6/7] bpf,x86: Fix exception unwinding with outgoing stack arguments Yonghong Song
2026-05-15 22:51 ` [PATCH bpf-next v3 7/7] selftests/bpf: Add exception tests with " Yonghong Song

This is an external index of several public inboxes,
see mirroring instructions on how to clone and mirror
all data and code used by this external index.