* [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; 10+ 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] 10+ 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; 10+ 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] 10+ 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; 10+ 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] 10+ 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; 10+ 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] 10+ 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; 10+ 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] 10+ 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-16 3:51 ` Kumar Kartikeya Dwivedi
2026-05-15 22:51 ` [PATCH bpf-next v3 7/7] selftests/bpf: Add exception tests with " Yonghong Song
6 siblings, 1 reply; 10+ 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] 10+ messages in thread* Re: [PATCH bpf-next v3 6/7] bpf,x86: Fix exception unwinding with outgoing stack arguments
2026-05-15 22:51 ` [PATCH bpf-next v3 6/7] bpf,x86: Fix exception unwinding with outgoing stack arguments Yonghong Song
@ 2026-05-16 3:51 ` Kumar Kartikeya Dwivedi
0 siblings, 0 replies; 10+ messages in thread
From: Kumar Kartikeya Dwivedi @ 2026-05-16 3:51 UTC (permalink / raw)
To: Yonghong Song, bpf
Cc: Alexei Starovoitov, Andrii Nakryiko, Daniel Borkmann, kernel-team,
Martin KaFai Lau
On Sat May 16, 2026 at 12:51 AM CEST, Yonghong Song wrote:
> 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>
> ---
Do we need any adjustment for arm64?
For this patch:
Acked-by: Kumar Kartikeya Dwivedi <memxor@gmail.com>
> [...]
^ permalink raw reply [flat|nested] 10+ 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
2026-05-16 3:53 ` Kumar Kartikeya Dwivedi
6 siblings, 1 reply; 10+ 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] 10+ messages in thread* Re: [PATCH bpf-next v3 7/7] selftests/bpf: Add exception tests with stack arguments
2026-05-15 22:51 ` [PATCH bpf-next v3 7/7] selftests/bpf: Add exception tests with " Yonghong Song
@ 2026-05-16 3:53 ` Kumar Kartikeya Dwivedi
0 siblings, 0 replies; 10+ messages in thread
From: Kumar Kartikeya Dwivedi @ 2026-05-16 3:53 UTC (permalink / raw)
To: Yonghong Song, bpf
Cc: Alexei Starovoitov, Andrii Nakryiko, Daniel Borkmann, kernel-team,
Martin KaFai Lau
On Sat May 16, 2026 at 12:51 AM CEST, Yonghong Song wrote:
> 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>
> ---
Acked-by: Kumar Kartikeya Dwivedi <memxor@gmail.com>
> [...]
^ permalink raw reply [flat|nested] 10+ messages in thread