eBPF Tailcall: 6 Common Pitfalls and How to Detect Them
The article outlines six distinct kernel‑level bugs affecting the eBPF tailcall feature across multiple Linux versions, explains the underlying causes and the commits that fixed them, and introduces a detection tool to verify whether a running kernel is affected.
tailcallwas introduced in Linux kernel 4.2, enabling one BPF program to invoke another without returning to the caller.
1. freplace treated as an illegal tailcall target
When the freplace capability was added in kernel 5.6, freplace programs were incorrectly rejected as tail‑call targets. The regression was unintentionally corrected in kernel 6.2 by commit 4a9c7bbe2ed4 ("bpf: Resolve to prog‑>aux‑>dst_prog‑>type only for BPF_PROG_TYPE_EXT").
2. Incorrect tail_call_cnt loading offset
Using tailcall inside a sub‑program requires the kernel flag bpf: allow for tailcalls in BPF subprograms for x64 JIT , present since kernel 5.10. When the bpf2bpf feature was introduced, the offset used to load tail_call_cnt was mis‑calculated, causing failures. The bug was fixed in kernel 5.19 by commit ff672c67ee76 ("bpf, x86: Fix tail call count offset calculation on bpf2bpf call").
3. Infinite loop triggered by fentry / fexit tracing sub‑programs
The fentry / fexit trampoline interferes with the register‑based passing of tail_call_cnt via rax, creating an endless tail‑call loop. Kernel 6.7 resolved the issue with commit 2b5dcb31a19a ("bpf, x64: Fix tailcall infinite loop").
4. Tailcall hierarchy issue when stacking tailcall with bpf2bpf
Because tail_call_cnt propagates only in one direction, nesting a tailcall inside a bpf2bpf call breaks the hierarchy. Kernel 6.12 fixed the problem via commit 116e04ba1459 ("bpf, x64: Fix tailcall hierarchy").
5. Kernel panic when attaching a freplace program to a prog_array
Commit 4a9c7bbe2ed4 (v6.2) introduced a bug where attaching a freplace program cleared prog‑>aux‑>dst_prog, leading to a kernel panic. The regression was corrected in kernel 6.11 by commit fdad456cbcca ("bpf: Fix updating attached freplace prog in prog_array map").
6. Infinite loop caused by freplace combined with tailcall
When a freplace program runs, it resets tail_call_cnt to zero, which can trigger another infinite‑loop bug. Kernel 6.13 fixed the issue with commit d6083f040d5d ("bpf: Prevent tailcall infinite loop caused by freplace").
Tailcall issue detection
A small utility named tailcall-issues probes kernel behavior to report which of the six problems are present and whether they have been fixed. Example output on a 6.8.0‑35‑generic system:
# uname -r
6.8.0-35-generic
# ./tailcall-issues
detection results:
issue: invalid tailcallee
state: fixed
issue: invalid loading offset of tail_call_cnt for bpf2bpf
state: fixed
issue: tailcall infinite loop caused by trampoline
state: fixed
issue: tailcall hierarchy
state: not fixed
issue: panic caused by updating attached freplace prog to prog array
state: cannot detect
issue: tailcall infinite loop caused by freplace
state: not fixedSource code for the detector is available at https://github.com/Asphaltt/tailcall-issues.
References
bpf: allow bpf programs to tail‑call other bpf programs –
https://github.com/torvalds/linux/commit/04fd61ab36ec065e194ab5e74ae34a5240d992bbbpf: allow for tailcalls in BPF subprograms for x64 JIT –
https://github.com/torvalds/linux/commit/e411901c0b775a3ae7f3e2505f8d2d90ac696178tailcall‑issues detector –
https://github.com/Asphaltt/tailcall-issuesSigned-in readers can open the original source through BestHub's protected redirect.
This article has been distilled and summarized from source material, then republished for learning and reference. If you believe it infringes your rights, please contactand we will review it promptly.
How this landed with the community
Was this worth your time?
0 Comments
Thoughtful readers leave field notes, pushback, and hard-won operational detail here.
