From patchwork Thu Apr 28 21:10:59 2022 Content-Type: text/plain; charset="utf-8" MIME-Version: 1.0 Content-Transfer-Encoding: 7bit X-Patchwork-Submitter: Joanne Koong X-Patchwork-Id: 12831157 X-Patchwork-Delegate: bpf@iogearbox.net Return-Path: X-Spam-Checker-Version: SpamAssassin 3.4.0 (2014-02-07) on aws-us-west-2-korg-lkml-1.web.codeaurora.org Received: from vger.kernel.org (vger.kernel.org [23.128.96.18]) by smtp.lore.kernel.org (Postfix) with ESMTP id C1C63C433FE for ; Thu, 28 Apr 2022 21:12:35 +0000 (UTC) Received: (majordomo@vger.kernel.org) by vger.kernel.org via listexpand id S1352128AbiD1VPt (ORCPT ); Thu, 28 Apr 2022 17:15:49 -0400 Received: from lindbergh.monkeyblade.net ([23.128.96.19]:56754 "EHLO lindbergh.monkeyblade.net" rhost-flags-OK-OK-OK-OK) by vger.kernel.org with ESMTP id S1352182AbiD1VPq (ORCPT ); Thu, 28 Apr 2022 17:15:46 -0400 Received: from 69-171-232-181.mail-mxout.facebook.com (69-171-232-181.mail-mxout.facebook.com [69.171.232.181]) by lindbergh.monkeyblade.net (Postfix) with ESMTPS id 1BC6E82323 for ; Thu, 28 Apr 2022 14:12:14 -0700 (PDT) Received: by devbig010.atn6.facebook.com (Postfix, from userid 115148) id 13BD9BAF4BD6; Thu, 28 Apr 2022 14:11:52 -0700 (PDT) From: Joanne Koong To: bpf@vger.kernel.org Cc: andrii@kernel.org, memxor@gmail.com, ast@kernel.org, daniel@iogearbox.net, toke@redhat.com, Joanne Koong Subject: [PATCH bpf-next v3 6/6] bpf: Dynptr tests Date: Thu, 28 Apr 2022 14:10:59 -0700 Message-Id: <20220428211059.4065379-7-joannelkoong@gmail.com> X-Mailer: git-send-email 2.30.2 In-Reply-To: <20220428211059.4065379-1-joannelkoong@gmail.com> References: <20220428211059.4065379-1-joannelkoong@gmail.com> MIME-Version: 1.0 Precedence: bulk List-ID: X-Mailing-List: bpf@vger.kernel.org X-Patchwork-Delegate: bpf@iogearbox.net This patch adds tests for dynptrs, which include cases that the verifier needs to reject (for example, invalid bpf_dynptr_put usages and invalid writes/reads), as well as cases that should successfully pass. Signed-off-by: Joanne Koong Acked-by: Andrii Nakryiko --- .../testing/selftests/bpf/prog_tests/dynptr.c | 132 ++++ .../testing/selftests/bpf/progs/dynptr_fail.c | 574 ++++++++++++++++++ .../selftests/bpf/progs/dynptr_success.c | 218 +++++++ 3 files changed, 924 insertions(+) create mode 100644 tools/testing/selftests/bpf/prog_tests/dynptr.c create mode 100644 tools/testing/selftests/bpf/progs/dynptr_fail.c create mode 100644 tools/testing/selftests/bpf/progs/dynptr_success.c diff --git a/tools/testing/selftests/bpf/prog_tests/dynptr.c b/tools/testing/selftests/bpf/prog_tests/dynptr.c new file mode 100644 index 000000000000..0bed39fd8dac --- /dev/null +++ b/tools/testing/selftests/bpf/prog_tests/dynptr.c @@ -0,0 +1,132 @@ +// SPDX-License-Identifier: GPL-2.0 +/* Copyright (c) 2022 Facebook */ + +#include +#include "dynptr_fail.skel.h" +#include "dynptr_success.skel.h" + +size_t log_buf_sz = 1048576; /* 1 MB */ +static char obj_log_buf[1048576]; + +struct { + const char *prog_name; + const char *expected_err_msg; +} dynptr_tests[] = { + /* failure cases */ + {"missing_put", "Unreleased reference id=1"}, + {"missing_put_callback", "Unreleased reference id=1"}, + {"put_nonalloc", "Expected an initialized malloc dynptr as arg #1"}, + {"put_data_slice", "type=dynptr_mem expected=fp"}, + {"put_uninit_dynptr", "arg 1 is an unacquired reference"}, + {"use_after_put", "Expected an initialized dynptr as arg #3"}, + {"alloc_twice", "Arg #3 dynptr has to be an uninitialized dynptr"}, + {"add_dynptr_to_map1", "invalid indirect read from stack"}, + {"add_dynptr_to_map2", "invalid indirect read from stack"}, + {"ringbuf_invalid_access", "invalid mem access 'scalar'"}, + {"ringbuf_invalid_api", "type=dynptr_mem expected=alloc_mem"}, + {"ringbuf_out_of_bounds", "value is outside of the allowed memory range"}, + {"data_slice_out_of_bounds", "value is outside of the allowed memory range"}, + {"data_slice_use_after_put", "invalid mem access 'scalar'"}, + {"invalid_helper1", "invalid indirect read from stack"}, + {"invalid_helper2", "Expected an initialized dynptr as arg #3"}, + {"invalid_write1", "Expected an initialized malloc dynptr as arg #1"}, + {"invalid_write2", "Expected an initialized dynptr as arg #3"}, + {"invalid_write3", "Expected an initialized malloc dynptr as arg #1"}, + {"invalid_write4", "arg 1 is an unacquired reference"}, + {"invalid_read1", "invalid read from stack"}, + {"invalid_read2", "cannot pass in non-zero dynptr offset"}, + {"invalid_read3", "invalid read from stack"}, + {"invalid_offset", "invalid write to stack"}, + {"global", "R3 type=map_value expected=fp"}, + {"put_twice", "arg 1 is an unacquired reference"}, + {"put_twice_callback", "arg 1 is an unacquired reference"}, + {"zero_slice_access", "invalid access to memory, mem_size=0 off=0 size=1"}, + /* success cases */ + {"test_basic", NULL}, + {"test_data_slice", NULL}, + {"test_ringbuf", NULL}, + {"test_alloc_zero_bytes", NULL}, +}; + +static void verify_fail(const char *prog_name, const char *expected_err_msg) +{ + LIBBPF_OPTS(bpf_object_open_opts, opts); + struct bpf_program *prog; + struct dynptr_fail *skel; + int err; + + opts.kernel_log_buf = obj_log_buf; + opts.kernel_log_size = log_buf_sz; + opts.kernel_log_level = 1; + + skel = dynptr_fail__open_opts(&opts); + if (!ASSERT_OK_PTR(skel, "dynptr_fail__open_opts")) + goto cleanup; + + prog = bpf_object__find_program_by_name(skel->obj, prog_name); + if (!ASSERT_OK_PTR(prog, "bpf_object__find_program_by_name")) + goto cleanup; + + bpf_program__set_autoload(prog, true); + + err = dynptr_fail__load(skel); + if (!ASSERT_ERR(err, "unexpected load success")) + goto cleanup; + + if (!ASSERT_OK_PTR(strstr(obj_log_buf, expected_err_msg), "expected_err_msg")) { + fprintf(stderr, "Expected err_msg: %s\n", expected_err_msg); + fprintf(stderr, "Verifier output: %s\n", obj_log_buf); + } + +cleanup: + dynptr_fail__destroy(skel); +} + +static void verify_success(const char *prog_name) +{ + struct dynptr_success *skel; + struct bpf_program *prog; + struct bpf_link *link; + + skel = dynptr_success__open(); + if (!ASSERT_OK_PTR(skel, "dynptr_success__open")) + return; + + skel->bss->pid = getpid(); + + dynptr_success__load(skel); + if (!ASSERT_OK_PTR(skel, "dynptr_success__load")) + goto cleanup; + + prog = bpf_object__find_program_by_name(skel->obj, prog_name); + if (!ASSERT_OK_PTR(prog, "bpf_object__find_program_by_name")) + goto cleanup; + + link = bpf_program__attach(prog); + if (!ASSERT_OK_PTR(link, "bpf_program__attach")) + goto cleanup; + + usleep(1); + + ASSERT_EQ(skel->bss->err, 0, "err"); + + bpf_link__destroy(link); + +cleanup: + dynptr_success__destroy(skel); +} + +void test_dynptr(void) +{ + int i; + + for (i = 0; i < ARRAY_SIZE(dynptr_tests); i++) { + if (!test__start_subtest(dynptr_tests[i].prog_name)) + continue; + + if (dynptr_tests[i].expected_err_msg) + verify_fail(dynptr_tests[i].prog_name, dynptr_tests[i].expected_err_msg); + else + verify_success(dynptr_tests[i].prog_name); + } +} diff --git a/tools/testing/selftests/bpf/progs/dynptr_fail.c b/tools/testing/selftests/bpf/progs/dynptr_fail.c new file mode 100644 index 000000000000..e4d5464e1865 --- /dev/null +++ b/tools/testing/selftests/bpf/progs/dynptr_fail.c @@ -0,0 +1,574 @@ +// SPDX-License-Identifier: GPL-2.0 +/* Copyright (c) 2022 Facebook */ + +#include +#include +#include +#include "bpf_misc.h" + +char _license[] SEC("license") = "GPL"; + +struct { + __uint(type, BPF_MAP_TYPE_ARRAY); + __uint(max_entries, 1); + __type(key, __u32); + __type(value, struct bpf_dynptr); +} array_map SEC(".maps"); + +struct sample { + int pid; + long value; + char comm[16]; +}; + +struct { + __uint(type, BPF_MAP_TYPE_RINGBUF); + __uint(max_entries, 1 << 12); +} ringbuf SEC(".maps"); + +int err = 0; +int val; + +/* Every bpf_dynptr_alloc call must have a corresponding bpf_dynptr_put call */ +SEC("?raw_tp/sys_nanosleep") +int missing_put(void *ctx) +{ + struct bpf_dynptr mem; + + bpf_dynptr_alloc(8, 0, &mem); + + /* missing a call to bpf_dynptr_put(&mem) */ + + return 0; +} + +/* A non-alloc-ed dynptr can't be used by bpf_dynptr_put */ +SEC("?raw_tp/sys_nanosleep") +int put_nonalloc(void *ctx) +{ + struct bpf_dynptr ptr; + bpf_ringbuf_reserve_dynptr(&ringbuf, val, 0, &ptr); + + /* this should fail */ + bpf_dynptr_put(&ptr); + + return 0; +} + +/* A data slice from a dynptr can't be used by bpf_dynptr_put */ +SEC("?raw_tp/sys_nanosleep") +int put_data_slice(void *ctx) +{ + struct bpf_dynptr ptr; + void *data; + + bpf_dynptr_alloc(8, 0, &ptr); + + data = bpf_dynptr_data(&ptr, 0, 8); + if (!data) + goto done; + + /* this should fail */ + bpf_dynptr_put(data); + +done: + bpf_dynptr_put(&ptr); + return 0; +} + +/* Can't call bpf_dynptr_put on a non-initialized dynptr */ +SEC("?raw_tp/sys_nanosleep") +int put_uninit_dynptr(void *ctx) +{ + struct bpf_dynptr ptr; + + /* this should fail */ + bpf_dynptr_put(&ptr); + + return 0; +} + +/* A dynptr can't be used after bpf_dynptr_put has been called on it */ +SEC("?raw_tp/sys_nanosleep") +int use_after_put(void *ctx) +{ + struct bpf_dynptr ptr = {}; + char read_data[64] = {}; + + bpf_dynptr_alloc(8, 0, &ptr); + + bpf_dynptr_read(read_data, sizeof(read_data), &ptr, 0); + + bpf_dynptr_put(&ptr); + + /* this should fail */ + bpf_dynptr_read(read_data, sizeof(read_data), &ptr, 0); + + return 0; +} + +/* + * Can't bpf_dynptr_alloc an existing allocated bpf_dynptr that bpf_dynptr_put + * hasn't been called on yet + */ +SEC("?raw_tp/sys_nanosleep") +int alloc_twice(void *ctx) +{ + struct bpf_dynptr ptr; + + bpf_dynptr_alloc(8, 0, &ptr); + + /* this should fail */ + bpf_dynptr_alloc(2, 0, &ptr); + + bpf_dynptr_put(&ptr); + + return 0; +} + +/* + * Can't access a ring buffer record after submit or discard has been called + * on the dynptr + */ +SEC("?raw_tp/sys_nanosleep") +int ringbuf_invalid_access(void *ctx) +{ + struct bpf_dynptr ptr; + struct sample *sample; + + err = bpf_ringbuf_reserve_dynptr(&ringbuf, sizeof(*sample), 0, &ptr); + sample = bpf_dynptr_data(&ptr, 0, sizeof(*sample)); + if (!sample) + goto done; + + sample->pid = 123; + + bpf_ringbuf_submit_dynptr(&ptr, 0); + + /* this should fail */ + err = sample->pid; + + return 0; + +done: + bpf_ringbuf_discard_dynptr(&ptr, 0); + return 0; +} + +/* Can't call non-dynptr ringbuf APIs on a dynptr ringbuf sample */ +SEC("?raw_tp/sys_nanosleep") +int ringbuf_invalid_api(void *ctx) +{ + struct bpf_dynptr ptr; + struct sample *sample; + + err = bpf_ringbuf_reserve_dynptr(&ringbuf, sizeof(*sample), 0, &ptr); + sample = bpf_dynptr_data(&ptr, 0, sizeof(*sample)); + if (!sample) + goto done; + + sample->pid = 123; + + /* invalid API use. need to use dynptr API to submit/discard */ + bpf_ringbuf_submit(sample, 0); + +done: + bpf_ringbuf_discard_dynptr(&ptr, 0); + return 0; +} + +/* Can't access memory outside a ringbuf record range */ +SEC("?raw_tp/sys_nanosleep") +int ringbuf_out_of_bounds(void *ctx) +{ + struct bpf_dynptr ptr; + struct sample *sample; + + err = bpf_ringbuf_reserve_dynptr(&ringbuf, sizeof(*sample), 0, &ptr); + sample = bpf_dynptr_data(&ptr, 0, sizeof(*sample)); + if (!sample) + goto done; + + /* Can't access beyond sample range */ + *(__u8 *)((void *)sample + sizeof(*sample)) = 123; + + bpf_ringbuf_submit_dynptr(&ptr, 0); + + return 0; + +done: + bpf_ringbuf_discard_dynptr(&ptr, 0); + return 0; +} + +/* Can't add a dynptr to a map */ +SEC("?raw_tp/sys_nanosleep") +int add_dynptr_to_map1(void *ctx) +{ + struct bpf_dynptr ptr = {}; + int key = 0; + + err = bpf_dynptr_alloc(sizeof(val), 0, &ptr); + + /* this should fail */ + bpf_map_update_elem(&array_map, &key, &ptr, 0); + + bpf_dynptr_put(&ptr); + + return 0; +} + +/* Can't add a struct with an embedded dynptr to a map */ +SEC("?raw_tp/sys_nanosleep") +int add_dynptr_to_map2(void *ctx) +{ + struct info { + int x; + struct bpf_dynptr ptr; + }; + struct info x; + int key = 0; + + bpf_dynptr_alloc(sizeof(val), 0, &x.ptr); + + /* this should fail */ + bpf_map_update_elem(&array_map, &key, &x, 0); + + return 0; +} + +/* Can't pass in a dynptr as an arg to a helper function that doesn't take in a + * dynptr argument + */ +SEC("?raw_tp/sys_nanosleep") +int invalid_helper1(void *ctx) +{ + struct bpf_dynptr ptr = {}; + + bpf_dynptr_alloc(8, 0, &ptr); + + /* this should fail */ + bpf_strncmp((const char *)&ptr, sizeof(ptr), "hello!"); + + bpf_dynptr_put(&ptr); + + return 0; +} + +/* A dynptr can't be passed into a helper function at a non-zero offset */ +SEC("?raw_tp/sys_nanosleep") +int invalid_helper2(void *ctx) +{ + struct bpf_dynptr ptr = {}; + char read_data[64] = {}; + + bpf_dynptr_alloc(sizeof(val), 0, &ptr); + + /* this should fail */ + bpf_dynptr_read(read_data, sizeof(read_data), (void *)&ptr + 8, 0); + + bpf_dynptr_put(&ptr); + + return 0; +} + +/* A data slice can't be accessed out of bounds */ +SEC("?raw_tp/sys_nanosleep") +int data_slice_out_of_bounds(void *ctx) +{ + struct bpf_dynptr ptr = {}; + void *data; + + bpf_dynptr_alloc(8, 0, &ptr); + + data = bpf_dynptr_data(&ptr, 0, 8); + if (!data) + goto done; + + /* can't index out of bounds of the data slice */ + val = *((char *)data + 8); + +done: + bpf_dynptr_put(&ptr); + return 0; +} + +/* A data slice can't be used after bpf_dynptr_put is called */ +SEC("?raw_tp/sys_nanosleep") +int data_slice_use_after_put(void *ctx) +{ + struct bpf_dynptr ptr = {}; + void *data; + + bpf_dynptr_alloc(8, 0, &ptr); + + data = bpf_dynptr_data(&ptr, 0, 8); + if (!data) + goto done; + + bpf_dynptr_put(&ptr); + + /* this should fail */ + val = *(__u8 *)data; + +done: + bpf_dynptr_put(&ptr); + return 0; +} + +/* A bpf_dynptr can't be used as a dynptr if it's been written into */ +SEC("?raw_tp/sys_nanosleep") +int invalid_write1(void *ctx) +{ + struct bpf_dynptr ptr = {}; + __u8 x = 0; + + bpf_dynptr_alloc(8, 0, &ptr); + + memcpy(&ptr, &x, sizeof(x)); + + /* this should fail */ + bpf_dynptr_put(&ptr); + + return 0; +} + +/* + * A bpf_dynptr can't be used as a dynptr if an offset into it has been + * written into + */ +SEC("?raw_tp/sys_nanosleep") +int invalid_write2(void *ctx) +{ + struct bpf_dynptr ptr = {}; + char read_data[64] = {}; + __u8 x = 0, y = 0; + + bpf_dynptr_alloc(sizeof(x), 0, &ptr); + + memcpy((void *)&ptr + 8, &y, sizeof(y)); + + /* this should fail */ + bpf_dynptr_read(read_data, sizeof(read_data), &ptr, 0); + + bpf_dynptr_put(&ptr); + + return 0; +} + +/* + * A bpf_dynptr can't be used as a dynptr if a non-const offset into it + * has been written into + */ +SEC("?raw_tp/sys_nanosleep") +int invalid_write3(void *ctx) +{ + struct bpf_dynptr ptr = {}; + char stack_buf[16]; + unsigned long len; + __u8 x = 0; + + bpf_dynptr_alloc(8, 0, &ptr); + + memcpy(stack_buf, &val, sizeof(val)); + len = stack_buf[0] & 0xf; + + memcpy((void *)&ptr + len, &x, sizeof(x)); + + /* this should fail */ + bpf_dynptr_put(&ptr); + + return 0; +} + +static int invalid_write4_callback(__u32 index, void *data) +{ + *(__u32 *)data = 123; + + return 0; +} + +/* If the dynptr is written into in a callback function, it should + * be invalidated as a dynptr + */ +SEC("?raw_tp/sys_nanosleep") +int invalid_write4(void *ctx) +{ + struct bpf_dynptr ptr; + __u64 x = 0; + + bpf_dynptr_alloc(sizeof(x), 0, &ptr); + + bpf_loop(10, invalid_write4_callback, &ptr, 0); + + /* this should fail */ + bpf_dynptr_put(&ptr); + + return 0; +} + +/* A globally-defined bpf_dynptr can't be used (it must reside as a stack frame) */ +struct bpf_dynptr global_dynptr; +SEC("?raw_tp/sys_nanosleep") +int global(void *ctx) +{ + /* this should fail */ + bpf_dynptr_alloc(4, 0, &global_dynptr); + + bpf_dynptr_put(&global_dynptr); + + return 0; +} + +/* A direct read should fail */ +SEC("?raw_tp/sys_nanosleep") +int invalid_read1(void *ctx) +{ + struct bpf_dynptr ptr = {}; + __u32 x = 2; + + bpf_dynptr_alloc(sizeof(x), 0, &ptr); + + /* this should fail */ + val = *(int *)&ptr; + + bpf_dynptr_put(&ptr); + + return 0; +} + +/* A direct read at an offset should fail */ +SEC("?raw_tp/sys_nanosleep") +int invalid_read2(void *ctx) +{ + struct bpf_dynptr ptr = {}; + char read_data[64] = {}; + __u64 x = 0; + + bpf_dynptr_alloc(sizeof(x), 0, &ptr); + + /* this should fail */ + bpf_dynptr_read(read_data, sizeof(read_data), (void *)&ptr + 1, 0); + + bpf_dynptr_put(&ptr); + + return 0; +} + +/* A direct read at an offset into the lower stack slot should fail */ +SEC("?raw_tp/sys_nanosleep") +int invalid_read3(void *ctx) +{ + struct bpf_dynptr ptr1 = {}; + struct bpf_dynptr ptr2 = {}; + + bpf_dynptr_alloc(sizeof(val), 0, &ptr1); + bpf_dynptr_alloc(sizeof(val), 0, &ptr2); + + /* this should fail */ + memcpy(&val, (void *)&ptr1 + 8, sizeof(val)); + + bpf_dynptr_put(&ptr1); + bpf_dynptr_put(&ptr2); + + return 0; +} + +/* Calling bpf_dynptr_alloc on an offset should fail */ +SEC("?raw_tp/sys_nanosleep") +int invalid_offset(void *ctx) +{ + struct bpf_dynptr ptr = {}; + + /* this should fail */ + bpf_dynptr_alloc(sizeof(val), 0, &ptr + 1); + + bpf_dynptr_put(&ptr); + + return 0; +} + +/* Can't call bpf_dynptr_put twice */ +SEC("?raw_tp/sys_nanosleep") +int put_twice(void *ctx) +{ + struct bpf_dynptr ptr; + + bpf_dynptr_alloc(8, 0, &ptr); + + bpf_dynptr_put(&ptr); + + /* this second put should fail */ + bpf_dynptr_put(&ptr); + + return 0; +} + +static int put_twice_callback_fn(__u32 index, void *data) +{ + /* this should fail */ + bpf_dynptr_put(data); + val = index; + return 0; +} + +/* Test that calling bpf_dynptr_put twice, where the 2nd put happens within a + * calback function, fails + */ +SEC("?raw_tp/sys_nanosleep") +int put_twice_callback(void *ctx) +{ + struct bpf_dynptr ptr; + + bpf_dynptr_alloc(8, 0, &ptr); + + bpf_dynptr_put(&ptr); + + bpf_loop(10, put_twice_callback_fn, &ptr, 0); + + return 0; +} + +static int missing_put_callback_fn(__u32 index, void *data) +{ + struct bpf_dynptr ptr; + + bpf_dynptr_alloc(8, 0, &ptr); + + val = index; + + /* missing bpf_dynptr_put(&ptr) */ + + return 0; +} + +/* Any dynptr initialized within a callback must have bpf_dynptr_put called */ +SEC("?raw_tp/sys_nanosleep") +int missing_put_callback(void *ctx) +{ + bpf_loop(10, missing_put_callback_fn, NULL, 0); + return 0; +} + +/* Can't access memory in a zero-slice */ +SEC("?raw_tp/sys_nanosleep") +int zero_slice_access(void *ctx) +{ + struct bpf_dynptr ptr; + void *data; + + bpf_dynptr_alloc(0, 0, &ptr); + + data = bpf_dynptr_data(&ptr, 0, 0); + if (!data) + goto done; + + /* this should fail */ + *(__u8 *)data = 23; + + val = *(__u8 *)data; + +done: + bpf_dynptr_put(&ptr); + return 0; +} diff --git a/tools/testing/selftests/bpf/progs/dynptr_success.c b/tools/testing/selftests/bpf/progs/dynptr_success.c new file mode 100644 index 000000000000..eff6272623c2 --- /dev/null +++ b/tools/testing/selftests/bpf/progs/dynptr_success.c @@ -0,0 +1,218 @@ +// SPDX-License-Identifier: GPL-2.0 +/* Copyright (c) 2022 Facebook */ + +#include +#include +#include +#include "bpf_misc.h" +#include "errno.h" + +char _license[] SEC("license") = "GPL"; + +int pid = 0; +int err = 0; +int val; + +struct sample { + int pid; + int seq; + long value; + char comm[16]; +}; + +struct { + __uint(type, BPF_MAP_TYPE_RINGBUF); + __uint(max_entries, 1 << 12); +} ringbuf SEC(".maps"); + +SEC("tp/syscalls/sys_enter_nanosleep") +int test_basic(void *ctx) +{ + char write_data[64] = "hello there, world!!"; + char read_data[64] = {}, buf[64] = {}; + struct bpf_dynptr ptr = {}; + int i; + + if (bpf_get_current_pid_tgid() >> 32 != pid) + return 0; + + err = bpf_dynptr_alloc(sizeof(write_data), 0, &ptr); + if (err) + goto done; + + /* Write data into the dynptr */ + err = bpf_dynptr_write(&ptr, 0, write_data, sizeof(write_data)); + if (err) + goto done; + + /* Read the data that was written into the dynptr */ + err = bpf_dynptr_read(read_data, sizeof(read_data), &ptr, 0); + if (err) + goto done; + + /* Ensure the data we read matches the data we wrote */ + for (i = 0; i < sizeof(read_data); i++) { + if (read_data[i] != write_data[i]) { + err = 1; + goto done; + } + } + +done: + bpf_dynptr_put(&ptr); + return 0; +} + +SEC("tp/syscalls/sys_enter_nanosleep") +int test_data_slice(void *ctx) +{ + struct bpf_dynptr ptr; + __u32 alloc_size = 16; + void *data; + + if (bpf_get_current_pid_tgid() >> 32 != pid) + return 0; + + /* test passing in an invalid flag */ + err = bpf_dynptr_alloc(alloc_size, 1, &ptr); + if (err != -EINVAL) { + err = 1; + goto done; + } + bpf_dynptr_put(&ptr); + + err = bpf_dynptr_alloc(alloc_size, 0, &ptr); + if (err) + goto done; + + /* Try getting a data slice that is out of range */ + data = bpf_dynptr_data(&ptr, alloc_size + 1, 1); + if (data) { + err = 2; + goto done; + } + + /* Try getting more bytes than available */ + data = bpf_dynptr_data(&ptr, 0, alloc_size + 1); + if (data) { + err = 3; + goto done; + } + + data = bpf_dynptr_data(&ptr, 0, sizeof(int)); + if (!data) { + err = 4; + goto done; + } + + *(__u32 *)data = 999; + + err = bpf_probe_read_kernel(&val, sizeof(val), data); + if (err) + goto done; + + if (val != *(int *)data) + err = 5; + +done: + bpf_dynptr_put(&ptr); + return 0; +} + +static int ringbuf_callback(__u32 index, void *data) +{ + struct sample *sample; + + struct bpf_dynptr *ptr = (struct bpf_dynptr *)data; + + sample = bpf_dynptr_data(ptr, 0, sizeof(*sample)); + if (!sample) { + err = 2; + return 0; + } + + sample->pid += val; + + return 0; +} + +SEC("tp/syscalls/sys_enter_nanosleep") +int test_ringbuf(void *ctx) +{ + struct bpf_dynptr ptr; + struct sample *sample; + + if (bpf_get_current_pid_tgid() >> 32 != pid) + return 0; + + val = 100; + + /* check that you can reserve a dynamic size reservation */ + err = bpf_ringbuf_reserve_dynptr(&ringbuf, val, 0, &ptr); + if (err) + goto done; + + sample = bpf_dynptr_data(&ptr, 0, sizeof(*sample)); + if (!sample) { + err = 1; + goto done; + } + + sample->pid = 123; + + /* Can pass dynptr to callback functions */ + bpf_loop(10, ringbuf_callback, &ptr, 0); + + bpf_ringbuf_submit_dynptr(&ptr, 0); + + return 0; + +done: + bpf_ringbuf_discard_dynptr(&ptr, 0); + return 0; +} + +SEC("tp/syscalls/sys_enter_nanosleep") +int test_alloc_zero_bytes(void *ctx) +{ + struct bpf_dynptr ptr; + void *data; + __u8 x = 0; + + if (bpf_get_current_pid_tgid() >> 32 != pid) + return 0; + + err = bpf_dynptr_alloc(0, 0, &ptr); + if (err) + goto done; + + err = bpf_dynptr_write(&ptr, 0, &x, sizeof(x)); + if (err != -EINVAL) { + err = 1; + goto done; + } + + err = bpf_dynptr_read(&x, sizeof(x), &ptr, 0); + if (err != -EINVAL) { + err = 2; + goto done; + } + err = 0; + + /* try to access memory we don't have access to */ + data = bpf_dynptr_data(&ptr, 0, 1); + if (data) { + err = 3; + goto done; + } + + data = bpf_dynptr_data(&ptr, 0, 0); + if (!data) { + err = 4; + goto done; + } + +done: + bpf_dynptr_put(&ptr); + return 0; +}