From 1ee399b9e608030101d7338de42923dcca736e7a Mon Sep 17 00:00:00 2001 From: Mattia Meleleo Date: Mon, 2 Oct 2023 17:50:49 +0200 Subject: [PATCH] fill field offset from userspace, fix loop --- GPL/Events/Helpers.h | 3 + GPL/Events/Process/Probe.bpf.c | 42 +++++-------- non-GPL/Events/EventsTrace/EventsTrace.c | 1 - non-GPL/Events/Lib/EbpfEvents.c | 76 ++++++++++++++++++++++++ 4 files changed, 94 insertions(+), 28 deletions(-) diff --git a/GPL/Events/Helpers.h b/GPL/Events/Helpers.h index 42874bf3..9045cd86 100644 --- a/GPL/Events/Helpers.h +++ b/GPL/Events/Helpers.h @@ -117,6 +117,9 @@ const volatile int consumer_pid = 0; #define DECL_FUNC_ARG_EXISTS(func, arg) const volatile bool exists__##func##__##arg##__ = false; #define FUNC_ARG_EXISTS(func, arg) exists__##func##__##arg##__ +#define DECL_FIELD_OFFSET(struct, field) const volatile int off__##struct##__##field##__ = 0; +#define FIELD_OFFSET(struct, field) off__##struct##__##field##__ + // From linux/err.h #define MAX_ERRNO 4095 diff --git a/GPL/Events/Process/Probe.bpf.c b/GPL/Events/Process/Probe.bpf.c index dd5e7861..557c12e8 100644 --- a/GPL/Events/Process/Probe.bpf.c +++ b/GPL/Events/Process/Probe.bpf.c @@ -17,6 +17,9 @@ #include "PathResolver.h" #include "Varlen.h" +/* tty_write */ +DECL_FIELD_OFFSET(iov_iter, __iov); + // Limits on large things we send up as variable length parameters. // // These should be kept _well_ under half the size of the event_buffer_map or @@ -332,22 +335,6 @@ static int output_tty_event(struct ebpf_tty_dev *slave, const void *base, size_t return ret; } -struct iov_iter____new { - union { - struct iovec __ubuf_iovec; - struct { - union { - const struct iovec *__iov; - const struct kvec *kvec; - const struct bio_vec *bvec; - struct xarray *xarray; - void *ubuf; - }; - size_t count; - }; - }; -} __attribute__((preserve_access_index)); - static int tty_write__enter(struct kiocb *iocb, struct iov_iter *from) { if (is_consumer()) { @@ -384,27 +371,28 @@ static int tty_write__enter(struct kiocb *iocb, struct iov_iter *from) goto out; } - u64 nr_segs = BPF_CORE_READ(from, nr_segs); - nr_segs = nr_segs > MAX_NR_SEGS ? MAX_NR_SEGS : nr_segs; - const struct iovec *iov; - if (bpf_core_field_exists(struct iov_iter____new, __iov)) { - u64 iov_off = offsetof(struct iov_iter____new, __iov); - bpf_core_read(&iov, bpf_core_type_size(struct iov_iter), (void *)from + iov_off); - } else if (bpf_core_field_exists(from->iov)) { - iov = BPF_CORE_READ(from, iov); + if (FIELD_OFFSET(iov_iter, __iov)) { + iov = (const struct iovec *)((char *)from + FIELD_OFFSET(iov_iter, __iov)); } else { - bpf_printk("tty_write__enter: error reading iov\n"); - goto out; + iov = BPF_CORE_READ(from, iov); } + u64 nr_segs = BPF_CORE_READ(from, nr_segs); + nr_segs = nr_segs > MAX_NR_SEGS ? MAX_NR_SEGS : nr_segs; + if (nr_segs == 0) { u64 count = BPF_CORE_READ(from, count); (void)output_tty_event(&slave, (void *)iov, count); goto out; } - for (u8 seg = 0; seg < nr_segs; seg++) { + for (int seg = 0; seg < nr_segs; seg++) { + // NOTE(matt): this check needs to be here because the verifier + // detects an infinite loop otherwise. + if (seg >= MAX_NR_SEGS) + goto out; + struct iovec *cur_iov = (struct iovec *)&iov[seg]; const char *base = BPF_CORE_READ(cur_iov, iov_base); size_t len = BPF_CORE_READ(cur_iov, iov_len); diff --git a/non-GPL/Events/EventsTrace/EventsTrace.c b/non-GPL/Events/EventsTrace/EventsTrace.c index e1231a5d..02c5e781 100644 --- a/non-GPL/Events/EventsTrace/EventsTrace.c +++ b/non-GPL/Events/EventsTrace/EventsTrace.c @@ -794,7 +794,6 @@ int main(int argc, char **argv) ebpf_set_verbose_logging(); err = ebpf_event_ctx__new(&ctx, event_ctx_callback, g_events_env); - if (err < 0) { fprintf(stderr, "Could not create event context: %d %s\n", err, strerror(-err)); goto out; diff --git a/non-GPL/Events/Lib/EbpfEvents.c b/non-GPL/Events/Lib/EbpfEvents.c index e3fb8ec8..afdd6bc4 100644 --- a/non-GPL/Events/Lib/EbpfEvents.c +++ b/non-GPL/Events/Lib/EbpfEvents.c @@ -60,6 +60,57 @@ static int ring_buf_cb(void *ctx, void *data, size_t size) return 0; } +/* https://www.kernel.org/doc/html/latest/bpf/btf.html#btf-kind-union */ +static int resolve_btf_field_off_recur(struct btf *btf, + int base_off, + const struct btf_type *t, + const char *field) +{ + struct btf_member *m = btf_members(t); + + for (int i = 0; i < btf_vlen(t); i++, m++) { + const char *name = btf__name_by_offset(btf, m->name_off); + if (name == NULL) + continue; + + if (strcmp(name, field) == 0) + return (base_off + m->offset) / 8; + + const struct btf_type *m_type = btf__type_by_id(btf, m->type); + if (!btf_is_struct(m_type) && !btf_is_union(m_type)) + continue; + + // Recursively traverse structs and unions + int ret = resolve_btf_field_off_recur(btf, base_off + m->offset, m_type, field); + if (ret == -1) + continue; + + return ret; + } + + return -1; +} + +/* Find the BTF field relocation offset for a named field of a kernel struct */ +static int resolve_btf_field_off(struct btf *btf, const char *struct_name, const char *field) +{ + int ret = -1; + + __s32 btf_id = btf__find_by_name_kind(btf, struct_name, BTF_KIND_STRUCT); + if (btf_id <= 0) + goto out; + + const struct btf_type *t = btf__type_by_id(btf, btf_id); + if (t == NULL) + goto out; + if (!btf_is_struct(t)) + goto out; + + return resolve_btf_field_off_recur(btf, 0, t, field); +out: + return ret; +} + const struct btf_type *resolve_btf_type_by_func(struct btf *btf, const char *func) { if (func == NULL) { @@ -176,9 +227,31 @@ static int resolve_btf_func_ret_idx(struct btf *btf, const char *func) __r; \ }) +/* Given a struct name and a field name, returns the field offset + * within the struct. + */ +#define FILL_FIELD_OFFSET(obj, btf, struct, field) \ + ({ \ + int __r = -1; \ + int r = resolve_btf_field_off(btf, #struct, #field); \ + if (r >= 0) \ + __r = 0; \ + obj->rodata->off__##struct##__##field##__ = r; \ + __r; \ + }) + /* Given a function name, returns whether it exists in the provided BTF. */ #define BTF_FUNC_EXISTS(btf, func) ({ (bool)resolve_btf_type_by_func(btf, #func); }) +/* Given a struct name and a field name, returns whether the field exists within the struct. */ +#define BTF_FIELD_EXISTS(btf, struct, field) \ + ({ \ + bool __r = false; \ + if (resolve_btf_field_off(btf, #struct, #field) >= 0) \ + __r = true; \ + __r; \ + }) + /* Fill context relocations for kernel functions * You can add additional functions here by using the macros defined above. * @@ -198,6 +271,9 @@ static int probe_fill_relos(struct btf *btf, struct EventProbe_bpf *obj) } err = err ?: FILL_FUNC_RET_IDX(obj, btf, vfs_rename); + if (BTF_FIELD_EXISTS(btf, iov_iter, __iov)) + err = err ?: FILL_FIELD_OFFSET(obj, btf, iov_iter, __iov); + return err; }