Re: [PATCH bpf-next v6 13/17] bpf: Reject stack arguments if tail call reachable
Yonghong Song <[email protected]>
| Newsgroups | dev.linux.lists.sashiko,org.kernel.vger.bpf |
|---|---|
| Message-ID | <[email protected]> |
On 4/19/26 10:08 AM, [email protected] wrote: > Sashiko AI review found 1 potential issue(s): > - [High] The verifier incorrectly rejects perfectly valid subprograms with stack arguments due to an order-dependent state leak of `tail_call_reachable` during the call graph traversal. > -- > > commit 2677e39a30cc9fcf44cadb1927d1362ad39f7b4e > Author: Yonghong Song <[email protected]> > > bpf: Reject stack arguments if tail call reachable > > This commit deprecates tailcalls in the presence of stack arguments, > rejecting subprograms with stack arguments if a tail call is reachable. > >> diff --git a/kernel/bpf/verifier.c b/kernel/bpf/verifier.c >> index 13f1fd7880921..af89750498836 100644 >> --- a/kernel/bpf/verifier.c >> +++ b/kernel/bpf/verifier.c > [ ... ] > >> @@ -5672,14 +5677,23 @@ static int check_max_stack_depth_subprog(struct bpf_verifier_env *env, int idx, >> * this info will be utilized by JIT so that we will be preserving the >> * tail call counter throughout bpf2bpf calls combined with tailcalls >> */ >> - if (tail_call_reachable) >> + if (tail_call_reachable) { >> for (tmp = idx; tmp >= 0; tmp = dinfo[tmp].caller) { >> if (subprog[tmp].is_exception_cb) { >> verbose(env, "cannot tail call within exception cb\n"); >> return -EINVAL; >> } >> + if (subprog_has_stack_args(&subprog[tmp])) { >> + verbose(env, "tail_calls are not allowed in programs with stack args\n"); >> + return -EINVAL; >> + } > Can this leak the tail_call_reachable state across disjoint call graph branches? I guess this is a known issue such that tail_call_reachable is marked more than necessar. Since this has been mentioned a couple of times. I will fix it in the next revision. > > In check_max_stack_depth_subprog(), tail_call_reachable is a local variable > that seems to act as an accumulator. If the main program first calls subprogram A > (which has a tail call) and subsequently calls subprogram B (which has stack > arguments but no tail call), tail_call_reachable remains true while traversing B. > > When the traversal finishes subprogram B, this newly added check evaluates the > current call stack (main -> B). Since tail_call_reachable leaked from the main > -> A traversal, and subprogram B has stack arguments, the check would falsely > reject the program. > > Conversely, if the bytecode is ordered so that B is called before A, > tail_call_reachable would be false while traversing B, allowing the program to > load successfully. > > Does this introduce an unpredictable, order-dependent functional regression? > >> subprog[tmp].tail_call_reachable = true; >> } >> + } else if (!idx && subprog[0].has_tail_call && subprog_has_stack_args(&subprog[0])) { >> + verbose(env, "tail_calls are not allowed in programs with stack args\n"); >> + return -EINVAL; >> + } >> + >> if (subprog[0].tail_call_reachable) >> env->prog->aux->tail_call_reachable = true; >>