From 36e17d38ddc3211e5f03a2137a0f44daf6c9ca37 Mon Sep 17 00:00:00 2001
From: Luis Gerhorst <privat@luisgerhorst.de>
Date: Fri, 2 Jul 2021 16:03:36 +0200
Subject: [PATCH] task_find: iterdents64, openat PoC

---
 libbpf              |   2 +-
 src/Makefile        |   2 +-
 src/task_find.bpf.c |  98 ++++++++++++++++++---------
 src/task_find.c     | 157 +++++++++++++++++++++++++++++++++++++++++++-
 src/task_find.h     |  11 +++-
 5 files changed, 234 insertions(+), 36 deletions(-)

diff --git a/libbpf b/libbpf
index e81b0f3..a258324 160000
--- a/libbpf
+++ b/libbpf
@@ -1 +1 @@
-Subproject commit e81b0f300b46c53a4a45763d77fa2c01cdbeca94
+Subproject commit a2583241a0cfe897a924acf7c666846f1ec3bda5
diff --git a/src/Makefile b/src/Makefile
index 70f6e4b..f46ca8a 100644
--- a/src/Makefile
+++ b/src/Makefile
@@ -9,7 +9,7 @@ INCLUDES := -I$(OUTPUT) -idirafter $(abspath ../libbpf/include)
 CFLAGS := -g -Wall
 ARCH := $(shell uname -m | sed 's/x86_64/x86/')
 
-APPS = minimal bootstrap uprobe task task_bulk task_mmap task_sleep task_bulk_minimal task_ag task_find task_filter_magic
+APPS = minimal bootstrap uprobe task_mmap task_bulk_minimal task_find task_filter_magic # task_ag task_sleep task task_bulk
 
 # Get Clang's default includes on this system. We'll explicitly add these dirs
 # to the includes list when compiling with `-target bpf` because otherwise some
diff --git a/src/task_find.bpf.c b/src/task_find.bpf.c
index 24b4817..190a303 100644
--- a/src/task_find.bpf.c
+++ b/src/task_find.bpf.c
@@ -8,55 +8,89 @@
 
 char LICENSE[] SEC("license") = "Dual BSD/GPL";
 
-#define PATHLEN UBUF_SIZE
-char start_path[PATHLEN];
-uintptr_t ubuf_addr;
+struct {
+	__uint(type, BPF_MAP_TYPE_ARRAY);
+	__uint(max_entries, 1);
+	__type(key, int);
+	__type(value, int);
+} pdpf_map SEC(".maps");
+
+uintptr_t root_path_user;
 
 SEC("task")
