All of lore.kernel.org
 help / color / mirror / Atom feed
From: Wenbo Zhang <ethercflow@gmail.com>
To: bpf@vger.kernel.org
Cc: yhs@fb.com, daniel@iogearbox.net, andrii.nakryiko@gmail.com,
	netdev@vger.kernel.org, Wenbo Zhang <ethercflow@gmail.com>
Subject: [PATCH bpf-next v3] selftests/bpf: test for bpf_get_file_path() from raw tracepoint
Date: Mon,  4 Nov 2019 23:12:23 -0500	[thread overview]
Message-ID: <20191105041223.5622-1-ethercflow@gmail.com> (raw)

trace fstat events by raw tracepoint sys_enter:newfstat, and handle events
only produced by test_file_get_path, which call fstat on several different
types of files to test bpf_get_file_path's feature.

v2->v3: addressed Andrii's feedback
- use global data instead of perf_buffer to simplified code

v1->v2: addressed Daniel's feedback
- rename bpf_fd2path to bpf_get_file_path to be consistent with other
helper's names

Signed-off-by: Wenbo Zhang <ethercflow@gmail.com>
---
 .../selftests/bpf/prog_tests/get_file_path.c  | 171 ++++++++++++++++++
 .../selftests/bpf/progs/test_get_file_path.c  |  71 ++++++++
 2 files changed, 242 insertions(+)
 create mode 100644 tools/testing/selftests/bpf/prog_tests/get_file_path.c
 create mode 100644 tools/testing/selftests/bpf/progs/test_get_file_path.c

