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