BPF List
 help / color / mirror / Atom feed
From: Andrii Nakryiko <andriin@fb.com>
To: <bpf@vger.kernel.org>, <netdev@vger.kernel.org>, <ast@fb.com>,
	<daniel@iogearbox.net>
Cc: <andrii.nakryiko@gmail.com>, <kernel-team@fb.com>,
	Andrii Nakryiko <andriin@fb.com>
Subject: [PATCH bpf-next 6/6] selftests/bpf: add tests for libbpf-provided externs
Date: Sat, 16 Nov 2019 23:08:07 -0800	[thread overview]
Message-ID: <20191117070807.251360-7-andriin@fb.com> (raw)
In-Reply-To: <20191117070807.251360-1-andriin@fb.com>

Add a set of tests validating libbpf-provided extern variables. One crucial
feature that's tested is dead code elimination together with using invalid BPF
helper. CONFIG_MISSING is not supposed to exist and should always be specified
by libbpf as zero, which allows BPF verifier to correctly do branch pruning
and not fail validation, when invalid BPF helper is called from dead if branch.

Signed-off-by: Andrii Nakryiko <andriin@fb.com>
---
 .../selftests/bpf/prog_tests/core_extern.c    | 186 ++++++++++++++++++
 .../selftests/bpf/progs/test_core_extern.c    |  43 ++++
 2 files changed, 229 insertions(+)
 create mode 100644 tools/testing/selftests/bpf/prog_tests/core_extern.c
 create mode 100644 tools/testing/selftests/bpf/progs/test_core_extern.c

diff --git a/tools/testing/selftests/bpf/prog_tests/core_extern.c b/tools/testing/selftests/bpf/prog_tests/core_extern.c
new file mode 100644
index 000000000000..2a87a5f1de4d
--- /dev/null
+++ b/tools/testing/selftests/bpf/prog_tests/core_extern.c
@@ -0,0 +1,186 @@
+// SPDX-License-Identifier: GPL-2.0
+#include <test_progs.h>
+#include <sys/mman.h>
+#include <sys/utsname.h>
+#include <linux/version.h>
+
+static size_t roundup_page(size_t sz)
+{
+	long page_size = sysconf(_SC_PAGE_SIZE);
+	return (sz + page_size - 1) / page_size * page_size;
+}
+
+static uint32_t get_kernel_version(void)
+{
+	uint32_t major, minor, patch;
+	struct utsname info;
+
+	uname(&info);
+	if (sscanf(info.release, "%u.%u.%u", &major, &minor, &patch) != 3)
+		return 0;
+	return KERNEL_VERSION(major, minor, patch);
+}
+
+struct data {
+	uint64_t kern_ver;
+	uint64_t tristate_val;
+	uint64_t bool_val;
+	uint64_t int_val;
+	uint64_t missing_val;
+};
+
+static struct test_case {
+	const char *name;
+	const char *cfg;
+	const char *cfg_path;
+	bool fails;
+	struct data data;
+} test_cases[] = {
+	{ .name = "default search path", .cfg_path = NULL },
+	{ .name = "/proc/config.gz", .cfg_path = "/proc/config.gz" },
+	{ .name = "missing config", .fails = true,
+	  .cfg_path = "/proc/invalid-config.gz" },
+	{
+		.name = "custom values",
+		.cfg = "CONFIG_TRISTATE=m\n"
+		       "CONFIG_BOOL=y\n"
+		       "CONFIG_INT=123456\n",
+		.data = {
+			.tristate_val = 2,
+			.bool_val = 1,
+			.int_val = 123456,
+		},
+	},
+	{
+		/* there is no real typing, so any valid value is accepted */
+		.name = "mixed up types",
+		.cfg = "CONFIG_TRISTATE=123\n"
+		       "CONFIG_BOOL=m\n"
+		       "CONFIG_INT=y\n",
+		.data = {
+			.tristate_val = 123,
+			.bool_val = 2,
+			.int_val = 1,
+		},
+	},
+	{
+		/* somewhat weird behavior of strtoull */
+		.name = "negative int",
+		.cfg = "CONFIG_INT=-12\n",
+		.data = { .int_val = (uint64_t)-12 },
+	},
+	{ .name = "bad tristate", .fails = true, .cfg = "CONFIG_TRISTATE=M" },
+	{ .name = "bad bool", .fails = true, .cfg = "CONFIG_BOOL=X" },
+	{ .name = "int (not int)", .fails = true, .cfg = "CONFIG_INT=abc" },
+	{ .name = "int (string)", .fails = true, .cfg = "CONFIG_INT=\"abc\"" },
+	{ .name = "int (empty)", .fails = true, .cfg = "CONFIG_INT=" },
+	{ .name = "int (mixed up 1)", .fails = true, .cfg = "CONFIG_INT=123abc",
+	  .fails = true, },
+	{ .name = "int (mixed up 2)", .fails = true, .cfg = "CONFIG_INT=123abc\n",
+	  .fails = true, },
+	{ .name = "int (too big)", .fails = true,
+	  .cfg = "CONFIG_INT=123456789123456789123\n" },
+};
+
+void test_core_extern(void)
+{
+	const char *file = "test_core_extern.o";
+	const char *probe_name = "raw_tp/sys_enter";
+	const char *tp_name = "sys_enter";
+	const size_t bss_sz = roundup_page(sizeof(struct data));
+	const uint32_t kern_ver = get_kernel_version();
+	int err, duration = 0, i;
+	struct bpf_program *prog;
+	struct bpf_object *obj;
+	struct bpf_link *link = NULL;
+	struct bpf_map *bss_map;
+	void *bss_mmaped = NULL;
+	volatile struct data *data;
+
+	for (i = 0; i < ARRAY_SIZE(test_cases); i++) {
+		char tmp_cfg_path[] = "/tmp/test_core_extern_cfg.XXXXXX";
+		const struct test_case *t = &test_cases[i];
+		DECLARE_LIBBPF_OPTS(bpf_object_open_opts, opts,
+			.kernel_config_path = t->cfg_path,
+		);
+
+		if (!test__start_subtest(t->name))
+			continue;
+
+		if (t->cfg) {
+			size_t n = strlen(t->cfg) + 1;
+			int fd = mkstemp(tmp_cfg_path);
+
+			if (CHECK(fd < 0, "mkstemp", "errno: %d\n", errno))
+				continue;
+			printf("using '%s' as config file\n", tmp_cfg_path);
+			if (CHECK_FAIL(write(fd, t->cfg, n) != n))
+				continue;
+			close(fd);
+			opts.kernel_config_path = tmp_cfg_path;
+		}
+
+		obj = bpf_object__open_file("test_core_extern.o", &opts);
+		if (t->fails) {
+			CHECK(!IS_ERR(obj), "obj_open",
+			      "shouldn't succeed opening '%s'!\n", file);
+			goto cleanup;
+		} else {
+			if (CHECK(IS_ERR(obj), "obj_open",
+				  "failed to open '%s': %ld\n",
+				  file, PTR_ERR(obj)))
+				goto cleanup;
+		}
+		prog = bpf_object__find_program_by_title(obj, probe_name);
+		if (CHECK(!prog, "find_prog", "prog %s missing\n", probe_name))
+			goto cleanup;
+		err = bpf_object__load(obj);
+		if (CHECK(err, "obj_load", "failed to load prog '%s': %d\n",
+			  probe_name, err))
+			goto cleanup;
+		bss_map = bpf_object__find_map_by_name(obj, "test_cor.bss");
+		if (CHECK(!bss_map, "find_bss_map", ".bss map not found\n"))
+			goto cleanup;
+		bss_mmaped = mmap(NULL, bss_sz, PROT_READ | PROT_WRITE,
+				  MAP_SHARED, bpf_map__fd(bss_map), 0);
+		if (CHECK(bss_mmaped == MAP_FAILED, "bss_mmap",
+			  ".bss mmap failed: %d\n", errno)) {
+			bss_mmaped = NULL;
+			goto cleanup;
+		}
+		data = bss_mmaped;
+
+		link = bpf_program__attach_raw_tracepoint(prog, tp_name);
+		if (CHECK(IS_ERR(link), "attach_raw_tp", "err %ld\n", PTR_ERR(link)))
+			goto cleanup;
+
+		usleep(1);
+
+		CHECK(data->kern_ver != kern_ver, "kern_ver",
+		      "exp %x, got %lx\n", kern_ver, data->kern_ver);
+		CHECK(data->missing_val != 0xDEADC0DE, "missing_val",
+		      "exp %x, got %lx\n", 0xDEADC0DE, data->missing_val);
+		CHECK(data->bool_val != t->data.bool_val, "bool_val",
+		      "exp %lx, got %lx\n", t->data.bool_val, data->bool_val);
+		CHECK(data->tristate_val != t->data.tristate_val,
+		      "tristate_val", "exp %lx, got %lx\n",
+		      t->data.tristate_val, data->tristate_val);
+		CHECK(data->int_val != t->data.int_val, "int_val",
+		      "exp %lx, got %lx\n", t->data.int_val, data->int_val);
+
+cleanup:
+		if (t->cfg)
+			unlink(tmp_cfg_path);
+		if (bss_mmaped) {
+			CHECK_FAIL(munmap(bss_mmaped, bss_sz));
+			bss_mmaped = NULL;
+			data = NULL;
+		}
+		if (!IS_ERR_OR_NULL(link)) {
+			bpf_link__destroy(link);
+			link = NULL;
+		}
+		if (!IS_ERR_OR_NULL(obj))
+			bpf_object__close(obj);
+	}
+}
diff --git a/tools/testing/selftests/bpf/progs/test_core_extern.c b/tools/testing/selftests/bpf/progs/test_core_extern.c
new file mode 100644
index 000000000000..adbc74113265
--- /dev/null
+++ b/tools/testing/selftests/bpf/progs/test_core_extern.c
@@ -0,0 +1,43 @@
+// SPDX-License-Identifier: GPL-2.0
+// Copyright (c) 2017 Facebook
+
+#include <stdint.h>
+#include <linux/ptrace.h>
+#include <linux/bpf.h>
+#include "bpf_helpers.h"
+
+/* non-existing BPF helper, to test dead code elimination */
+static int (*bpf_missing_helper)(const void *arg1, int arg2) = (void *) 999;
+
+extern uint64_t LINUX_KERNEL_VERSION;
+extern uint64_t CONFIG_TRISTATE;
+extern uint64_t CONFIG_BOOL;
+extern uint64_t CONFIG_INT;
+extern uint64_t CONFIG_MISSING;
+
+volatile struct {
+	uint64_t kern_ver;
+	uint64_t tristate_val;
+	uint64_t bool_val;
+	uint64_t int_val;
+	uint64_t missing_val;
+} out = {};
+
+SEC("raw_tp/sys_enter")
+int handle_sys_enter(struct pt_regs *ctx)
+{
+	out.kern_ver = LINUX_KERNEL_VERSION;
+	out.tristate_val = CONFIG_TRISTATE;
+	out.bool_val = CONFIG_BOOL;
+	out.int_val = CONFIG_INT;
+
+	if (CONFIG_MISSING)
+		/* invalid, but dead code - never executed */
+		out.missing_val = bpf_missing_helper(ctx, 123);
+	else
+		out.missing_val = 0xDEADC0DE;
+
+	return 0;
+}
+
+char _license[] SEC("license") = "GPL";
-- 
2.17.1


  parent reply	other threads:[~2019-11-17  7:08 UTC|newest]

