From 4d4003bde88b7c8d960ac0b9203ddf922c0dc6b8 Mon Sep 17 00:00:00 2001 From: Nicholas Berlin Date: Sun, 29 Jun 2025 21:45:57 -0400 Subject: [PATCH 1/3] Probe fsnotify_create On RHEL kernels where ebpf has been backported but FMODE_CREATED does not yet exist, use fsnotify_create as an indication of file creation. This requires a new probe to set a thread local map entry, which the current do_filp_open probe can then test for. --- elastic-ebpf/GPL/Events/File/Probe.bpf.c | 39 +++++++++++++++++++++++- elastic-ebpf/GPL/Events/State.h | 2 ++ 2 files changed, 40 insertions(+), 1 deletion(-) diff --git a/elastic-ebpf/GPL/Events/File/Probe.bpf.c b/elastic-ebpf/GPL/Events/File/Probe.bpf.c index bca6ef78..6e1647c7 100644 --- a/elastic-ebpf/GPL/Events/File/Probe.bpf.c +++ b/elastic-ebpf/GPL/Events/File/Probe.bpf.c @@ -286,7 +286,8 @@ static int do_filp_open__exit(struct file *f) goto out; fmode_t fmode = BPF_CORE_READ(f, f_mode); - if (fmode & (fmode_t)0x100000) { // FMODE_CREATED + if ((fmode & (fmode_t)0x100000) || // FMODE_CREATED + (ebpf_events_state__get(EBPF_EVENTS_STATE_FS_CREATE) != NULL)) { // 4.18.x // generate a file creation event prepare_and_send_file_event(f, EBPF_EVENT_FILE_CREATE, NULL, 0); } else { @@ -330,9 +331,45 @@ static int do_filp_open__exit(struct file *f) } out: + ebpf_events_state__del(EBPF_EVENTS_STATE_FS_CREATE); + + return 0; +} + +static int fsnotify__enter(u32 mask) +{ + if (mask & 0x100) { // FS_CREATE + struct ebpf_events_state state = {}; + ebpf_events_state__set(EBPF_EVENTS_STATE_FS_CREATE, &state); + } + return 0; } +SEC("kprobe/fsnotify") +int BPF_KPROBE(kprobe__fsnotify, + struct inode *to_tell, + u32 mask, + const void *data, + int data_is, + const unsigned char *file_name, + u32 cookie) +{ + return fsnotify__enter(mask); +} + +SEC("fentry/fsnotify") +int BPF_PROG(fentry__fsnotify, + struct inode *to_tell, + u32 mask, + const void *data, + int data_is, + const unsigned char *file_name, + u32 cookie) +{ + return fsnotify__enter(mask); +} + SEC("fexit/do_filp_open") int BPF_PROG(fexit__do_filp_open, int dfd, diff --git a/elastic-ebpf/GPL/Events/State.h b/elastic-ebpf/GPL/Events/State.h index 45e6c505..c3ae0e5c 100644 --- a/elastic-ebpf/GPL/Events/State.h +++ b/elastic-ebpf/GPL/Events/State.h @@ -22,6 +22,7 @@ enum ebpf_events_state_op { EBPF_EVENTS_STATE_WRITEV = 8, EBPF_EVENTS_STATE_CHOWN = 9, EBPF_EVENTS_STATE_GROUP_DEAD = 10, + EBPF_EVENTS_STATE_FS_CREATE = 11, }; struct ebpf_events_key { @@ -93,6 +94,7 @@ struct ebpf_events_state { struct ebpf_events_writev_state writev; struct ebpf_events_chown_state chown; /* struct ebpf_events_group_dead group_dead; nada */ + /* struct ebpf_events_fs_create fs_create; nada */ }; }; From 291f95162f9d2289cdb6196dbdbe9ddf730c02c3 Mon Sep 17 00:00:00 2001 From: Nicholas Berlin Date: Sun, 29 Jun 2025 21:45:00 -0400 Subject: [PATCH 2/3] Add new func to count op func params Since the fsnotify probe can be noisy and the parameters have changed over time, only enable that probe when necessary. Ideally, we would test for the existence of FMODE_CREATED, but as far as I'm aware, there's not a way to detect defines. There's another commit (https://github.com/torvalds/linux/commit/44907d79002466049fdbb8ef15730d185e0808b4) related to the addition of FMODE_CREATED which is detectable by testing the parameter count to the atomic_open function pointer within struct inode_operations. This commit adds a function to detect the commit using btf, and conditionally adds the fsnotify probe. --- bpf_queue.c | 7 +++++++ btf.c | 43 ++++++++++++++++++++++++++++++++++++++++--- quark.h | 1 + 3 files changed, 48 insertions(+), 3 deletions(-) diff --git a/bpf_queue.c b/bpf_queue.c index c934c674..05788c53 100644 --- a/bpf_queue.c +++ b/bpf_queue.c @@ -667,9 +667,14 @@ bpf_queue_open1(struct quark_queue *qq, int use_fentry) } if (qq->flags & QQ_FILE) { + int use_fsnotify = + (btf_number_of_params_of_ptr(btf, "inode_operations", "atomic_open") == 6); + if (use_fentry) { bpf_program__set_autoload(p->progs.fentry__do_renameat2, 1); bpf_program__set_autoload(p->progs.fentry__do_unlinkat, 1); + if (use_fsnotify) + bpf_program__set_autoload(p->progs.fentry__fsnotify, 1); bpf_program__set_autoload(p->progs.fentry__mnt_want_write, 1); bpf_program__set_autoload(p->progs.fentry__vfs_rename, 1); bpf_program__set_autoload(p->progs.fentry__vfs_unlink, 1); @@ -688,6 +693,8 @@ bpf_queue_open1(struct quark_queue *qq, int use_fentry) bpf_program__set_autoload(p->progs.kretprobe__chown_common, 1); bpf_program__set_autoload(p->progs.kprobe__do_truncate, 1); bpf_program__set_autoload(p->progs.kretprobe__do_truncate, 1); + if (use_fsnotify) + bpf_program__set_autoload(p->progs.kprobe__fsnotify, 1); bpf_program__set_autoload(p->progs.kprobe__vfs_writev, 1); bpf_program__set_autoload(p->progs.kretprobe__vfs_writev, 1); bpf_program__set_autoload(p->progs.kprobe__vfs_rename, 1); diff --git a/btf.c b/btf.c index b75d1185..6d0bf36f 100644 --- a/btf.c +++ b/btf.c @@ -186,13 +186,29 @@ btf_offsetof(struct btf *btf, const char *parent_name, const char *member_name, if (parent_t == NULL) parent_t = btf_type_by_name_kind(btf, NULL, parent_name, BTF_KIND_UNION); - if (parent_t == NULL) + if (parent_t == NULL) { + if (ret_member != NULL) + *ret_member = NULL; return (-1); + } return (btf_offsetof_rec(btf, parent_t, member_name, ret_member, 0)); } +/* + * Given a struct or union parent_name, find the btf_member{} of member_name. + */ +static struct btf_member * +btf_find_member(struct btf *btf, const char *parent_name, const char *member_name) +{ + struct btf_member *member = NULL; + + btf_offsetof(btf, parent_name, member_name, &member); + + return (member); +} + static s32 btf_root_offset2(struct btf *btf, const char *dotname) { @@ -292,11 +308,32 @@ btf_enum_value(struct btf *btf, const char *dotname, ssize_t *uv) return (-1); } +int +btf_number_of_params_of_ptr(struct btf *btf, const char *parent_name, const char *name) +{ + struct btf_member *m; + const struct btf_type *t; + + m = btf_find_member(btf, parent_name, name); + if (m == NULL) + return (-1); + t = btf__type_by_id(btf, m->type); + if (t == NULL) + return (-1); + t = btf__type_by_id(btf, t->type); + if (t == NULL) + return (-1); + if (!btf_is_func_proto(t)) + return (-1); + + return (btf_vlen(t)); +} + int btf_number_of_params(struct btf *btf, const char *func) { - s32 off; - const struct btf_type *t; + const struct btf_type *t; + s32 off; off = btf__find_by_name_kind(btf, func, BTF_KIND_FUNC); if (off < 0) diff --git a/quark.h b/quark.h index 838df0e8..0d5b1fcb 100644 --- a/quark.h +++ b/quark.h @@ -80,6 +80,7 @@ ssize_t quark_btf_offset(struct quark_btf *, const char *); struct btf; s32 btf_root_offset(struct btf *, const char *, int); int btf_number_of_params(struct btf *, const char *); +int btf_number_of_params_of_ptr(struct btf *, const char *, const char *); int btf_index_of_param(struct btf *, const char *, const char *); /* bpf_queue.c */ From 5835923ed5ef8afe86ddbdab3768ff97f3eb0e6a Mon Sep 17 00:00:00 2001 From: Nicholas Berlin Date: Sun, 29 Jun 2025 21:46:54 -0400 Subject: [PATCH 3/3] Re-enable file creation tests in CI --- .buildkite/pipeline.yml | 12 ++++++------ 1 file changed, 6 insertions(+), 6 deletions(-) diff --git a/.buildkite/pipeline.yml b/.buildkite/pipeline.yml index 1e150129..3af8158c 100644 --- a/.buildkite/pipeline.yml +++ b/.buildkite/pipeline.yml @@ -220,9 +220,9 @@ steps: machineType: n2-standard-2 enableNestedVirtualization: true - - label: "quark-test on rhel 8 (file creation broken)" + - label: "quark-test on rhel 8" key: test_rhel_8 - command: "./.buildkite/runtest_distro.sh rhel 8 -x t_file" + command: "./.buildkite/runtest_distro.sh rhel 8" depends_on: - make_docker agents: @@ -286,9 +286,9 @@ steps: machineType: n2-standard-2 enableNestedVirtualization: true - - label: "quark-test on rhel 8.8 (file creation broken)" + - label: "quark-test on rhel 8.8" key: test_rhel_8_8 - command: "./.buildkite/runtest_distro.sh rhel 8.8 -x t_file" + command: "./.buildkite/runtest_distro.sh rhel 8.8" depends_on: - make_docker agents: @@ -297,9 +297,9 @@ steps: machineType: n2-standard-2 enableNestedVirtualization: true - - label: "quark-test on rhel 8.9 (file creation broken)" + - label: "quark-test on rhel 8.9" key: test_rhel_8_9 - command: "./.buildkite/runtest_distro.sh rhel 8.9 -x t_file" + command: "./.buildkite/runtest_distro.sh rhel 8.9" depends_on: - make_docker agents: