All of lore.kernel.org
 help / color / mirror / Atom feed
From: Jiri Olsa <jolsa@kernel.org>
To: Alexei Starovoitov <ast@kernel.org>,
	Daniel Borkmann <daniel@iogearbox.net>
Cc: netdev@vger.kernel.org, bpf@vger.kernel.org,
	Yonghong Song <yhs@fb.com>, Martin KaFai Lau <kafai@fb.com>,
	David Miller <davem@redhat.com>,
	John Fastabend <john.fastabend@gmail.com>,
	Jesper Dangaard Brouer <hawk@kernel.org>,
	Wenbo Zhang <ethercflow@gmail.com>,
	KP Singh <kpsingh@chromium.org>, Andrii Nakryiko <andriin@fb.com>,
	bgregg@netflix.com, Florent Revest <revest@chromium.org>,
	Al Viro <viro@zeniv.linux.org.uk>
Subject: [PATCH 3/9] bpf: Add bpfwl tool to construct bpf whitelists
Date: Wed,  6 May 2020 15:29:40 +0200	[thread overview]
Message-ID: <20200506132946.2164578-4-jolsa@kernel.org> (raw)
In-Reply-To: <20200506132946.2164578-1-jolsa@kernel.org>

This tool takes vmlinux object and whitelist directory on input
and produces C source object with BPF whitelist data.

The vmlinux object needs to have a BTF information compiled in.

The whitelist directory is expected to contain files with helper
names, where each file contains list of functions/probes that
helper is allowed to be called from - whitelist.

The bpfwl tool has following output:

  $ bpfwl vmlinux dir
  unsigned long d_path[] __attribute__((section(".BTF_whitelist_d_path"))) = \
  { 24507, 24511, 24537, 24539, 24545, 24588, 24602, 24920 };

Each array are sorted BTF ids of the functions provided in the
helper file.

Each array will be compiled into kernel and used during the helper
check in verifier.

Signed-off-by: Jiri Olsa <jolsa@kernel.org>
---
 tools/bpf/bpfwl/Build    |  11 ++
 tools/bpf/bpfwl/Makefile |  60 +++++++++
 tools/bpf/bpfwl/bpfwl.c  | 285 +++++++++++++++++++++++++++++++++++++++
 3 files changed, 356 insertions(+)
 create mode 100644 tools/bpf/bpfwl/Build
 create mode 100644 tools/bpf/bpfwl/Makefile
 create mode 100644 tools/bpf/bpfwl/bpfwl.c

