linux/tools/testing/selftests/bpf/progs/tailcall_callback.c
Kumar Kartikeya Dwivedi d9ae3e4c7f selftests/bpf: Test direct tail calls from callbacks
tailcall_callback tests a tail call one static subprogram below a callback.
That reaches the later stack-depth rejection, but it does not exercise the
tail-call helper while the current frame is itself a callback.

Add a callback that calls bpf_tail_call directly and expect the existing
"cannot tail call within callback" diagnostic. On an affected kernel, the
load instead reaches the "callback unexpected regs" verifier bug, so the
expected message is absent and the test fails. The existing ordinary
subprogram case remains a success control for legitimate tail calls.

Signed-off-by: Kumar Kartikeya Dwivedi <memxor@gmail.com>
Link: https://lore.kernel.org/r/20260903144433.1716731-5-memxor@gmail.com
Signed-off-by: Alexei Starovoitov <ast@kernel.org>
2026-09-03 09:44:51 -07:00

98 lines
1.7 KiB
C

// SPDX-License-Identifier: GPL-2.0
#include <linux/bpf.h>
#include <bpf/bpf_helpers.h>
#include "bpf_misc.h"
#include "bpf_test_utils.h"
int classifier_0(struct __sk_buff *skb);
struct {
__uint(type, BPF_MAP_TYPE_PROG_ARRAY);
__uint(max_entries, 1);
__uint(key_size, sizeof(__u32));
__array(values, void (void));
} jmp_table SEC(".maps") = {
.values = {
[0] = (void *) &classifier_0,
},
};
__auxiliary
SEC("tc")
int classifier_0(struct __sk_buff *skb)
{
return 0;
}
static __noinline
int subprog_tail0(struct __sk_buff *skb)
{
int ret = 0;
bpf_tail_call_static(skb, &jmp_table, 0);
barrier_var(ret);
return ret;
}
static __noinline
int callback_loop(int index, void **cb_ctx)
{
int ret;
ret = subprog_tail0(*cb_ctx);
barrier_var(ret);
return ret ? 1 : 0;
}
static __noinline
int callback_tail(int index, void **cb_ctx)
{
bpf_tail_call_static(*cb_ctx, &jmp_table, 0);
return 0;
}
static __noinline
int callback_empty(int index, void *data)
{
return 0;
}
/* callback involving subprog with tail call is rejected */
SEC("tc")
__failure __msg("cannot tail call within callback")
int tailcall_callback_1(struct __sk_buff *skb)
{
clobber_regs_stack();
bpf_loop(1, callback_loop, &skb, 0);
return 0;
}
/* subprogs with tailcall do not affect no-tailcall callback */
SEC("tc")
__success
__retval(0)
int tailcall_callback_2(struct __sk_buff *skb)
{
int ret;
clobber_regs_stack();
ret = subprog_tail0(skb);
__sink(ret);
bpf_loop(1, callback_empty, NULL, 0);
return 0;
}
/* callback with a direct tail call is rejected without a verifier bug */
SEC("tc")
__failure __msg("cannot tail call within callback")
int tailcall_callback_3(struct __sk_buff *skb)
{
bpf_loop(1, callback_tail, &skb, 0);
return 0;
}
char __license[] SEC("license") = "GPL";