mirror of
https://git.kernel.org/pub/scm/linux/kernel/git/torvalds/linux.git
synced 2026-04-05 07:08:06 -04:00
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:
committed by
Alexei Starovoitov
parent
e3245f8990
commit
978da762ea
@@ -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";
|
||||
|
||||
@@ -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";
|
||||
|
||||
Reference in New Issue
Block a user