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;
>>
lmpx.com only provides a reader for public news (NNTP) servers. It is not affiliated with the servers or forums shown here and is not responsible for the content of articles, which is written by their respective authors.