From c85a7cba8491ebbdb61f00b042ef8f016d9261c8 Mon Sep 17 00:00:00 2001
From: Luis Gerhorst <privat@luisgerhorst.de>
Date: Fri, 7 May 2021 11:15:14 +0200
Subject: [PATCH] task_ag and task_find boilerplate

---
 src/Makefile        |   2 +-
 src/task_ag.bpf.c   |  20 ++++
 src/task_ag.c       | 270 ++++++++++++++++++++++++++++++++++++++++++++
 src/task_ag.h       |   6 +
 src/task_find.bpf.c |  62 ++++++++++
 src/task_find.c     | 125 ++++++++++++++++++++
 src/task_find.h     |   6 +
 src/task_lib.bpf.h  |   6 +-
 8 files changed, 493 insertions(+), 4 deletions(-)
 create mode 100644 src/task_ag.bpf.c
 create mode 100644 src/task_ag.c
 create mode 100644 src/task_ag.h
 create mode 100644 src/task_find.bpf.c
 create mode 100644 src/task_find.c
 create mode 100644 src/task_find.h

diff --git a/src/Makefile b/src/Makefile
index a46ce5c..a446d76 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
+APPS = minimal bootstrap uprobe task task_bulk task_mmap task_sleep task_bulk_minimal task_ag task_find
 
 # 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_ag.bpf.c b/src/task_ag.bpf.c
