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 <daniel@iogearbox.net>
Signed-off-by: Alexei Starovoitov <ast@kernel.org>
Link: https://patch.msgid.link/20260925175244.1136329-13-daniel@iogearbox.net
This commit is contained in:
Daniel Borkmann 2026-09-25 19:52:39 +02:00 committed by Alexei Starovoitov
parent db8c58c83f
commit 5add2c64f4
No known key found for this signature in database

View File

@ -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";