[PATCH bpf-next 1/2] bpf: Reject callback subprogs invoke tailcall

From: Pu Lehui

Date: Sat Jul 11 2026 - 06:45:31 EST


From: Pu Lehui <pulehui@xxxxxxxxxx>

Some JIT compilers, such as x86_64, rely on a register to pass the TCC.
When subprograms of synchronous callback invoke tailcall, C helpers
invoking bpf callback clobber this register, and the corrupted TCC may
bypass the TCC limit, leading to infinite tailcall.

Fix this by rejecting tailcall inside all subprogs of sync callback.
This also cleanly consolidates the existing async and exception callback
checks into a single unified `is_cb` check.

Reported-by: Sashiko <sashiko-bot@xxxxxxxxxx>
Reported-by: Björn Töpel <bjorn@xxxxxxxxxx>
Signed-off-by: Pu Lehui <pulehui@xxxxxxxxxx>
---
kernel/bpf/verifier.c | 8 ++------
1 file changed, 2 insertions(+), 6 deletions(-)

diff --git a/kernel/bpf/verifier.c b/kernel/bpf/verifier.c
index 03e2202cca13..3f6c8b8fc04d 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;
- }
/* 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;
}
if (subprog[tmp].stack_arg_cnt) {
--
2.34.1