From fc3ae8fa94cbf48d8234b163f166afc9c44ca33b Mon Sep 17 00:00:00 2001 From: Mauro Ezequiel Moltrasio Date: Tue, 25 Aug 2026 15:12:14 +0200 Subject: [PATCH 1/3] feat: use bpf_path_d_path for reading paths This is a safer alternative to the bpf_d_path helper that enforces the use of KF_TRUSTED_ARGS semantics, meaning we need to use proper RCU and refcounting to prevent the underlying memory being walked from disappearing from underneath our feet. --- fact-ebpf/src/bpf/d_path.h | 4 +++- fact-ebpf/src/bpf/main.c | 12 ++++++++++-- fact-ebpf/src/bpf/process.h | 31 +++++++++++++++++++++++++------ 3 files changed, 38 insertions(+), 9 deletions(-) diff --git a/fact-ebpf/src/bpf/d_path.h b/fact-ebpf/src/bpf/d_path.h index a922600e..f563215d 100644 --- a/fact-ebpf/src/bpf/d_path.h +++ b/fact-ebpf/src/bpf/d_path.h @@ -141,7 +141,9 @@ __always_inline static long __d_path(const struct path* path, char* buf, int buf } __always_inline static long d_path(struct path* path, char* buf, int buflen, bool use_bpf_helper) { - if (use_bpf_helper) { + if (bpf_ksym_exists(bpf_path_d_path)) { + return bpf_path_d_path(path, buf, buflen); + } else if (use_bpf_helper) { return bpf_d_path(path, buf, buflen); } return __d_path(path, buf, buflen); diff --git a/fact-ebpf/src/bpf/main.c b/fact-ebpf/src/bpf/main.c index 3477ebcc..77cd50e7 100644 --- a/fact-ebpf/src/bpf/main.c +++ b/fact-ebpf/src/bpf/main.c @@ -1,4 +1,5 @@ // clang-format off +#include "d_path.h" #include "vmlinux.h" #include "file.h" @@ -552,9 +553,16 @@ FACT_BPF_PROG2(sb_umount, struct vfsmount*, mnt, int, flags) { struct submit_event_args_t args = {.metrics = &m->sb_umount}; args.metrics->total++; - struct path p = {.dentry = BPF_CORE_READ(mnt, mnt_root), .mnt = mnt}; - struct bound_path_t* bound_path = _path_read(&p, BOUND_PATH_MAIN, false); + // TODO: Figure out a better way to read the path with bpf_path_d_path. + struct bound_path_t* bound_path = get_bound_path(BOUND_PATH_MAIN); if (bound_path == NULL) { + bpf_printk("Failed to get bound_path buffer"); + args.metrics->error++; + return 0; + } + + struct path p = {.dentry = BPF_CORE_READ(mnt, mnt_root), .mnt = mnt}; + if (__d_path(&p, bound_path->path, PATH_MAX) <= 0) { bpf_printk("Failed to read umount directory"); args.metrics->error++; return 0; diff --git a/fact-ebpf/src/bpf/process.h b/fact-ebpf/src/bpf/process.h index 5061ee38..3822cc3d 100644 --- a/fact-ebpf/src/bpf/process.h +++ b/fact-ebpf/src/bpf/process.h @@ -78,22 +78,37 @@ __always_inline static const char* get_memory_cgroup(struct helper_t* helper) { } __always_inline static void process_fill_lineage(process_t* p, struct helper_t* helper, bool use_bpf_d_path) { - struct task_struct* task = (struct task_struct*)bpf_get_current_task_btf(); + struct task_struct* task = bpf_task_acquire(bpf_get_current_task_btf()); + if (task == NULL) { + return; + } p->lineage_len = 0; + bpf_rcu_read_lock(); for (int i = 0; i < LINEAGE_MAX; i++) { - struct task_struct* parent = task->real_parent; + struct task_struct* parent = bpf_task_acquire(task->real_parent); - if (task == parent || parent->pid == 0) { - return; + if (parent == NULL) { + break; + } else if (task == parent || parent->pid == 0) { + bpf_task_release(parent); + break; } + + bpf_task_release(task); task = parent; p->lineage[i].uid = task->cred->uid.val; - d_path(&task->mm->exe_file->f_path, p->lineage[i].exe_path, PATH_MAX, use_bpf_d_path); + struct file* exe_file = bpf_get_task_exe_file(task); + if (exe_file != NULL) { + d_path(&exe_file->f_path, p->lineage[i].exe_path, PATH_MAX, use_bpf_d_path); + bpf_put_file(exe_file); + } p->lineage_len++; } + bpf_rcu_read_unlock(); + bpf_task_release(task); } __always_inline static unsigned long get_mount_ns() { @@ -131,7 +146,11 @@ __always_inline static int64_t process_fill(process_t* p, bool use_bpf_d_path) { return -1; } - d_path(&task->mm->exe_file->f_path, p->exe_path, PATH_MAX, use_bpf_d_path); + struct file* exe_file = bpf_get_task_exe_file(task); + if (exe_file != NULL) { + d_path(&exe_file->f_path, p->exe_path, PATH_MAX, use_bpf_d_path); + bpf_put_file(exe_file); + } const char* cg = get_memory_cgroup(helper); if (cg != NULL) { From 3dd7c707b5a4652a85bcbc3b8246347300e47139 Mon Sep 17 00:00:00 2001 From: Mauro Ezequiel Moltrasio Date: Tue, 25 Aug 2026 16:27:18 +0200 Subject: [PATCH 2/3] fix: avoid bpf_get_task_exe_file on unsupported kernels --- fact-ebpf/src/bpf/process.h | 29 ++++++++++++++++++----------- 1 file changed, 18 insertions(+), 11 deletions(-) diff --git a/fact-ebpf/src/bpf/process.h b/fact-ebpf/src/bpf/process.h index 3822cc3d..4ae7af9a 100644 --- a/fact-ebpf/src/bpf/process.h +++ b/fact-ebpf/src/bpf/process.h @@ -77,6 +77,22 @@ __always_inline static const char* get_memory_cgroup(struct helper_t* helper) { return helper->buf; } +__always_inline static long read_exe_file(struct task_struct* task, char buf[PATH_MAX], bool use_bpf_d_path) { + if (bpf_ksym_exists(bpf_get_task_exe_file)) { + long res = -1; + struct file* exe_file = bpf_get_task_exe_file(task); + if (exe_file != NULL) { + res = d_path(&exe_file->f_path, buf, PATH_MAX, use_bpf_d_path); + bpf_put_file(exe_file); + } + return res; + } else if (use_bpf_d_path) { + return bpf_d_path(&task->mm->exe_file->f_path, buf, PATH_MAX); + } else { + return __d_path(&task->mm->exe_file->f_path, buf, PATH_MAX); + } +} + __always_inline static void process_fill_lineage(process_t* p, struct helper_t* helper, bool use_bpf_d_path) { struct task_struct* task = bpf_task_acquire(bpf_get_current_task_btf()); if (task == NULL) { @@ -99,12 +115,7 @@ __always_inline static void process_fill_lineage(process_t* p, struct helper_t* task = parent; p->lineage[i].uid = task->cred->uid.val; - - struct file* exe_file = bpf_get_task_exe_file(task); - if (exe_file != NULL) { - d_path(&exe_file->f_path, p->lineage[i].exe_path, PATH_MAX, use_bpf_d_path); - bpf_put_file(exe_file); - } + read_exe_file(task, p->lineage[i].exe_path, use_bpf_d_path); p->lineage_len++; } bpf_rcu_read_unlock(); @@ -146,11 +157,7 @@ __always_inline static int64_t process_fill(process_t* p, bool use_bpf_d_path) { return -1; } - struct file* exe_file = bpf_get_task_exe_file(task); - if (exe_file != NULL) { - d_path(&exe_file->f_path, p->exe_path, PATH_MAX, use_bpf_d_path); - bpf_put_file(exe_file); - } + read_exe_file(task, p->exe_path, use_bpf_d_path); const char* cg = get_memory_cgroup(helper); if (cg != NULL) { From fce4d892b4cc90db1531eadaa19995d4101ac52a Mon Sep 17 00:00:00 2001 From: Mauro Ezequiel Moltrasio Date: Tue, 25 Aug 2026 17:58:08 +0200 Subject: [PATCH 3/3] fix: tighter RCU region --- fact-ebpf/src/bpf/process.h | 4 ++-- 1 file changed, 2 insertions(+), 2 deletions(-) diff --git a/fact-ebpf/src/bpf/process.h b/fact-ebpf/src/bpf/process.h index 4ae7af9a..dcd04992 100644 --- a/fact-ebpf/src/bpf/process.h +++ b/fact-ebpf/src/bpf/process.h @@ -100,9 +100,10 @@ __always_inline static void process_fill_lineage(process_t* p, struct helper_t* } p->lineage_len = 0; - bpf_rcu_read_lock(); for (int i = 0; i < LINEAGE_MAX; i++) { + bpf_rcu_read_lock(); struct task_struct* parent = bpf_task_acquire(task->real_parent); + bpf_rcu_read_unlock(); if (parent == NULL) { break; @@ -118,7 +119,6 @@ __always_inline static void process_fill_lineage(process_t* p, struct helper_t* read_exe_file(task, p->lineage[i].exe_path, use_bpf_d_path); p->lineage_len++; } - bpf_rcu_read_unlock(); bpf_task_release(task); }