From 5add2c64f4740ca4a1944b50f6427eef219dac8c Mon Sep 17 00:00:00 2001 From: Daniel Borkmann Date: Fri, 25 Sep 2026 19:52:39 +0200 Subject: [PATCH] selftests/bpf: Add a test for a tail call in a subprogram without an exit Add a test with a subprogram which leaves through a tail call or a bpf_throw() and never reaches an exit insn, so that the hidden edge of the tail call has no exit to point at. The subprogram accesses its stack, so that the liveness pass walks it. # LDLIBS=-static PKG_CONFIG='pkg-config --static' ./vmtest.sh -- ./test_progs -t verifier_cfg [...] #624/21 verifier_cfg/tail call in a subprogram without an exit:OK #624 verifier_cfg:OK Summary: 1/21 PASSED, 0 SKIPPED, 0/0 FAILED Signed-off-by: Daniel Borkmann Signed-off-by: Alexei Starovoitov Link: https://patch.msgid.link/20260925175244.1136329-13-daniel@iogearbox.net --- .../selftests/bpf/progs/verifier_cfg.c | 38 +++++++++++++++++++ 1 file changed, 38 insertions(+) diff --git a/tools/testing/selftests/bpf/progs/verifier_cfg.c b/tools/testing/selftests/bpf/progs/verifier_cfg.c index 3c3bb03e8217..07388a922e5b 100644 --- a/tools/testing/selftests/bpf/progs/verifier_cfg.c +++ b/tools/testing/selftests/bpf/progs/verifier_cfg.c @@ -173,4 +173,42 @@ l1_%=: exit; \ " ::: __clobber_all); } +struct { + __uint(type, BPF_MAP_TYPE_PROG_ARRAY); + __uint(max_entries, 1); + __type(key, __u32); + __type(value, __u32); +} map_array SEC(".maps"); + +extern void bpf_throw(__u64 cookie) __ksym; + +/* + * The subprogram leaves through the tail call or the exception and never + * reaches an exit insn, so the hidden edge of the tail call has no exit to + * point at. The stack access makes the liveness pass walk the subprogram, + * and the loop compiles to a single jump back onto itself. + */ +__noinline __used +static int tail_call_or_throw_subprog(void *ctx) +{ + volatile __u32 idx = 0; + + bpf_tail_call(ctx, &map_array, idx); + bpf_throw(0); + for (;;) {} +} + +SEC("tc") +__description("tail call in a subprogram without an exit") +__arch_x86_64 +__arch_arm64 +__success +__naked void tail_call_in_subprog_without_exit(void) +{ + asm volatile (" \ + call tail_call_or_throw_subprog; \ + exit; \ +" ::: __clobber_all); +} + char _license[] SEC("license") = "GPL";