Skip to content

Commit

Permalink
selftests/bpf: Test r0 and ref lifetime after BPF-BPF call with abnor…
Browse files Browse the repository at this point in the history
…mal return

In all three cases where a callee can abnormally return (tail_call(),
LD_ABS, and LD_IND), test the verifier doesn't know the bounds of:

- r0 / what the callee returned.
- References to the caller's stack passed to the callee.

Additionally, ensure the tail_call fallthrough case can't access r0, as
bpf_tail_call() returns nothing on failure.

Signed-off-by: Arthur Fabre <[email protected]>
Acked-by: Eduard Zingerman <[email protected]>
  • Loading branch information
arthurfabre authored and Kernel Patches Daemon committed Jan 6, 2025
1 parent 7ec0d3d commit 9c7423f
Show file tree
Hide file tree
Showing 2 changed files with 117 additions and 0 deletions.
2 changes: 2 additions & 0 deletions tools/testing/selftests/bpf/prog_tests/verifier.c
Original file line number Diff line number Diff line change
Expand Up @@ -3,6 +3,7 @@
#include <test_progs.h>

#include "cap_helpers.h"
#include "verifier_abnormal_ret.skel.h"
#include "verifier_and.skel.h"
#include "verifier_arena.skel.h"
#include "verifier_arena_large.skel.h"
Expand Down Expand Up @@ -133,6 +134,7 @@ static void run_tests_aux(const char *skel_name,

#define RUN(skel) run_tests_aux(#skel, skel##__elf_bytes, NULL)

void test_verifier_abnormal_ret(void) { RUN(verifier_abnormal_ret); }
void test_verifier_and(void) { RUN(verifier_and); }
void test_verifier_arena(void) { RUN(verifier_arena); }
void test_verifier_arena_large(void) { RUN(verifier_arena_large); }
Expand Down
115 changes: 115 additions & 0 deletions tools/testing/selftests/bpf/progs/verifier_abnormal_ret.c
Original file line number Diff line number Diff line change
@@ -0,0 +1,115 @@
// SPDX-License-Identifier: GPL-2.0

#include <linux/bpf.h>
#include <bpf/bpf_helpers.h>
#include "../../../include/linux/filter.h"
#include "bpf_misc.h"

#define TEST(NAME, CALLEE) \
SEC("socket") \
__description("r0: " #NAME) \
__failure __msg("math between ctx pointer and register with unbounded min value") \
__naked int check_abnormal_ret_r0_##NAME(void) \
{ \
asm volatile(" \
r6 = r1; \
r2 = r10; \
r2 += -8; \
call " #CALLEE "; \
r6 += r0; \
r0 = 0; \
exit; \
" : \
: \
: __clobber_all); \
} \
\
SEC("socket") \
__description("ref: " #NAME) \
__failure __msg("math between ctx pointer and register with unbounded min value") \
__naked int check_abnormal_ret_ref_##NAME(void) \
{ \
asm volatile(" \
r6 = r1; \
r7 = r10; \
r7 += -8; \
r2 = r7; \
call " #CALLEE "; \
r0 = *(u64*)(r7 + 0); \
r6 += r0; \
exit; \
" : \
: \
: __clobber_all); \
}

TEST(ld_abs, callee_ld_abs);
TEST(ld_ind, callee_ld_ind);
TEST(tail_call, callee_tail_call);

static __naked __noinline __used
int callee_ld_abs(void)
{
asm volatile(" \
r6 = r1; \
r9 = r2; \
.8byte %[ld_abs]; \
*(u64*)(r9 + 0) = 1; \
r0 = 0; \
exit; \
" :
: __imm_insn(ld_abs, BPF_LD_ABS(BPF_W, 0))
: __clobber_all);
}

static __naked __noinline __used
int callee_ld_ind(void)
{
asm volatile(" \
r6 = r1; \
r7 = 1; \
r9 = r2; \
.8byte %[ld_ind]; \
*(u64*)(r9 + 0) = 1; \
r0 = 0; \
exit; \
" :
: __imm_insn(ld_ind, BPF_LD_IND(BPF_W, BPF_REG_7, 0))
: __clobber_all);
}

SEC("socket")
__auxiliary __naked
int dummy_prog(void)
{
asm volatile("r0 = 1; exit;");
}

struct {
__uint(type, BPF_MAP_TYPE_PROG_ARRAY);
__uint(max_entries, 1);
__uint(key_size, sizeof(int));
__array(values, void(void));
} map_prog SEC(".maps") = {
.values = {
[0] = (void *)&dummy_prog,
},
};

static __noinline __used
int callee_tail_call(struct __sk_buff *skb, __u64 *foo)
{
bpf_tail_call(skb, &map_prog, 0);
*foo = 1;
return 0;
}

SEC("socket")
__description("r0 not set by tail_call")
__failure __msg("R0 !read_ok")
int check_abnormal_ret_tail_call_fail(struct __sk_buff *skb)
{
return bpf_tail_call(skb, &map_prog, 0);
}

char _license[] SEC("license") = "GPL";

0 comments on commit 9c7423f

Please sign in to comment.