-int handle_tp(void *ctx)
+int entry(void *ctx)
 {
 	int err = 0;
 
-	bpf_printk("ctx = %llx", ctx);
-
-	char *ubuf;
-	ubuf = bpf_task_map(UBUF_SIZE, ubuf_addr);
-	if (ubuf == NULL) {
-		bpf_printk("Error: ubuf is NULL");
-		return -1;
+	char root_path[256];
+	err = bpf_probe_read_user(root_path, 256, root_path_user);
+	if (err) {
+		bpf_printk("Error: read_user = %d", err);
+		goto end;
 	}
-	bpf_printk("ubuf = %llx:%llx, start_path = %s", ubuf, ubuf + UBUF_SIZE, start_path);
-
-	strncpy(ubuf, start_path, PATHLEN);
-	bpf_printk("ubuf = %s", ubuf);
+	bpf_printk("root_path = %s", root_path);
 
-	int dfd = bpf_task_sys_open_3((uint64_t) ubuf_addr, O_RDONLY | O_DIRECTORY, 0777);
+	int dfd = bpf_task_sys_open_3(root_path_user, O_RDONLY | O_DIRECTORY, 0777);
 	if (dfd < 0) {
 		err = dfd;
 		bpf_printk("Error: sys_open = %d", dfd);
-		goto unmap;
+		goto end;
+	}
+
+	int *prog_fd = bpf_map_lookup_elem(&pdpf_map, &PDPF_KEY);
+	if (!prog_fd) {
+		bpf_printk("Error: !bpf_map_lookup_elem(&pdpf_map, &PDPF_KEY)");
+		goto close;
+	}
+
+	err = bpf_task_iterdents64(dfd, *prog_fd);
+	if (err) {
+		bpf_printk("iterdents64(%d, %d) = %d", dfd, *prog_fd, err);
+		goto close;
+	}
+
+close:
+	err = bpf_task_sys_close_1(dfd);
+	if (err) {
+		bpf_printk("close_err = %d", err);
+		goto end;
 	}
+end:
+	return err;
+}
 
-	long nread = bpf_task_sys_getdents64_3(dfd, (uint64_t) ubuf_addr, UBUF_SIZE);
-	size_t recpos = 0;
+SEC("iterdents64")
+int process_dirent(struct bpf_dirent64 *d) {
+	int err = 0;
 
-#pragma clang loop unroll(full)
-	for (size_t bpos = 0; bpos < UBUF_SIZE - sizeof(struct linux_dirent64); bpos++) {
-		if (bpos < nread && bpos == recpos) {
-			/* TODO: Fix verifier error. */
-			struct linux_dirent64 *d = (struct linux_dirent64 *) (ubuf + bpos);
+	if (d->namelen <= 2) {
+		goto end;
+	}
 
-			bpf_printk("d->d_name = %s, reclen = %d.", d->d_name, (int) d->d_reclen);
+	int dfd = bpf_task_openat(d, O_RDONLY | O_DIRECTORY, 0777);
+	if (dfd < 0) {
+		goto end;
+	}
+
+	bpf_printk("iterating %d/%s", d->dfd, d->name);
+
+	int *prog_fd = bpf_map_lookup_elem(&pdpf_map, &PDPF_KEY);
+	if (!prog_fd) {
+		bpf_printk("Error: !bpf_map_lookup_elem(&pdpf_map, &PDPF_KEY)");
+		goto close;
+	}
 
-			recpos += d->d_reclen;
-		}
+	err = bpf_task_iterdents64(dfd, *prog_fd);
+	if (err) {
+		bpf_printk("iterdents64(%d, %d) = %d", dfd, *prog_fd, err);
+		goto close;
 	}
 
-	int close_err = bpf_task_sys_close_1(dfd);
-	if (close_err) {
-		bpf_printk("close_err = %d", close_err);
+close:
+	err = bpf_task_sys_close_1(dfd);
+	if (err) {
+		bpf_printk("close_err = %d", err);
 	}
-unmap:
-	bpf_task_unmap(ubuf, UBUF_SIZE);
+end:
 	return err;
 }
diff --git a/src/task_find.c b/src/task_find.c
index 11e2d60..93e3654 100644
--- a/src/task_find.c
+++ b/src/task_find.c
@@ -2,7 +2,9 @@
 /* Copyright (c) 2020 Facebook */
 #include <stdio.h>
 #include <unistd.h>
+#include <errno.h>
 #include <assert.h>
+#include <pthread.h>
 
 /* man open(2) */
 #include <sys/types.h>
@@ -50,10 +52,152 @@ static void bump_memlock_rlimit(void)
 	}
 }
 
+static pthread_mutex_t print_mtx = PTHREAD_MUTEX_INITIALIZER;
+enum log_level {
+	LOG_LEVEL_DEBUG = 10,
+	LOG_LEVEL_MSG = 20,
+	LOG_LEVEL_WARN = 30,
+	LOG_LEVEL_ERR = 40,
+	LOG_LEVEL_NONE = 100
+};
+static enum log_level log_threshold = LOG_LEVEL_ERR;
+
+static void set_log_level(enum log_level threshold) {
+	log_threshold = threshold;
+}
+
+static void vplog(const unsigned int level, const char *fmt, va_list args) {
+	if (level < log_threshold) {
+		return;
+	}
+
+	pthread_mutex_lock(&print_mtx);
+	FILE *stream = stderr;
+
+	switch (level) {
+	case LOG_LEVEL_DEBUG:
+		fprintf(stream, "DEBUG: ");
+		break;
+	case LOG_LEVEL_MSG:
+		fprintf(stream, "MSG: ");
+		break;
+	case LOG_LEVEL_WARN:
+		fprintf(stream, "WARN: ");
+		break;
+	case LOG_LEVEL_ERR:
+		stream = stderr;
+		fprintf(stream, "ERR: ");
+		break;
+	}
+
+	vfprintf(stream, fmt, args);
+	fprintf(stream, "\n");
+	pthread_mutex_unlock(&print_mtx);
+}
+
+static void log_debug(const char *fmt, ...) {
+	va_list args;
+	va_start(args, fmt);
+	vplog(LOG_LEVEL_DEBUG, fmt, args);
+	va_end(args);
+}
+
+static void log_msg(const char *fmt, ...) {
+	va_list args;
+	va_start(args, fmt);
+	vplog(LOG_LEVEL_MSG, fmt, args);
+	va_end(args);
+}
+
+static void log_warn(const char *fmt, ...) {
+	va_list args;
+	va_start(args, fmt);
+	vplog(LOG_LEVEL_WARN, fmt, args);
+	va_end(args);
+}
+
+static void log_err(const char *fmt, ...) {
+	va_list args;
+	va_start(args, fmt);
+	vplog(LOG_LEVEL_ERR, fmt, args);
+	va_end(args);
+}
+
+static void plog(const unsigned int level, const char *fmt, ...) {
+	va_list args;
+	va_start(args, fmt);
+	vplog(level, fmt, args);
+	va_end(args);
+}
+
+#define NAME task_find
+#define NAME_BPF task_find_bpf
+#define BPF_OPEN task_find_bpf__open
+#define BPF_LOAD task_find_bpf__load
+#define BPF_DESTROY task_find_bpf__destroy
+
+/* https://stackoverflow.com/questions/5873722/c-macro-dynamic-include */
+#define __header(x) #x
+#define _name_skel_h(x) __header(x.skel.h)
+#define name_skel_h(x) _name_skel_h(x)
+#define _name_h(x) __header(x.h)
+#define name_h(x) _name_h(x)
+
+#include name_skel_h(NAME)
+#include name_h(NAME)
+
 static void print_usage(char **argv) {
 	fprintf(stderr, "Usage: %s [-D --debug] [-v --variant bpf] PATH -name LITERAL\n", argv[0]);
 }
 
+static int bpf_find_by_name(const char *path, const char *name, bool debug) {
+	struct NAME_BPF *skel;
+	int err = 0, ret = EXIT_SUCCESS;
+
+	/* Set up libbpf errors and debug info callback */
+	libbpf_set_print(libbpf_print_fn);
+
+	/* Bump RLIMIT_MEMLOCK to allow BPF sub-system to do anything */
+	bump_memlock_rlimit();
+
+	/* Open BPF application */
+	skel = BPF_OPEN();
+	if (!skel) {
+		fprintf(stderr, "Failed to open BPF skeleton\n");
+		return EXIT_FAILURE;
+	}
+
+	skel->bss->root_path_user = path;
+
+	/* Load & verify BPF programs */
+	err = BPF_LOAD(skel);
+	if (err) {
+		fprintf(stderr, "Failed to load and verify BPF skeleton\n");
+		ret = EXIT_FAILURE;
+		goto destroy;
+	}
+
+	/* BPF_NOEXIST means: add new element if it doesn't exist */
+	int value = bpf_program__fd(skel->progs.process_dirent);
+	err = bpf_map_update_elem(bpf_map__fd(skel->maps.pdpf_map), &PDPF_KEY, &value, BPF_ANY);
+	if (err == -1) {
+		perror("bpf_map_update_elem(bpf_map__fd(skel->maps.pdpf_map), &PDPF_KEY, &value, BPF_NOEXIST)");
+		goto destroy;
+	}
+
+	int prog_fd = bpf_program__fd(skel->progs.entry);
+	err = syscall(SYS_bpftask, prog_fd, NULL);
+	if (err) {
+		log_err("sys_bpftaks = %d", err);
+		ret = EXIT_FAILURE;
+		goto destroy;
+	}
+
+destroy:
+	BPF_DESTROY(skel);
+	return ret;
+}
+
 int main(int argc, char **argv)
 {
 	bool debug = false;
@@ -121,12 +265,23 @@ int main(int argc, char **argv)
 	}
 
 	if (debug) {
+		set_log_level(LOG_LEVEL_DEBUG);
+
 		for (size_t i = 0; i < argc; i++) {
-			fprintf(stderr, "%llu: %s, ", i, argv[i]);
+			fprintf(stderr, "%lu: %s, ", i, argv[i]);
 		}
 		fprintf(stderr, "\n");
 
 		fprintf(stderr, "%s: variant_bpf=%d, debug=%d, name='%s', path='%s'\n",
 			argv[0], variant_bpf, debug, name, path);
 	}
+
+	if (variant_bpf) {
+		return bpf_find_by_name(path, name, debug);
+	} else {
+		fprintf(stderr, "Only bpf variant supported.\n");
+		return EXIT_FAILURE;
+	}
+
+	return EXIT_SUCCESS;
 }
diff --git a/src/task_find.h b/src/task_find.h
index 3fe592a..dd9954f 100644
--- a/src/task_find.h
+++ b/src/task_find.h
@@ -1,6 +1,15 @@
 #ifndef __TASK_H_
 #define __TASK_H_
 
-#define UBUF_SIZE 512
+#define MAX_DEPTH 16
+#define UBUF_SIZE 1024
+#define PATHLEN 256
+
+#define __packed __attribute__((__packed__))
+
+struct __packed __attribute__((__aligned__(4096))) bpfmem {
+};
+
+static int PDPF_KEY = 0;
 
 #endif // __TASK_H_
-- 
GitLab