summaryrefslogtreecommitdiff
diff options
context:
space:
mode:
authorKumar Kartikeya Dwivedi <memxor@gmail.com>2026-09-24 18:57:10 +0200
committerAlexei Starovoitov <ast@kernel.org>2026-09-24 18:04:45 +0000
commit7fc140ba3dd95fb0d94414ebe1b52ca990c3dc3a (patch)
tree6b5cd1c88fe772f3666b2e201feb6e05500720a4
parent9f3d900a69be7e52d82bc06577d55a33cc41c30c (diff)
selftests/bpf: Cover the tail call caller stack depth limit
A tail call from a subprog only unwinds that subprog's frame, so the verifier refuses tail calls once the frames of the callers add up to 256 bytes or more. Nothing exercised that rule. Add a pair of tests with a caller using 240 and 256 bytes of stack respectively, the latter expecting the "tail_calls are not allowed when call stack of previous frames is 256 bytes" rejection. Signed-off-by: Kumar Kartikeya Dwivedi <memxor@gmail.com> Signed-off-by: Alexei Starovoitov <ast@kernel.org> Link: https://patch.msgid.link/20260924165740.2146806-10-memxor@gmail.com
-rw-r--r--tools/testing/selftests/bpf/progs/verifier_tailcall.c57
1 files changed, 57 insertions, 0 deletions
diff --git a/tools/testing/selftests/bpf/progs/verifier_tailcall.c b/tools/testing/selftests/bpf/progs/verifier_tailcall.c
index b4acce60fb9b..51687da97225 100644
--- a/tools/testing/selftests/bpf/progs/verifier_tailcall.c
+++ b/tools/testing/selftests/bpf/progs/verifier_tailcall.c
@@ -28,4 +28,61 @@ __naked void invalid_map_for_tail_call(void)
: __clobber_all);
}
+struct {
+ __uint(type, BPF_MAP_TYPE_PROG_ARRAY);
+ __uint(max_entries, 1);
+ __uint(key_size, sizeof(__u32));
+ __uint(value_size, sizeof(__u32));
+} jmp_table SEC(".maps");
+
+__used __naked
+static int subprog_tail_call(void)
+{
+ asm volatile (" \
+ r2 = %[jmp_table] ll; \
+ r3 = 0; \
+ call %[bpf_tail_call]; \
+ r0 = 0; \
+ exit; \
+" :
+ : __imm(bpf_tail_call),
+ __imm_addr(jmp_table)
+ : __clobber_all);
+}
+
+/*
+ * A tail call unwinds only the frame of the subprog doing it, so the
+ * frames of its callers stay on the stack. With up to 33 tail calls in
+ * a chain the verifier caps the stack those frames may add up to at
+ * 256 bytes.
+ */
+SEC("tc")
+__description("tail call from subprog with 240 bytes of caller stack")
+__success
+__naked void tail_call_caller_stack_ok(void)
+{
+ asm volatile (" \
+ r2 = 42; \
+ *(u64 *)(r10 - 240) = r2; \
+ call subprog_tail_call; \
+ r0 = 0; \
+ exit; \
+" ::: __clobber_all);
+}
+
+SEC("tc")
+__description("tail call from subprog with 256 bytes of caller stack")
+__failure
+__msg("tail_calls are not allowed when call stack of previous frames is 256 bytes. Too large")
+__naked void tail_call_caller_stack_too_large(void)
+{
+ asm volatile (" \
+ r2 = 42; \
+ *(u64 *)(r10 - 256) = r2; \
+ call subprog_tail_call; \
+ r0 = 0; \
+ exit; \
+" ::: __clobber_all);
+}
+
char _license[] SEC("license") = "GPL";