]> git.ipfire.org Git - thirdparty/linux.git/commitdiff
bpf: test the proper verification of tail calls
authorMartin Teichmann <martin.teichmann@xfel.eu>
Wed, 19 Nov 2025 16:03:53 +0000 (17:03 +0100)
committerAlexei Starovoitov <ast@kernel.org>
Sat, 22 Nov 2025 01:45:30 +0000 (17:45 -0800)
Three tests are added:

- invalidate_pkt_pointers_by_tail_call checks that one can use the
  packet pointer after a tail call. This was originally possible
  and also poses not problems, but was made impossible by 1a4607ffba35.

- invalidate_pkt_pointers_by_static_tail_call tests a corner case
  found by Eduard Zingerman during the discussion of the original fix,
  which was broken in that fix.

- subprog_result_tail_call tests that precision propagation works
  correctly across tail calls. This did not work before.

Signed-off-by: Martin Teichmann <martin.teichmann@xfel.eu>
Acked-by: Eduard Zingerman <eddyz87@gmail.com>
Link: https://lore.kernel.org/r/20251119160355.1160932-3-martin.teichmann@xfel.eu
Signed-off-by: Alexei Starovoitov <ast@kernel.org>
tools/testing/selftests/bpf/progs/verifier_sock.c
tools/testing/selftests/bpf/progs/verifier_subprog_precision.c

index 2b4610b53382d0c98f1e2d22c49be7fd45612b19..a2132c72d3b806e18ce36fd7105e89b2f749e27e 100644 (file)
@@ -1117,10 +1117,17 @@ int tail_call(struct __sk_buff *sk)
        return 0;
 }
 
-/* Tail calls invalidate packet pointers. */
+static __noinline
+int static_tail_call(struct __sk_buff *sk)
+{
+       bpf_tail_call_static(sk, &jmp_table, 0);
+       return 0;
+}
+
+/* Tail calls in sub-programs invalidate packet pointers. */
 SEC("tc")
 __failure __msg("invalid mem access")
-int invalidate_pkt_pointers_by_tail_call(struct __sk_buff *sk)
+int invalidate_pkt_pointers_by_global_tail_call(struct __sk_buff *sk)
 {
        int *p = (void *)(long)sk->data;
 
@@ -1131,4 +1138,32 @@ int invalidate_pkt_pointers_by_tail_call(struct __sk_buff *sk)
        return TCX_PASS;
 }
 
+/* Tail calls in static sub-programs invalidate packet pointers. */
+SEC("tc")
+__failure __msg("invalid mem access")
+int invalidate_pkt_pointers_by_static_tail_call(struct __sk_buff *sk)
+{
+       int *p = (void *)(long)sk->data;
+
+       if ((void *)(p + 1) > (void *)(long)sk->data_end)
+               return TCX_DROP;
+       static_tail_call(sk);
+       *p = 42; /* this is unsafe */
+       return TCX_PASS;
+}
+
+/* Direct tail calls do not invalidate packet pointers. */
+SEC("tc")
+__success
+int invalidate_pkt_pointers_by_tail_call(struct __sk_buff *sk)
+{
+       int *p = (void *)(long)sk->data;
+
+       if ((void *)(p + 1) > (void *)(long)sk->data_end)
+               return TCX_DROP;
+       bpf_tail_call_static(sk, &jmp_table, 0);
+       *p = 42; /* this is NOT unsafe: tail calls don't return */
+       return TCX_PASS;
+}
+
 char _license[] SEC("license") = "GPL";
index ac3e418c2a9616f9d6329ff50ce97cf357ed80dd..61886ed554de80e3349fd2315df5741d2cde4541 100644 (file)
@@ -793,4 +793,57 @@ __naked int stack_slot_aliases_precision(void)
        );
 }
 
+struct {
+        __uint(type, BPF_MAP_TYPE_PROG_ARRAY);
+        __uint(max_entries, 1);
+        __type(key, __u32);
+        __type(value, __u32);
+} map_array SEC(".maps");
+
+__naked __noinline __used
+static unsigned long identity_tail_call(void)
+{
+       /* the simplest identity function involving a tail call */
+        asm volatile (
+               "r6 = r2;"
+               "r2 = %[map_array] ll;"
+               "r3 = 0;"
+               "call %[bpf_tail_call];"
+               "r0 = r6;"
+               "exit;"
+               :
+               : __imm(bpf_tail_call),
+                 __imm_addr(map_array)
+               : __clobber_all);
+}
+
+SEC("?raw_tp")
+__failure __log_level(2)
+__msg("13: (85) call bpf_tail_call#12")
+__msg("mark_precise: frame1: last_idx 13 first_idx 0 subseq_idx -1 ")
+__msg("returning from callee:")
+__msg("frame1: R0=scalar() R6=3 R10=fp0")
+__msg("to caller at 4:")
+__msg("R0=scalar() R6=map_value(map=.data.vals,ks=4,vs=16) R10=fp0")
+__msg("6: (0f) r1 += r0")
+__msg("mark_precise: frame0: regs=r0 stack= before 5: (bf) r1 = r6")
+__msg("mark_precise: frame0: regs=r0 stack= before 4: (27) r0 *= 4")
+__msg("mark_precise: frame0: parent state regs=r0 stack=:  R0=Pscalar() R6=map_value(map=.data.vals,ks=4,vs=16) R10=fp0")
+__msg("math between map_value pointer and register with unbounded min value is not allowed")
+__naked int subprog_result_tail_call(void)
+{
+       asm volatile (
+               "r2 = 3;"
+               "call identity_tail_call;"
+               "r0 *= 4;"
+               "r1 = %[vals];"
+               "r1 += r0;"
+               "r0 = *(u32 *)(r1 + 0);"
+               "exit;"
+               :
+               : __imm_ptr(vals)
+               : __clobber_common
+       );
+}
+
 char _license[] SEC("license") = "GPL";