diff --git a/tools/bpf/bpfwl/Build b/tools/bpf/bpfwl/Build
new file mode 100644
index 000000000000..667e30d6ce79
--- /dev/null
+++ b/tools/bpf/bpfwl/Build
@@ -0,0 +1,11 @@
+bpfwl-y += bpfwl.o
+bpfwl-y += rbtree.o
+bpfwl-y += zalloc.o
+
+$(OUTPUT)rbtree.o: ../../lib/rbtree.c FORCE
+	$(call rule_mkdir)
+	$(call if_changed_dep,cc_o_c)
+
+$(OUTPUT)zalloc.o: ../../lib/zalloc.c FORCE
+	$(call rule_mkdir)
+	$(call if_changed_dep,cc_o_c)
diff --git a/tools/bpf/bpfwl/Makefile b/tools/bpf/bpfwl/Makefile
new file mode 100644
index 000000000000..8174eeb7eea6
--- /dev/null
+++ b/tools/bpf/bpfwl/Makefile
@@ -0,0 +1,60 @@
+# SPDX-License-Identifier: GPL-2.0-only
+include ../../scripts/Makefile.include
+
+MAKEFLAGS=--no-print-directory
+
+ifeq ($(srctree),)
+srctree := $(patsubst %/,%,$(dir $(CURDIR)))
+srctree := $(patsubst %/,%,$(dir $(srctree)))
+srctree := $(patsubst %/,%,$(dir $(srctree)))
+endif
+
+ifeq ($(V),1)
+  Q =
+else
+  Q = @
+endif
+
+BPF_DIR = $(srctree)/tools/lib/bpf/
+
+ifneq ($(OUTPUT),)
+  LIBBPF_PATH = $(OUTPUT)/libbpf/
+else
+  LIBBPF_PATH = $(BPF_DIR)
+endif
+
+LIBBPF    = $(LIBBPF_PATH)libbpf.a
+BPFWL     = $(OUTPUT)bpfwl
+BPFWL_IN  = $(BPFWL)-in.o
+
+all: $(OUTPUT)bpfwl
+
+$(LIBBPF): FORCE
+	$(if $(LIBBPF_PATH),@mkdir -p $(LIBBPF_PATH))
+	$(Q)$(MAKE) -C $(BPF_DIR) OUTPUT=$(LIBBPF_PATH) $(LIBBPF_PATH)libbpf.a
+
+$(LIBBPF)-clean:
+	$(call QUIET_CLEAN, libbpf)
+	$(Q)$(MAKE) -C $(BPF_DIR) OUTPUT=$(LIBBPF_PATH) clean >/dev/null
+
+CFLAGS := -g -I$(srctree)/tools/include -I$(BPF_DIR)
+
+LIBS = -lelf -lz
+
+export srctree OUTPUT CFLAGS
+include $(srctree)/tools/build/Makefile.include
+
+$(BPFWL_IN): fixdep FORCE
+	$(Q)$(MAKE) $(build)=bpfwl
+
+$(BPFWL): $(LIBBPF) $(BPFWL_IN)
+	$(QUIET_LINK)$(CC) $(BPFWL_IN) $(LDFLAGS) -o $@ $(LIBBPF) $(LIBS)
+
+clean: $(LIBBPF)-clean
+	$(call QUIET_CLEAN, bpfwl)
+	$(Q)$(RM) -f $(BPFWL)
+	$(Q)find $(if $(OUTPUT),$(OUTPUT),.) -name '*.o' -delete -o -name '\.*.cmd' -delete -o -name '\.*.d' -delete
+
+FORCE:
+
+.PHONY: all FORCE clean
diff --git a/tools/bpf/bpfwl/bpfwl.c b/tools/bpf/bpfwl/bpfwl.c
new file mode 100644
index 000000000000..495c2bcf620a
--- /dev/null
+++ b/tools/bpf/bpfwl/bpfwl.c
@@ -0,0 +1,285 @@
+// SPDX-License-Identifier: (LGPL-2.1 OR BSD-2-Clause)
+#define  _GNU_SOURCE
+
+#include <stdio.h>
+#include <string.h>
+#include <stdlib.h>
+#include <sys/types.h>
+#include <dirent.h>
+#include <stdlib.h>
+#include <linux/rbtree.h>
+#include <linux/list.h>
+#include <linux/kernel.h>
+#include <linux/zalloc.h>
+#include <linux/limits.h>
+#include <btf.h>
+#include <libbpf.h>
+
+struct func {
+	char			*name;
+	unsigned long		 id;
+	struct rb_node		 rb_node;
+	struct list_head	 list[];
+};
+
+struct helper {
+	char			*name;
+	int			 idx;
+	int			 count;
+	struct list_head	 node;
+	struct list_head	 funcs;
+};
+
+static struct rb_root funcs;
+static LIST_HEAD(helpers);
+static int idxs;
+
+static struct func *func__new(const char *name)
+{
+	size_t size = idxs * sizeof(struct list_head);
+	struct func *func;
+	int i;
+
+	func = zalloc(sizeof(*func) + size);
+	if (func) {
+		func->name = strdup(name);
+		for (i = 0; i < idxs; i++)
+			INIT_LIST_HEAD(&func->list[i]);
+	}
+	return func;
+}
+
+static struct helper *helper__new(const char *name)
+{
+	struct helper *helper = zalloc(sizeof(*helper));
+
+	if (helper) {
+		helper->idx = idxs++;
+		helper->name = strdup(name);
+		INIT_LIST_HEAD(&helper->funcs);
+		list_add_tail(&helper->node, &helpers);
+	}
+	return helper;
+}
+
+static struct func *func__add(char *name)
+{
+	struct rb_node **p = &funcs.rb_node;
+	struct rb_node *parent = NULL;
+	struct func *func;
+	int cmp;
+
+	while (*p != NULL) {
+		parent = *p;
+		func = rb_entry(parent, struct func, rb_node);
+		cmp = strcmp(func->name, name);
+		if (cmp < 0)
+			p = &(*p)->rb_left;
+		else if (cmp > 0)
+			p = &(*p)->rb_right;
+		else
+			return func;
+	}
+
+	func = func__new(name);
+	if (func) {
+		rb_link_node(&func->rb_node, parent, p);
+		rb_insert_color(&func->rb_node, &funcs);
+	}
+	return func;
+}
+
+static struct func *func__find(const char *name)
+{
+	struct rb_node *p = funcs.rb_node;
+	struct func *func;
+	int cmp;
+
+	while (p != NULL) {
+		func = rb_entry(p, struct func, rb_node);
+		cmp = strcmp(func->name, name);
+		if (cmp < 0)
+			p = p->rb_left;
+		else if (cmp > 0)
+			p = p->rb_right;
+		else
+			return func;
+	}
+	return NULL;
+}
+
+static int helper__read(struct helper *helper, const char *base)
+{
+	char path[PATH_MAX];
+	char *line = NULL;
+	size_t len = 0;
+	ssize_t nread;
+	int err = -1;
+	FILE *file;
+
+	snprintf(path, sizeof(path), "%s/%s", base, helper->name);
+
+	file = fopen(path, "r");
+	if (file == NULL) {
+		perror("FAILED fopen whitelist");
+		return -1;
+	}
+
+	while ((nread = getline(&line, &len, file)) != -1) {
+		struct func *func;
+		char *p;
+
+		p = strchr(line, '\n');
+		if (p)
+			*p = 0;
+
+		func = func__add(line);
+		if (!func)
+			goto err;
+
+		list_add_tail(&func->list[helper->idx], &helper->funcs);
+		helper->count++;
+	}
+
+	err = 0;
+err:
+	free(line);
+	fclose(file);
+	return err;
+}
+
+static int comp(const void *a, const void *b)
+{
+	const unsigned long *pa = a;
+	const unsigned long *pb = b;
+
+	return *pa - *pb;
+}
+
+int main(int argc, char **argv)
+{
+	struct helper *helper;
+	const char *wl_path;
+	const char *vmlinux;
+	struct dirent *d;
+	struct btf *btf;
+	int id, err;
+	DIR *dir;
+	__u32 nr;
+
+	if (argc != 3) {
+		fprintf(stderr, "%s <vmlinux> <dir>", argv[0]);
+		return -1;
+	}
+
+	vmlinux = argv[1];
+	wl_path = argv[2];
+
+	dir = opendir(wl_path);
+	if (dir == NULL) {
+		perror("FAILED: open directory");
+		return -1;
+	}
+
+	/*
+	 * Scan the whitelist directory and create 'struct helper'
+	 * object for every file. Read and put all the functions
+	 * into funcs rb tree and link them to each helper.
+	 */
+	while ((d = readdir(dir)) != NULL) {
+		if (!strcmp(d->d_name, ".") ||
+		    !strcmp(d->d_name, ".."))
+			continue;
+
+		helper = helper__new(d->d_name);
+		if (!helper) {
+			fprintf(stderr, "FAILED: not enough memory\n");
+			return -1;
+		}
+
+		if (helper__read(helper, wl_path))
+			return -1;
+	}
+
+	closedir(dir);
+
+	btf = btf__parse_elf(vmlinux, NULL);
+	err = libbpf_get_error(btf);
+	if (err) {
+		fprintf(stderr, "FAILED: load BTF from %s: %s",
+			vmlinux, strerror(err));
+		return -1;
+	}
+
+	nr = btf__get_nr_types(btf);
+
+	/* Iterate all the BTF types and resolve all the function IDs. */
+	for (id = 0; id < nr; id++) {
+		const struct btf_type *type;
+		struct func *func;
+		const char *str;
+
+		type = btf__type_by_id(btf, id);
+		if (!type)
+			continue;
+
+		if (BTF_INFO_KIND(type->info) != BTF_KIND_FUNC)
+			continue;
+
+		str = btf__name_by_offset(btf, type->name_off);
+		if (!str)
+			continue;
+
+		func = func__find(str);
+		if (func)
+			func->id = id;
+	}
+
+	/*
+	 * Load BTF IDs for each helper into array, sort it,
+	 * and dump the C code for the helper array.
+	 */
+	list_for_each_entry(helper, &helpers, node) {
+		unsigned long *ids;
+		bool first = true;
+		struct func *func;
+		int idx = 0;
+
+		ids = malloc(helper->count * sizeof(unsigned long));
+		if (!ids) {
+			fprintf(stderr, "FAILED: not enough memory\n");
+			return -1;
+		}
+
+		list_for_each_entry(func, &helper->funcs, list[helper->idx]) {
+			if (!func->id) {
+				fprintf(stderr, "FAILED: '%s' function not found in BTF data\n",
+					func->name);
+				return -1;
+			}
+
+			ids[idx++] = func->id;
+		}
+
+		qsort(ids, helper->count, sizeof(unsigned long), comp);
+
+		fprintf(stdout,
+			"unsigned long %s[] __attribute__((section(\".BTF_whitelist_%s\"))) = { ",
+			helper->name, helper->name);
+
+		for (idx = 0; idx < helper->count; idx++, first = false)
+			fprintf(stdout, "%s%lu", first ? "" : ", ", ids[idx]);
+
+		fprintf(stdout, " };\n");
+
+		free(ids);
+	}
+
+	/*
+	 * Not releasing anything intentionaly..
+	 *
+	 * btf__free(btf)
+	 * free helpers/funcs
+	 */
+	return 0;
+}
-- 
2.25.4


  parent reply	other threads:[~2020-05-06 13:30 UTC|newest]