diff --git a/tools/testing/selftests/bpf/prog_tests/get_file_path.c b/tools/testing/selftests/bpf/prog_tests/get_file_path.c
new file mode 100644
index 000000000000..26126e55c1f0
--- /dev/null
+++ b/tools/testing/selftests/bpf/prog_tests/get_file_path.c
@@ -0,0 +1,171 @@
+// SPDX-License-Identifier: GPL-2.0
+#define _GNU_SOURCE
+#include <test_progs.h>
+#include <alloca.h>
+#include <sys/stat.h>
+
+#ifndef MAX_PATH_LENGTH
+#define MAX_PATH_LENGTH		128
+#endif
+
+#ifndef TASK_COMM_LEN
+#define TASK_COMM_LEN		16
+#endif
+
+struct get_path_trace_t {
+	unsigned long fd;
+	char path[MAX_PATH_LENGTH];
+};
+
+enum FS_TYPE {
+	PIPE_0,
+	PIPE_1,
+	SOCK,
+	PROC,
+	DEV,
+	LOCAL,
+	INDICATOR,
+	MAX_FDS
+};
+
+struct path_info {
+	int fd;
+	char name[MAX_PATH_LENGTH];
+};
+
+static struct path_info path_infos[MAX_FDS];
+static int path_info_index;
+static int hits;
+
+static inline int set_pathname(pid_t pid, int fd)
+{
+	char buf[MAX_PATH_LENGTH] = {'0'};
+
+	snprintf(buf, MAX_PATH_LENGTH, "/proc/%d/fd/%d", pid, fd);
+	path_infos[path_info_index].fd = fd;
+	return readlink(buf, path_infos[path_info_index++].name,
+			MAX_PATH_LENGTH);
+}
+
+static inline int compare_pathname(struct get_path_trace_t *data)
+{
+	for (int i = 0; i < MAX_FDS; i++) {
+		if (path_infos[i].fd == data->fd) {
+			hits++;
+			return strncmp(path_infos[i].name, data->path,
+					MAX_PATH_LENGTH);
+		}
+	}
+	return 0;
+}
+
+static int trigger_fstat_events(void)
+{
+	int *fds = alloca(sizeof(int) * MAX_FDS);
+	int *pipefd = fds;
+	int *sockfd = fds + SOCK;
+	int *procfd = fds + PROC;
+	int *devfd = fds + DEV;
+	int *localfd = fds + LOCAL;
+	int *indicatorfd = fds + INDICATOR;
+	pid_t pid = getpid();
+
+	/* unmountable pseudo-filesystems */
+	if (pipe(pipefd) < 0 || set_pathname(pid, *pipefd++) < 0 ||
+		set_pathname(pid, *pipefd) < 0)
+		return -1;
+
+	/* unmountable pseudo-filesystems */
+	*sockfd = socket(AF_INET, SOCK_STREAM, 0);
+	if (*sockfd < 0 || set_pathname(pid, *sockfd) < 0)
+		return -1;
+
+	/* mountable pseudo-filesystems */
+	*procfd = open("/proc/self/comm", O_RDONLY);
+	if (*procfd < 0 || set_pathname(pid, *procfd) < 0)
+		return -1;
+
+	*devfd = open("/dev/urandom", O_RDONLY);
+	if (*devfd < 0 || set_pathname(pid, *devfd) < 0)
+		return -1;
+
+	*localfd = open("/tmp/fd2path_loadgen.txt", O_CREAT|O_RDONLY);
+	if (*localfd < 0 || set_pathname(pid, *localfd) < 0)
+		return -1;
+
+	*indicatorfd = open("/tmp/", O_PATH);
+	if (*indicatorfd < 0 || set_pathname(pid, *indicatorfd) < 0)
+		return -1;
+
+	for (int i = 0; i < MAX_FDS; i++)
+		close(fds[i]);
+
+	remove("/tmp/fd2path_loadgen.txt");
+	return 0;
+}
+
+void test_get_file_path(void)
+{
+	const char *prog_name = "raw_tracepoint/sys_enter:newfstat";
+	const char *file = "./test_get_file_path.o";
+	int pidfilter_map_fd, pathdata_map_fd;
+	__u32 key, previous_key, duration = 0;
+	struct get_path_trace_t val = {};
+	struct bpf_program *prog = NULL;
+	struct bpf_object *obj = NULL;
+	struct bpf_link *link = NULL;
+	__u32 pid = getpid();
+	int err, prog_fd;
+
+	err = bpf_prog_load(file, BPF_PROG_TYPE_RAW_TRACEPOINT, &obj, &prog_fd);
+	if (CHECK(err, "prog_load", "err %d errno %d\n", err, errno))
+		return;
+
+	prog = bpf_object__find_program_by_title(obj, prog_name);
+	if (CHECK(!prog, "find_prog", "prog %s not found\n", prog_name))
+		goto out_close;
+
+	link = bpf_program__attach_raw_tracepoint(prog, "sys_enter");
+	if (CHECK(IS_ERR(link), "attach_tp", "err %ld\n", PTR_ERR(link)))
+		goto out_close;
+
+	pidfilter_map_fd = bpf_find_map(__func__, obj, "pidfilter_map");
+	if (CHECK(pidfilter_map_fd < 0, "bpf_find_map pidfilter_map",
+		  "err: %s\n", strerror(errno)))
+		goto out_detach;
+
+	err = bpf_map_update_elem(pidfilter_map_fd, &key, &pid, 0);
+	if (CHECK(err, "pidfilter_map update_elem", "err: %s\n",
+			  strerror(errno)))
+		goto out_detach;
+
+	err = trigger_fstat_events();
+	if (CHECK(err, "trigger_fstat_events", "open fd failed: %s\n",
+			  strerror(errno)))
+		goto out_detach;
+
+	pathdata_map_fd = bpf_find_map(__func__, obj, "pathdata_map");
+	if (CHECK_FAIL(pathdata_map_fd < 0))
+		goto out_detach;
+
+	do {
+		err = bpf_map_lookup_elem(pathdata_map_fd, &key, &val);
+		if (CHECK(err, "lookup_elem from pathdata_map",
+				  "err %s\n", strerror(errno)))
+			goto out_detach;
+
+		CHECK(compare_pathname(&val) != 0,
+			  "get_file_path", "failed to get path: %lu->%s\n",
+			  val.fd, val.path);
+
+		previous_key = key;
+	} while (bpf_map_get_next_key(pathdata_map_fd,
+					&previous_key, &key) == 0);
+
+	CHECK(hits != MAX_FDS, "Lost event?", "%d != %d\n", hits, MAX_FDS);
+
+out_detach:
+	bpf_link__destroy(link);
+out_close:
+	bpf_object__close(obj);
+}
diff --git a/tools/testing/selftests/bpf/progs/test_get_file_path.c b/tools/testing/selftests/bpf/progs/test_get_file_path.c
new file mode 100644
index 000000000000..10ec9a70c81c
--- /dev/null
+++ b/tools/testing/selftests/bpf/progs/test_get_file_path.c
@@ -0,0 +1,71 @@
+// SPDX-License-Identifier: GPL-2.0
+#include <linux/bpf.h>
+#include <linux/ptrace.h>
+#include <linux/sched.h>
+#include <stdbool.h>
+#include <string.h>
+#include "bpf_helpers.h"
+
+#ifndef MAX_PATH_LENGTH
+#define MAX_PATH_LENGTH		128
+#endif
+
+#ifndef MAX_EVENT_NUM
+#define MAX_EVENT_NUM		32
+#endif
+
+struct path_trace_t {
+	unsigned long fd;
+	char path[MAX_PATH_LENGTH];
+};
+
+struct {
+	__uint(type, BPF_MAP_TYPE_ARRAY);
+	__uint(max_entries, MAX_EVENT_NUM);
+	__type(key, __u32);
+	__type(value, struct path_trace_t);
+} pathdata_map SEC(".maps");
+
+struct {
+	__uint(type, BPF_MAP_TYPE_ARRAY);
+	__uint(max_entries, 1);
+	__type(key, __u32);
+	__type(value, __u32);
+} pidfilter_map SEC(".maps");
+
+struct {
+	__uint(type, BPF_MAP_TYPE_ARRAY);
+	__uint(max_entries, 1);
+	__type(key, __u32);
+	__type(value, __u32);
+} index_map SEC(".maps");
+
+SEC("raw_tracepoint/sys_enter:newfstat")
+int bpf_prog(struct bpf_raw_tracepoint_args *ctx)
+{
+	struct path_trace_t *data;
+	struct pt_regs *regs;
+	__u32 key = 0, *i, *pidfilter, pid;
+
+	pidfilter = bpf_map_lookup_elem(&pidfilter_map, &key);
+	if (!pidfilter || *pidfilter == 0)
+		return 0;
+	i = bpf_map_lookup_elem(&index_map, &key);
+	if (!i || *i == MAX_EVENT_NUM)
+		return 0;
+	pid = bpf_get_current_pid_tgid() >> 32;
+	if (pid != *pidfilter)
+		return 0;
+	data = bpf_map_lookup_elem(&pathdata_map, i);
+	if (!data)
+		return 0;
+
+	regs = (struct pt_regs *)ctx->args[0];
+	bpf_probe_read(&data->fd, sizeof(data->fd), &regs->rdi);
+	bpf_get_file_path(data->path, MAX_PATH_LENGTH, data->fd);
+	*i += 1;
+
+	return 0;
+}
+
+char _license[] SEC("license") = "GPL";
-- 
2.17.1


             reply	other threads:[~2019-11-05  4:15 UTC|newest]

Thread overview: 3+ messages / expand[flat|nested]  mbox.gz  Atom feed  top
2019-11-05  4:12 Wenbo Zhang [this message]
2019-11-05 19:07 ` [PATCH bpf-next v3] selftests/bpf: test for bpf_get_file_path() from raw tracepoint Andrii Nakryiko
2019-11-07 17:09   ` Wenbo Zhang

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=20191105041223.5622-1-ethercflow@gmail.com \
    --to=ethercflow@gmail.com \
    --cc=andrii.nakryiko@gmail.com \
    --cc=bpf@vger.kernel.org \
    --cc=daniel@iogearbox.net \
    --cc=netdev@vger.kernel.org \
    --cc=yhs@fb.com \
    /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 an external index of several public inboxes,
see mirroring instructions on how to clone and mirror
all data and code used by this external index.