* Re: [PATCH bpf-next v3 1/2] bpf: Only enforce 8 frame call stack limit for all-static stacks
2026-03-03 4:31 ` [PATCH bpf-next v3 1/2] bpf: Only enforce 8 frame call stack limit for all-static stacks Emil Tsalapatis
@ 2026-03-03 5:13 ` bot+bpf-ci
2026-03-03 17:55 ` Emil Tsalapatis
2026-03-04 1:01 ` Eduard Zingerman
2026-03-04 16:51 ` Mykyta Yatsenko
2 siblings, 1 reply; 12+ messages in thread
From: bot+bpf-ci @ 2026-03-03 5:13 UTC (permalink / raw)
To: emil, bpf
Cc: andrii, ast, daniel, eddyz87, martin.lau, memxor, song,
yonghong.song, emil, ast, andrii, daniel, martin.lau, eddyz87,
yonghong.song, clm, ihor.solodrai
[-- Attachment #1: Type: text/plain, Size: 3048 bytes --]
> diff --git a/include/linux/bpf_verifier.h b/include/linux/bpf_verifier.h
> --- a/include/linux/bpf_verifier.h
> +++ b/include/linux/bpf_verifier.h
> @@ -650,6 +650,8 @@ enum priv_stack_mode {
> PRIV_STACK_ADAPTIVE,
> };
>
> +struct bpf_subprog_info;
> +
> struct bpf_subprog_info {
This isn't a bug, but the forward declaration above the struct
definition appears unnecessary. The new fields (ret_insn, frame,
cidx) are all plain int types, not self-referential pointers that
would require forward declaration. Is there a reason for it?
> diff --git a/kernel/bpf/verifier.c b/kernel/bpf/verifier.c
> --- a/kernel/bpf/verifier.c
> +++ b/kernel/bpf/verifier.c
[ ... ]
> @@ -6799,7 +6808,7 @@
> if (subprog[idx].has_tail_call)
> tail_call_reachable = true;
>
> - frame++;
> + frame = subprog_is_global(env, idx) ? 0 : frame + 1;
> if (frame >= MAX_CALL_FRAMES) {
> verbose(env, "the call stack of %d frames is too deep !\n",
> frame);
In the selftest below, the expected error message was changed to
"the call stack of 9 frames", but this verbose() call still prints
the raw frame counter.
With 9 static functions in the chain (global_func3 + f1 through f8),
frame reaches 8 when entering f1, and the verifier outputs "the call
stack of 8 frames is too deep !" -- which does not contain "9 frames".
The MAX_BPF_STACK error path was updated from frame + 1 to a cidx
chain walk for total count:
for (total = 1; subprog[idx].cidx >= 0 ; total++)
idx = subprog[idx].cidx;
verbose(env, "combined stack size of %d calls ...\n", total, depth);
Was a similar update intended here? Changing frame to frame + 1 in the
verbose() call would match the selftest expectation and be consistent
with how the stack overflow message now reports total count.
> diff --git a/tools/testing/selftests/bpf/progs/test_global_func3.c b/tools/testing/selftests/bpf/progs/test_global_func3.c
> --- a/tools/testing/selftests/bpf/progs/test_global_func3.c
> +++ b/tools/testing/selftests/bpf/progs/test_global_func3.c
[ ... ]
> @@ -5,56 +5,56 @@
> SEC("tc")
> -__failure __msg("the call stack of 8 frames")
> +__failure __msg("the call stack of 9 frames")
> int global_func3(struct __sk_buff *skb)
> {
> return f8(skb);
This expected message does not match the verifier output as described
above.
Also, the function header comment for check_max_stack_depth_subprog()
currently reads:
Since recursion is prevented by check_cfg() this algorithm
only needs a local stack of MAX_CALL_FRAMES to remember callsites
The local arrays ret_insn[] and ret_prog[] have been removed in favor
of struct fields (subprog[].cidx, subprog[].frame, subprog[].ret_insn).
Should this comment be updated to reflect the new approach? The v2
patch did update this comment for its design.
---
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/22608683847
^ permalink raw reply [flat|nested] 12+ messages in thread* Re: [PATCH bpf-next v3 1/2] bpf: Only enforce 8 frame call stack limit for all-static stacks
2026-03-03 5:13 ` bot+bpf-ci
@ 2026-03-03 17:55 ` Emil Tsalapatis
0 siblings, 0 replies; 12+ messages in thread
From: Emil Tsalapatis @ 2026-03-03 17:55 UTC (permalink / raw)
To: bot+bpf-ci, bpf
Cc: andrii, ast, daniel, eddyz87, martin.lau, memxor, song,
yonghong.song, clm, ihor.solodrai
On Tue Mar 3, 2026 at 12:13 AM EST, bot+bpf-ci wrote:
>> diff --git a/include/linux/bpf_verifier.h b/include/linux/bpf_verifier.h
>> --- a/include/linux/bpf_verifier.h
>> +++ b/include/linux/bpf_verifier.h
>> @@ -650,6 +650,8 @@ enum priv_stack_mode {
>> PRIV_STACK_ADAPTIVE,
>> };
>>
>> +struct bpf_subprog_info;
>> +
>> struct bpf_subprog_info {
>
> This isn't a bug, but the forward declaration above the struct
> definition appears unnecessary. The new fields (ret_insn, frame,
> cidx) are all plain int types, not self-referential pointers that
> would require forward declaration. Is there a reason for it?
>
Ack, it _used_ to be self referential and I forgot to remove it.
Will fix.
>> diff --git a/kernel/bpf/verifier.c b/kernel/bpf/verifier.c
>> --- a/kernel/bpf/verifier.c
>> +++ b/kernel/bpf/verifier.c
>
> [ ... ]
>
>> @@ -6799,7 +6808,7 @@
>> if (subprog[idx].has_tail_call)
>> tail_call_reachable = true;
>>
>> - frame++;
>> + frame = subprog_is_global(env, idx) ? 0 : frame + 1;
>> if (frame >= MAX_CALL_FRAMES) {
>> verbose(env, "the call stack of %d frames is too deep !\n",
>> frame);
>
> In the selftest below, the expected error message was changed to
> "the call stack of 9 frames", but this verbose() call still prints
> the raw frame counter.
>
> With 9 static functions in the chain (global_func3 + f1 through f8),
> frame reaches 8 when entering f1, and the verifier outputs "the call
> stack of 8 frames is too deep !" -- which does not contain "9 frames".
>
> The MAX_BPF_STACK error path was updated from frame + 1 to a cidx
> chain walk for total count:
>
> for (total = 1; subprog[idx].cidx >= 0 ; total++)
> idx = subprog[idx].cidx;
> verbose(env, "combined stack size of %d calls ...\n", total, depth);
>
> Was a similar update intended here? Changing frame to frame + 1 in the
> verbose() call would match the selftest expectation and be consistent
> with how the stack overflow message now reports total count.
>
No, that one should still be either 8 or 9 (see below) because it's the
actual limit - including the global frames in the tally would be confusing.
Maybe the right thing is to adjust the commit message.
>> diff --git a/tools/testing/selftests/bpf/progs/test_global_func3.c b/tools/testing/selftests/bpf/progs/test_global_func3.c
>> --- a/tools/testing/selftests/bpf/progs/test_global_func3.c
>> +++ b/tools/testing/selftests/bpf/progs/test_global_func3.c
>
> [ ... ]
>
>> @@ -5,56 +5,56 @@
>> SEC("tc")
>> -__failure __msg("the call stack of 8 frames")
>> +__failure __msg("the call stack of 9 frames")
>> int global_func3(struct __sk_buff *skb)
>> {
>> return f8(skb);
>
> This expected message does not match the verifier output as described
> above.
>
> Also, the function header comment for check_max_stack_depth_subprog()
> currently reads:
>
> Since recursion is prevented by check_cfg() this algorithm
> only needs a local stack of MAX_CALL_FRAMES to remember callsites
>
> The local arrays ret_insn[] and ret_prog[] have been removed in favor
> of struct fields (subprog[].cidx, subprog[].frame, subprog[].ret_insn).
> Should this comment be updated to reflect the new approach? The v2
> patch did update this comment for its design.
>
It does though, all tests are passing: There are two almost identical messages
in the verifier that report off-by-one different numbers:
"the call stack of %d frames is too deep", (setup_func_entry)
"the call stack of %d frames is too deep !" (check_max_stack_depth_subprog)
The one triggering now is in setup_func_entry inside do_check_{main,
subprogs}, so the number of frames reported has changed. Expecting 9
frames is consistent with other selftests.
>
> ---
> 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/22608683847
^ permalink raw reply [flat|nested] 12+ messages in thread
* Re: [PATCH bpf-next v3 1/2] bpf: Only enforce 8 frame call stack limit for all-static stacks
2026-03-03 4:31 ` [PATCH bpf-next v3 1/2] bpf: Only enforce 8 frame call stack limit for all-static stacks Emil Tsalapatis
2026-03-03 5:13 ` bot+bpf-ci
@ 2026-03-04 1:01 ` Eduard Zingerman
2026-03-05 19:38 ` Emil Tsalapatis
2026-03-04 16:51 ` Mykyta Yatsenko
2 siblings, 1 reply; 12+ messages in thread
From: Eduard Zingerman @ 2026-03-04 1:01 UTC (permalink / raw)
To: Emil Tsalapatis, bpf
Cc: andrii, ast, daniel, martin.lau, memxor, song, yonghong.song
On Mon, 2026-03-02 at 23:31 -0500, Emil Tsalapatis wrote:
> The BPF verifier currently enforces a call stack depth of 8 frames,
> regardless of the actual stack space consumption of those frames. The
> limit is necessary for static call stacks, because the bookkeeping data
> structures used by the verifier when stepping into static functions
> during verification only support 8 stack frames. However, this
> limitation only matters for static stack frames: Global subprogs are
> verified by themselves and do not require limiting the call depth.
>
> Relax this limitation to only apply to static stack frames. Verification
> now only fails when there is a sequence of 8 calls to non-global
> subprogs. Calling into a global subprog resets the counter. This allows
> deeper call stacks, provided all frames still fit in the stack.
>
> The change does not increase the maximum size of the call stack, only
> the maximum number of frames we can place in it.
>
> Also change the progs/test_global_func3.c selftest to use static
> functions, since with the new patch it would otherwise unexpectedly
> pass verification.
>
> Signed-off-by: Emil Tsalapatis <emil@etsalapatis.com>
> ---
> include/linux/bpf_verifier.h | 6 +++
> kernel/bpf/verifier.c | 43 ++++++++++++-------
> .../selftests/bpf/progs/test_global_func3.c | 18 ++++----
> 3 files changed, 42 insertions(+), 25 deletions(-)
>
> diff --git a/include/linux/bpf_verifier.h b/include/linux/bpf_verifier.h
> index c1e30096ea7b..39a54e631bcd 100644
> --- a/include/linux/bpf_verifier.h
> +++ b/include/linux/bpf_verifier.h
> @@ -650,6 +650,8 @@ enum priv_stack_mode {
> PRIV_STACK_ADAPTIVE,
> };
>
> +struct bpf_subprog_info;
> +
> struct bpf_subprog_info {
> /* 'start' has to be the first field otherwise find_subprog() won't work */
> u32 start; /* insn idx of function entry point */
> @@ -677,6 +679,10 @@ struct bpf_subprog_info {
>
> enum priv_stack_mode priv_stack_mode;
> struct bpf_subprog_arg_info args[MAX_BPF_FUNC_REG_ARGS];
> +
Nit: please add a comment here saying the below are temporary values
used in check_max_stack_depth_subprog() (tbh, I'd move those to a
separate array in env but maybe that's an overkill).
> + int ret_insn;
> + int frame;
> + int cidx;
Nit: please rename to `caller` and add a comment.
> };
>
> struct bpf_verifier_env;
> diff --git a/kernel/bpf/verifier.c b/kernel/bpf/verifier.c
> index 1153a828ce8d..d362ddd47d71 100644
> --- a/kernel/bpf/verifier.c
> +++ b/kernel/bpf/verifier.c
> @@ -6652,9 +6652,11 @@ static int check_max_stack_depth_subprog(struct bpf_verifier_env *env, int idx,
> struct bpf_insn *insn = env->prog->insnsi;
> int depth = 0, frame = 0, i, subprog_end, subprog_depth;
> bool tail_call_reachable = false;
> - int ret_insn[MAX_CALL_FRAMES];
> - int ret_prog[MAX_CALL_FRAMES];
> - int j;
> + int total;
> + int tmp;
> +
> + /* no caller idx */
> + subprog[idx].cidx = -1;
>
> i = subprog[idx].start;
> if (!priv_stack_supported)
> @@ -6706,8 +6708,11 @@ static int check_max_stack_depth_subprog(struct bpf_verifier_env *env, int idx,
> } else {
> depth += subprog_depth;
> if (depth > MAX_BPF_STACK) {
> + for (total = 1; subprog[idx].cidx >= 0 ; total++)
> + idx = subprog[idx].cidx;
> +
> verbose(env, "combined stack size of %d calls is %d. Too large\n",
> - frame + 1, depth);
> + total, depth);
> return -EACCES;
> }
> }
> @@ -6723,8 +6728,8 @@ static int check_max_stack_depth_subprog(struct bpf_verifier_env *env, int idx,
> continue;
> if (subprog[idx].is_cb)
> err = true;
Below the old version of the loop does not include current frame, but
the new version *does* include current frame. Looks like the above
check can be removed.
> - for (int c = 0; c < frame && !err; c++) {
> - if (subprog[ret_prog[c]].is_cb) {
> + for (tmp = idx; tmp >= 0 && !err; tmp = subprog[tmp].cidx) {
> + if (subprog[tmp].is_cb) {
> err = true;
> break;
> }
This code checks that bpf_throw() cannot be called from callbacks.
The `is_cb` is set in push_callback_call() both for sync and async
callbacks, it is also set for exception callback separately.
Meaning that check_return_code() can be simplified further.
> @@ -6740,8 +6745,9 @@ static int check_max_stack_depth_subprog(struct bpf_verifier_env *env, int idx,
> if (!bpf_pseudo_call(insn + i) && !bpf_pseudo_func(insn + i))
> continue;
> /* remember insn and function to return to */
> - ret_insn[frame] = i + 1;
> - ret_prog[frame] = idx;
> +
> + subprog[idx].frame = frame;
> + subprog[idx].ret_insn = i + 1;
Nit: move this down to `subprog[sidx].cidx = idx;`, so that the whole
"frame" setup is done in one place?
>
> /* find the callee */
> next_insn = i + insn[i].imm + 1;
> @@ -6762,6 +6768,9 @@ static int check_max_stack_depth_subprog(struct bpf_verifier_env *env, int idx,
> }
> }
> i = next_insn;
> +
> + /* caller idx */
> + subprog[sidx].cidx = idx;
> idx = sidx;
> if (!priv_stack_supported)
> subprog[idx].priv_stack_mode = NO_PRIV_STACK;
> @@ -6769,7 +6778,7 @@ static int check_max_stack_depth_subprog(struct bpf_verifier_env *env, int idx,
> if (subprog[idx].has_tail_call)
> tail_call_reachable = true;
>
> - frame++;
> + frame = subprog_is_global(env, idx) ? 0 : frame + 1;
> if (frame >= MAX_CALL_FRAMES) {
> verbose(env, "the call stack of %d frames is too deep !\n",
> frame);
> @@ -6783,12 +6792,12 @@ static int check_max_stack_depth_subprog(struct bpf_verifier_env *env, int idx,
> * tail call counter throughout bpf2bpf calls combined with tailcalls
> */
> if (tail_call_reachable)
Unrelated to current patch, but looks like an issue.
`tail_call_reachable` is not reset anywhere in
check_max_stack_depth_subprog(). Once it flips to true all calls
visited afterwards trigger spine to be marked.
> - for (j = 0; j < frame; j++) {
> - if (subprog[ret_prog[j]].is_exception_cb) {
> + for (tmp = idx; tmp >= 0; tmp = subprog[tmp].cidx) {
> + if (subprog[tmp].is_exception_cb) {
As with previous such loop, the new code seem to include current frame.
Does this change anything?
> verbose(env, "cannot tail call within exception cb\n");
> return -EINVAL;
> }
> - subprog[ret_prog[j]].tail_call_reachable = true;
> + subprog[tmp].tail_call_reachable = true;
> }
> if (subprog[0].tail_call_reachable)
> env->prog->aux->tail_call_reachable = true;
> @@ -6796,13 +6805,15 @@ static int check_max_stack_depth_subprog(struct bpf_verifier_env *env, int idx,
> /* end of for() loop means the last insn of the 'subprog'
> * was reached. Doesn't matter whether it was JA or EXIT
> */
> - if (frame == 0)
> + if (frame == 0 && subprog[idx].cidx < 0)
> return 0;
> if (subprog[idx].priv_stack_mode != PRIV_STACK_ADAPTIVE)
> depth -= round_up_stack_depth(env, subprog[idx].stack_depth);
> - frame--;
> - i = ret_insn[frame];
> - idx = ret_prog[frame];
> +
> + idx = subprog[idx].cidx;
> + frame = subprog[idx].frame;
> + i = subprog[idx].ret_insn;
> +
> goto continue_func;
> }
>
[...]
^ permalink raw reply [flat|nested] 12+ messages in thread* Re: [PATCH bpf-next v3 1/2] bpf: Only enforce 8 frame call stack limit for all-static stacks
2026-03-04 1:01 ` Eduard Zingerman
@ 2026-03-05 19:38 ` Emil Tsalapatis
2026-03-05 20:46 ` Eduard Zingerman
0 siblings, 1 reply; 12+ messages in thread
From: Emil Tsalapatis @ 2026-03-05 19:38 UTC (permalink / raw)
To: Eduard Zingerman, bpf
Cc: andrii, ast, daniel, martin.lau, memxor, song, yonghong.song
On Tue Mar 3, 2026 at 8:01 PM EST, Eduard Zingerman wrote:
> On Mon, 2026-03-02 at 23:31 -0500, Emil Tsalapatis wrote:
>> The BPF verifier currently enforces a call stack depth of 8 frames,
>> regardless of the actual stack space consumption of those frames. The
>> limit is necessary for static call stacks, because the bookkeeping data
>> structures used by the verifier when stepping into static functions
>> during verification only support 8 stack frames. However, this
>> limitation only matters for static stack frames: Global subprogs are
>> verified by themselves and do not require limiting the call depth.
>>
>> Relax this limitation to only apply to static stack frames. Verification
>> now only fails when there is a sequence of 8 calls to non-global
>> subprogs. Calling into a global subprog resets the counter. This allows
>> deeper call stacks, provided all frames still fit in the stack.
>>
>> The change does not increase the maximum size of the call stack, only
>> the maximum number of frames we can place in it.
>>
>> Also change the progs/test_global_func3.c selftest to use static
>> functions, since with the new patch it would otherwise unexpectedly
>> pass verification.
>>
>> Signed-off-by: Emil Tsalapatis <emil@etsalapatis.com>
>> ---
>> include/linux/bpf_verifier.h | 6 +++
>> kernel/bpf/verifier.c | 43 ++++++++++++-------
>> .../selftests/bpf/progs/test_global_func3.c | 18 ++++----
>> 3 files changed, 42 insertions(+), 25 deletions(-)
>>
>> diff --git a/include/linux/bpf_verifier.h b/include/linux/bpf_verifier.h
>> index c1e30096ea7b..39a54e631bcd 100644
>> --- a/include/linux/bpf_verifier.h
>> +++ b/include/linux/bpf_verifier.h
>> @@ -650,6 +650,8 @@ enum priv_stack_mode {
>> PRIV_STACK_ADAPTIVE,
>> };
>>
>> +struct bpf_subprog_info;
>> +
>> struct bpf_subprog_info {
>> /* 'start' has to be the first field otherwise find_subprog() won't work */
>> u32 start; /* insn idx of function entry point */
>> @@ -677,6 +679,10 @@ struct bpf_subprog_info {
>>
>> enum priv_stack_mode priv_stack_mode;
>> struct bpf_subprog_arg_info args[MAX_BPF_FUNC_REG_ARGS];
>> +
>
> Nit: please add a comment here saying the below are temporary values
> used in check_max_stack_depth_subprog() (tbh, I'd move those to a
> separate array in env but maybe that's an overkill).
>
>> + int ret_insn;
>> + int frame;
>> + int cidx;
>
> Nit: please rename to `caller` and add a comment.
>
>> };
Ack (and to all other nits).
>>
>> struct bpf_verifier_env;
>> diff --git a/kernel/bpf/verifier.c b/kernel/bpf/verifier.c
>> index 1153a828ce8d..d362ddd47d71 100644
>> --- a/kernel/bpf/verifier.c
>> +++ b/kernel/bpf/verifier.c
>> @@ -6652,9 +6652,11 @@ static int check_max_stack_depth_subprog(struct bpf_verifier_env *env, int idx,
>> struct bpf_insn *insn = env->prog->insnsi;
>> int depth = 0, frame = 0, i, subprog_end, subprog_depth;
>> bool tail_call_reachable = false;
>> - int ret_insn[MAX_CALL_FRAMES];
>> - int ret_prog[MAX_CALL_FRAMES];
>> - int j;
>> + int total;
>> + int tmp;
>> +
>> + /* no caller idx */
>> + subprog[idx].cidx = -1;
>>
>> i = subprog[idx].start;
>> if (!priv_stack_supported)
>> @@ -6706,8 +6708,11 @@ static int check_max_stack_depth_subprog(struct bpf_verifier_env *env, int idx,
>> } else {
>> depth += subprog_depth;
>> if (depth > MAX_BPF_STACK) {
>> + for (total = 1; subprog[idx].cidx >= 0 ; total++)
>> + idx = subprog[idx].cidx;
>> +
>> verbose(env, "combined stack size of %d calls is %d. Too large\n",
>> - frame + 1, depth);
>> + total, depth);
>> return -EACCES;
>> }
>> }
>> @@ -6723,8 +6728,8 @@ static int check_max_stack_depth_subprog(struct bpf_verifier_env *env, int idx,
>> continue;
>> if (subprog[idx].is_cb)
>> err = true;
>
> Below the old version of the loop does not include current frame, but
> the new version *does* include current frame. Looks like the above
> check can be removed.
>
Makes sense.
>> - for (int c = 0; c < frame && !err; c++) {
>> - if (subprog[ret_prog[c]].is_cb) {
>> + for (tmp = idx; tmp >= 0 && !err; tmp = subprog[tmp].cidx) {
>> + if (subprog[tmp].is_cb) {
>> err = true;
>> break;
>> }
>
> This code checks that bpf_throw() cannot be called from callbacks.
> The `is_cb` is set in push_callback_call() both for sync and async
> callbacks, it is also set for exception callback separately.
> Meaning that check_return_code() can be simplified further.
>
Do you mean we can simplify the check for choosing between
check_return_code and check_global_subprog_return_code to just check
is_cb instead of checking both is_async_cb and is_exception_cb?
>> @@ -6740,8 +6745,9 @@ static int check_max_stack_depth_subprog(struct bpf_verifier_env *env, int idx,
>> if (!bpf_pseudo_call(insn + i) && !bpf_pseudo_func(insn + i))
>> continue;
>> /* remember insn and function to return to */
>> - ret_insn[frame] = i + 1;
>> - ret_prog[frame] = idx;
>> +
>> + subprog[idx].frame = frame;
>> + subprog[idx].ret_insn = i + 1;
>
> Nit: move this down to `subprog[sidx].cidx = idx;`, so that the whole
> "frame" setup is done in one place?
>
>>
>> /* find the callee */
>> next_insn = i + insn[i].imm + 1;
>> @@ -6762,6 +6768,9 @@ static int check_max_stack_depth_subprog(struct bpf_verifier_env *env, int idx,
>> }
>> }
>> i = next_insn;
>> +
>> + /* caller idx */
>> + subprog[sidx].cidx = idx;
>> idx = sidx;
>> if (!priv_stack_supported)
>> subprog[idx].priv_stack_mode = NO_PRIV_STACK;
>> @@ -6769,7 +6778,7 @@ static int check_max_stack_depth_subprog(struct bpf_verifier_env *env, int idx,
>> if (subprog[idx].has_tail_call)
>> tail_call_reachable = true;
>>
>> - frame++;
>> + frame = subprog_is_global(env, idx) ? 0 : frame + 1;
>> if (frame >= MAX_CALL_FRAMES) {
>> verbose(env, "the call stack of %d frames is too deep !\n",
>> frame);
>> @@ -6783,12 +6792,12 @@ static int check_max_stack_depth_subprog(struct bpf_verifier_env *env, int idx,
>> * tail call counter throughout bpf2bpf calls combined with tailcalls
>> */
>> if (tail_call_reachable)
>
> Unrelated to current patch, but looks like an issue.
> `tail_call_reachable` is not reset anywhere in
> check_max_stack_depth_subprog(). Once it flips to true all calls
> visited afterwards trigger spine to be marked.
>
You're right, this is never unset. I can send a followup patchset for
this along with the followups for the check_return_code patch. I believe
we can change this to eagerly set the subprog's tail_call_reachable flag
directly instead of using a stack variable.
>> - for (j = 0; j < frame; j++) {
>> - if (subprog[ret_prog[j]].is_exception_cb) {
>> + for (tmp = idx; tmp >= 0; tmp = subprog[tmp].cidx) {
>> + if (subprog[tmp].is_exception_cb) {
>
> As with previous such loop, the new code seem to include current frame.
> Does this change anything?
>
I think it is ok, though since as you pointed out tail_call_reachable doesn't
currently seem to work correctly I can only assume: We set tail_call_reachable
if any of the subprogs called by the current one make a tail call. If
that's the case, the current frame should also count as tail_call_reachable.
It all depends on where tail_call_reachable is supposed to be set and unset. I
think it's supposed to flow from callee to caller, and if that's the
case we should hoist up the whole loop to where we initially set the
stack variable.
>> verbose(env, "cannot tail call within exception cb\n");
>> return -EINVAL;
>> }
>> - subprog[ret_prog[j]].tail_call_reachable = true;
>> + subprog[tmp].tail_call_reachable = true;
>> }
>> if (subprog[0].tail_call_reachable)
>> env->prog->aux->tail_call_reachable = true;
>> @@ -6796,13 +6805,15 @@ static int check_max_stack_depth_subprog(struct bpf_verifier_env *env, int idx,
>> /* end of for() loop means the last insn of the 'subprog'
>> * was reached. Doesn't matter whether it was JA or EXIT
>> */
>> - if (frame == 0)
>> + if (frame == 0 && subprog[idx].cidx < 0)
>> return 0;
>> if (subprog[idx].priv_stack_mode != PRIV_STACK_ADAPTIVE)
>> depth -= round_up_stack_depth(env, subprog[idx].stack_depth);
>> - frame--;
>> - i = ret_insn[frame];
>> - idx = ret_prog[frame];
>> +
>> + idx = subprog[idx].cidx;
>> + frame = subprog[idx].frame;
>> + i = subprog[idx].ret_insn;
>> +
>> goto continue_func;
>> }
>>
>
> [...]
^ permalink raw reply [flat|nested] 12+ messages in thread* Re: [PATCH bpf-next v3 1/2] bpf: Only enforce 8 frame call stack limit for all-static stacks
2026-03-05 19:38 ` Emil Tsalapatis
@ 2026-03-05 20:46 ` Eduard Zingerman
0 siblings, 0 replies; 12+ messages in thread
From: Eduard Zingerman @ 2026-03-05 20:46 UTC (permalink / raw)
To: Emil Tsalapatis, bpf
Cc: andrii, ast, daniel, martin.lau, memxor, song, yonghong.song
On Thu, 2026-03-05 at 14:38 -0500, Emil Tsalapatis wrote:
[...]
> > > @@ -6723,8 +6728,8 @@ static int check_max_stack_depth_subprog(struct bpf_verifier_env *env, int idx,
> > > continue;
> > > if (subprog[idx].is_cb)
> > > err = true;
> >
> > Below the old version of the loop does not include current frame, but
> > the new version *does* include current frame. Looks like the above
> > check can be removed.
> >
>
> Makes sense.
>
> > > - for (int c = 0; c < frame && !err; c++) {
> > > - if (subprog[ret_prog[c]].is_cb) {
> > > + for (tmp = idx; tmp >= 0 && !err; tmp = subprog[tmp].cidx) {
> > > + if (subprog[tmp].is_cb) {
> > > err = true;
> > > break;
> > > }
> >
> > This code checks that bpf_throw() cannot be called from callbacks.
> > The `is_cb` is set in push_callback_call() both for sync and async
> > callbacks, it is also set for exception callback separately.
> > Meaning that check_return_code() can be simplified further.
> >
>
> Do you mean we can simplify the check for choosing between
> check_return_code and check_global_subprog_return_code to just check
> is_cb instead of checking both is_async_cb and is_exception_cb?
Actually, I was thinking about `frame->in_async_callback_fn` checks in
check_return_code(). But on a second thought these are unrelated.
Please skip my comment above.
[...]
> > > @@ -6783,12 +6792,12 @@ static int check_max_stack_depth_subprog(struct bpf_verifier_env *env, int idx,
> > > * tail call counter throughout bpf2bpf calls combined with tailcalls
> > > */
> > > if (tail_call_reachable)
> >
> > Unrelated to current patch, but looks like an issue.
> > `tail_call_reachable` is not reset anywhere in
> > check_max_stack_depth_subprog(). Once it flips to true all calls
> > visited afterwards trigger spine to be marked.
> >
>
> You're right, this is never unset. I can send a followup patchset for
> this along with the followups for the check_return_code patch. I believe
> we can change this to eagerly set the subprog's tail_call_reachable flag
> directly instead of using a stack variable.
>
> > > - for (j = 0; j < frame; j++) {
> > > - if (subprog[ret_prog[j]].is_exception_cb) {
> > > + for (tmp = idx; tmp >= 0; tmp = subprog[tmp].cidx) {
> > > + if (subprog[tmp].is_exception_cb) {
> >
> > As with previous such loop, the new code seem to include current frame.
> > Does this change anything?
> >
>
> I think it is ok, though since as you pointed out tail_call_reachable doesn't
> currently seem to work correctly I can only assume: We set tail_call_reachable
> if any of the subprogs called by the current one make a tail call. If
> that's the case, the current frame should also count as tail_call_reachable.
>
> It all depends on where tail_call_reachable is supposed to be set and unset. I
> think it's supposed to flow from callee to caller, and if that's the
> case we should hoist up the whole loop to where we initially set the
> stack variable.
So, the tail_call_reachable flag is transferred to subprog[*].tail_call_reachable
property. In check_subprogs() there is direct `subprog[cur_subprog].tail_call_reachable = true`
if tail call exists in the subprogram. Looks like behavior does not really change
with this patch.
> > > verbose(env, "cannot tail call within exception cb\n");
> > > return -EINVAL;
> > > }
> > > - subprog[ret_prog[j]].tail_call_reachable = true;
> > > + subprog[tmp].tail_call_reachable = true;
> > > }
> > > if (subprog[0].tail_call_reachable)
> > > env->prog->aux->tail_call_reachable = true;
[...]
^ permalink raw reply [flat|nested] 12+ messages in thread
* Re: [PATCH bpf-next v3 1/2] bpf: Only enforce 8 frame call stack limit for all-static stacks
2026-03-03 4:31 ` [PATCH bpf-next v3 1/2] bpf: Only enforce 8 frame call stack limit for all-static stacks Emil Tsalapatis
2026-03-03 5:13 ` bot+bpf-ci
2026-03-04 1:01 ` Eduard Zingerman
@ 2026-03-04 16:51 ` Mykyta Yatsenko
2026-03-05 17:36 ` Emil Tsalapatis
2 siblings, 1 reply; 12+ messages in thread
From: Mykyta Yatsenko @ 2026-03-04 16:51 UTC (permalink / raw)
To: Emil Tsalapatis, bpf
Cc: andrii, ast, daniel, eddyz87, martin.lau, memxor, song,
yonghong.song, Emil Tsalapatis
Emil Tsalapatis <emil@etsalapatis.com> writes:
> The BPF verifier currently enforces a call stack depth of 8 frames,
> regardless of the actual stack space consumption of those frames. The
> limit is necessary for static call stacks, because the bookkeeping data
> structures used by the verifier when stepping into static functions
> during verification only support 8 stack frames. However, this
> limitation only matters for static stack frames: Global subprogs are
> verified by themselves and do not require limiting the call depth.
>
> Relax this limitation to only apply to static stack frames. Verification
> now only fails when there is a sequence of 8 calls to non-global
> subprogs. Calling into a global subprog resets the counter. This allows
> deeper call stacks, provided all frames still fit in the stack.
>
> The change does not increase the maximum size of the call stack, only
> the maximum number of frames we can place in it.
>
> Also change the progs/test_global_func3.c selftest to use static
> functions, since with the new patch it would otherwise unexpectedly
> pass verification.
>
> Signed-off-by: Emil Tsalapatis <emil@etsalapatis.com>
> ---
> include/linux/bpf_verifier.h | 6 +++
> kernel/bpf/verifier.c | 43 ++++++++++++-------
> .../selftests/bpf/progs/test_global_func3.c | 18 ++++----
> 3 files changed, 42 insertions(+), 25 deletions(-)
>
> diff --git a/include/linux/bpf_verifier.h b/include/linux/bpf_verifier.h
> index c1e30096ea7b..39a54e631bcd 100644
> --- a/include/linux/bpf_verifier.h
> +++ b/include/linux/bpf_verifier.h
> @@ -650,6 +650,8 @@ enum priv_stack_mode {
> PRIV_STACK_ADAPTIVE,
> };
>
> +struct bpf_subprog_info;
> +
> struct bpf_subprog_info {
> /* 'start' has to be the first field otherwise find_subprog() won't work */
> u32 start; /* insn idx of function entry point */
> @@ -677,6 +679,10 @@ struct bpf_subprog_info {
>
> enum priv_stack_mode priv_stack_mode;
> struct bpf_subprog_arg_info args[MAX_BPF_FUNC_REG_ARGS];
> +
> + int ret_insn;
> + int frame;
> + int cidx;
nit: can you add comment that cidx means caller index, i saw it below
and it clarifies this code a lot.
> };
>
> struct bpf_verifier_env;
> diff --git a/kernel/bpf/verifier.c b/kernel/bpf/verifier.c
> index 1153a828ce8d..d362ddd47d71 100644
> --- a/kernel/bpf/verifier.c
> +++ b/kernel/bpf/verifier.c
> @@ -6652,9 +6652,11 @@ static int check_max_stack_depth_subprog(struct bpf_verifier_env *env, int idx,
> struct bpf_insn *insn = env->prog->insnsi;
> int depth = 0, frame = 0, i, subprog_end, subprog_depth;
> bool tail_call_reachable = false;
> - int ret_insn[MAX_CALL_FRAMES];
> - int ret_prog[MAX_CALL_FRAMES];
> - int j;
> + int total;
> + int tmp;
> +
> + /* no caller idx */
> + subprog[idx].cidx = -1;
>
> i = subprog[idx].start;
> if (!priv_stack_supported)
> @@ -6706,8 +6708,11 @@ static int check_max_stack_depth_subprog(struct bpf_verifier_env *env, int idx,
> } else {
> depth += subprog_depth;
> if (depth > MAX_BPF_STACK) {
> + for (total = 1; subprog[idx].cidx >= 0 ; total++)
nit: I think the way this loop is implemented below is more readable:
for (tmp = idx; tmp >= 0; tmp = subprog[tmp].cidx)
total++;
it is confusing now, because for loop initializes and increments total,
but tracks cidx for exit check.
> + idx = subprog[idx].cidx;
> +
> verbose(env, "combined stack size of %d calls is %d. Too large\n",
> - frame + 1, depth);
> + total, depth);
> return -EACCES;
> }
> }
> @@ -6723,8 +6728,8 @@ static int check_max_stack_depth_subprog(struct bpf_verifier_env *env, int idx,
> continue;
> if (subprog[idx].is_cb)
> err = true;
> - for (int c = 0; c < frame && !err; c++) {
> - if (subprog[ret_prog[c]].is_cb) {
> + for (tmp = idx; tmp >= 0 && !err; tmp = subprog[tmp].cidx) {
> + if (subprog[tmp].is_cb) {
> err = true;
> break;
> }
> @@ -6740,8 +6745,9 @@ static int check_max_stack_depth_subprog(struct bpf_verifier_env *env, int idx,
> if (!bpf_pseudo_call(insn + i) && !bpf_pseudo_func(insn + i))
> continue;
> /* remember insn and function to return to */
> - ret_insn[frame] = i + 1;
> - ret_prog[frame] = idx;
> +
> + subprog[idx].frame = frame;
> + subprog[idx].ret_insn = i + 1;
>
> /* find the callee */
> next_insn = i + insn[i].imm + 1;
> @@ -6762,6 +6768,9 @@ static int check_max_stack_depth_subprog(struct bpf_verifier_env *env, int idx,
> }
> }
> i = next_insn;
> +
> + /* caller idx */
> + subprog[sidx].cidx = idx;
> idx = sidx;
> if (!priv_stack_supported)
> subprog[idx].priv_stack_mode = NO_PRIV_STACK;
> @@ -6769,7 +6778,7 @@ static int check_max_stack_depth_subprog(struct bpf_verifier_env *env, int idx,
> if (subprog[idx].has_tail_call)
> tail_call_reachable = true;
>
> - frame++;
> + frame = subprog_is_global(env, idx) ? 0 : frame + 1;
> if (frame >= MAX_CALL_FRAMES) {
> verbose(env, "the call stack of %d frames is too deep !\n",
> frame);
> @@ -6783,12 +6792,12 @@ static int check_max_stack_depth_subprog(struct bpf_verifier_env *env, int idx,
> * tail call counter throughout bpf2bpf calls combined with tailcalls
> */
> if (tail_call_reachable)
> - for (j = 0; j < frame; j++) {
> - if (subprog[ret_prog[j]].is_exception_cb) {
> + for (tmp = idx; tmp >= 0; tmp = subprog[tmp].cidx) {
> + if (subprog[tmp].is_exception_cb) {
> verbose(env, "cannot tail call within exception cb\n");
> return -EINVAL;
> }
> - subprog[ret_prog[j]].tail_call_reachable = true;
> + subprog[tmp].tail_call_reachable = true;
> }
> if (subprog[0].tail_call_reachable)
> env->prog->aux->tail_call_reachable = true;
> @@ -6796,13 +6805,15 @@ static int check_max_stack_depth_subprog(struct bpf_verifier_env *env, int idx,
> /* end of for() loop means the last insn of the 'subprog'
> * was reached. Doesn't matter whether it was JA or EXIT
> */
> - if (frame == 0)
> + if (frame == 0 && subprog[idx].cidx < 0)
> return 0;
> if (subprog[idx].priv_stack_mode != PRIV_STACK_ADAPTIVE)
> depth -= round_up_stack_depth(env, subprog[idx].stack_depth);
> - frame--;
> - i = ret_insn[frame];
> - idx = ret_prog[frame];
> +
> + idx = subprog[idx].cidx;
> + frame = subprog[idx].frame;
> + i = subprog[idx].ret_insn;
> +
> goto continue_func;
> }
>
> diff --git a/tools/testing/selftests/bpf/progs/test_global_func3.c b/tools/testing/selftests/bpf/progs/test_global_func3.c
> index 142b682d3c2f..974fd8c19561 100644
> --- a/tools/testing/selftests/bpf/progs/test_global_func3.c
> +++ b/tools/testing/selftests/bpf/progs/test_global_func3.c
> @@ -5,56 +5,56 @@
> #include <bpf/bpf_helpers.h>
> #include "bpf_misc.h"
>
> -__attribute__ ((noinline))
> +static __attribute__ ((noinline))
> int f1(struct __sk_buff *skb)
> {
> return skb->len;
> }
>
> -__attribute__ ((noinline))
> +static __attribute__ ((noinline))
> int f2(int val, struct __sk_buff *skb)
> {
> return f1(skb) + val;
> }
>
> -__attribute__ ((noinline))
> +static __attribute__ ((noinline))
> int f3(int val, struct __sk_buff *skb, int var)
> {
> return f2(var, skb) + val;
> }
>
> -__attribute__ ((noinline))
> +static __attribute__ ((noinline))
> int f4(struct __sk_buff *skb)
> {
> return f3(1, skb, 2);
> }
>
> -__attribute__ ((noinline))
> +static __attribute__ ((noinline))
> int f5(struct __sk_buff *skb)
> {
> return f4(skb);
> }
>
> -__attribute__ ((noinline))
> +static __attribute__ ((noinline))
> int f6(struct __sk_buff *skb)
> {
> return f5(skb);
> }
>
> -__attribute__ ((noinline))
> +static __attribute__ ((noinline))
> int f7(struct __sk_buff *skb)
> {
> return f6(skb);
> }
>
> -__attribute__ ((noinline))
> +static __attribute__ ((noinline))
> int f8(struct __sk_buff *skb)
> {
> return f7(skb);
> }
>
> SEC("tc")
> -__failure __msg("the call stack of 8 frames")
> +__failure __msg("the call stack of 9 frames")
> int global_func3(struct __sk_buff *skb)
> {
> return f8(skb);
> --
> 2.49.0
^ permalink raw reply [flat|nested] 12+ messages in thread* Re: [PATCH bpf-next v3 1/2] bpf: Only enforce 8 frame call stack limit for all-static stacks
2026-03-04 16:51 ` Mykyta Yatsenko
@ 2026-03-05 17:36 ` Emil Tsalapatis
0 siblings, 0 replies; 12+ messages in thread
From: Emil Tsalapatis @ 2026-03-05 17:36 UTC (permalink / raw)
To: Mykyta Yatsenko, bpf
Cc: andrii, ast, daniel, eddyz87, martin.lau, memxor, song,
yonghong.song
On Wed Mar 4, 2026 at 11:51 AM EST, Mykyta Yatsenko wrote:
> Emil Tsalapatis <emil@etsalapatis.com> writes:
>
>> The BPF verifier currently enforces a call stack depth of 8 frames,
>> regardless of the actual stack space consumption of those frames. The
>> limit is necessary for static call stacks, because the bookkeeping data
>> structures used by the verifier when stepping into static functions
>> during verification only support 8 stack frames. However, this
>> limitation only matters for static stack frames: Global subprogs are
>> verified by themselves and do not require limiting the call depth.
>>
>> Relax this limitation to only apply to static stack frames. Verification
>> now only fails when there is a sequence of 8 calls to non-global
>> subprogs. Calling into a global subprog resets the counter. This allows
>> deeper call stacks, provided all frames still fit in the stack.
>>
>> The change does not increase the maximum size of the call stack, only
>> the maximum number of frames we can place in it.
>>
>> Also change the progs/test_global_func3.c selftest to use static
>> functions, since with the new patch it would otherwise unexpectedly
>> pass verification.
>>
>> Signed-off-by: Emil Tsalapatis <emil@etsalapatis.com>
>> ---
>> include/linux/bpf_verifier.h | 6 +++
>> kernel/bpf/verifier.c | 43 ++++++++++++-------
>> .../selftests/bpf/progs/test_global_func3.c | 18 ++++----
>> 3 files changed, 42 insertions(+), 25 deletions(-)
>>
>> diff --git a/include/linux/bpf_verifier.h b/include/linux/bpf_verifier.h
>> index c1e30096ea7b..39a54e631bcd 100644
>> --- a/include/linux/bpf_verifier.h
>> +++ b/include/linux/bpf_verifier.h
>> @@ -650,6 +650,8 @@ enum priv_stack_mode {
>> PRIV_STACK_ADAPTIVE,
>> };
>>
>> +struct bpf_subprog_info;
>> +
>> struct bpf_subprog_info {
>> /* 'start' has to be the first field otherwise find_subprog() won't work */
>> u32 start; /* insn idx of function entry point */
>> @@ -677,6 +679,10 @@ struct bpf_subprog_info {
>>
>> enum priv_stack_mode priv_stack_mode;
>> struct bpf_subprog_arg_info args[MAX_BPF_FUNC_REG_ARGS];
>> +
>> + int ret_insn;
>> + int frame;
>> + int cidx;
> nit: can you add comment that cidx means caller index, i saw it below
> and it clarifies this code a lot.
>> };
Ack, I will add a comment on this.
>>
>> struct bpf_verifier_env;
>> diff --git a/kernel/bpf/verifier.c b/kernel/bpf/verifier.c
>> index 1153a828ce8d..d362ddd47d71 100644
>> --- a/kernel/bpf/verifier.c
>> +++ b/kernel/bpf/verifier.c
>> @@ -6652,9 +6652,11 @@ static int check_max_stack_depth_subprog(struct bpf_verifier_env *env, int idx,
>> struct bpf_insn *insn = env->prog->insnsi;
>> int depth = 0, frame = 0, i, subprog_end, subprog_depth;
>> bool tail_call_reachable = false;
>> - int ret_insn[MAX_CALL_FRAMES];
>> - int ret_prog[MAX_CALL_FRAMES];
>> - int j;
>> + int total;
>> + int tmp;
>> +
>> + /* no caller idx */
>> + subprog[idx].cidx = -1;
>>
>> i = subprog[idx].start;
>> if (!priv_stack_supported)
>> @@ -6706,8 +6708,11 @@ static int check_max_stack_depth_subprog(struct bpf_verifier_env *env, int idx,
>> } else {
>> depth += subprog_depth;
>> if (depth > MAX_BPF_STACK) {
>> + for (total = 1; subprog[idx].cidx >= 0 ; total++)
> nit: I think the way this loop is implemented below is more readable:
> for (tmp = idx; tmp >= 0; tmp = subprog[tmp].cidx)
> total++;
> it is confusing now, because for loop initializes and increments total,
> but tracks cidx for exit check.
Ack, I'll update it in the respin to be more like the others.
>> + idx = subprog[idx].cidx;
>> +
>> verbose(env, "combined stack size of %d calls is %d. Too large\n",
>> - frame + 1, depth);
>> + total, depth);
>> return -EACCES;
>> }
>> }
>> @@ -6723,8 +6728,8 @@ static int check_max_stack_depth_subprog(struct bpf_verifier_env *env, int idx,
>> continue;
>> if (subprog[idx].is_cb)
>> err = true;
>> - for (int c = 0; c < frame && !err; c++) {
>> - if (subprog[ret_prog[c]].is_cb) {
>> + for (tmp = idx; tmp >= 0 && !err; tmp = subprog[tmp].cidx) {
>> + if (subprog[tmp].is_cb) {
>> err = true;
>> break;
>> }
>> @@ -6740,8 +6745,9 @@ static int check_max_stack_depth_subprog(struct bpf_verifier_env *env, int idx,
>> if (!bpf_pseudo_call(insn + i) && !bpf_pseudo_func(insn + i))
>> continue;
>> /* remember insn and function to return to */
>> - ret_insn[frame] = i + 1;
>> - ret_prog[frame] = idx;
>> +
>> + subprog[idx].frame = frame;
>> + subprog[idx].ret_insn = i + 1;
>>
>> /* find the callee */
>> next_insn = i + insn[i].imm + 1;
>> @@ -6762,6 +6768,9 @@ static int check_max_stack_depth_subprog(struct bpf_verifier_env *env, int idx,
>> }
>> }
>> i = next_insn;
>> +
>> + /* caller idx */
>> + subprog[sidx].cidx = idx;
>> idx = sidx;
>> if (!priv_stack_supported)
>> subprog[idx].priv_stack_mode = NO_PRIV_STACK;
>> @@ -6769,7 +6778,7 @@ static int check_max_stack_depth_subprog(struct bpf_verifier_env *env, int idx,
>> if (subprog[idx].has_tail_call)
>> tail_call_reachable = true;
>>
>> - frame++;
>> + frame = subprog_is_global(env, idx) ? 0 : frame + 1;
>> if (frame >= MAX_CALL_FRAMES) {
>> verbose(env, "the call stack of %d frames is too deep !\n",
>> frame);
>> @@ -6783,12 +6792,12 @@ static int check_max_stack_depth_subprog(struct bpf_verifier_env *env, int idx,
>> * tail call counter throughout bpf2bpf calls combined with tailcalls
>> */
>> if (tail_call_reachable)
>> - for (j = 0; j < frame; j++) {
>> - if (subprog[ret_prog[j]].is_exception_cb) {
>> + for (tmp = idx; tmp >= 0; tmp = subprog[tmp].cidx) {
>> + if (subprog[tmp].is_exception_cb) {
>> verbose(env, "cannot tail call within exception cb\n");
>> return -EINVAL;
>> }
>> - subprog[ret_prog[j]].tail_call_reachable = true;
>> + subprog[tmp].tail_call_reachable = true;
>> }
>> if (subprog[0].tail_call_reachable)
>> env->prog->aux->tail_call_reachable = true;
>> @@ -6796,13 +6805,15 @@ static int check_max_stack_depth_subprog(struct bpf_verifier_env *env, int idx,
>> /* end of for() loop means the last insn of the 'subprog'
>> * was reached. Doesn't matter whether it was JA or EXIT
>> */
>> - if (frame == 0)
>> + if (frame == 0 && subprog[idx].cidx < 0)
>> return 0;
>> if (subprog[idx].priv_stack_mode != PRIV_STACK_ADAPTIVE)
>> depth -= round_up_stack_depth(env, subprog[idx].stack_depth);
>> - frame--;
>> - i = ret_insn[frame];
>> - idx = ret_prog[frame];
>> +
>> + idx = subprog[idx].cidx;
>> + frame = subprog[idx].frame;
>> + i = subprog[idx].ret_insn;
>> +
>> goto continue_func;
>> }
>>
>> diff --git a/tools/testing/selftests/bpf/progs/test_global_func3.c b/tools/testing/selftests/bpf/progs/test_global_func3.c
>> index 142b682d3c2f..974fd8c19561 100644
>> --- a/tools/testing/selftests/bpf/progs/test_global_func3.c
>> +++ b/tools/testing/selftests/bpf/progs/test_global_func3.c
>> @@ -5,56 +5,56 @@
>> #include <bpf/bpf_helpers.h>
>> #include "bpf_misc.h"
>>
>> -__attribute__ ((noinline))
>> +static __attribute__ ((noinline))
>> int f1(struct __sk_buff *skb)
>> {
>> return skb->len;
>> }
>>
>> -__attribute__ ((noinline))
>> +static __attribute__ ((noinline))
>> int f2(int val, struct __sk_buff *skb)
>> {
>> return f1(skb) + val;
>> }
>>
>> -__attribute__ ((noinline))
>> +static __attribute__ ((noinline))
>> int f3(int val, struct __sk_buff *skb, int var)
>> {
>> return f2(var, skb) + val;
>> }
>>
>> -__attribute__ ((noinline))
>> +static __attribute__ ((noinline))
>> int f4(struct __sk_buff *skb)
>> {
>> return f3(1, skb, 2);
>> }
>>
>> -__attribute__ ((noinline))
>> +static __attribute__ ((noinline))
>> int f5(struct __sk_buff *skb)
>> {
>> return f4(skb);
>> }
>>
>> -__attribute__ ((noinline))
>> +static __attribute__ ((noinline))
>> int f6(struct __sk_buff *skb)
>> {
>> return f5(skb);
>> }
>>
>> -__attribute__ ((noinline))
>> +static __attribute__ ((noinline))
>> int f7(struct __sk_buff *skb)
>> {
>> return f6(skb);
>> }
>>
>> -__attribute__ ((noinline))
>> +static __attribute__ ((noinline))
>> int f8(struct __sk_buff *skb)
>> {
>> return f7(skb);
>> }
>>
>> SEC("tc")
>> -__failure __msg("the call stack of 8 frames")
>> +__failure __msg("the call stack of 9 frames")
>> int global_func3(struct __sk_buff *skb)
>> {
>> return f8(skb);
>> --
>> 2.49.0
^ permalink raw reply [flat|nested] 12+ messages in thread