On Thu, Feb 01, 2024 at 04:21:09AM +0000, Kumar Kartikeya Dwivedi wrote: > Add tests for the runtime cleanup support for exceptions, ensuring that > resources are correctly identified and released when an exception is > thrown. Also, we add negative tests to exercise corner cases the > verifier should reject. > > Signed-off-by: Kumar Kartikeya Dwivedi <memxor@xxxxxxxxx> > --- > tools/testing/selftests/bpf/DENYLIST.aarch64 | 1 + > tools/testing/selftests/bpf/DENYLIST.s390x | 1 + > .../bpf/prog_tests/exceptions_cleanup.c | 65 +++ > .../selftests/bpf/progs/exceptions_cleanup.c | 468 ++++++++++++++++++ > .../bpf/progs/exceptions_cleanup_fail.c | 154 ++++++ > .../selftests/bpf/progs/exceptions_fail.c | 13 - > 6 files changed, 689 insertions(+), 13 deletions(-) > create mode 100644 tools/testing/selftests/bpf/prog_tests/exceptions_cleanup.c > create mode 100644 tools/testing/selftests/bpf/progs/exceptions_cleanup.c > create mode 100644 tools/testing/selftests/bpf/progs/exceptions_cleanup_fail.c > > diff --git a/tools/testing/selftests/bpf/DENYLIST.aarch64 b/tools/testing/selftests/bpf/DENYLIST.aarch64 > index 5c2cc7e8c5d0..6fc79727cd14 100644 > --- a/tools/testing/selftests/bpf/DENYLIST.aarch64 > +++ b/tools/testing/selftests/bpf/DENYLIST.aarch64 > @@ -1,6 +1,7 @@ > bpf_cookie/multi_kprobe_attach_api # kprobe_multi_link_api_subtest:FAIL:fentry_raw_skel_load unexpected error: -3 > bpf_cookie/multi_kprobe_link_api # kprobe_multi_link_api_subtest:FAIL:fentry_raw_skel_load unexpected error: -3 > exceptions # JIT does not support calling kfunc bpf_throw: -524 > +exceptions_unwind # JIT does not support calling kfunc bpf_throw: -524 > fexit_sleep # The test never returns. The remaining tests cannot start. > kprobe_multi_bench_attach # needs CONFIG_FPROBE > kprobe_multi_test # needs CONFIG_FPROBE > diff --git a/tools/testing/selftests/bpf/DENYLIST.s390x b/tools/testing/selftests/bpf/DENYLIST.s390x > index 1a63996c0304..f09a73dee72c 100644 > --- a/tools/testing/selftests/bpf/DENYLIST.s390x > +++ b/tools/testing/selftests/bpf/DENYLIST.s390x > @@ -1,5 +1,6 @@ > # TEMPORARY > # Alphabetical order > exceptions # JIT does not support calling kfunc bpf_throw (exceptions) > +exceptions_unwind # JIT does not support calling kfunc bpf_throw (exceptions) > get_stack_raw_tp # user_stack corrupted user stack (no backchain userspace) > stacktrace_build_id # compare_map_keys stackid_hmap vs. stackmap err -2 errno 2 (?) > diff --git a/tools/testing/selftests/bpf/prog_tests/exceptions_cleanup.c b/tools/testing/selftests/bpf/prog_tests/exceptions_cleanup.c > new file mode 100644 > index 000000000000..78df037b60ea > --- /dev/null > +++ b/tools/testing/selftests/bpf/prog_tests/exceptions_cleanup.c > @@ -0,0 +1,65 @@ > +#include "bpf/bpf.h" > +#include "exceptions.skel.h" > +#include <test_progs.h> > +#include <network_helpers.h> > + > +#include "exceptions_cleanup.skel.h" > +#include "exceptions_cleanup_fail.skel.h" > + > +static void test_exceptions_cleanup_fail(void) > +{ > + RUN_TESTS(exceptions_cleanup_fail); > +} > + > +void test_exceptions_cleanup(void) > +{ > + LIBBPF_OPTS(bpf_test_run_opts, ropts, > + .data_in = &pkt_v4, > + .data_size_in = sizeof(pkt_v4), > + .repeat = 1, > + ); > + struct exceptions_cleanup *skel; > + int ret; > + > + if (test__start_subtest("exceptions_cleanup_fail")) > + test_exceptions_cleanup_fail(); RUN_TESTS takes care of doing test__start_subtest(), etc. You should be able to just call RUN_TESTS(exceptions_cleanup_fail) directly here. > + > + skel = exceptions_cleanup__open_and_load(); > + if (!ASSERT_OK_PTR(skel, "exceptions_cleanup__open_and_load")) > + return; > + > + ret = exceptions_cleanup__attach(skel); > + if (!ASSERT_OK(ret, "exceptions_cleanup__attach")) > + return; > + > +#define RUN_EXC_CLEANUP_TEST(name) \ Should we add a call to if (test__start_subtest(#name)) to this macro? > + ret = bpf_prog_test_run_opts(bpf_program__fd(skel->progs.name), \ > + &ropts); \ > + if (!ASSERT_OK(ret, #name ": return value")) \ > + return; \ > + if (!ASSERT_EQ(ropts.retval, 0xeB9F, #name ": opts.retval")) \ > + return; \ > + ret = bpf_prog_test_run_opts( \ > + bpf_program__fd(skel->progs.exceptions_cleanup_check), \ > + &ropts); \ > + if (!ASSERT_OK(ret, #name " CHECK: return value")) \ > + return; \ > + if (!ASSERT_EQ(ropts.retval, 0, #name " CHECK: opts.retval")) \ > + return; \ > + skel->bss->only_count = 0; > + > + RUN_EXC_CLEANUP_TEST(exceptions_cleanup_prog_num_iter); > + RUN_EXC_CLEANUP_TEST(exceptions_cleanup_prog_num_iter_mult); > + RUN_EXC_CLEANUP_TEST(exceptions_cleanup_prog_dynptr_iter); > + RUN_EXC_CLEANUP_TEST(exceptions_cleanup_obj); > + RUN_EXC_CLEANUP_TEST(exceptions_cleanup_percpu_obj); > + RUN_EXC_CLEANUP_TEST(exceptions_cleanup_ringbuf); > + RUN_EXC_CLEANUP_TEST(exceptions_cleanup_reg); > + RUN_EXC_CLEANUP_TEST(exceptions_cleanup_null_or_ptr_do_ptr); > + RUN_EXC_CLEANUP_TEST(exceptions_cleanup_null_or_ptr_do_null); > + RUN_EXC_CLEANUP_TEST(exceptions_cleanup_callee_saved); > + RUN_EXC_CLEANUP_TEST(exceptions_cleanup_frame); > + RUN_EXC_CLEANUP_TEST(exceptions_cleanup_loop_iterations); > + RUN_EXC_CLEANUP_TEST(exceptions_cleanup_dead_code_elim); > + RUN_EXC_CLEANUP_TEST(exceptions_cleanup_frame_dce); > +} > diff --git a/tools/testing/selftests/bpf/progs/exceptions_cleanup.c b/tools/testing/selftests/bpf/progs/exceptions_cleanup.c > new file mode 100644 > index 000000000000..ccf14fe6bd1b > --- /dev/null > +++ b/tools/testing/selftests/bpf/progs/exceptions_cleanup.c > @@ -0,0 +1,468 @@ > +// SPDX-License-Identifier: GPL-2.0 > +#include <vmlinux.h> > +#include <bpf/bpf_tracing.h> > +#include <bpf/bpf_helpers.h> > +#include <bpf/bpf_core_read.h> > +#include <bpf/bpf_endian.h> > +#include "bpf_misc.h" > +#include "bpf_kfuncs.h" > +#include "bpf_experimental.h" > + > +struct { > + __uint(type, BPF_MAP_TYPE_RINGBUF); > + __uint(max_entries, 8); > +} ringbuf SEC(".maps"); > + > +enum { > + RES_DYNPTR, > + RES_ITER, > + RES_REG, > + RES_SPILL, > + __RES_MAX, > +}; > + > +struct bpf_resource { > + int type; > +}; > + > +struct { > + __uint(type, BPF_MAP_TYPE_HASH); > + __uint(max_entries, 1024); > + __type(key, int); > + __type(value, struct bpf_resource); > +} hashmap SEC(".maps"); > + > +const volatile bool always_false = false; > +bool only_count = false; > +int res_count = 0; > + > +#define MARK_RESOURCE(ptr, type) ({ res_count++; bpf_map_update_elem(&hashmap, &(void *){ptr}, &(struct bpf_resource){type}, 0); }); > +#define FIND_RESOURCE(ptr) ((struct bpf_resource *)bpf_map_lookup_elem(&hashmap, &(void *){ptr}) ?: &(struct bpf_resource){__RES_MAX}) > +#define FREE_RESOURCE(ptr) bpf_map_delete_elem(&hashmap, &(void *){ptr}) > +#define VAL 0xeB9F > + > +SEC("fentry/bpf_cleanup_resource") > +int BPF_PROG(exception_cleanup_mark_free, struct bpf_frame_desc_reg_entry *fd, void *ptr) > +{ > + if (fd->spill_type == STACK_INVALID) > + bpf_probe_read_kernel(&ptr, sizeof(ptr), ptr); > + if (only_count) { > + res_count--; > + return 0; > + } > + switch (fd->spill_type) { > + case STACK_SPILL: > + if (FIND_RESOURCE(ptr)->type == RES_SPILL) > + FREE_RESOURCE(ptr); > + break; > + case STACK_INVALID: > + if (FIND_RESOURCE(ptr)->type == RES_REG) > + FREE_RESOURCE(ptr); > + break; > + case STACK_ITER: > + if (FIND_RESOURCE(ptr)->type == RES_ITER) > + FREE_RESOURCE(ptr); > + break; > + case STACK_DYNPTR: > + if (FIND_RESOURCE(ptr)->type == RES_DYNPTR) > + FREE_RESOURCE(ptr); > + break; > + } > + return 0; > +} > + > +static long map_cb(struct bpf_map *map, void *key, void *value, void *ctx) > +{ > + int *cnt = ctx; > + > + (*cnt)++; > + return 0; > +} > + > +SEC("tc") > +int exceptions_cleanup_check(struct __sk_buff *ctx) > +{ > + int cnt = 0; > + > + if (only_count) > + return res_count; > + bpf_for_each_map_elem(&hashmap, map_cb, &cnt, 0); > + return cnt; > +} > + > +SEC("tc") > +int exceptions_cleanup_prog_num_iter(struct __sk_buff *ctx) > +{ > + int i; > + > + bpf_for(i, 0, 10) { > + MARK_RESOURCE(&___it, RES_ITER); > + bpf_throw(VAL); > + } > + return 0; > +} > + > +SEC("tc") > +int exceptions_cleanup_prog_num_iter_mult(struct __sk_buff *ctx) > +{ > + int i, j, k; > + > + bpf_for(i, 0, 10) { > + MARK_RESOURCE(&___it, RES_ITER); > + bpf_for(j, 0, 10) { > + MARK_RESOURCE(&___it, RES_ITER); > + bpf_for(k, 0, 10) { > + MARK_RESOURCE(&___it, RES_ITER); > + bpf_throw(VAL); > + } > + } > + } > + return 0; > +} > + > +__noinline > +static int exceptions_cleanup_subprog(struct __sk_buff *ctx) > +{ > + int i; > + > + bpf_for(i, 0, 10) { > + MARK_RESOURCE(&___it, RES_ITER); > + bpf_throw(VAL); > + } > + return ctx->len; > +} > + > +SEC("tc") > +int exceptions_cleanup_prog_dynptr_iter(struct __sk_buff *ctx) > +{ > + struct bpf_dynptr rbuf; > + int ret = 0; > + > + bpf_ringbuf_reserve_dynptr(&ringbuf, 8, 0, &rbuf); > + MARK_RESOURCE(&rbuf, RES_DYNPTR); > + if (ctx->protocol) > + ret = exceptions_cleanup_subprog(ctx); > + bpf_ringbuf_discard_dynptr(&rbuf, 0); > + return ret; > +} > + > +SEC("tc") > +int exceptions_cleanup_obj(struct __sk_buff *ctx) > +{ > + struct { int i; } *p; > + > + p = bpf_obj_new(typeof(*p)); > + MARK_RESOURCE(&p, RES_SPILL); > + bpf_throw(VAL); > + return p->i; > +} > + > +SEC("tc") > +int exceptions_cleanup_percpu_obj(struct __sk_buff *ctx) > +{ > + struct { int i; } *p; > + > + p = bpf_percpu_obj_new(typeof(*p)); > + MARK_RESOURCE(&p, RES_SPILL); > + bpf_throw(VAL); It would be neat if we could have the bpf_throw() kfunc signature be marked as __attribute__((noreturn)) and have things work correctly; meaning you wouldn't have to even return a value here. The verifier should know that bpf_throw() is terminal, so it should be able to prune any subsequent instructions as unreachable anyways. > + return !p; > +} > + > +SEC("tc") > +int exceptions_cleanup_ringbuf(struct __sk_buff *ctx) > +{ > + void *p; > + > + p = bpf_ringbuf_reserve(&ringbuf, 8, 0); > + MARK_RESOURCE(&p, RES_SPILL); > + bpf_throw(VAL); > + return 0; > +} > + > +SEC("tc") > +int exceptions_cleanup_reg(struct __sk_buff *ctx) > +{ > + void *p; > + > + p = bpf_ringbuf_reserve(&ringbuf, 8, 0); > + MARK_RESOURCE(p, RES_REG); > + bpf_throw(VAL); > + if (p) > + bpf_ringbuf_discard(p, 0); Does the prog fail to load if you don't have this bpf_ringbuf_discard() check? I assume not given that in exceptions_cleanup_null_or_ptr_do_ptr() and elsewhere we do a reserve without discarding. Is there some subtle stack state difference here or something? > + return 0; > +} > + > +SEC("tc") > +int exceptions_cleanup_null_or_ptr_do_ptr(struct __sk_buff *ctx) > +{ > + union { > + void *p; > + char buf[8]; > + } volatile p; > + u64 z = 0; > + > + __builtin_memcpy((void *)&p.p, &z, sizeof(z)); > + MARK_RESOURCE((void *)&p.p, RES_SPILL); > + if (ctx->len) > + p.p = bpf_ringbuf_reserve(&ringbuf, 8, 0); > + bpf_throw(VAL); > + return 0; > +} > + > +SEC("tc") > +int exceptions_cleanup_null_or_ptr_do_null(struct __sk_buff *ctx) > +{ > + union { > + void *p; > + char buf[8]; > + } volatile p; > + > + p.p = 0; > + MARK_RESOURCE((void *)p.buf, RES_SPILL); > + if (!ctx->len) > + p.p = bpf_ringbuf_reserve(&ringbuf, 8, 0); > + bpf_throw(VAL); > + return 0; > +} > + > +__noinline static int mark_resource_subprog(u64 a, u64 b, u64 c, u64 d) > +{ > + MARK_RESOURCE((void *)a, RES_REG); > + MARK_RESOURCE((void *)b, RES_REG); > + MARK_RESOURCE((void *)c, RES_REG); > + MARK_RESOURCE((void *)d, RES_REG); > + return 0; > +} > + > +SEC("tc") > +int exceptions_cleanup_callee_saved(struct __sk_buff *ctx) > +{ > + asm volatile ( > + "r1 = %[ringbuf] ll; \ > + r2 = 8; \ > + r3 = 0; \ > + call %[bpf_ringbuf_reserve]; \ > + r6 = r0; \ > + r1 = %[ringbuf] ll; \ > + r2 = 8; \ > + r3 = 0; \ > + call %[bpf_ringbuf_reserve]; \ > + r7 = r0; \ > + r1 = %[ringbuf] ll; \ > + r2 = 8; \ > + r3 = 0; \ > + call %[bpf_ringbuf_reserve]; \ > + r8 = r0; \ > + r1 = %[ringbuf] ll; \ > + r2 = 8; \ > + r3 = 0; \ > + call %[bpf_ringbuf_reserve]; \ > + r9 = r0; \ > + r1 = r6; \ > + r2 = r7; \ > + r3 = r8; \ > + r4 = r9; \ > + call mark_resource_subprog; \ > + r1 = 0xeB9F; \ > + call bpf_throw; \ > + " : : __imm(bpf_ringbuf_reserve), > + __imm_addr(ringbuf) > + : __clobber_all); > + mark_resource_subprog(0, 0, 0, 0); > + return 0; > +} > + > +SEC("tc") > +int exceptions_cleanup_callee_saved_noopt(struct __sk_buff *ctx) > +{ > + mark_resource_subprog(1, 2, 3, 4); > + return 0; > +} > + > +__noinline int global_subprog_throw(struct __sk_buff *ctx) > +{ > + u64 *p = bpf_ringbuf_reserve(&ringbuf, 8, 0); > + bpf_throw(VAL); > + return p ? *p : 0 + ctx->len; > +} > + > +__noinline int global_subprog(struct __sk_buff *ctx) > +{ > + u64 *p = bpf_ringbuf_reserve(&ringbuf, 8, 0); > + if (!p) > + return ctx->len; > + global_subprog_throw(ctx); > + bpf_ringbuf_discard(p, 0); > + return !!p + ctx->len; > +} > + > +__noinline static int static_subprog(struct __sk_buff *ctx) > +{ > + struct bpf_dynptr rbuf; > + u64 *p, r = 0; > + > + bpf_ringbuf_reserve_dynptr(&ringbuf, 8, 0, &rbuf); > + p = bpf_dynptr_data(&rbuf, 0, 8); > + if (!p) > + goto end; > + *p = global_subprog(ctx); > + r += *p; > +end: > + bpf_ringbuf_discard_dynptr(&rbuf, 0); > + return r + ctx->len; > +} > + > +SEC("tc") > +int exceptions_cleanup_frame(struct __sk_buff *ctx) > +{ > + struct foo { int i; } *p = bpf_obj_new(typeof(*p)); > + int i; > + only_count = 1; > + res_count = 4; > + if (!p) > + return 1; > + p->i = static_subprog(ctx); > + i = p->i; > + bpf_obj_drop(p); > + return i + ctx->len; > +} > + > +SEC("tc") > +__success > +int exceptions_cleanup_loop_iterations(struct __sk_buff *ctx) > +{ > + struct { int i; } *f[50] = {}; > + int i; > + > + only_count = true; > + > + for (i = 0; i < 50; i++) { > + f[i] = bpf_obj_new(typeof(*f[0])); > + if (!f[i]) > + goto end; > + res_count++; > + if (i == 49) { > + bpf_throw(VAL); > + } > + } > +end: > + for (i = 0; i < 50; i++) { > + if (!f[i]) > + continue; > + bpf_obj_drop(f[i]); > + } > + return 0; > +} > + > +SEC("tc") > +int exceptions_cleanup_dead_code_elim(struct __sk_buff *ctx) > +{ > + void *p; > + > + p = bpf_ringbuf_reserve(&ringbuf, 8, 0); > + if (!p) > + return 0; > + asm volatile ( > + "r0 = r0; \ > + r0 = r0; \ > + r0 = r0; \ > + r0 = r0; \ > + r0 = r0; \ > + r0 = r0; \ > + r0 = r0; \ > + " ::: "r0"); > + bpf_throw(VAL); > + bpf_ringbuf_discard(p, 0); > + return 0; > +} > + > +__noinline int global_subprog_throw_dce(struct __sk_buff *ctx) > +{ > + u64 *p = bpf_ringbuf_reserve(&ringbuf, 8, 0); > + bpf_throw(VAL); > + return p ? *p : 0 + ctx->len; > +} > + > +__noinline int global_subprog_dce(struct __sk_buff *ctx) > +{ > + u64 *p = bpf_ringbuf_reserve(&ringbuf, 8, 0); > + if (!p) > + return ctx->len; > + asm volatile ( > + "r0 = r0; \ > + r0 = r0; \ > + r0 = r0; \ > + r0 = r0; \ > + r0 = r0; \ > + r0 = r0; \ > + r0 = r0; \ > + " ::: "r0"); > + global_subprog_throw_dce(ctx); > + bpf_ringbuf_discard(p, 0); > + return !!p + ctx->len; > +} > + > +__noinline static int static_subprog_dce(struct __sk_buff *ctx) > +{ > + struct bpf_dynptr rbuf; > + u64 *p, r = 0; > + > + bpf_ringbuf_reserve_dynptr(&ringbuf, 8, 0, &rbuf); > + p = bpf_dynptr_data(&rbuf, 0, 8); > + if (!p) > + goto end; > + asm volatile ( > + "r0 = r0; \ > + r0 = r0; \ > + r0 = r0; \ > + r0 = r0; \ > + r0 = r0; \ > + r0 = r0; \ > + r0 = r0; \ > + " ::: "r0"); > + *p = global_subprog_dce(ctx); > + r += *p; > +end: > + bpf_ringbuf_discard_dynptr(&rbuf, 0); > + return r + ctx->len; > +} > + > +SEC("tc") > +int exceptions_cleanup_frame_dce(struct __sk_buff *ctx) > +{ > + struct foo { int i; } *p = bpf_obj_new(typeof(*p)); > + int i; > + only_count = 1; > + res_count = 4; > + if (!p) > + return 1; > + p->i = static_subprog_dce(ctx); > + i = p->i; > + bpf_obj_drop(p); > + return i + ctx->len; > +} > + > +SEC("tc") > +int reject_slot_with_zero_vs_ptr_ok(struct __sk_buff *ctx) > +{ > + asm volatile ( > + "r7 = *(u32 *)(r1 + 0); \ > + r0 = 0; \ > + *(u64 *)(r10 - 8) = r0; \ > + r1 = %[ringbuf] ll; \ > + r2 = 8; \ > + r3 = 0; \ > + if r7 != 0 goto jump4; \ > + call %[bpf_ringbuf_reserve]; \ > + *(u64 *)(r10 - 8) = r0; \ > + jump4: \ > + r0 = 0; \ > + r1 = 0; \ > + call bpf_throw; \ > + " : : __imm(bpf_ringbuf_reserve), > + __imm_addr(ringbuf) > + : "memory"); > + return 0; > +} > + > +char _license[] SEC("license") = "GPL"; > diff --git a/tools/testing/selftests/bpf/progs/exceptions_cleanup_fail.c b/tools/testing/selftests/bpf/progs/exceptions_cleanup_fail.c > new file mode 100644 > index 000000000000..b3c70f92b35f > --- /dev/null > +++ b/tools/testing/selftests/bpf/progs/exceptions_cleanup_fail.c > @@ -0,0 +1,154 @@ > +// SPDX-License-Identifier: GPL-2.0 > +#include <vmlinux.h> > +#include <bpf/bpf_tracing.h> > +#include <bpf/bpf_helpers.h> > +#include <bpf/bpf_core_read.h> > + > +#include "bpf_misc.h" > +#include "bpf_experimental.h" > + > +struct { > + __uint(type, BPF_MAP_TYPE_RINGBUF); > + __uint(max_entries, 8); > +} ringbuf SEC(".maps"); > + > +SEC("?tc") > +__failure __msg("Unreleased reference") > +int reject_with_reference(void *ctx) > +{ > + struct { int i; } *f; > + > + f = bpf_obj_new(typeof(*f)); > + if (!f) > + return 0; > + bpf_throw(0); > + return 0; > +} > + > +SEC("?tc") > +__failure __msg("frame_desc: merge: failed to merge old and new frame desc entry") > +int reject_slot_with_distinct_ptr(struct __sk_buff *ctx) > +{ > + void *p; > + > + if (ctx->len) { > + p = bpf_ringbuf_reserve(&ringbuf, 8, 0); > + } else { > + p = bpf_obj_new(typeof(struct { int i; })); > + } > + bpf_throw(0); > + return !p; > +} > + > +SEC("?tc") > +__failure __msg("frame_desc: merge: failed to merge old and new frame desc entry") > +int reject_slot_with_distinct_ptr_old(struct __sk_buff *ctx) > +{ > + void *p; > + > + if (ctx->len) { > + p = bpf_obj_new(typeof(struct { int i; })); > + } else { > + p = bpf_ringbuf_reserve(&ringbuf, 8, 0); > + } > + bpf_throw(0); > + return !p; > +} > + > +SEC("?tc") > +__failure __msg("frame_desc: merge: failed to merge old and new frame desc entry") > +int reject_slot_with_misc_vs_ptr(struct __sk_buff *ctx) > +{ > + void *p = (void *)bpf_ktime_get_ns(); > + > + if (ctx->protocol) > + p = bpf_ringbuf_reserve(&ringbuf, 8, 0); > + bpf_throw(0); > + return !p; > +} > + > +SEC("?tc") > +__failure __msg("Unreleased reference") > +int reject_slot_with_misc_vs_ptr_old(struct __sk_buff *ctx) > +{ > + void *p = bpf_ringbuf_reserve(&ringbuf, 8, 0); > + > + if (ctx->protocol) > + p = (void *)bpf_ktime_get_ns(); > + bpf_throw(0); > + return !p; > +} > + > +SEC("?tc") > +__failure __msg("frame_desc: merge: failed to merge old and new frame desc entry") > +int reject_slot_with_invalid_vs_ptr(struct __sk_buff *ctx) > +{ > + asm volatile ( > + "r7 = r1; \ > + r1 = %[ringbuf] ll; \ > + r2 = 8; \ > + r3 = 0; \ > + r4 = *(u32 *)(r7 + 0); \ > + r6 = *(u64 *)(r10 - 8); \ > + if r4 == 0 goto jump; \ > + call %[bpf_ringbuf_reserve]; \ > + r6 = r0; \ > + jump: \ > + r0 = 0; \ > + r1 = 0; \ > + call bpf_throw; \ > + " : : __imm(bpf_ringbuf_reserve), > + __imm_addr(ringbuf) > + : "memory"); > + return 0; > +} > + > +SEC("?tc") > +__failure __msg("Unreleased reference") > +int reject_slot_with_invalid_vs_ptr_old(struct __sk_buff *ctx) > +{ > + asm volatile ( > + "r7 = r1; \ > + r1 = %[ringbuf] ll; \ > + r2 = 8; \ > + r3 = 0; \ > + call %[bpf_ringbuf_reserve]; \ > + r6 = r0; \ > + r4 = *(u32 *)(r7 + 0); \ > + if r4 == 0 goto jump2; \ > + r6 = *(u64 *)(r10 - 8); \ > + jump2: \ > + r0 = 0; \ > + r1 = 0; \ > + call bpf_throw; \ > + " : : __imm(bpf_ringbuf_reserve), > + __imm_addr(ringbuf) > + : "memory"); > + return 0; > +} > + > +SEC("?tc") > +__failure __msg("Unreleased reference") > +int reject_slot_with_zero_vs_ptr(struct __sk_buff *ctx) > +{ > + asm volatile ( > + "r7 = *(u32 *)(r1 + 0); \ > + r1 = %[ringbuf] ll; \ > + r2 = 8; \ > + r3 = 0; \ > + call %[bpf_ringbuf_reserve]; \ > + *(u64 *)(r10 - 8) = r0; \ > + r0 = 0; \ > + if r7 != 0 goto jump3; \ > + *(u64 *)(r10 - 8) = r0; \ > + jump3: \ > + r0 = 0; \ > + r1 = 0; \ > + call bpf_throw; \ > + " : : __imm(bpf_ringbuf_reserve), > + __imm_addr(ringbuf) > + : "memory"); > + return 0; > +} > + > +char _license[] SEC("license") = "GPL"; > diff --git a/tools/testing/selftests/bpf/progs/exceptions_fail.c b/tools/testing/selftests/bpf/progs/exceptions_fail.c > index dfd164a7a261..1e73200c6276 100644 > --- a/tools/testing/selftests/bpf/progs/exceptions_fail.c > +++ b/tools/testing/selftests/bpf/progs/exceptions_fail.c > @@ -182,19 +182,6 @@ int reject_with_rbtree_add_throw(void *ctx) > return 0; > } > > -SEC("?tc") > -__failure __msg("Unreleased reference") > -int reject_with_reference(void *ctx) > -{ > - struct foo *f; > - > - f = bpf_obj_new(typeof(*f)); > - if (!f) > - return 0; > - bpf_throw(0); Hmm, so why is this a memory leak exactly? Apologies if this is already explained clearly elsewhere in the stack. > - return 0; > -} > - > __noinline static int subprog_ref(struct __sk_buff *ctx) > { > struct foo *f; > -- > 2.40.1 >
Attachment:
signature.asc
Description: PGP signature