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