Commit 978da762 authored by Martin Teichmann's avatar Martin Teichmann Committed by Alexei Starovoitov
Browse files

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

- 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: default avatarMartin Teichmann <martin.teichmann@xfel.eu>
Acked-by: default avatarEduard Zingerman <eddyz87@gmail.com>
Link: https://lore.kernel.org/r/20251119160355.1160932-3-martin.teichmann@xfel.eu


Signed-off-by: default avatarAlexei Starovoitov <ast@kernel.org>
parent e3245f89
Loading
Loading
Loading
Loading
+37 −2
Original line number Diff line number Diff line
@@ -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";
+53 −0
Original line number Diff line number Diff line
@@ -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";