[PATCH bpf-next v1 7/7] bpf: Dynptr tests

[Date Prev][Date Next][Thread Prev][Thread Next][Date Index][Thread Index]

 



From: Joanne Koong <joannelkoong@xxxxxxxxx>

This patch adds tests for dynptrs. These include scenarios that the
verifier needs to reject, as well as some successful use cases of
dynptrs that should pass.

Some of the failure scenarios include checking against invalid bpf_frees,
invalid writes, invalid reads, and invalid ringbuf API usages.

Signed-off-by: Joanne Koong <joannelkoong@xxxxxxxxx>
---
 .../testing/selftests/bpf/prog_tests/dynptr.c | 303 ++++++++++
 .../testing/selftests/bpf/progs/dynptr_fail.c | 527 ++++++++++++++++++
 .../selftests/bpf/progs/dynptr_success.c      | 147 +++++
 3 files changed, 977 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..7107ebee3427
--- /dev/null
+++ b/tools/testing/selftests/bpf/prog_tests/dynptr.c
@@ -0,0 +1,303 @@
+// SPDX-License-Identifier: GPL-2.0
+/* Copyright (c) 2022 Facebook */
+
+#include <test_progs.h>
+#include "dynptr_fail.skel.h"
+#include "dynptr_success.skel.h"
+
+size_t log_buf_sz = 1024 * 1024;
+
+enum fail_case {
+	MISSING_FREE,
+	MISSING_FREE_CALLBACK,
+	INVALID_FREE1,
+	INVALID_FREE2,
+	USE_AFTER_FREE,
+	MALLOC_TWICE,
+	INVALID_MAP_CALL1,
+	INVALID_MAP_CALL2,
+	RINGBUF_INVALID_ACCESS,
+	RINGBUF_INVALID_API,
+	RINGBUF_OUT_OF_BOUNDS,
+	DATA_SLICE_OUT_OF_BOUNDS,
+	DATA_SLICE_USE_AFTER_FREE,
+	INVALID_HELPER1,
+	INVALID_HELPER2,
+	INVALID_WRITE1,
+	INVALID_WRITE2,
+	INVALID_WRITE3,
+	INVALID_WRITE4,
+	INVALID_READ1,
+	INVALID_READ2,
+	INVALID_READ3,
+	INVALID_OFFSET,
+	GLOBAL,
+	FREE_TWICE,
+	FREE_TWICE_CALLBACK,
+};
+
+static void verify_fail(enum fail_case fail, char *obj_log_buf,  char *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, "skel_open"))
+		return;
+
+	bpf_object__for_each_program(prog, skel->obj)
+		bpf_program__set_autoload(prog, false);
+
+	/* these programs should all be rejected by the verifier */
+	switch (fail) {
+	case MISSING_FREE:
+		prog = skel->progs.missing_free;
+		break;
+	case MISSING_FREE_CALLBACK:
+		prog = skel->progs.missing_free_callback;
+		break;
+	case INVALID_FREE1:
+		prog = skel->progs.invalid_free1;
+		break;
+	case INVALID_FREE2:
+		prog = skel->progs.invalid_free2;
+		break;
+	case USE_AFTER_FREE:
+		prog = skel->progs.use_after_free;
+		break;
+	case MALLOC_TWICE:
+		prog = skel->progs.malloc_twice;
+		break;
+	case INVALID_MAP_CALL1:
+		prog = skel->progs.invalid_map_call1;
+		break;
+	case INVALID_MAP_CALL2:
+		prog = skel->progs.invalid_map_call2;
+		break;
+	case RINGBUF_INVALID_ACCESS:
+		prog = skel->progs.ringbuf_invalid_access;
+		break;
+	case RINGBUF_INVALID_API:
+		prog = skel->progs.ringbuf_invalid_api;
+		break;
+	case RINGBUF_OUT_OF_BOUNDS:
+		prog = skel->progs.ringbuf_out_of_bounds;
+		break;
+	case DATA_SLICE_OUT_OF_BOUNDS:
+		prog = skel->progs.data_slice_out_of_bounds;
+		break;
+	case DATA_SLICE_USE_AFTER_FREE:
+		prog = skel->progs.data_slice_use_after_free;
+		break;
+	case INVALID_HELPER1:
+		prog = skel->progs.invalid_helper1;
+		break;
+	case INVALID_HELPER2:
+		prog = skel->progs.invalid_helper2;
+		break;
+	case INVALID_WRITE1:
+		prog = skel->progs.invalid_write1;
+		break;
+	case INVALID_WRITE2:
+		prog = skel->progs.invalid_write2;
+		break;
+	case INVALID_WRITE3:
+		prog = skel->progs.invalid_write3;
+		break;
+	case INVALID_WRITE4:
+		prog = skel->progs.invalid_write4;
+		break;
+	case INVALID_READ1:
+		prog = skel->progs.invalid_read1;
+		break;
+	case INVALID_READ2:
+		prog = skel->progs.invalid_read2;
+		break;
+	case INVALID_READ3:
+		prog = skel->progs.invalid_read3;
+		break;
+	case INVALID_OFFSET:
+		prog = skel->progs.invalid_offset;
+		break;
+	case GLOBAL:
+		prog = skel->progs.global;
+		break;
+	case FREE_TWICE:
+		prog = skel->progs.free_twice;
+		break;
+	case FREE_TWICE_CALLBACK:
+		prog = skel->progs.free_twice_callback;
+		break;
+	default:
+		fprintf(stderr, "unknown fail_case\n");
+		return;
+	}
+
+	bpf_program__set_autoload(prog, true);
+
+	err = dynptr_fail__load(skel);
+
+	ASSERT_OK_PTR(strstr(obj_log_buf, err_msg), "err_msg not found");
+
+	ASSERT_ERR(err, "unexpected load success");
+
+	dynptr_fail__destroy(skel);
+}
+
+static void run_prog(struct dynptr_success *skel, struct bpf_program *prog)
+{
+	struct bpf_link *link;
+
+	link = bpf_program__attach(prog);
+	if (!ASSERT_OK_PTR(link, "bpf program attach"))
+		return;
+
+	usleep(1);
+
+	ASSERT_EQ(skel->bss->err, 0, "err");
+
+	bpf_link__destroy(link);
+}
+
+static void verify_success(void)
+{
+	struct dynptr_success *skel;
+
+	skel = dynptr_success__open();
+
+	skel->bss->pid = getpid();
+
+	dynptr_success__load(skel);
+	if (!ASSERT_OK_PTR(skel, "dynptr__open_and_load"))
+		return;
+
+	run_prog(skel, skel->progs.prog_success);
+	run_prog(skel, skel->progs.prog_success_data_slice);
+	run_prog(skel, skel->progs.prog_success_ringbuf);
+
+	dynptr_success__destroy(skel);
+}
+
+void test_dynptr(void)
+{
+	char *obj_log_buf;
+
+	obj_log_buf = malloc(3 * log_buf_sz);
+	if (!ASSERT_OK_PTR(obj_log_buf, "obj_log_buf"))
+		return;
+	obj_log_buf[0] = '\0';
+
+	if (test__start_subtest("missing_free"))
+		verify_fail(MISSING_FREE, obj_log_buf,
+			    "spi=0 is an unreleased dynptr");
+
+	if (test__start_subtest("missing_free_callback"))
+		verify_fail(MISSING_FREE_CALLBACK, obj_log_buf,
+			    "spi=0 is an unreleased dynptr");
+
+	if (test__start_subtest("invalid_free1"))
+		verify_fail(INVALID_FREE1, obj_log_buf,
+			    "arg #1 is an unacquired reference and hence cannot be released");
+
+	if (test__start_subtest("invalid_free2"))
+		verify_fail(INVALID_FREE2, obj_log_buf, "type=alloc_mem_or_null expected=fp");
+
+	if (test__start_subtest("use_after_free"))
+		verify_fail(USE_AFTER_FREE, obj_log_buf,
+			    "Expected an initialized dynptr as arg #3");
+
+	if (test__start_subtest("malloc_twice"))
+		verify_fail(MALLOC_TWICE, obj_log_buf,
+			    "Arg #2 dynptr cannot be an initialized dynptr");
+
+	if (test__start_subtest("invalid_map_call1"))
+		verify_fail(INVALID_MAP_CALL1, obj_log_buf,
+			    "invalid indirect read from stack");
+
+	if (test__start_subtest("invalid_map_call2"))
+		verify_fail(INVALID_MAP_CALL2, obj_log_buf,
+			    "invalid indirect read from stack");
+
+	if (test__start_subtest("invalid_helper1"))
+		verify_fail(INVALID_HELPER1, obj_log_buf,
+			    "invalid indirect read from stack");
+
+	if (test__start_subtest("ringbuf_invalid_access"))
+		verify_fail(RINGBUF_INVALID_ACCESS, obj_log_buf,
+			    "invalid mem access 'scalar'");
+
+	if (test__start_subtest("ringbuf_invalid_api"))
+		verify_fail(RINGBUF_INVALID_API, obj_log_buf,
+			    "func bpf_ringbuf_submit#132 reference has not been acquired before");
+
+	if (test__start_subtest("ringbuf_out_of_bounds"))
+		verify_fail(RINGBUF_OUT_OF_BOUNDS, obj_log_buf,
+			    "value is outside of the allowed memory range");
+
+	if (test__start_subtest("data_slice_out_of_bounds"))
+		verify_fail(DATA_SLICE_OUT_OF_BOUNDS, obj_log_buf,
+			    "value is outside of the allowed memory range");
+
+	if (test__start_subtest("data_slice_use_after_free"))
+		verify_fail(DATA_SLICE_USE_AFTER_FREE, obj_log_buf,
+			    "invalid mem access 'scalar'");
+
+	if (test__start_subtest("invalid_helper2"))
+		verify_fail(INVALID_HELPER2, obj_log_buf,
+			    "Expected an initialized dynptr as arg #3");
+
+	if (test__start_subtest("invalid_write1"))
+		verify_fail(INVALID_WRITE1, obj_log_buf,
+			    "direct write into dynptr is not permitted");
+
+	if (test__start_subtest("invalid_write2"))
+		verify_fail(INVALID_WRITE2, obj_log_buf,
+			    "direct write into dynptr is not permitted");
+
+	if (test__start_subtest("invalid_write3"))
+		verify_fail(INVALID_WRITE3, obj_log_buf,
+			    "direct write into dynptr is not permitted");
+
+	if (test__start_subtest("invalid_write4"))
+		verify_fail(INVALID_WRITE4, obj_log_buf,
+			    "direct write into dynptr is not permitted");
+
+	if (test__start_subtest("invalid_read1"))
+		verify_fail(INVALID_READ1, obj_log_buf,
+			    "invalid read from stack");
+
+	if (test__start_subtest("invalid_read2"))
+		verify_fail(INVALID_READ2, obj_log_buf,
+			    "Expected an initialized dynptr as arg #3");
+
+	if (test__start_subtest("invalid_read3"))
+		verify_fail(INVALID_READ3, obj_log_buf,
+			    "invalid read from stack");
+
+	if (test__start_subtest("invalid_offset"))
+		verify_fail(INVALID_OFFSET, obj_log_buf,
+			    "invalid indirect access to stack");
+
+	if (test__start_subtest("global"))
+		verify_fail(GLOBAL, obj_log_buf,
+			    "R2 type=map_value expected=fp");
+
+	if (test__start_subtest("free_twice"))
+		verify_fail(FREE_TWICE, obj_log_buf,
+			    "arg #1 is an unacquired reference and hence cannot be released");
+
+	if (test__start_subtest("free_twice_callback"))
+		verify_fail(FREE_TWICE_CALLBACK, obj_log_buf,
+			    "arg #1 is an unacquired reference and hence cannot be released");
+
+	if (test__start_subtest("success"))
+		verify_success();
+
+	free(obj_log_buf);
+}
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..0b19eeb83e36
--- /dev/null
+++ b/tools/testing/selftests/bpf/progs/dynptr_fail.c
@@ -0,0 +1,527 @@
+// SPDX-License-Identifier: GPL-2.0
+/* Copyright (c) 2022 Facebook */
+
+#include <string.h>
+#include <linux/bpf.h>
+#include <bpf/bpf_helpers.h>
+#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;
+
+/* A dynptr can't be used after bpf_free has been called on it */
+SEC("raw_tp/sys_nanosleep")
+int use_after_free(void *ctx)
+{
+	struct bpf_dynptr ptr = {};
+	char read_data[64] = {};
+
+	bpf_malloc(8, &ptr);
+
+	bpf_dynptr_read(read_data, sizeof(read_data), &ptr, 0);
+
+	bpf_free(&ptr);
+
+	/* this should fail */
+	bpf_dynptr_read(read_data, sizeof(read_data), &ptr, 0);
+
+	return 0;
+}
+
+/* Every bpf_malloc call must have a corresponding bpf_free call */
+SEC("raw_tp/sys_nanosleep")
+int missing_free(void *ctx)
+{
+	struct bpf_dynptr mem;
+
+	bpf_malloc(8, &mem);
+
+	/* missing a call to bpf_free(&mem) */
+
+	return 0;
+}
+
+/* A non-malloc-ed dynptr can't be freed */
+SEC("raw_tp/sys_nanosleep")
+int invalid_free1(void *ctx)
+{
+	struct bpf_dynptr ptr;
+	__u32 x = 0;
+
+	bpf_dynptr_from_mem(&x, sizeof(x), &ptr);
+
+	/* this should fail */
+	bpf_free(&ptr);
+
+	return 0;
+}
+
+/* A data slice from a dynptr can't be freed */
+SEC("raw_tp/sys_nanosleep")
+int invalid_free2(void *ctx)
+{
+	struct bpf_dynptr ptr;
+	void *data;
+
+	bpf_malloc(8, &ptr);
+
+	data = bpf_dynptr_data(&ptr, 0, 8);
+
+	/* this should fail */
+	bpf_free(data);
+
+	return 0;
+}
+
+/*
+ * Can't bpf_malloc an existing malloc-ed bpf_dynptr that hasn't been
+ * freed yet
+ */
+SEC("raw_tp/sys_nanosleep")
+int malloc_twice(void *ctx)
+{
+	struct bpf_dynptr ptr;
+
+	bpf_malloc(8, &ptr);
+
+	/* this should fail */
+	bpf_malloc(2, &ptr);
+
+	bpf_free(&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);
+
+	return 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 invalid_map_call1(void *ctx)
+{
+	struct bpf_dynptr ptr = {};
+	char buf[64] = {};
+	int key = 0;
+
+	err = bpf_dynptr_from_mem(buf, sizeof(buf), &ptr);
+
+	/* this should fail */
+	bpf_map_update_elem(&array_map, &key, &ptr, 0);
+
+	return 0;
+}
+
+/* Can't add a struct with an embedded dynptr to a map */
+SEC("raw_tp/sys_nanosleep")
+int invalid_map_call2(void *ctx)
+{
+	struct info {
+		int x;
+		struct bpf_dynptr ptr;
+	};
+	struct info x;
+	int key = 0;
+
+	bpf_malloc(8, &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_malloc(8, &ptr);
+
+	/* this should fail */
+	bpf_strncmp((const char *)&ptr, sizeof(ptr), "hello!");
+
+	bpf_free(&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] = {};
+	__u64 x = 0;
+
+	bpf_dynptr_from_mem(&x, sizeof(x), &ptr);
+
+	/* this should fail */
+	bpf_dynptr_read(read_data, sizeof(read_data), (void *)&ptr + 8, 0);
+
+	return 0;
+}
+
+/* A data slice can't be accessed out of bounds */
+SEC("fentry/" SYS_PREFIX "sys_nanosleep")
+int data_slice_out_of_bounds(void *ctx)
+{
+	struct bpf_dynptr ptr = {};
+	void *data;
+
+	bpf_malloc(8, &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_free(&ptr);
+	return 0;
+}
+
+/* A data slice can't be used after it's freed */
+SEC("fentry/" SYS_PREFIX "sys_nanosleep")
+int data_slice_use_after_free(void *ctx)
+{
+	struct bpf_dynptr ptr = {};
+	void *data;
+
+	bpf_malloc(8, &ptr);
+
+	data = bpf_dynptr_data(&ptr, 0, 8);
+	if (!data)
+		goto done;
+
+	bpf_free(&ptr);
+
+	/* this should fail */
+	val = *(__u8 *)data;
+
+done:
+	bpf_free(&ptr);
+	return 0;
+}
+
+/*
+ * A bpf_dynptr can't be written directly to by the bpf program,
+ * only through dynptr helper functions
+ */
+SEC("raw_tp/sys_nanosleep")
+int invalid_write1(void *ctx)
+{
+	struct bpf_dynptr ptr = {};
+	__u8 x = 0;
+
+	bpf_malloc(8, &ptr);
+
+	/* this should fail */
+	memcpy(&ptr, &x, sizeof(x));
+
+	bpf_free(&ptr);
+
+	return 0;
+}
+
+/*
+ * A bpf_dynptr at a non-zero offset can't be written directly to by the bpf program,
+ * only through dynptr helper functions
+ */
+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_from_mem(&x, sizeof(x), &ptr);
+
+	/* this should fail */
+	memcpy((void *)&ptr, &y, sizeof(y));
+
+	bpf_dynptr_read(read_data, sizeof(read_data), &ptr, 0);
+
+	return 0;
+}
+
+/* A non-const write into a dynptr is not permitted */
+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_malloc(8, &ptr);
+
+	memcpy(stack_buf, &val, sizeof(val));
+	len = stack_buf[0] & 0xf;
+
+	/* this should fail */
+	memcpy((void *)&ptr + len, &x, sizeof(x));
+
+	bpf_free(&ptr);
+
+	return 0;
+}
+
+static int invalid_write4_callback(__u32 index, void *data)
+{
+	/* this should fail */
+	*(__u32 *)data = 123;
+
+	bpf_free(data);
+
+	return 0;
+}
+
+/* An invalid write can't occur in a callback function */
+SEC("raw_tp/sys_nanosleep")
+int invalid_write4(void *ctx)
+{
+	struct bpf_dynptr ptr;
+	__u64 x = 0;
+
+	bpf_dynptr_from_mem(&x, sizeof(x), &ptr);
+
+	bpf_loop(10, invalid_write4_callback, &ptr, 0);
+
+	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_malloc(4, &global_dynptr);
+
+	bpf_free(&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_from_mem(&x, sizeof(x), &ptr);
+
+	/* this should fail */
+	val = *(int *)&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_from_mem(&x, sizeof(x), &ptr);
+
+	/* this should fail */
+	bpf_dynptr_read(read_data, sizeof(read_data), (void *)&ptr + 1, 0);
+
+	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 ptr = {};
+	struct bpf_dynptr ptr2 = {};
+	__u32 x = 2;
+
+	bpf_dynptr_from_mem(&x, sizeof(x), &ptr);
+	bpf_dynptr_from_mem(&x, sizeof(x), &ptr2);
+
+	/* this should fail */
+	memcpy(&val, (void *)&ptr + 8, sizeof(val));
+
+	return 0;
+}
+
+/* Calling bpf_dynptr_from_mem on an offset should fail */
+SEC("raw_tp/sys_nanosleep")
+int invalid_offset(void *ctx)
+{
+	struct bpf_dynptr ptr = {};
+	__u64 x = 0;
+
+	/* this should fail */
+	bpf_dynptr_from_mem(&x, sizeof(x), &ptr + 1);
+
+	return 0;
+}
+
+/* A malloc can't be freed twice */
+SEC("raw_tp/sys_nanosleep")
+int free_twice(void *ctx)
+{
+	struct bpf_dynptr ptr;
+
+	bpf_malloc(8, &ptr);
+
+	bpf_free(&ptr);
+
+	/* this second free should fail */
+	bpf_free(&ptr);
+
+	return 0;
+}
+
+static int free_twice_callback_fn(__u32 index, void *data)
+{
+	/* this should fail */
+	bpf_free(data);
+	val = index;
+	return 0;
+}
+
+/* Test that freeing a malloc twice, where the 2nd free happens within a
+ * calback function, fails
+ */
+SEC("raw_tp/sys_nanosleep")
+int free_twice_callback(void *ctx)
+{
+	struct bpf_dynptr ptr;
+
+	bpf_malloc(8, &ptr);
+
+	bpf_free(&ptr);
+
+	bpf_loop(10, free_twice_callback_fn, &ptr, 0);
+
+	return 0;
+}
+
+static int missing_free_callback_fn(__u32 index, void *data)
+{
+	struct bpf_dynptr ptr;
+
+	bpf_malloc(8, &ptr);
+
+	val = index;
+
+	/* missing bpf_free(&ptr) */
+
+	return 0;
+}
+
+/* Any dynptr initialized within a callback must be freed */
+SEC("raw_tp/sys_nanosleep")
+int missing_free_callback(void *ctx)
+{
+	bpf_loop(10, missing_free_callback_fn, NULL, 0);
+	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..1b605bbc17f3
--- /dev/null
+++ b/tools/testing/selftests/bpf/progs/dynptr_success.c
@@ -0,0 +1,147 @@
+// SPDX-License-Identifier: GPL-2.0
+/* Copyright (c) 2022 Facebook */
+
+#include <string.h>
+#include <linux/bpf.h>
+#include <bpf/bpf_helpers.h>
+#include "bpf_misc.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 prog_success(void *ctx)
+{
+	char buf[64] = {};
+	char write_data[64] = "hello there, world!!";
+	struct bpf_dynptr ptr = {}, mem = {};
+	__u8 mem_allocated = 0;
+	char read_data[64] = {};
+	__u32 val = 0;
+	void *data;
+	int i;
+
+	if (bpf_get_current_pid_tgid() >> 32 != pid)
+		return 0;
+
+	err = bpf_dynptr_from_mem(buf, sizeof(buf), &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:
+	if (mem_allocated)
+		bpf_free(&mem);
+	return 0;
+}
+
+SEC("tp/syscalls/sys_enter_nanosleep")
+int prog_success_data_slice(void *ctx)
+{
+	struct bpf_dynptr mem;
+	void *data;
+
+	if (bpf_get_current_pid_tgid() >> 32 != pid)
+		return 0;
+
+	err = bpf_malloc(16, &mem);
+	if (err)
+		goto done;
+
+	data = bpf_dynptr_data(&mem, 0, sizeof(__u32));
+	if (!data)
+		goto done;
+
+	*(__u32 *)data = 999;
+
+	err = bpf_probe_read_kernel(&val, sizeof(val), data);
+	if (err)
+		goto done;
+
+	if (val != *(__u32 *)data)
+		err = 2;
+
+done:
+	bpf_free(&mem);
+	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)
+		return 0;
+
+	sample->pid += val;
+
+	return 0;
+}
+
+SEC("tp/syscalls/sys_enter_nanosleep")
+int prog_success_ringbuf(void *ctx)
+{
+	struct bpf_dynptr ptr;
+	void *data;
+	struct sample *sample;
+
+	if (bpf_get_current_pid_tgid() >> 32 != pid)
+		return 0;
+
+	/* 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)
+		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;
+}
-- 
2.30.2





[Index of Archives]     [Linux Samsung SoC]     [Linux Rockchip SoC]     [Linux Actions SoC]     [Linux for Synopsys ARC Processors]     [Linux NFS]     [Linux NILFS]     [Linux USB Devel]     [Video for Linux]     [Linux Audio Users]     [Yosemite News]     [Linux Kernel]     [Linux SCSI]


  Powered by Linux