bpf: test the proper verification of tail calls

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 1a4607ffba.

- 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>
This commit is contained in:
Martin Teichmann
2025-11-19 17:03:53 +01:00
committed by Alexei Starovoitov
parent e3245f8990
commit 978da762ea
2 changed files with 90 additions and 2 deletions

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

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