Thread overview: 28+ messages / expand[flat|nested]  mbox.gz  Atom feed  top
2020-05-06 13:29 [RFCv2 0/9] bpf: Add d_path helper Jiri Olsa
2020-05-06 13:29 ` [PATCH 1/9] " Jiri Olsa
2020-05-14 22:06   ` Andrii Nakryiko
2020-05-15 14:59     ` Jiri Olsa
2020-05-06 13:29 ` [PATCH 2/9] bpf: Add d_path whitelist Jiri Olsa
2020-05-06 13:29 ` Jiri Olsa [this message]
2020-05-14 22:20   ` [PATCH 3/9] bpf: Add bpfwl tool to construct bpf whitelists Andrii Nakryiko
2020-05-15 14:58     ` Jiri Olsa
2020-05-06 13:29 ` [PATCH 4/9] bpf: Allow nested BTF object to be refferenced by BTF object + offset Jiri Olsa
2020-05-14 22:32   ` Andrii Nakryiko
2020-05-06 13:29 ` [PATCH 5/9] bpf: Add support to check on BTF id whitelist for d_path helper Jiri Olsa
2020-05-06 13:29 ` [PATCH 6/9] bpf: Compile bpfwl tool at kernel compilation start Jiri Olsa
2020-05-14 22:38   ` Andrii Nakryiko
2020-05-15 14:57     ` Jiri Olsa
2020-05-06 13:29 ` [PATCH 7/9] bpf: Compile the BTF id whitelist data in vmlinux Jiri Olsa
2020-05-13 18:29   ` Alexei Starovoitov
2020-05-14  8:05     ` Jiri Olsa
2020-05-14 22:46       ` Andrii Nakryiko
2020-05-15 14:57         ` Jiri Olsa
2020-05-28 17:23         ` Jiri Olsa
2020-05-29 20:48           ` Andrii Nakryiko
2020-05-31 15:10             ` Jiri Olsa
2020-06-01 19:06               ` Andrii Nakryiko
2020-06-02  8:16                 ` Jiri Olsa
2020-05-06 13:29 ` [PATCH 8/9] selftests/bpf: Add test for d_path helper Jiri Olsa
2020-05-14 22:48   ` Andrii Nakryiko
2020-05-15 14:57     ` Jiri Olsa
2020-05-06 13:29 ` [PATCH 9/9] selftests/bpf: Add verifier " Jiri Olsa

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=20200506132946.2164578-4-jolsa@kernel.org \
    --to=jolsa@kernel.org \
    --cc=andriin@fb.com \
    --cc=ast@kernel.org \
    --cc=bgregg@netflix.com \
    --cc=bpf@vger.kernel.org \
    --cc=daniel@iogearbox.net \
    --cc=davem@redhat.com \
    --cc=ethercflow@gmail.com \
    --cc=hawk@kernel.org \
    --cc=john.fastabend@gmail.com \
    --cc=kafai@fb.com \
    --cc=kpsingh@chromium.org \
    --cc=netdev@vger.kernel.org \
    --cc=revest@chromium.org \
    --cc=viro@zeniv.linux.org.uk \
    --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.