BPF List
 help / color / mirror / Atom feed
From: sashiko-bot@kernel.org
To: "Pu Lehui" <pulehui@huaweicloud.com>
Cc: bpf@vger.kernel.org
Subject: Re: [PATCH bpf-next v2 1/2] bpf: Reject callback subprogs invoke tailcall
Date: Tue, 14 Jul 2026 02:56:44 +0000	[thread overview]
Message-ID: <20260714025645.101971F000E9@smtp.kernel.org> (raw)
In-Reply-To: <20260714024459.420075-2-pulehui@huaweicloud.com>

Thank you for your contribution! Sashiko AI review found 2 potential issue(s) to consider:
- [High] The patch inadvertently allows async and exception callbacks to execute tail calls, bypassing verifier protections and reintroducing the TCC register clobbering vulnerability.
- [High] The unified `is_cb` check introduces a DFS state leakage that incorrectly rejects valid BPF programs containing both a tail call and an unrelated synchronous callback.
--

--- Patch [1]: [PATCH bpf-next v2 1/2] bpf: Reject callback subprogs invoke tailcall ---
commit d260c3afbc5fedfa9c5c074686a31f057a21a53a
Author: Pu Lehui <pulehui@huawei.com>

bpf: Reject callback subprogs invoke tailcall

This patch rejects tailcalls inside all subprograms of synchronous callbacks
to prevent clobbering the TCC limit register on some JIT compilers. It also
consolidates async and exception callback checks into a single unified
is_cb check.

> diff --git a/kernel/bpf/verifier.c b/kernel/bpf/verifier.c
> index 03e2202cca13d..3f6c8b8fc04d0 100644
> --- a/kernel/bpf/verifier.c
> +++ b/kernel/bpf/verifier.c
> @@ -5258,10 +5258,6 @@ static int check_max_stack_depth_subprog(struct bpf_verifier_env *env, int idx,
>  		if (verifier_bug_if(sidx < 0, env, "callee not found at insn %d", next_insn))
>  			return -EFAULT;
>  		if (subprog[sidx].is_async_cb) {
> -			if (subprog[sidx].has_tail_call) {
> -				verifier_bug(env, "subprog has tail_call and async cb");
> -				return -EFAULT;
> -			}

[Severity: High]
Does removing this explicit check allow async and exception callbacks to
execute tail calls when they contain no further subprogram calls?

Because async and exception callbacks are evaluated as the root of their own
call graphs by check_max_stack_depth_subprog() (with idx > 0), the local
tail_call_reachable variable remains false throughout the evaluation loop if
the callback makes no normal subprogram calls.

When the loop ends, the new unified is_cb check is bypassed because
tail_call_reachable is false, and the fallback check is skipped because !idx
is false for async callbacks. Can this bypass the TCC limit protections and
lead to infinite tailcalls?

>  			/* async callbacks don't increase bpf prog stack size unless called directly */
>  			if (!bpf_pseudo_call(insn + i))
>  				continue;
> @@ -5302,8 +5298,8 @@ static int check_max_stack_depth_subprog(struct bpf_verifier_env *env, int idx,
>  	 */
>  	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");
> +			if (subprog[tmp].is_cb) {
> +				verbose(env, "cannot tail call within callback\n");
>  				return -EINVAL;
>  			}

[Severity: High]
Can the local tail_call_reachable variable leak across unrelated subprograms
during the verifier's Depth-First Search?

If a BPF program first calls a subprogram containing a tail call, the DFS
will set tail_call_reachable = true and mark it true for all ancestors.
When backtracking from the callee to the main program, tail_call_reachable
is restored to the ancestor's state:

kernel/bpf/verifier.c:check_max_stack_depth_subprog() {
    ...
	/* reset tail_call_reachable to the parent's actual state */
	tail_call_reachable = subprog[idx].tail_call_reachable;

	goto continue_func;
    ...
}

If the program subsequently sets up a synchronous callback (e.g., via
bpf_loop()), the loop jumps back up without resetting tail_call_reachable to
false. When it reaches the end of the callback subprogram,
tail_call_reachable will still be true from the leaked state.

Will this cause the new unified is_cb check to incorrectly reject perfectly
valid BPF programs that contain both a tail call and an unrelated synchronous
callback?

-- 
Sashiko AI review · https://sashiko.dev/#/patchset/20260714024459.420075-1-pulehui@huaweicloud.com?part=1

  reply	other threads:[~2026-07-14  2:56 UTC|newest]

Thread overview: 6+ messages / expand[flat|nested]  mbox.gz  Atom feed  top
2026-07-14  2:44 [PATCH bpf-next v2 0/2] Reject callback subprogs invoke tailcall Pu Lehui
2026-07-14  2:44 ` [PATCH bpf-next v2 1/2] bpf: " Pu Lehui
2026-07-14  2:56   ` sashiko-bot [this message]
2026-07-14 13:47     ` Björn Töpel
2026-07-14  2:44 ` [PATCH bpf-next v2 2/2] selftests/bpf: Add testcase for callback with tailcall Pu Lehui
2026-07-14  2:53   ` sashiko-bot

Reply instructions:

You may reply publicly to this message via plain-text email
using any one of the following methods:

* Save the following mbox file, import it into your mail client,
  and reply-to-all from there: mbox

  Avoid top-posting and favor interleaved quoting:
  https://en.wikipedia.org/wiki/Posting_style#Interleaved_style

* Reply using the --to, --cc, and --in-reply-to
  switches of git-send-email(1):

  git send-email \
    --in-reply-to=20260714025645.101971F000E9@smtp.kernel.org \
    --to=sashiko-bot@kernel.org \
    --cc=bpf@vger.kernel.org \
    --cc=pulehui@huaweicloud.com \
    --cc=sashiko-reviews@lists.linux.dev \
    /path/to/YOUR_REPLY

  https://kernel.org/pub/software/scm/git/docs/git-send-email.html

* If your mail client supports setting the In-Reply-To header
  via mailto: links, try the mailto: link
Be sure your reply has a Subject: header at the top and a blank line before the message body.
This is a public inbox, see mirroring instructions
for how to clone and mirror all data and code used for this inbox