From 063b9cb305be439cd57ee5c7d652ac49d8340425 Mon Sep 17 00:00:00 2001 From: Luis Gerhorst <privat@luisgerhorst.de> Date: Wed, 7 Jul 2021 14:44:33 +0200 Subject: [PATCH] task_find: Print pathnames from user pointer --- libbpf | 2 +- src/task_find.bpf.c | 151 +++++++++++++++++++++++++++++--------------- src/task_find.c | 22 +++++-- src/task_find.h | 8 ++- src/task_lib.bpf.h | 31 +++++++++ 5 files changed, 154 insertions(+), 60 deletions(-) diff --git a/libbpf b/libbpf index e6fdf39..8f83692 160000 --- a/libbpf +++ b/libbpf @@ -1 +1 @@ -Subproject commit e6fdf39f4c704221159e9f45f27a3ec6d56cfd4b +Subproject commit 8f8369223c327743277a8e4fcbf87c5d9d6f3c88 diff --git a/src/task_find.bpf.c b/src/task_find.bpf.c index a0f8244..5f84a91 100644 --- a/src/task_find.bpf.c +++ b/src/task_find.bpf.c @@ -10,49 +10,116 @@ char LICENSE[] SEC("license") = "Dual BSD/GPL"; struct { __uint(type, BPF_MAP_TYPE_ARRAY); - __uint(max_entries, 1); + __uint(max_entries, NR_STATE_MAP_KEYS); __type(key, int); __type(value, int); -} pdpf_map SEC(".maps"); +} state_map SEC(".maps"); -uintptr_t root_path_user; +struct { + __uint(type, BPF_MAP_TYPE_ARRAY); + __uint(max_entries, MAX_DEPTH); + __type(key, int); + __type(value, int); +} dfd_map SEC(".maps"); -SEC("task") -int entry(void *ctx) -{ - int err = 0; +void *u_descend; +void *u_ascend; +void *u_newline; +void *u_iterdents64_buf; +size_t root_namelen; - char root_path[PATHLEN]; - err = bpf_probe_read_user(root_path, PATHLEN, root_path_user); - if (err) { - bpf_printk("Error: read_user = %d", err); +static long iter_dir(size_t namelen) { + long err = 0; + + if (namelen + 1 > PATHLEN) { + bpf_printk("Error: too long name"); + err = -1; + goto end; + } + + int *prog_fd = bpf_map_lookup_elem(&state_map, &PDPF_KEY); + if (!prog_fd) { + bpf_printk("Error: !bpf_map_lookup_elem(&state_map, &PDPF_KEY)"); + goto end; + } + + int *curr_depth_p = bpf_map_lookup_elem(&state_map, &CURR_DEPTH_KEY); + if (!curr_depth_p) { + bpf_printk("Error: bpf_map_lookup_elem(&state_map, &CURR_DEPTH_KEY)"); + goto end; + } + int depth = *curr_depth_p; + if (depth >= MAX_DEPTH) { + bpf_printk("Error: max_depth exceeded"); + err = -1; + goto end; + } + + void *name_user = u_iterdents64_buf + depth * PATHLEN; + + int *parent_dfd = bpf_map_lookup_elem(&dfd_map, &depth); + if (!parent_dfd) { + bpf_printk("Error: bpf_map_lookup_elem(&dfd_map, depth)"); goto end; } - bpf_printk("root_path = %s", root_path); - int dfd = bpf_task_sys_open_3(root_path_user, O_RDONLY | O_DIRECTORY, 0777); + int dfd = openat(*parent_dfd, name_user, O_RDONLY | O_DIRECTORY, 0777); if (dfd < 0) { - err = dfd; - bpf_printk("Error: sys_open = %d", dfd); + bpf_printk("file, depth = %d", depth); + write(STDOUT_FILENO, name_user, namelen); + write(STDOUT_FILENO, u_newline, 1); + /* Not a directory, no need to iterate. */ 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)"); + bpf_printk("descend dir %d, depth = %d", dfd, depth); + write(STDOUT_FILENO, name_user, namelen); + write(STDOUT_FILENO, u_descend, 2); + + int child_depth = depth + 1; + void *child_name_user = u_iterdents64_buf + child_depth * PATHLEN; + + err = bpf_map_update_elem(&state_map, &CURR_DEPTH_KEY, &child_depth, BPF_ANY); + if (err) { + bpf_printk("Error: bpf_map_update_elem(&state_map, &curr_depth_key, &child_depth, bpf_exist)"); goto close; } - err = bpf_task_iterdents64(dfd, *prog_fd); + err = bpf_map_update_elem(&dfd_map, &child_depth, &dfd, BPF_ANY); if (err) { - bpf_printk("iterdents64(%d, %d) = %d", dfd, *prog_fd, err); - goto close; + bpf_printk("Error: bpf_map_update_elem(&dfd_map, &child_depth, &dfd, BPF_ANY)"); + goto reset_depth; } + err = bpf_task_iterdents64(dfd, *prog_fd, child_name_user, PATHLEN); + if (err) { + bpf_printk("Error: iterdents64(%d, %d) = %d", dfd, *prog_fd, err); + goto reset_depth; + } + + bpf_printk("ascend dir %d, depth = %d done", dfd, depth); +reset_depth: + err = bpf_map_update_elem(&state_map, &CURR_DEPTH_KEY, &depth, BPF_ANY); + if (err) { + bpf_printk("bpf_map_update_elem(&state_map, &CURR_DEPTH_KEY, depth, BPF_ANY) = %d", err); + } close: err = bpf_task_sys_close_1(dfd); if (err) { - bpf_printk("close_err = %d", err); + bpf_printk("close(%d) = %d", dfd, err); + } + write(STDOUT_FILENO, u_ascend, 3); +end: + return err; +} + +SEC("task") +int entry(void *ctx) +{ + long err = 0; + + err = iter_dir(root_namelen); + if (err) { goto end; } end: @@ -64,43 +131,23 @@ int process_dirent(struct bpf_dirent64 *d) { int err = 0; /* Skip "." and "..": */ - /* TODO: Why doesn't the verifier get this? */ - if (d->name + 2 > d->name_end) { - goto end; - } - if (d->name[0] == '.' && d->namelen == 1) { - goto end; + if (!(d->name + 2 > (char *) d->name_end)) { + if (d->name[0] == '.' && d->namelen == 1) { + goto end; + } } - if (d->name + 3 > d->name_end) { - goto end; - } - if (d->name[0] == '.' && d->name[1] == '.' && d->namelen == 2) { - goto end; - } - - int dfd = bpf_task_openat(d, O_RDONLY | O_DIRECTORY, 0777); - if (dfd < 0) { - goto end; + if (!(d->name + 3 > (char *) d->name_end)) { + if (d->name[0] == '.' && d->name[1] == '.' && d->namelen == 2) { + 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); + err = iter_dir(d->namelen); if (err) { - bpf_printk("iterdents64(%d, %d) = %d", dfd, *prog_fd, err); - goto close; + goto end; } -close: - err = bpf_task_sys_close_1(dfd); - if (err) { - bpf_printk("close_err = %d", err); - } end: return err; } diff --git a/src/task_find.c b/src/task_find.c index 21512fc..cd44ea4 100644 --- a/src/task_find.c +++ b/src/task_find.c @@ -167,9 +167,15 @@ static int bpf_find_by_name(const char *path, const char *name, bool debug) { return EXIT_FAILURE; } - char root_path_user[PATHLEN]; - strncpy(root_path_user, path, PATHLEN); - skel->bss->root_path_user = root_path_user; + skel->bss->u_newline = "\n"; + skel->bss->u_descend = "/\n"; + skel->bss->u_ascend = "..\n"; + + char iterdents64_buf[MAX_DEPTH * PATHLEN]; + skel->bss->u_iterdents64_buf = iterdents64_buf; + strncpy(iterdents64_buf, path, PATHLEN); + + skel->bss->root_namelen = strlen(path); /* Load & verify BPF programs */ err = BPF_LOAD(skel); @@ -181,9 +187,15 @@ static int bpf_find_by_name(const char *path, const char *name, bool debug) { /* 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); + err = bpf_map_update_elem(bpf_map__fd(skel->maps.state_map), &PDPF_KEY, &value, BPF_ANY); + if (err == -1) { + perror("bpf_map_update_elem(bpf_map__fd(skel->maps.state_map), &PDPF_KEY, &value, BPF_NOEXIST)"); + goto destroy; + } + + int root_key = 0, root_value = AT_FDCWD; + err = bpf_map_update_elem(bpf_map__fd(skel->maps.dfd_map), &root_key, &root_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; } diff --git a/src/task_find.h b/src/task_find.h index dd9954f..5703074 100644 --- a/src/task_find.h +++ b/src/task_find.h @@ -1,15 +1,19 @@ #ifndef __TASK_H_ #define __TASK_H_ -#define MAX_DEPTH 16 +#define MAX_DEPTH 32 #define UBUF_SIZE 1024 -#define PATHLEN 256 +#define PATHLEN 512 #define __packed __attribute__((__packed__)) struct __packed __attribute__((__aligned__(4096))) bpfmem { }; +/* Can not be an enum because we have to pass pointers to the values to + * bpf_map_*() helpers. */ static int PDPF_KEY = 0; +static int CURR_DEPTH_KEY = 1; +#define NR_STATE_MAP_KEYS 2 #endif // __TASK_H_ diff --git a/src/task_lib.bpf.h b/src/task_lib.bpf.h index d39a226..e4e1784 100644 --- a/src/task_lib.bpf.h +++ b/src/task_lib.bpf.h @@ -30,6 +30,33 @@ typedef off64_t off_t; #define STDOUT_FILENO 1 #define STDERR_FILENO 2 +/* + * The constants AT_REMOVEDIR and AT_EACCESS have the same value. AT_EACCESS is + * meaningful only to faccessat, while AT_REMOVEDIR is meaningful only to + * unlinkat. The two functions do completely different things and therefore, + * the flags can be allowed to overlap. For example, passing AT_REMOVEDIR to + * faccessat would be undefined behavior and thus treating it equivalent to + * AT_EACCESS is valid undefined behavior. + */ +#define AT_FDCWD -100 /* Special value used to indicate + openat should use the current + working directory. */ +#define AT_SYMLINK_NOFOLLOW 0x100 /* Do not follow symbolic links. */ +#define AT_EACCESS 0x200 /* Test access permitted for + effective IDs, not real IDs. */ +#define AT_REMOVEDIR 0x200 /* Remove directory instead of + unlinking file. */ +#define AT_SYMLINK_FOLLOW 0x400 /* Follow symbolic links. */ +#define AT_NO_AUTOMOUNT 0x800 /* Suppress terminal automount traversal */ +#define AT_EMPTY_PATH 0x1000 /* Allow empty relative pathname */ + +#define AT_STATX_SYNC_TYPE 0x6000 /* Type of synchronisation required from statx() */ +#define AT_STATX_SYNC_AS_STAT 0x0000 /* - Do whatever stat() does */ +#define AT_STATX_FORCE_SYNC 0x2000 /* - Force the attributes to be sync'd with the server */ +#define AT_STATX_DONT_SYNC 0x4000 /* - Don't sync attributes with the server */ + +#define AT_RECURSIVE 0x8000 /* Apply to the entire subtree */ + /* Values for the WHENCE argument to lseek. */ #define SEEK_SET 0 /* Seek from beginning of file. */ #define SEEK_CUR 1 /* Seek from current position. */ @@ -125,6 +152,10 @@ static int open(uintptr_t pathname, int flags, mode_t mode) { return (int) bpf_task_sys_open_3((uint64_t) pathname, flags, mode); } +static int openat(int dfd, void *pathname, int flags, mode_t mode) { + return (int) bpf_task_sys_openat_4(dfd, (uint64_t) pathname, flags, mode); +} + static ssize_t read(int fd, void *buf, size_t count) { return (ssize_t) bpf_task_sys_read_3(fd, (uint64_t) buf, count); } -- GitLab