From e09ebcd1a57e5b48386800fb43966528c626e5bd Mon Sep 17 00:00:00 2001 From: Mauro Ezequiel Moltrasio Date: Tue, 25 Aug 2026 10:10:55 +0200 Subject: [PATCH 1/3] fix(bpf): disable preemption during hook processing This prevents the hooks from being preempted and replaced by another hook that might overwrite the contents of the shared per-CPU maps before sending the event to the ringbuffer. A similar issue was found to be corrupting events in Falco and is addressed in this PR of our fork, see the discussion there for more details: https://github.com/stackrox/falcosecurity-libs/pull/98 In order to call `bpf_preempt_enable/disable` aya needs to support ksyms, which is currently only supported on main, therefore we pin the dependency to a commit known to work. We will unpin this once a new aya release is created. --- Cargo.lock | 6 +- Cargo.toml | 2 +- fact-ebpf/src/bpf/main.c | 132 ++++++++++++++++++++++++++++----------- fact/src/bpf/mod.rs | 2 +- fact/src/host_scanner.rs | 2 +- 5 files changed, 100 insertions(+), 44 deletions(-) diff --git a/Cargo.lock b/Cargo.lock index 0659775d..b9400130 100644 --- a/Cargo.lock +++ b/Cargo.lock @@ -148,8 +148,7 @@ dependencies = [ [[package]] name = "aya" version = "0.14.0" -source = "registry+https://github.com/rust-lang/crates.io-index" -checksum = "66e644424fada9fff4fdc63848db1732fb69b626e8328202ef55c03df1f4d939" +source = "git+https://github.com/aya-rs/aya.git?rev=c29cd71cb4fe1440bc0d566633afa822f1b41fc5#c29cd71cb4fe1440bc0d566633afa822f1b41fc5" dependencies = [ "assert_matches", "aya-obj", @@ -166,8 +165,7 @@ dependencies = [ [[package]] name = "aya-obj" version = "0.3.0" -source = "registry+https://github.com/rust-lang/crates.io-index" -checksum = "8c76b9c75d9cdc155ff8f6a06d61e873f67bf47be8cfa92a3b5aaea43f4b4077" +source = "git+https://github.com/aya-rs/aya.git?rev=c29cd71cb4fe1440bc0d566633afa822f1b41fc5#c29cd71cb4fe1440bc0d566633afa822f1b41fc5" dependencies = [ "bytes", "log", diff --git a/Cargo.toml b/Cargo.toml index f59a4de9..27a4bf87 100644 --- a/Cargo.toml +++ b/Cargo.toml @@ -11,7 +11,7 @@ default-members = ["fact"] license = "MIT OR Apache-2.0" [workspace.dependencies] -aya = { version = "0.14.0", default-features = false } +aya = { git = "https://github.com/aya-rs/aya.git", rev = "c29cd71cb4fe1440bc0d566633afa822f1b41fc5", default-features = false } anyhow = { version = "1", default-features = false, features = ["std", "backtrace"] } clap = { version = "4.5.41", features = ["derive", "env"] } diff --git a/fact-ebpf/src/bpf/main.c b/fact-ebpf/src/bpf/main.c index 94471cdd..2efd73b9 100644 --- a/fact-ebpf/src/bpf/main.c +++ b/fact-ebpf/src/bpf/main.c @@ -21,9 +21,10 @@ char _license[] SEC("license") = "Dual MIT/GPL"; SEC("lsm/file_open") int BPF_PROG(trace_file_open, struct file* file) { + bpf_preempt_disable(); struct metrics_t* m = get_metrics(); if (m == NULL) { - return 0; + goto end; } struct submit_event_args_t args = {.metrics = &m->file_open}; @@ -58,7 +59,7 @@ int BPF_PROG(trace_file_open, struct file* file) { if (path == NULL) { bpf_printk("Failed to read path"); m->file_open.error++; - return 0; + goto end; } args.filename = path->path; @@ -79,18 +80,22 @@ int BPF_PROG(trace_file_open, struct file* file) { submit_open_event(&args, event_type); +end: + bpf_preempt_enable(); return 0; ignored: m->file_open.ignored++; + bpf_preempt_enable(); return 0; } SEC("lsm/path_unlink") int BPF_PROG(trace_path_unlink, struct path* dir, struct dentry* dentry) { + bpf_preempt_disable(); struct metrics_t* m = get_metrics(); if (m == NULL) { - return 0; + goto end; } struct submit_event_args_t args = {.metrics = &m->path_unlink}; @@ -100,7 +105,7 @@ int BPF_PROG(trace_path_unlink, struct path* dir, struct dentry* dentry) { if (path == NULL) { bpf_printk("Failed to read path"); m->path_unlink.error++; - return 0; + goto end; } args.filename = path->path; @@ -109,21 +114,25 @@ int BPF_PROG(trace_path_unlink, struct path* dir, struct dentry* dentry) { if (args.monitored == NOT_MONITORED) { m->path_unlink.ignored++; - return 0; + goto end; } // We only support files with one link for now inode_remove(&args.inode); submit_unlink_event(&args); + +end: + bpf_preempt_enable(); return 0; } SEC("lsm/path_chmod") int BPF_PROG(trace_path_chmod, struct path* path, umode_t mode) { + bpf_preempt_disable(); struct metrics_t* m = get_metrics(); if (m == NULL) { - return 0; + goto end; } struct submit_event_args_t args = {.metrics = &m->path_chmod}; @@ -133,7 +142,7 @@ int BPF_PROG(trace_path_chmod, struct path* path, umode_t mode) { if (bound_path == NULL) { bpf_printk("Failed to read path"); args.metrics->error++; - return 0; + goto end; } args.filename = bound_path->path; @@ -142,12 +151,14 @@ int BPF_PROG(trace_path_chmod, struct path* path, umode_t mode) { if (args.monitored == NOT_MONITORED) { args.metrics->ignored++; - return 0; + goto end; } umode_t old_mode = BPF_CORE_READ(path, dentry, d_inode, i_mode); submit_mode_event(&args, mode, old_mode); +end: + bpf_preempt_enable(); return 0; } @@ -156,9 +167,10 @@ int BPF_PROG(trace_path_chmod, struct path* path, umode_t mode) { size of the BPF registers (64 bits) to simplify further arithmetic operations. */ SEC("lsm/path_chown") int BPF_PROG(trace_path_chown, struct path* path, unsigned long long uid, unsigned long long gid) { + bpf_preempt_disable(); struct metrics_t* m = get_metrics(); if (m == NULL) { - return 0; + goto end; } struct submit_event_args_t args = {.metrics = &m->path_chown}; @@ -168,7 +180,7 @@ int BPF_PROG(trace_path_chown, struct path* path, unsigned long long uid, unsign if (bound_path == NULL) { bpf_printk("Failed to read path"); args.metrics->error++; - return 0; + goto end; } args.filename = bound_path->path; @@ -177,7 +189,7 @@ int BPF_PROG(trace_path_chown, struct path* path, unsigned long long uid, unsign if (args.monitored == NOT_MONITORED) { args.metrics->ignored++; - return 0; + goto end; } struct dentry* d = BPF_CORE_READ(path, dentry); @@ -186,6 +198,8 @@ int BPF_PROG(trace_path_chown, struct path* path, unsigned long long uid, unsign submit_ownership_event(&args, uid, gid, old_uid, old_gid); +end: + bpf_preempt_enable(); return 0; } @@ -193,9 +207,10 @@ SEC("lsm/path_rename") int BPF_PROG(trace_path_rename, struct path* old_dir, struct dentry* old_dentry, struct path* new_dir, struct dentry* new_dentry, unsigned int flags) { + bpf_preempt_disable(); struct metrics_t* m = get_metrics(); if (m == NULL) { - return 0; + goto end; } struct submit_event_args_t args = {.metrics = &m->path_rename}; @@ -231,7 +246,7 @@ int BPF_PROG(trace_path_rename, struct path* old_dir, case NOT_MONITORED: if (old_monitored == NOT_MONITORED) { m->path_rename.ignored++; - return 0; + goto end; } if (old_monitored == MONITORED_BY_INODE) { @@ -284,18 +299,23 @@ int BPF_PROG(trace_path_rename, struct path* old_dir, } submit_rename_event(&args, old_path->path, &old_inode, old_monitored); - return 0; + + goto end; error: args.metrics->error++; + +end: + bpf_preempt_enable(); return 0; } SEC("lsm/path_mkdir") int BPF_PROG(trace_path_mkdir, struct path* dir, struct dentry* dentry, umode_t mode) { + bpf_preempt_disable(); struct metrics_t* m = get_metrics(); if (m == NULL) { - return 0; + goto end; } m->path_mkdir.total++; @@ -318,23 +338,27 @@ int BPF_PROG(trace_path_mkdir, struct path* dir, struct dentry* dentry, umode_t if (mkdir_ctx->monitored != MONITORED_BY_PARENT) { delete_d_instantiate_ctx(); m->path_mkdir.ignored++; - return 0; + goto end; } mkdir_ctx->event_type = DIR_ACTIVITY_CREATION; - return 0; + goto end; error: delete_d_instantiate_ctx(); m->path_mkdir.error++; + +end: + bpf_preempt_enable(); return 0; } SEC("lsm/d_instantiate") int BPF_PROG(trace_d_instantiate, struct dentry* dentry, struct inode* inode) { + bpf_preempt_disable(); struct metrics_t* m = get_metrics(); if (m == NULL) { - return 0; + goto end; } struct submit_event_args_t args = {.metrics = &m->d_instantiate.base}; @@ -350,7 +374,7 @@ int BPF_PROG(trace_d_instantiate, struct dentry* dentry, struct inode* inode) { struct d_instantiate_ctx_t* d_inst_ctx = get_d_instantiate_ctx(); if (d_inst_ctx == NULL || d_inst_ctx->event_type == FILE_ACTIVITY_INIT) { args.metrics->ignored++; - return 0; + goto end; } args.filename = d_inst_ctx->path.path; args.parent_inode = d_inst_ctx->parent_inode; @@ -390,6 +414,9 @@ int BPF_PROG(trace_d_instantiate, struct dentry* dentry, struct inode* inode) { cleanup: bpf_map_delete_elem(&d_instantiate_ctx, &pid_tgid); + +end: + bpf_preempt_enable(); return 0; } @@ -418,29 +445,38 @@ __always_inline static int handle_xattr(struct metrics_by_hook_t* hook_metrics, SEC("lsm/inode_setxattr") int BPF_PROG(trace_inode_setxattr, struct mnt_idmap* idmap, struct dentry* dentry, const char* name, const void* value, size_t size, int flags) { + bpf_preempt_disable(); struct metrics_t* m = get_metrics(); if (m == NULL) { return 0; } - return handle_xattr(&m->inode_setxattr, dentry, name, FILE_ACTIVITY_SETXATTR); + int res = handle_xattr(&m->inode_setxattr, dentry, name, FILE_ACTIVITY_SETXATTR); + + bpf_preempt_enable(); + return res; } SEC("lsm/inode_removexattr") int BPF_PROG(trace_inode_removexattr, struct mnt_idmap* idmap, struct dentry* dentry, const char* name) { + bpf_preempt_disable(); struct metrics_t* m = get_metrics(); if (m == NULL) { return 0; } - return handle_xattr(&m->inode_removexattr, dentry, name, FILE_ACTIVITY_REMOVEXATTR); + int res = handle_xattr(&m->inode_removexattr, dentry, name, FILE_ACTIVITY_REMOVEXATTR); + + bpf_preempt_enable(); + return res; } SEC("lsm/inode_set_acl") int BPF_PROG(trace_inode_set_acl, struct mnt_idmap* idmap, struct dentry* dentry, const char* acl_name, struct posix_acl* kacl) { + bpf_preempt_disable(); struct metrics_t* m = get_metrics(); if (m == NULL) { - return 0; + goto end; } struct submit_event_args_t args = {.metrics = &m->inode_set_acl}; @@ -453,18 +489,22 @@ int BPF_PROG(trace_inode_set_acl, struct mnt_idmap* idmap, struct dentry* dentry if (args.monitored == NOT_MONITORED) { args.metrics->ignored++; - return 0; + goto end; } submit_acl_event(&args, acl_name, kacl); + +end: + bpf_preempt_enable(); return 0; } SEC("lsm/path_rmdir") int BPF_PROG(trace_path_rmdir, struct path* dir, struct dentry* dentry) { + bpf_preempt_disable(); struct metrics_t* m = get_metrics(); if (m == NULL) { - return 0; + goto end; } struct submit_event_args_t args = {.metrics = &m->path_rmdir}; @@ -474,7 +514,7 @@ int BPF_PROG(trace_path_rmdir, struct path* dir, struct dentry* dentry) { if (path == NULL) { bpf_printk("Failed to read directory path"); m->path_rmdir.error++; - return 0; + goto end; } args.filename = path->path; @@ -482,18 +522,22 @@ int BPF_PROG(trace_path_rmdir, struct path* dir, struct dentry* dentry) { if (inode_remove(&args.inode) < 0) { m->path_rmdir.ignored++; - return 0; + goto end; } submit_rmdir_event(&args); + +end: + bpf_preempt_enable(); return 0; } SEC("lsm/sb_mount") int BPF_PROG(trace_sb_mount, const char* dev_name, struct path* path, const char* type, unsigned long flags, void* data) { + bpf_preempt_disable(); struct metrics_t* m = get_metrics(); if (m == NULL) { - return 0; + goto end; } struct submit_event_args_t args = {.metrics = &m->sb_mount}; args.metrics->total++; @@ -502,7 +546,7 @@ int BPF_PROG(trace_sb_mount, const char* dev_name, struct path* path, const char if (bound_path == NULL) { bpf_printk("Failed to read mount directory"); args.metrics->error++; - return 0; + goto end; } args.filename = bound_path->path; @@ -515,19 +559,22 @@ int BPF_PROG(trace_sb_mount, const char* dev_name, struct path* path, const char args.monitored = is_monitored(&args.inode, bound_path, &args.parent_inode); if (args.monitored == NOT_MONITORED) { args.metrics->ignored++; - return 0; + goto end; } submit_mount_event(&args); +end: + bpf_preempt_enable(); return 0; } SEC("lsm/sb_umount") int BPF_PROG(trace_sb_umount, struct vfsmount* mnt, int flags) { + bpf_preempt_disable(); struct metrics_t* m = get_metrics(); if (m == NULL) { - return 0; + goto end; } struct submit_event_args_t args = {.metrics = &m->sb_umount}; args.metrics->total++; @@ -537,7 +584,7 @@ int BPF_PROG(trace_sb_umount, struct vfsmount* mnt, int flags) { if (bound_path == NULL) { bpf_printk("Failed to read umount directory"); args.metrics->error++; - return 0; + goto end; } args.filename = bound_path->path; @@ -550,19 +597,22 @@ int BPF_PROG(trace_sb_umount, struct vfsmount* mnt, int flags) { args.monitored = is_monitored(&args.inode, bound_path, &args.parent_inode); if (args.monitored == NOT_MONITORED) { args.metrics->ignored++; - return 0; + goto end; } submit_umount_event(&args); +end: + bpf_preempt_enable(); return 0; } SEC("lsm/move_mount") int BPF_PROG(trace_move_mount, struct path* from, struct path* to) { + bpf_preempt_disable(); struct metrics_t* m = get_metrics(); if (m == NULL) { - return 0; + goto end; } struct submit_event_args_t args = {.metrics = &m->move_mount}; @@ -590,7 +640,7 @@ int BPF_PROG(trace_move_mount, struct path* from, struct path* to) { if (args.monitored != MONITORED_BY_INODE) { args.metrics->ignored++; - return 0; + goto end; } // Ensure the new mount is tracked. @@ -599,18 +649,23 @@ int BPF_PROG(trace_move_mount, struct path* from, struct path* to) { } submit_move_mount_event(&args, from_path->path, &from_inode, from_monitored); - return 0; + + goto end; error: args.metrics->error++; + +end: + bpf_preempt_enable(); return 0; } SEC("lsm/path_symlink") int BPF_PROG(trace_path_symlink, struct path* dir, struct dentry* dentry, const char* old_name) { + bpf_preempt_disable(); struct metrics_t* m = get_metrics(); if (m == NULL) { - return 0; + goto end; } m->path_symlink.total++; @@ -633,10 +688,13 @@ int BPF_PROG(trace_path_symlink, struct path* dir, struct dentry* dentry, const goto error; } - return 0; + goto end; error: delete_d_instantiate_ctx(); m->path_symlink.error++; + +end: + bpf_preempt_enable(); return 0; } diff --git a/fact/src/bpf/mod.rs b/fact/src/bpf/mod.rs index 1eb58add..dc422edd 100644 --- a/fact/src/bpf/mod.rs +++ b/fact/src/bpf/mod.rs @@ -202,7 +202,7 @@ impl Bpf { ); let prefix = path_prefix_t::try_from(p)?; - self.paths_lpm_map.insert(&prefix.into(), 0, 0)?; + self.paths_lpm_map.insert(&prefix.into(), &0, 0)?; new_paths.push(prefix); } self.paths_globset = builder.build()?; diff --git a/fact/src/host_scanner.rs b/fact/src/host_scanner.rs index 1ea00a5d..3e8a808c 100644 --- a/fact/src/host_scanner.rs +++ b/fact/src/host_scanner.rs @@ -295,7 +295,7 @@ impl HostScanner { } }; - match self.kernel_inode_map.borrow_mut().insert(inode, 0, 0) { + match self.kernel_inode_map.borrow_mut().insert(&inode, &0, 0) { Ok(_) => Ok(()), Err(MapError::SyscallError(SyscallError { io_error, .. })) if io_error.kind() == io::ErrorKind::ArgumentListTooLong => From bfb13c4ac8a92b761a472dd14dce21271f6f1b23 Mon Sep 17 00:00:00 2001 From: Mauro Ezequiel Moltrasio Date: Tue, 25 Aug 2026 12:04:36 +0200 Subject: [PATCH 2/3] cleanup(bpf): use macro magic to simplify preempt enable/disable calls --- fact-ebpf/src/bpf/main.c | 226 ++++++++++++++++----------------------- 1 file changed, 90 insertions(+), 136 deletions(-) diff --git a/fact-ebpf/src/bpf/main.c b/fact-ebpf/src/bpf/main.c index 2efd73b9..a5cee4ac 100644 --- a/fact-ebpf/src/bpf/main.c +++ b/fact-ebpf/src/bpf/main.c @@ -19,12 +19,43 @@ char _license[] SEC("license") = "Dual MIT/GPL"; #define FMODE_PWRITE ((fmode_t)(1 << 4)) #define FMODE_CREATED ((fmode_t)(1 << 20)) -SEC("lsm/file_open") -int BPF_PROG(trace_file_open, struct file* file) { - bpf_preempt_disable(); +#define STRINGIFY(a) STR(a) +#define STR(a) #a + +#define __MAP0(m, ...) +#define __MAP1(m, t, a, ...) m(t, a) +#define __MAP2(m, t, a, ...) m(t, a), __MAP1(m, __VA_ARGS__) +#define __MAP3(m, t, a, ...) m(t, a), __MAP2(m, __VA_ARGS__) +#define __MAP4(m, t, a, ...) m(t, a), __MAP3(m, __VA_ARGS__) +#define __MAP5(m, t, a, ...) m(t, a), __MAP4(m, __VA_ARGS__) +#define __MAP6(m, t, a, ...) m(t, a), __MAP5(m, __VA_ARGS__) +#define __MAP(n, ...) __MAP##n(__VA_ARGS__) + +#define __CAT(t, a) t a +#define __ARG(t, a) a + +#define FACT_BPF_PROG(hook, n, args...) \ + static __always_inline int _handle_##hook(__MAP(n, __CAT, args)); \ + SEC("lsm/" STRINGIFY(hook)) \ + int BPF_PROG(trace_##hook, __MAP(n, __CAT, args)) { \ + bpf_preempt_disable(); \ + int res = _handle_##hook(__MAP(n, __ARG, args)); \ + bpf_preempt_enable(); \ + return res; \ + } \ + static __always_inline int _handle_##hook(__MAP(n, __CAT, args)) + +#define FACT_BPF_PROG1(hook, args...) FACT_BPF_PROG(hook, 1, args) +#define FACT_BPF_PROG2(hook, args...) FACT_BPF_PROG(hook, 2, args) +#define FACT_BPF_PROG3(hook, args...) FACT_BPF_PROG(hook, 3, args) +#define FACT_BPF_PROG4(hook, args...) FACT_BPF_PROG(hook, 4, args) +#define FACT_BPF_PROG5(hook, args...) FACT_BPF_PROG(hook, 5, args) +#define FACT_BPF_PROG6(hook, args...) FACT_BPF_PROG(hook, 6, args) + +FACT_BPF_PROG1(file_open, struct file*, file) { struct metrics_t* m = get_metrics(); if (m == NULL) { - goto end; + return 0; } struct submit_event_args_t args = {.metrics = &m->file_open}; @@ -59,7 +90,7 @@ int BPF_PROG(trace_file_open, struct file* file) { if (path == NULL) { bpf_printk("Failed to read path"); m->file_open.error++; - goto end; + return 0; } args.filename = path->path; @@ -79,23 +110,17 @@ int BPF_PROG(trace_file_open, struct file* file) { } submit_open_event(&args, event_type); - -end: - bpf_preempt_enable(); return 0; ignored: m->file_open.ignored++; - bpf_preempt_enable(); return 0; } -SEC("lsm/path_unlink") -int BPF_PROG(trace_path_unlink, struct path* dir, struct dentry* dentry) { - bpf_preempt_disable(); +FACT_BPF_PROG2(path_unlink, struct path*, dir, struct dentry*, dentry) { struct metrics_t* m = get_metrics(); if (m == NULL) { - goto end; + return 0; } struct submit_event_args_t args = {.metrics = &m->path_unlink}; @@ -105,7 +130,7 @@ int BPF_PROG(trace_path_unlink, struct path* dir, struct dentry* dentry) { if (path == NULL) { bpf_printk("Failed to read path"); m->path_unlink.error++; - goto end; + return 0; } args.filename = path->path; @@ -114,25 +139,20 @@ int BPF_PROG(trace_path_unlink, struct path* dir, struct dentry* dentry) { if (args.monitored == NOT_MONITORED) { m->path_unlink.ignored++; - goto end; + return 0; } // We only support files with one link for now inode_remove(&args.inode); submit_unlink_event(&args); - -end: - bpf_preempt_enable(); return 0; } -SEC("lsm/path_chmod") -int BPF_PROG(trace_path_chmod, struct path* path, umode_t mode) { - bpf_preempt_disable(); +FACT_BPF_PROG2(path_chmod, struct path*, path, umode_t, mode) { struct metrics_t* m = get_metrics(); if (m == NULL) { - goto end; + return 0; } struct submit_event_args_t args = {.metrics = &m->path_chmod}; @@ -142,7 +162,7 @@ int BPF_PROG(trace_path_chmod, struct path* path, umode_t mode) { if (bound_path == NULL) { bpf_printk("Failed to read path"); args.metrics->error++; - goto end; + return 0; } args.filename = bound_path->path; @@ -151,26 +171,21 @@ int BPF_PROG(trace_path_chmod, struct path* path, umode_t mode) { if (args.monitored == NOT_MONITORED) { args.metrics->ignored++; - goto end; + return 0; } umode_t old_mode = BPF_CORE_READ(path, dentry, d_inode, i_mode); submit_mode_event(&args, mode, old_mode); - -end: - bpf_preempt_enable(); return 0; } /* path_chown takes _unsigned long long_ for uid and gid because kuid_t and kgid_t (structs) fit in registers and since they contain only one integer, their content is extended to the size of the BPF registers (64 bits) to simplify further arithmetic operations. */ -SEC("lsm/path_chown") -int BPF_PROG(trace_path_chown, struct path* path, unsigned long long uid, unsigned long long gid) { - bpf_preempt_disable(); +FACT_BPF_PROG3(path_chown, struct path*, path, unsigned long long, uid, unsigned long long, gid) { struct metrics_t* m = get_metrics(); if (m == NULL) { - goto end; + return 0; } struct submit_event_args_t args = {.metrics = &m->path_chown}; @@ -180,7 +195,7 @@ int BPF_PROG(trace_path_chown, struct path* path, unsigned long long uid, unsign if (bound_path == NULL) { bpf_printk("Failed to read path"); args.metrics->error++; - goto end; + return 0; } args.filename = bound_path->path; @@ -189,7 +204,7 @@ int BPF_PROG(trace_path_chown, struct path* path, unsigned long long uid, unsign if (args.monitored == NOT_MONITORED) { args.metrics->ignored++; - goto end; + return 0; } struct dentry* d = BPF_CORE_READ(path, dentry); @@ -197,20 +212,15 @@ int BPF_PROG(trace_path_chown, struct path* path, unsigned long long uid, unsign unsigned long long old_gid = BPF_CORE_READ(d, d_inode, i_gid.val); submit_ownership_event(&args, uid, gid, old_uid, old_gid); - -end: - bpf_preempt_enable(); return 0; } -SEC("lsm/path_rename") -int BPF_PROG(trace_path_rename, struct path* old_dir, - struct dentry* old_dentry, struct path* new_dir, - struct dentry* new_dentry, unsigned int flags) { - bpf_preempt_disable(); +FACT_BPF_PROG5(path_rename, struct path*, old_dir, + struct dentry*, old_dentry, struct path*, new_dir, + struct dentry*, new_dentry, unsigned int, flags) { struct metrics_t* m = get_metrics(); if (m == NULL) { - goto end; + return 0; } struct submit_event_args_t args = {.metrics = &m->path_rename}; @@ -246,7 +256,7 @@ int BPF_PROG(trace_path_rename, struct path* old_dir, case NOT_MONITORED: if (old_monitored == NOT_MONITORED) { m->path_rename.ignored++; - goto end; + return 0; } if (old_monitored == MONITORED_BY_INODE) { @@ -299,23 +309,17 @@ int BPF_PROG(trace_path_rename, struct path* old_dir, } submit_rename_event(&args, old_path->path, &old_inode, old_monitored); - - goto end; + return 0; error: args.metrics->error++; - -end: - bpf_preempt_enable(); return 0; } -SEC("lsm/path_mkdir") -int BPF_PROG(trace_path_mkdir, struct path* dir, struct dentry* dentry, umode_t mode) { - bpf_preempt_disable(); +FACT_BPF_PROG3(path_mkdir, struct path*, dir, struct dentry*, dentry, umode_t, mode) { struct metrics_t* m = get_metrics(); if (m == NULL) { - goto end; + return 0; } m->path_mkdir.total++; @@ -338,27 +342,21 @@ int BPF_PROG(trace_path_mkdir, struct path* dir, struct dentry* dentry, umode_t if (mkdir_ctx->monitored != MONITORED_BY_PARENT) { delete_d_instantiate_ctx(); m->path_mkdir.ignored++; - goto end; + return 0; } mkdir_ctx->event_type = DIR_ACTIVITY_CREATION; - - goto end; + return 0; error: delete_d_instantiate_ctx(); m->path_mkdir.error++; - -end: - bpf_preempt_enable(); return 0; } -SEC("lsm/d_instantiate") -int BPF_PROG(trace_d_instantiate, struct dentry* dentry, struct inode* inode) { - bpf_preempt_disable(); +FACT_BPF_PROG2(d_instantiate, struct dentry*, dentry, struct inode*, inode) { struct metrics_t* m = get_metrics(); if (m == NULL) { - goto end; + return 0; } struct submit_event_args_t args = {.metrics = &m->d_instantiate.base}; @@ -374,7 +372,7 @@ int BPF_PROG(trace_d_instantiate, struct dentry* dentry, struct inode* inode) { struct d_instantiate_ctx_t* d_inst_ctx = get_d_instantiate_ctx(); if (d_inst_ctx == NULL || d_inst_ctx->event_type == FILE_ACTIVITY_INIT) { args.metrics->ignored++; - goto end; + return 0; } args.filename = d_inst_ctx->path.path; args.parent_inode = d_inst_ctx->parent_inode; @@ -414,9 +412,6 @@ int BPF_PROG(trace_d_instantiate, struct dentry* dentry, struct inode* inode) { cleanup: bpf_map_delete_elem(&d_instantiate_ctx, &pid_tgid); - -end: - bpf_preempt_enable(); return 0; } @@ -442,41 +437,29 @@ __always_inline static int handle_xattr(struct metrics_by_hook_t* hook_metrics, return 0; } -SEC("lsm/inode_setxattr") -int BPF_PROG(trace_inode_setxattr, struct mnt_idmap* idmap, struct dentry* dentry, - const char* name, const void* value, size_t size, int flags) { - bpf_preempt_disable(); +FACT_BPF_PROG6(inode_setxattr, struct mnt_idmap*, idmap, struct dentry*, dentry, + const char*, name, const void*, value, size_t, size, int, flags) { struct metrics_t* m = get_metrics(); if (m == NULL) { return 0; } - int res = handle_xattr(&m->inode_setxattr, dentry, name, FILE_ACTIVITY_SETXATTR); - - bpf_preempt_enable(); - return res; + return handle_xattr(&m->inode_setxattr, dentry, name, FILE_ACTIVITY_SETXATTR); } -SEC("lsm/inode_removexattr") -int BPF_PROG(trace_inode_removexattr, struct mnt_idmap* idmap, struct dentry* dentry, - const char* name) { - bpf_preempt_disable(); +FACT_BPF_PROG3(inode_removexattr, struct mnt_idmap*, idmap, struct dentry*, dentry, + const char*, name) { struct metrics_t* m = get_metrics(); if (m == NULL) { return 0; } - int res = handle_xattr(&m->inode_removexattr, dentry, name, FILE_ACTIVITY_REMOVEXATTR); - - bpf_preempt_enable(); - return res; + return handle_xattr(&m->inode_removexattr, dentry, name, FILE_ACTIVITY_REMOVEXATTR); } -SEC("lsm/inode_set_acl") -int BPF_PROG(trace_inode_set_acl, struct mnt_idmap* idmap, struct dentry* dentry, - const char* acl_name, struct posix_acl* kacl) { - bpf_preempt_disable(); +FACT_BPF_PROG4(inode_set_acl, struct mnt_idmap*, idmap, struct dentry*, dentry, + const char*, acl_name, struct posix_acl*, kacl) { struct metrics_t* m = get_metrics(); if (m == NULL) { - goto end; + return 0; } struct submit_event_args_t args = {.metrics = &m->inode_set_acl}; @@ -489,22 +472,17 @@ int BPF_PROG(trace_inode_set_acl, struct mnt_idmap* idmap, struct dentry* dentry if (args.monitored == NOT_MONITORED) { args.metrics->ignored++; - goto end; + return 0; } submit_acl_event(&args, acl_name, kacl); - -end: - bpf_preempt_enable(); return 0; } -SEC("lsm/path_rmdir") -int BPF_PROG(trace_path_rmdir, struct path* dir, struct dentry* dentry) { - bpf_preempt_disable(); +FACT_BPF_PROG2(path_rmdir, struct path*, dir, struct dentry*, dentry) { struct metrics_t* m = get_metrics(); if (m == NULL) { - goto end; + return 0; } struct submit_event_args_t args = {.metrics = &m->path_rmdir}; @@ -514,7 +492,7 @@ int BPF_PROG(trace_path_rmdir, struct path* dir, struct dentry* dentry) { if (path == NULL) { bpf_printk("Failed to read directory path"); m->path_rmdir.error++; - goto end; + return 0; } args.filename = path->path; @@ -522,22 +500,17 @@ int BPF_PROG(trace_path_rmdir, struct path* dir, struct dentry* dentry) { if (inode_remove(&args.inode) < 0) { m->path_rmdir.ignored++; - goto end; + return 0; } submit_rmdir_event(&args); - -end: - bpf_preempt_enable(); return 0; } -SEC("lsm/sb_mount") -int BPF_PROG(trace_sb_mount, const char* dev_name, struct path* path, const char* type, unsigned long flags, void* data) { - bpf_preempt_disable(); +FACT_BPF_PROG5(sb_mount, const char*, dev_name, struct path*, path, const char*, type, unsigned long, flags, void*, data) { struct metrics_t* m = get_metrics(); if (m == NULL) { - goto end; + return 0; } struct submit_event_args_t args = {.metrics = &m->sb_mount}; args.metrics->total++; @@ -546,7 +519,7 @@ int BPF_PROG(trace_sb_mount, const char* dev_name, struct path* path, const char if (bound_path == NULL) { bpf_printk("Failed to read mount directory"); args.metrics->error++; - goto end; + return 0; } args.filename = bound_path->path; @@ -559,22 +532,17 @@ int BPF_PROG(trace_sb_mount, const char* dev_name, struct path* path, const char args.monitored = is_monitored(&args.inode, bound_path, &args.parent_inode); if (args.monitored == NOT_MONITORED) { args.metrics->ignored++; - goto end; + return 0; } submit_mount_event(&args); - -end: - bpf_preempt_enable(); return 0; } -SEC("lsm/sb_umount") -int BPF_PROG(trace_sb_umount, struct vfsmount* mnt, int flags) { - bpf_preempt_disable(); +FACT_BPF_PROG2(sb_umount, struct vfsmount*, mnt, int, flags) { struct metrics_t* m = get_metrics(); if (m == NULL) { - goto end; + return 0; } struct submit_event_args_t args = {.metrics = &m->sb_umount}; args.metrics->total++; @@ -584,7 +552,7 @@ int BPF_PROG(trace_sb_umount, struct vfsmount* mnt, int flags) { if (bound_path == NULL) { bpf_printk("Failed to read umount directory"); args.metrics->error++; - goto end; + return 0; } args.filename = bound_path->path; @@ -597,22 +565,17 @@ int BPF_PROG(trace_sb_umount, struct vfsmount* mnt, int flags) { args.monitored = is_monitored(&args.inode, bound_path, &args.parent_inode); if (args.monitored == NOT_MONITORED) { args.metrics->ignored++; - goto end; + return 0; } submit_umount_event(&args); - -end: - bpf_preempt_enable(); return 0; } -SEC("lsm/move_mount") -int BPF_PROG(trace_move_mount, struct path* from, struct path* to) { - bpf_preempt_disable(); +FACT_BPF_PROG2(move_mount, struct path*, from, struct path*, to) { struct metrics_t* m = get_metrics(); if (m == NULL) { - goto end; + return 0; } struct submit_event_args_t args = {.metrics = &m->move_mount}; @@ -640,7 +603,7 @@ int BPF_PROG(trace_move_mount, struct path* from, struct path* to) { if (args.monitored != MONITORED_BY_INODE) { args.metrics->ignored++; - goto end; + return 0; } // Ensure the new mount is tracked. @@ -649,23 +612,17 @@ int BPF_PROG(trace_move_mount, struct path* from, struct path* to) { } submit_move_mount_event(&args, from_path->path, &from_inode, from_monitored); - - goto end; + return 0; error: args.metrics->error++; - -end: - bpf_preempt_enable(); return 0; } -SEC("lsm/path_symlink") -int BPF_PROG(trace_path_symlink, struct path* dir, struct dentry* dentry, const char* old_name) { - bpf_preempt_disable(); +FACT_BPF_PROG3(path_symlink, struct path*, dir, struct dentry*, dentry, const char*, old_name) { struct metrics_t* m = get_metrics(); if (m == NULL) { - goto end; + return 0; } m->path_symlink.total++; @@ -688,13 +645,10 @@ int BPF_PROG(trace_path_symlink, struct path* dir, struct dentry* dentry, const goto error; } - goto end; + return 0; error: delete_d_instantiate_ctx(); m->path_symlink.error++; - -end: - bpf_preempt_enable(); return 0; } From 61cebd76923001634fe44ffcbee7043859e1f290 Mon Sep 17 00:00:00 2001 From: Mauro Ezequiel Moltrasio Date: Tue, 25 Aug 2026 12:35:12 +0200 Subject: [PATCH 3/3] fix(bpf): check bpf_preempt_enable/disable exist before call These kfuncs where added in kernel version 6.12 and causes verifier issues on RHCOS 4.16 and 4.18 on our CI. --- fact-ebpf/src/bpf/main.c | 9 +++++++-- 1 file changed, 7 insertions(+), 2 deletions(-) diff --git a/fact-ebpf/src/bpf/main.c b/fact-ebpf/src/bpf/main.c index a5cee4ac..3477ebcc 100644 --- a/fact-ebpf/src/bpf/main.c +++ b/fact-ebpf/src/bpf/main.c @@ -38,9 +38,14 @@ char _license[] SEC("license") = "Dual MIT/GPL"; static __always_inline int _handle_##hook(__MAP(n, __CAT, args)); \ SEC("lsm/" STRINGIFY(hook)) \ int BPF_PROG(trace_##hook, __MAP(n, __CAT, args)) { \ - bpf_preempt_disable(); \ + if (bpf_ksym_exists(bpf_preempt_disable)) { \ + bpf_preempt_disable(); \ + } \ int res = _handle_##hook(__MAP(n, __ARG, args)); \ - bpf_preempt_enable(); \ + \ + if (bpf_ksym_exists(bpf_preempt_enable)) { \ + bpf_preempt_enable(); \ + } \ return res; \ } \ static __always_inline int _handle_##hook(__MAP(n, __CAT, args))