Thread overview: 20+ messages / expand[flat|nested]  mbox.gz  Atom feed  top
2019-11-17  7:08 [PATCH bpf-next 0/6] Add libbpf-provided extern variables support Andrii Nakryiko
2019-11-17  7:08 ` [PATCH bpf-next 1/6] selftests/bpf: ensure no DWARF relocations for BPF object files Andrii Nakryiko
2019-11-17  7:08 ` [PATCH bpf-next 2/6] libbpf: refactor relocation handling Andrii Nakryiko
2019-11-17  7:08 ` [PATCH bpf-next 3/6] libbpf: fix various errors and warning reported by checkpatch.pl Andrii Nakryiko
2019-11-17  7:08 ` [PATCH bpf-next 4/6] libbpf: support initialized global variables Andrii Nakryiko
2019-11-17  7:08 ` [PATCH bpf-next 5/6] libbpf: support libbpf-provided extern variables Andrii Nakryiko
2019-11-19  3:21   ` Alexei Starovoitov
2019-11-19  6:57     ` Andrii Nakryiko
2019-11-19 15:42       ` Andrii Nakryiko
2019-11-19 15:58         ` Alexei Starovoitov
2019-11-19 23:32           ` Daniel Borkmann
2019-11-20  3:17             ` Andrii Nakryiko
2019-11-20  3:44           ` Andrii Nakryiko
2019-11-20 21:39             ` Alexei Starovoitov
2019-11-20 22:47               ` Andrii Nakryiko
2019-11-21  0:18                 ` Alexei Starovoitov
2019-11-21  2:13                   ` Andrii Nakryiko
2019-11-21  4:39                     ` Alexei Starovoitov
2019-11-17  7:08 ` Andrii Nakryiko [this message]
2019-11-17  7:12 ` [PATCH bpf-next 0/6] Add libbpf-provided extern variables support Andrii Nakryiko

Reply instructions:

You may reply publicly to this message via plain-text email
using any one of the following methods:

* Save the following mbox file, import it into your mail client,
  and reply-to-all from there: mbox

  Avoid top-posting and favor interleaved quoting:
  https://en.wikipedia.org/wiki/Posting_style#Interleaved_style

* Reply using the --to, --cc, and --in-reply-to
  switches of git-send-email(1):

  git send-email \
    --in-reply-to=20191117070807.251360-7-andriin@fb.com \
    --to=andriin@fb.com \
    --cc=andrii.nakryiko@gmail.com \
    --cc=ast@fb.com \
    --cc=bpf@vger.kernel.org \
    --cc=daniel@iogearbox.net \
    --cc=kernel-team@fb.com \
    --cc=netdev@vger.kernel.org \
    /path/to/YOUR_REPLY

  https://kernel.org/pub/software/scm/git/docs/git-send-email.html

* If your mail client supports setting the In-Reply-To header
  via mailto: links, try the mailto: link
Be sure your reply has a Subject: header at the top and a blank line before the message body.
This is a public inbox, see mirroring instructions
for how to clone and mirror all data and code used for this inbox