new file mode 100644
index 0000000..35b171e
--- /dev/null
+++ b/src/task_ag.bpf.c
@@ -0,0 +1,20 @@
+// SPDX-License-Identifier: GPL-2.0 OR BSD-3-Clause
+/* Copyright (c) 2020 Facebook */
+#include <linux/bpf.h>
+#include <bpf/bpf_helpers.h>
+
+#include "task_lib.bpf.h"
+#include "task_bulk.h"
+
+char LICENSE[] SEC("license") = "Dual BSD/GPL";
+
+size_t nr_syscalls;
+
+SEC("task")
+int v(long **ctx)
+{
+	for (int i = 0; i < SIZE && i < nr_syscalls; i++) {
+		bpf_task_sys_getpid_0();
+	}
+	return 0;
+}
diff --git a/src/task_ag.c b/src/task_ag.c
new file mode 100644
index 0000000..3ecc643
--- /dev/null
+++ b/src/task_ag.c
@@ -0,0 +1,270 @@
+// SPDX-License-Identifier: (LGPL-2.1 OR BSD-2-Clause)
+/* Copyright (c) 2020 Facebook */
+#include <stdio.h>
+#include <unistd.h>
+#include <errno.h>
+#include <pthread.h>
+
+/* man open(2) */
+#include <sys/types.h>
+#include <sys/stat.h>
+#include <fcntl.h>
+
+/* man times(2) */
+#include <sys/times.h>
+
+/* man mmap(2) */
+#include <sys/mman.h>
+
+#include <sys/resource.h>
+#include <bpf/libbpf.h>
+#include "task_bulk_minimal.skel.h"
+#include "task_bulk_minimal.h"
+
+/* BUG: Why is this needed. */
+#ifndef O_TMPFILE
+#define O_TMPFILE (__O_TMPFILE | O_DIRECTORY)
+#define O_TMPFILE_MASK (__O_TMPFILE | O_DIRECTORY | O_CREAT)
+#endif
+
+/* Must match __NR_bpftask in unistd.h of the target kernel. */
+#define SYS_bpftask 442
+
+static int libbpf_print_fn(enum libbpf_print_level level, const char *format, va_list args)
+{
+	return vfprintf(stderr, format, args);
+}
+
+static void bump_memlock_rlimit(void)
+{
+	struct rlimit rlim_new = {
+		.rlim_cur	= RLIM_INFINITY,
+		.rlim_max	= RLIM_INFINITY,
+	};
+
+	if (setrlimit(RLIMIT_MEMLOCK, &rlim_new)) {
+		fprintf(stderr, "Failed to increase RLIMIT_MEMLOCK limit!\n");
+		exit(1);
+	}
+}
+
+static void print_usage(char **argv) {
+	fprintf(stderr, "Usage: %s -v user|bpf -F LITERAL PATH", argv[0]);
+}
+
+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);
+}
+
+static int search_buf(buf, f_len, file_full_path, literal) {
+	fprintf(stderr, "%d: %s", f_len, buf);
+	return EXIT_SUCCESS;
+}
+
+static int search_file(const char *file_full_path, const char *literal) {
+	int err = EXIT_SUCCESS;
+	int fd = -1;
+	off_t f_len = 0;
+	char *buf = NULL;
+	struct stat statbuf;
+	int rv = 0;
+	int matches_count = -1;
+	FILE *fp = NULL;
+
+	rv = stat(file_full_path, &statbuf);
+	if (rv != 0) {
+		log_err("Skipping %s: Error fstat()ing file.", file_full_path);
+		err = EXIT_FAILURE;
+		goto cleanup;
+	}
+
+	// handling only regular files and FIFOs
+	if (!S_ISREG(statbuf.st_mode) && !S_ISFIFO(statbuf.st_mode)) {
+		log_err("Skipping %s: Mode %u is not a file.", file_full_path, statbuf.st_mode);
+		err = EXIT_FAILURE;
+		goto cleanup;
+	}
+
+	fd = open(file_full_path, O_RDONLY);
+	if (fd < 0) {
+		/* XXXX: strerror is not thread-safe */
+		log_err("Skipping %s: Error opening file: %s", file_full_path, strerror(errno));
+		err = EXIT_FAILURE;
+		goto cleanup;
+	}
+
+	// repeating stat check with file handle to prevent TOCTOU issue
+	rv = fstat(fd, &statbuf);
+	if (rv != 0) {
+		log_err("Skipping %s: Error fstat()ing file.", file_full_path);
+		err = EXIT_FAILURE;
+		goto cleanup;
+	}
+
+	// handling only regular files and FIFOs
+	if (!S_ISREG(statbuf.st_mode) && !S_ISFIFO(statbuf.st_mode)) {
+		log_err("Skipping %s: Mode %u is not a file.", file_full_path, statbuf.st_mode);
+		err = EXIT_FAILURE;
+		goto cleanup;
+	}
+
+	if (statbuf.st_mode & S_IFIFO) {
+		log_debug("%s is a named pipe. stream searching", file_full_path);
+		err = EXIT_FAILURE;
+		goto cleanup;
+	}
+
+	f_len = statbuf.st_size;
+
+	buf = mmap(0, f_len, PROT_READ, MAP_PRIVATE, fd, 0);
+	if (buf == MAP_FAILED) {
+		log_err("File %s failed to load: %s.", file_full_path, strerror(errno));
+		err = EXIT_FAILURE;
+		goto cleanup;
+	}
+#if HAVE_MADVISE
+	madvise(buf, f_len, MADV_SEQUENTIAL);
+#elif HAVE_POSIX_FADVISE
+	posix_fadvise(fd, 0, f_len, POSIX_MADV_SEQUENTIAL);
+#endif
+
+	search_buf(buf, f_len, file_full_path, literal);
+
+cleanup:
+	if (buf != NULL) {
+		if (buf != MAP_FAILED) {
+			munmap(buf, f_len);
+		}
+	}
+	if (fd != -1) {
+		close(fd);
+	}
+
+	return err;
+}
+
+int main(int argc, char **argv)
+{
+	bool variant_bpf;
+	bool fixed_strings = false; /* literal */
+	bool debug = false;
+	char *literal;
+	char *path;
+
+	int c;
+	opterr = 0;
+	while ((c = getopt(argc, argv, "v:" "F" "D")) != -1) {
+		switch (c) {
+		case 'v':
+			variant_bpf = strcmp("bpf", optarg) == 0;
+			break;
+		case 'F':
+			fixed_strings = true;
+			break;
+		case 'D':
+			debug = true;
+			break;
+		case '?':
+			if (optopt == 'v') {
+				fprintf(stderr, "Option -%c requires an argument.\n", optopt);
+			} else if (isprint(optopt)) {
+				fprintf(stderr, "Unknown option '-%c'.\n", optopt);
+			} else {
+				fprintf(stderr, "Unknown option character '\\x%x'.\n", optopt);
+			}
+			return EXIT_FAILURE;
+		default:
+			print_usage(argv);
+			return EXIT_FAILURE;
+		}
+	}
+	if (optind != argc - 2 || !fixed_strings) {
+		print_usage(argv);
+		return EXIT_FAILURE;
+	} else {
+		literal = argv[optind];
+		path = argv[optind+1];
+	}
+
+	if (debug) {
+		set_log_level(LOG_LEVEL_DEBUG);
+		fprintf(stderr, "%s: variant_bpf=%d, fixed_strings=%d, debug=%d, literal='%s', path='%s'\n",
+			argv[0], variant_bpf, fixed_strings, debug, literal, path);
+	}
+
+	return search_file(path, literal);
+}
diff --git a/src/task_ag.h b/src/task_ag.h
new file mode 100644
index 0000000..ce6021e
--- /dev/null
+++ b/src/task_ag.h
@@ -0,0 +1,6 @@
+#ifndef __TASK_BULK_H_
+#define __TASK_BULK_H_
+
+#define SIZE 4096
+
+#endif // __TASK_BULK_H_
diff --git a/src/task_find.bpf.c b/src/task_find.bpf.c
new file mode 100644
index 0000000..24b4817
--- /dev/null
+++ b/src/task_find.bpf.c
@@ -0,0 +1,62 @@
+// SPDX-License-Identifier: GPL-2.0 OR BSD-3-Clause
+/* Copyright (c) 2020 Facebook */
+#include <linux/bpf.h>
+#include <bpf/bpf_helpers.h>
+
+#include "task_lib.bpf.h"
+#include "task_find.h"
+
+char LICENSE[] SEC("license") = "Dual BSD/GPL";
+
+#define PATHLEN UBUF_SIZE
+char start_path[PATHLEN];
+uintptr_t ubuf_addr;
+
+SEC("task")
+int handle_tp(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;
+	}
+	bpf_printk("ubuf = %llx:%llx, start_path = %s", ubuf, ubuf + UBUF_SIZE, start_path);
+
+	strncpy(ubuf, start_path, PATHLEN);
+	bpf_printk("ubuf = %s", ubuf);
+
+	int dfd = bpf_task_sys_open_3((uint64_t) ubuf_addr, O_RDONLY | O_DIRECTORY, 0777);
+	if (dfd < 0) {
+		err = dfd;
+		bpf_printk("Error: sys_open = %d", dfd);
+		goto unmap;
+	}
+
+	long nread = bpf_task_sys_getdents64_3(dfd, (uint64_t) ubuf_addr, UBUF_SIZE);
+	size_t recpos = 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);
+
+			bpf_printk("d->d_name = %s, reclen = %d.", d->d_name, (int) d->d_reclen);
+
+			recpos += d->d_reclen;
+		}
+	}
+
+	int close_err = bpf_task_sys_close_1(dfd);
+	if (close_err) {
+		bpf_printk("close_err = %d", close_err);
+	}
+unmap:
+	bpf_task_unmap(ubuf, UBUF_SIZE);
+	return err;
+}
diff --git a/src/task_find.c b/src/task_find.c
new file mode 100644
index 0000000..f6f4402
--- /dev/null
+++ b/src/task_find.c
@@ -0,0 +1,125 @@
+// SPDX-License-Identifier: (LGPL-2.1 OR BSD-2-Clause)
+/* Copyright (c) 2020 Facebook */
+#include <stdio.h>
+#include <unistd.h>
+
+/* man open(2) */
+#include <sys/types.h>
+#include <sys/stat.h>
+#include <fcntl.h>
+
+/* man times(2) */
+#include <sys/times.h>
+
+/* man getopt(3) */
+#include <getopt.h>
+
+/* isprint */
+#include <ctype.h>
+
+#include <sys/resource.h>
+#include <bpf/libbpf.h>
+#include "task_find.skel.h"
+#include "task_find.h"
+
+/* BUG: Why is this needed. */
+#ifndef O_TMPFILE
+#define O_TMPFILE (__O_TMPFILE | O_DIRECTORY)
+#define O_TMPFILE_MASK (__O_TMPFILE | O_DIRECTORY | O_CREAT)
+#endif
+
+/* Must match __NR_bpftask in unistd.h of the target kernel. */
+#define SYS_bpftask 442
+
+static int libbpf_print_fn(enum libbpf_print_level level, const char *format, va_list args)
+{
+	return vfprintf(stderr, format, args);
+}
+
+static void bump_memlock_rlimit(void)
+{
+	struct rlimit rlim_new = {
+		.rlim_cur	= RLIM_INFINITY,
+		.rlim_max	= RLIM_INFINITY,
+	};
+
+	if (setrlimit(RLIMIT_MEMLOCK, &rlim_new)) {
+		fprintf(stderr, "Failed to increase RLIMIT_MEMLOCK limit!\n");
+		exit(1);
+	}
+}
+
+static void print_usage(char **argv) {
+	fprintf(stderr, "Usage: %s [-D --debug] [-v --variant bpf] PATH -name LITERAL", argv[0]);
+}
+
+int main(int argc, char **argv)
+{
+	bool debug = false;
+	bool variant_bpf = true;
+	const char *path;
+	const char *name = NULL;
+
+	static struct option longopts[] = {
+		{"debug", no_argument, 0, 'D' },
+		{"variant", required_argument, 0, 'v' },
+		{0, 0, 0, 0 }
+	};
+
+	int optc;
+	int longindex = 0;
+	opterr = 0;
+	while ((optc = getopt_long(argc, argv, "D" "v:", longopts, &longindex)) != -1) {
+		switch (optc) {
+		case 'D':
+			debug = true;
+			break;
+		case 'v':
+			variant_bpf = strcmp("bpf", optarg) == 0;
+			break;
+		default:
+			fprintf(stderr, "error: getopt returned character code %c ??\n", optc);
+			print_usage(argv);
+			return EXIT_FAILURE;
+		}
+	}
+
+	/* BUG: getoptlong tries to parse path as an option */
+	if (!(optind < argc) || !variant_bpf) {
+		fprintf(stderr, "error: variant_bpf=%d, optind=%d < argc=%d\n",
+			variant_bpf, optind, argc);
+		print_usage(argv);
+		return EXIT_FAILURE;
+	} else {
+		path = argv[optind];
+		optind++;
+	}
+
+	static struct option expression_longopts[] = {
+		{"name", required_argument, 0, 'n' },
+		{0, 0, 0, 0 }
+	};
+	longindex = 0;
+	opterr = 0;
+	while ((optc = getopt_long_only(argc, argv, "", expression_longopts, &longindex)) != -1) {
+		switch (optc) {
+		case 'n':
+			name = optarg;
+			break;
+		default:
+			fprintf(stderr, "?? expression getopt returned character code 0%o ??\n", optc);
+			print_usage(argv);
+			return EXIT_FAILURE;
+		}
+	}
+	if (!(optind == argc-1)) {
+		fprintf(stderr, "Unused positional arguments.\n");
+		print_usage(argv);
+		return EXIT_FAILURE;
+	}
+
+	if (debug) {
+		fprintf(stderr, "%s: variant_bpf=%d, debug=%d, name='%s', path='%s'\n",
+			argv[0], variant_bpf, debug, name, path);
+	}
+}
diff --git a/src/task_find.h b/src/task_find.h
new file mode 100644
index 0000000..3fe592a
--- /dev/null
+++ b/src/task_find.h
@@ -0,0 +1,6 @@
+#ifndef __TASK_H_
+#define __TASK_H_
+
+#define UBUF_SIZE 512
+
+#endif // __TASK_H_
diff --git a/src/task_lib.bpf.h b/src/task_lib.bpf.h
index dac5d38..59d1e96 100644
--- a/src/task_lib.bpf.h
+++ b/src/task_lib.bpf.h
@@ -41,7 +41,7 @@ struct timespec {
 };
 
 static char *strncpy(char *dest, const char *src, size_t n) {
-	for (int i = 0; i < n; i++) {
+	for (size_t i = 0; i < n; i++) {
 		dest[i] = src[i];
 		if (!dest[i]) {
 			break;
@@ -52,11 +52,11 @@ static char *strncpy(char *dest, const char *src, size_t n) {
 
 static void *mmap(void *addr, size_t length, int prot, int flags, int fd, off_t offset) {
 	uint64_t arg_tail[3] = {flags, fd, offset};
-	return bpf_task_sys_mmap_6((uint64_t) addr, length, prot, arg_tail, sizeof(arg_tail));
+	return (void *) bpf_task_sys_mmap_6((uint64_t) addr, length, prot, arg_tail, sizeof(arg_tail));
 }
 
 static int nanosleep(const struct timespec *req, struct timespec *rem) {
-	return bpf_task_sys_nanosleep_2(req, rem);
+	return (int) bpf_task_sys_nanosleep_2(req, rem);
 }
 
 #endif // __TASK_LIBC_BPF_H_
-- 
GitLab