From 8cf747ec549185203ab6cdad85c17ff211fd4bd6 Mon Sep 17 00:00:00 2001
From: Luis Gerhorst <privat@luisgerhorst.de>
Date: Thu, 13 May 2021 17:11:26 +0200
Subject: [PATCH] task_find_magic boilerplate

---
 src/Makefile              |   2 +-
 src/task_ag.bpf.c         |  22 ++-
 src/task_ag.c             |   4 +-
 src/task_find_magic.bpf.c |  30 ++++
 src/task_find_magic.c     | 335 ++++++++++++++++++++++++++++++++++++++
 src/task_find_magic.h     |   6 +
 6 files changed, 392 insertions(+), 7 deletions(-)
 create mode 100644 src/task_find_magic.bpf.c
 create mode 100644 src/task_find_magic.c
 create mode 100644 src/task_find_magic.h

diff --git a/src/Makefile b/src/Makefile
index a446d76..8be6c3d 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
+APPS = minimal bootstrap uprobe task task_bulk task_mmap task_sleep task_bulk_minimal task_ag task_find task_find_magic
 
 # 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
index 2817e5c..18807f2 100644
--- a/src/task_ag.bpf.c
+++ b/src/task_ag.bpf.c
@@ -4,15 +4,27 @@
 #include <bpf/bpf_helpers.h>
 
 #include "task_lib.bpf.h"
-#include "task_bulk.h"
+#include "task_ag.h"
 
 char LICENSE[] SEC("license") = "Dual BSD/GPL";
 
 SEC("task")
-int entry(long **ctx)
+int entry(void *ctx)
 {
-	for (int i = 0; i < SIZE; i++) {
-		bpf_task_sys_getpid_0();
+	int err = 0;
+
+	bpf_printk("ctx = %llx", ctx);
+
+	/* BUG: why does this reutrn -1? */
+	char *buf = bpf_task_map(SIZE, ctx);
+	if (buf == NULL) {
+		bpf_printk("Error: buf is NULL");
+		return -1;
 	}
-	return 0;
+
+	err = *buf;
+
+unmap:
+	bpf_task_unmap(buf, SIZE);
+	return err;
 }
diff --git a/src/task_ag.c b/src/task_ag.c
index d649142..c780577 100644
--- a/src/task_ag.c
+++ b/src/task_ag.c
@@ -181,7 +181,9 @@ static int search_buf_bpf(const char *buf, size_t f_len, const char *file_full_p
 	int prog_fd = bpf_program__fd(skel->progs.entry);
 
 	/* TODO: Pass buf, f_len and litral to bpf. */
-	long ret = syscall(SYS_bpftask, prog_fd, NULL);
+	long ret = syscall(SYS_bpftask, prog_fd, buf);
+
+	printf("%d = %d\n", ret, (long) *buf);
 
 destroy:
 	BPF_DESTROY(skel);
diff --git a/src/task_find_magic.bpf.c b/src/task_find_magic.bpf.c
new file mode 100644
index 0000000..18807f2
--- /dev/null
+++ b/src/task_find_magic.bpf.c
@@ -0,0 +1,30 @@
+// 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_ag.h"
+
+char LICENSE[] SEC("license") = "Dual BSD/GPL";
+
+SEC("task")
+int entry(void *ctx)
+{
+	int err = 0;
+
+	bpf_printk("ctx = %llx", ctx);
+
+	/* BUG: why does this reutrn -1? */
+	char *buf = bpf_task_map(SIZE, ctx);
+	if (buf == NULL) {
+		bpf_printk("Error: buf is NULL");
+		return -1;
+	}
+
+	err = *buf;
+
+unmap:
+	bpf_task_unmap(buf, SIZE);
+	return err;
+}
diff --git a/src/task_find_magic.c b/src/task_find_magic.c
new file mode 100644
index 0000000..ff69d61
--- /dev/null
+++ b/src/task_find_magic.c
@@ -0,0 +1,335 @@
+// 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>
+
+#define NAME task_find_magic
+#define NAME_BPF task_find_magic_bpf
+#define BPF_OPEN task_find_magic_bpf__open
+#define BPF_LOAD task_find_magic_bpf__load
+#define BPF_DESTROY task_find_magic_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)
+
+/* 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_user(const char *buf, size_t f_len, const char *file_full_path,
+		      const char *literal) {
+	size_t literal_len = strlen(literal);
+	for (int i = 0; i < f_len - literal_len + 1; i++) {
+		if (!memcmp(buf + i, literal, literal_len))
+			printf("%d\n", i);
+	}
+	return EXIT_SUCCESS;
+}
+
+static int search_buf_bpf(const char *buf, size_t f_len, const char *file_full_path,
+			  const char *literal) {
+	struct NAME_BPF *skel;
+	int err;
+
+	/* 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;
+	}
+
+	/* Load & verify BPF programs */
+	err = BPF_LOAD(skel);
+	if (err) {
+		fprintf(stderr, "Failed to load and verify BPF skeleton\n");
+		goto destroy;
+	}
+
+	int prog_fd = bpf_program__fd(skel->progs.entry);
+
+	/* TODO: Pass buf, f_len and litral to bpf. */
+	long ret = syscall(SYS_bpftask, prog_fd, buf);
+
+	printf("%d = %d\n", ret, (long) *buf);
+
+destroy:
+	BPF_DESTROY(skel);
+	return EXIT_SUCCESS;
+}
+
+static int search_buf(const char *buf, size_t f_len, const char *file_full_path,
+		      const char *literal, bool variant_bpf) {
+	if (variant_bpf) {
+		return search_buf_bpf(buf, f_len, file_full_path, literal);
+	} else {
+		return search_buf_user(buf, f_len, file_full_path, literal);
+	}
+}
+
+static int search_file(const char *file_full_path, const char *literal, bool variant_bpf) {
+	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, variant_bpf);
+
+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 = true;
+	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, variant_bpf);
+}
diff --git a/src/task_find_magic.h b/src/task_find_magic.h
new file mode 100644
index 0000000..ce6021e
--- /dev/null
+++ b/src/task_find_magic.h
@@ -0,0 +1,6 @@
+#ifndef __TASK_BULK_H_
+#define __TASK_BULK_H_
+
+#define SIZE 4096
+
+#endif // __TASK_BULK_H_
-- 
GitLab