shell bypass 403

Cubjrnet7 Shell


name : ftrace_events.c
/**
@file     ftrace_events.c
@brief    Events acquired via ftrace
@details  Copyright (c) 2024 Acronis International GmbH
@author   Bruce Wang ([email protected])
@since    $Id: $
*/

#include "ftrace_hooks/ftrace_events.h"

// This is true for roughly Linux Kernel 3.7.
#ifdef HAVE_FTRACE_OPS_FL_SAVE_REGS

#ifdef KERNEL_MOCK
#include <mock/mock.h>
#endif

#include <linux/dcache.h>
#include <linux/fs.h>
#include <linux/file.h>
#include <linux/fcntl.h>
#include <linux/ftrace.h>
#include <linux/fsnotify.h>
#include <linux/namei.h>
#include <linux/skbuff.h>
#include <linux/splice.h>
#include <linux/types.h>
#include <net/netlink.h>

#include "audit_user.h"
#include "compat.h"
#include "debug.h"
#include "exec_event.h"
#include "exit_event.h"
#include "file_contexts.h"
#include "file_key_tools.h"
#include "fsnotify_events.h"
#include "fsnotify_listener.h"
#include "fork_event.h"
#include "fs_event.h"
#include "lsm_hooks/lsm_common.h"
#include "memory.h"
#include "message.h"
#include "path_tools.h"
#include "reg_tools.h"
#include "si_size.h"
#include "si_templates.h"
#include "si_writer_common.h"
#include "syscall_common.h"
#include "task_tools.h"

#if 0
FSNOTIFY_V1
// version <= 5.8:
static inline void fsnotify_move(struct inode *old_dir, struct inode *new_dir,
                                 const struct qstr *old_name,
                                 int isdir, struct inode *target,
                                 struct dentry *moved)
{
	fsnotify_name(old_dir, old_dir_mask, source, old_name, fs_cookie);
	fsnotify_name(new_dir, new_dir_mask, source, new_name, fs_cookie);
}

static inline void fsnotify_create(struct inode *inode, struct dentry *dentry)
{
	fsnotify_dirent(inode, dentry, FS_CREATE);
}

static inline void fsnotify_unlink(struct inode *dir, struct dentry *dentry)
{
	fsnotify_dirent(dir, dentry, FS_DELETE);
}

static inline void fsnotify_dirent(struct inode *dir, struct dentry *dentry,
                   __u32 mask)
{
	fsnotify_name(dir, mask, d_inode(dentry), &dentry->d_name, 0);
}

static inline void fsnotify_name(struct inode *dir, __u32 mask,
                                 struct inode *child,
                                 const struct qstr *name, u32 cookie)
{
	fsnotify(dir, mask, child, FSNOTIFY_EVENT_INODE, name, cookie);
}

int fsnotify(struct inode *to_tell, __u32 mask, const void *data,
             int data_type, const struct qstr *name, u32 cookie);

####################
FSNOTIFY_V2
// 5.8 < version < 5.16
static inline void fsnotify_move(struct inode *old_dir, struct inode *new_dir,
                 const struct qstr *old_name,
                 int isdir, struct inode *target,
                 struct dentry *moved)
{
	fsnotify_name(old_dir, old_dir_mask, source, old_name, fs_cookie);
	fsnotify_name(new_dir, new_dir_mask, source, new_name, fs_cookie);
}

static inline void fsnotify_create(struct inode *inode, struct dentry *dentry)
{
	fsnotify_dirent(inode, dentry, FS_CREATE);
}

static inline void fsnotify_unlink(struct inode *dir, struct dentry *dentry)
{
	fsnotify_dirent(dir, dentry, FS_DELETE);
}

static inline void fsnotify_dirent(struct inode *dir, struct dentry *dentry,
                   __u32 mask)
{
	fsnotify_name(dir, mask, d_inode(dentry), &dentry->d_name, 0);
}

static inline void fsnotify_name(struct inode *dir, __u32 mask,
                 struct inode *child,
                 const struct qstr *name, u32 cookie)
{
	fsnotify(mask, child, FSNOTIFY_EVENT_INODE, dir, name, NULL, cookie);
}

int fsnotify(__u32 mask, const void *data, int data_type, struct inode *dir,
         const struct qstr *file_name, struct inode *inode, u32 cookie);

###########
FSNOTIFY_V3 (or V2)
// version >= 5.16

static inline void fsnotify_move(struct inode *old_dir, struct inode *new_dir,
                 const struct qstr *old_name,
                 int isdir, struct inode *target,
                 struct dentry *moved)
{
	fsnotify_name(old_dir_mask, source, FSNOTIFY_EVENT_INODE,
	          old_dir, old_name, fs_cookie);
	fsnotify_name(new_dir_mask, source, FSNOTIFY_EVENT_INODE,
	          new_dir, new_name, fs_cookie);
}

static inline void fsnotify_delete(struct inode *dir, struct inode *inode,
                   struct dentry *dentry)
{
	fsnotify_name(mask, inode, FSNOTIFY_EVENT_INODE, dir, &dentry->d_name,
	          0);
}

static inline void fsnotify_create(struct inode *dir, struct dentry *dentry)
{
	fsnotify_dirent(dir, dentry, FS_CREATE);
}

static inline void fsnotify_dirent(struct inode *dir, struct dentry *dentry,
                   __u32 mask)
{
	fsnotify_name(mask, dentry, FSNOTIFY_EVENT_DENTRY, dir, &dentry->d_name, 0);
}

static inline int fsnotify_name(__u32 mask, const void *data, int data_type,
                struct inode *dir, const struct qstr *name,
                u32 cookie)
{
	return fsnotify(mask, data, data_type, dir, name, NULL, cookie);
}

int fsnotify(__u32 mask, const void *data, int data_type,
            struct inode *dir, const struct qstr *name,
            struct inode *inode, u32 cookie);
#endif

static void fsnotify_entry_handler(unsigned long ip, unsigned long parent_ip, struct ftrace_ops *op, struct pt_regs *regs)
{
#ifndef HAVE_FSNOTIFY_PUBLIC_API
	uint32_t mask;
	const void *data;
	int data_type;
	const void *name_ptr;

	(void) ip; (void) parent_ip; (void) op;
	if (!HOOK_PROLOG())
		return;

#ifdef FSNOTIFY_MASK_SECOND
	mask = GET_KERNEL_ARGUMENT(regs, 1);
	data = (const void*)GET_KERNEL_ARGUMENT(regs, 2);
	data_type = (int) GET_KERNEL_ARGUMENT(regs, 3);
#else
	mask = GET_KERNEL_ARGUMENT(regs, 0);
	data = (const void*)GET_KERNEL_ARGUMENT(regs, 1);
	data_type = (int) GET_KERNEL_ARGUMENT(regs, 2);
#endif
	name_ptr = (const void*)GET_KERNEL_ARGUMENT(regs, 4);
#ifdef HAVE_FSNOTIFY_QSTR
	name_ptr = name_ptr ? ((const struct qstr *)name_ptr)->name : NULL;
#endif

	handle_fsnotify_event(mask, data, data_type, name_ptr);

	HOOK_EPILOG();
#else
	(void) regs; (void) op; (void) ip; (void) parent_ip;
#endif
}

static void exec_entry_handler(unsigned long ip, unsigned long parent_ip, struct ftrace_ops *op, struct pt_regs *regs)
{
	struct task_struct* tsk = (struct task_struct *)GET_KERNEL_ARGUMENT(regs, 0);
	(void) ip; (void) parent_ip; (void) op;
	if (!HOOK_PROLOG())
	{
		return;
	}

	exec_event(tsk);
	HOOK_EPILOG();
}

static void exit_entry_handler(unsigned long ip, unsigned long parent_ip, struct ftrace_ops *op, struct pt_regs *regs)
{
	struct task_struct* tsk = (struct task_struct *)GET_KERNEL_ARGUMENT(regs, 0);
	(void) ip; (void) parent_ip; (void) op;
	if (!HOOK_PROLOG())
	{
		return;
	}

	exit_event(tsk);
	HOOK_EPILOG();
}

static void fork_entry_handler(unsigned long ip, unsigned long parent_ip, struct ftrace_ops *op, struct pt_regs *regs)
{
	struct task_struct* tsk = (struct task_struct *)GET_KERNEL_ARGUMENT(regs, 0);
	(void) ip; (void) parent_ip; (void) op;
	if (!HOOK_PROLOG())
	{
		return;
	}

	fork_event(tsk);
	HOOK_EPILOG();
}

static const uint8_t k_login_logout_fields[] = { SI_COMMON_FIELDS, FP_SI_PI_OBJECT_NAME, FP_SI_PI_FLAGS };

static void audit_handle_msg(struct sk_buff* skb, struct nlmsghdr *nlh)
{
	u16 msg_type = nlh->nlmsg_type;
	(void) skb;
	if (msg_type == AUDIT_USER_LOGIN || msg_type == AUDIT_USER_LOGOUT)
	{
		int msg_len = nlmsg_len(nlh);
		msg_t* msg;
		size_t msg_size;
		uint64_t event_uid;
		SiSizedString data;
		task_info_t* task_info;

		if (msg_len < 2)
			return;

		{
			transport_ids_t ids;
			transport_global_get_ids(&ids);
			task_info = task_info_map_get(current);
			refresh_task(task_info, &ids);
			// not skipping event - always send login/logout events
		}

		data.value = (const char*) NLMSG_DATA(nlh);
		data.length = strnlen(data.value, msg_len);
		msg_size = SI_ESTIMATE_SIZE_CONST_PARAMS(k_login_logout_fields) + data.length;
		msg = msg_new(FP_SI_OT_NOTIFY_LOGIN_LOGOUT, 0, SI_CT_POST_CALLBACK, make_unique_pid(current), msg_size);
		if (msg) {
			event_uid = transport_global_sequence_next();
			{
				si_property_writer_t writer;
				si_event_writer_init(&writer, &msg->event, msg_size);
				si_property_writer_write_common(&writer, event_uid, current->pid, current->tgid, task_info);
				si_property_writer_write_object_name(&writer, data);
				si_property_writer_write_flags(&writer, msg_type);
				si_event_writer_finalize(&msg->event, &writer);
			}

			send_msg_async_unref(msg);
		}

		if (task_info)
			task_info_put(task_info);
	}
}

static void audit_entry_handler(unsigned long ip, unsigned long parent_ip, struct ftrace_ops *op, struct pt_regs *regs)
{
	struct sk_buff* skb = (struct sk_buff*)GET_KERNEL_ARGUMENT(regs, 0);
	struct nlmsghdr *nlh;
	int len;
	const uint64_t generatedEventsMask = MSG_TYPE_TO_EVENT_MASK(FP_SI_OT_NOTIFY_LOGIN_LOGOUT);

	if (!(transport_global_get_combined_mask() & generatedEventsMask)
	   || transport_process_belongs_to_control(current)) {
		return;
	}

	(void) ip; (void) parent_ip; (void) op;
	nlh = nlmsg_hdr(skb);
	len = skb->len;

	while (NLMSG_OK(nlh, len)) {
		audit_handle_msg(skb, nlh);
		nlh = NLMSG_NEXT(nlh, len);
	}
}

static void pre_write(const struct path *path, int flags, loff_t offset, size_t count)
{
	transport_ids_t transport_ids;
	task_info_t* task_info;
	file_context_info_t file_context_info = {0};
	const uint64_t generatedEventsMask = MSG_TYPE_TO_EVENT_MASK(FP_SI_OT_SYNC_FILE_PRE_WRITE);

	if (!(transport_global_get_combined_mask() & generatedEventsMask)
	   || transport_process_belongs_to_control(current)) {
		return;
	}

	task_info = task_info_map_get(current);
	if (!task_info)
		return;

	transport_global_get_ids(&transport_ids);
	refresh_task(task_info, &transport_ids);

	if (task_info_can_skip(task_info, &transport_ids, generatedEventsMask))
		goto err_skipped;

	if (make_file_context_info(&file_context_info, path, flags, offset, offset + count, READ_ONCE(task_info->pid_version)) &&
		check_write_cache(&transport_ids, &file_context_info, FILE_CONTEXT_WRITE_TABLE))
		goto err_skipped;

	(void) fs_event_pre_write(task_info, flags, offset, count, path, &file_context_info.msg_info);

err_skipped:
	task_info_put(task_info);
}

static void pre_read(const struct path *path, int flags, loff_t offset, size_t count)
{
	transport_ids_t transport_ids;
	task_info_t* task_info;
	file_key_t key;
	const uint64_t generatedEventsMask = MSG_TYPE_TO_EVENT_MASK(FP_SI_OT_NOTIFY_FILE_FULL_READ);

	if (!(transport_global_get_combined_mask() & generatedEventsMask)
	   || transport_process_belongs_to_control(current)) {
		return;
	}

	task_info = task_info_map_get(current);
	if (!task_info)
		return;

	transport_global_get_ids(&transport_ids);
	refresh_task(task_info, &transport_ids);

	if (task_info_can_skip(task_info, &transport_ids, generatedEventsMask))
		goto err_skipped;

	{
		file_context_info_t file_context_info = {0};
		if (make_file_context_info(&file_context_info, path, flags, offset, offset + count, READ_ONCE(task_info->pid_version)) &&
			check_and_update_read_cache(&transport_ids, &file_context_info))
		{
			goto err_skipped;
		}
	}

	make_key(&key, path);
	fs_event_pre_full_read(task_info, &key, flags, offset, count);

err_skipped:
	task_info_put(task_info);
}

static void rw_verify_area_entry_handler(unsigned long ip, unsigned long parent_ip, struct ftrace_ops *op, struct pt_regs *regs)
{
	int read_write = (int)GET_KERNEL_ARGUMENT(regs, 0);
	bool write = WRITE == read_write;
	bool read = READ == read_write;
	struct file *file = (struct file *)GET_KERNEL_ARGUMENT(regs, 1);
	const loff_t *ppos = (const loff_t *)GET_KERNEL_ARGUMENT(regs, 2);
	size_t count = (size_t)GET_KERNEL_ARGUMENT(regs, 3);
	loff_t pos;

#if LINUX_VERSION_CODE >= KERNEL_VERSION(5, 18, 0)
	BUILD_BUG_ON(!__builtin_types_compatible_p(typeof(&rw_verify_area), int (*)(int, struct file*, const loff_t*, size_t)));
#endif

	(void) ip; (void) parent_ip; (void) op;
	if (!file || !ppos)
		return;
	if (!read && !write)
		return;

#ifdef FMODE_NONOTIFY
	if (file->f_mode & FMODE_NONOTIFY)
		return;
#endif

	pos = *ppos;
	// I am not even going to consider these weird cases
	// rw_verify_area will check for those and I will do so too
	if (pos < 0)
		return;
	if ((ssize_t) count < 0)
		return;
	if ((loff_t) (pos + count) < 0)
		return;
	if (!file_is_valid(file))
		return;
	if (!S_ISREG(file->f_path.dentry->d_inode->i_mode))
		return;

	if (!HOOK_PROLOG())
		return;

	if (write)
		pre_write(&file->f_path, file->f_flags, pos, count);
	if (read)
		pre_read(&file->f_path, file->f_flags, pos, count);

	HOOK_EPILOG();
}

static void check_copy_file(struct file *file_in, loff_t pos_in, struct file *file_out, loff_t pos_out, loff_t len)
{
	transport_ids_t transport_ids;
	task_info_t* task_info;
	const uint64_t generatedEventsMask = MSG_TYPE_TO_EVENT_MASK(FP_SI_OT_NOTIFY_FILE_COPY);
	loff_t in_size;
	loff_t out_size;
	if (0 != pos_in || 0 != pos_out)
		return;

	if (!path_is_valid(&file_in->f_path) || !path_is_valid(&file_out->f_path))
		return;

	in_size = i_size_read(file_in->f_path.dentry->d_inode);
	out_size = i_size_read(file_out->f_path.dentry->d_inode);
	if (in_size > len || out_size > len)
		return;

	if (!HOOK_PROLOG())
		return;

	if (!(transport_global_get_combined_mask() & generatedEventsMask)
	   || transport_process_belongs_to_control(current)) {
		goto out;
	}

	task_info = task_info_map_get(current);
	if (!task_info)
		goto out;

	transport_global_get_ids(&transport_ids);
	refresh_task(task_info, &transport_ids);

	if (!task_info_can_skip(task_info, &transport_ids, generatedEventsMask))
		fs_event_pre_copyfile(task_info, &file_in->f_path, &file_out->f_path);

	task_info_put(task_info);
out:
	HOOK_EPILOG();
}

#ifdef HAVE_VFS_CLONE_FILE_RANGE
static void clone_file_range_entry_handler(unsigned long ip, unsigned long parent_ip, struct ftrace_ops *op, struct pt_regs *regs)
{
	struct file *file_in = (struct file *)GET_KERNEL_ARGUMENT(regs, 0);
	loff_t pos_in = (loff_t)GET_KERNEL_ARGUMENT(regs, 1);
	struct file *file_out = (struct file *)GET_KERNEL_ARGUMENT(regs, 2);
	loff_t pos_out = (loff_t)GET_KERNEL_ARGUMENT(regs, 3);
	loff_t len = (loff_t)GET_KERNEL_ARGUMENT(regs, 4);

	BUILD_BUG_ON(!(__builtin_types_compatible_p(typeof(&vfs_clone_file_range), int    (*)(struct file*, loff_t, struct file*, loff_t, u64))
	            || __builtin_types_compatible_p(typeof(&vfs_clone_file_range), loff_t (*)(struct file*, loff_t, struct file*, loff_t, loff_t, unsigned int))));
	(void) ip; (void) parent_ip; (void) op;
	check_copy_file(file_in, pos_in, file_out, pos_out, len);
}
#endif

#ifdef HAVE_VFS_COPY_FILE_RANGE
static void copy_file_range_entry_handler(unsigned long ip, unsigned long parent_ip, struct ftrace_ops *op, struct pt_regs *regs)
{
	struct file *file_in = (struct file *)GET_KERNEL_ARGUMENT(regs, 0);
	loff_t pos_in = (loff_t)GET_KERNEL_ARGUMENT(regs, 1);
	struct file *file_out = (struct file *)GET_KERNEL_ARGUMENT(regs, 2);
	loff_t pos_out = (loff_t)GET_KERNEL_ARGUMENT(regs, 3);
	size_t len = (size_t)GET_KERNEL_ARGUMENT(regs, 4);

	BUILD_BUG_ON(!__builtin_types_compatible_p(typeof(&vfs_copy_file_range), ssize_t (*)(struct file*, loff_t, struct file *, loff_t, size_t, unsigned int)));
	(void) ip; (void) parent_ip; (void) op;
	check_copy_file(file_in, pos_in, file_out, pos_out, len);
}
#endif

static void do_splice_direct_entry_handler(unsigned long ip, unsigned long parent_ip, struct ftrace_ops *op, struct pt_regs *regs)
{
	struct file *in = (struct file *)GET_KERNEL_ARGUMENT(regs, 0);
	loff_t *ppos = (loff_t *)GET_KERNEL_ARGUMENT(regs, 1);
	struct file *out = (struct file *)GET_KERNEL_ARGUMENT(regs, 2);
	loff_t *opos = NULL;
	size_t len;

	(void) ip; (void) parent_ip; (void) op;

#if LINUX_VERSION_CODE >= KERNEL_VERSION(3, 18, 0)
	BUILD_BUG_ON(!__builtin_types_compatible_p(typeof(&do_splice_direct), long (*)(struct file*, loff_t*, struct file*, loff_t*, size_t, unsigned int)));
	opos = (loff_t *)GET_KERNEL_ARGUMENT(regs, 3);
	len = (size_t)GET_KERNEL_ARGUMENT(regs, 4);
#elif defined(HAVE_DO_SPLICE_DIRECT_IN_FS)
	if (__builtin_types_compatible_p(typeof(&do_splice_direct), long (*)(struct file*, loff_t*, struct file*, size_t, unsigned int))) {
		len = (size_t)GET_KERNEL_ARGUMENT(regs, 3);
	} else {
		opos = (loff_t *)GET_KERNEL_ARGUMENT(regs, 3);
		len = (size_t)GET_KERNEL_ARGUMENT(regs, 4);
	}
#else
	opos = (loff_t *)GET_KERNEL_ARGUMENT(regs, 3);
	len = (size_t)GET_KERNEL_ARGUMENT(regs, 4);
#endif

	if (!ppos || !opos)
		return;

	check_copy_file(in, *ppos, out, *opos, len);
}

typedef struct
{
	struct ftrace_ops ops;
	unsigned long fn;
	const char* name;
	bool registered;
} fp_probe_t;

// !!! These must align exactly with ftrace_post_event_type_t !!!
static fp_probe_t g_probes[] = {
	{
		.ops = {
			.func = (ftrace_func_t) fsnotify_entry_handler,
			.flags = FTRACE_OPS_FL_SAVE_REGS,
		},
		.fn = (unsigned long) fsnotify,
		.name = NULL,
		.registered = false,
	},
	{
		.ops = {
			.func = (ftrace_func_t) exec_entry_handler,
			.flags = FTRACE_OPS_FL_SAVE_REGS,
		},
		.fn = 0,
		.name = "proc_exec_connector",
		.registered = false,
	},
	{
		.ops = {
			.func = (ftrace_func_t) exit_entry_handler,
			.flags = FTRACE_OPS_FL_SAVE_REGS,
		},
		.fn = 0,
		.name = "proc_exit_connector",
		.registered = false,
	},
	{
		.ops = {
			.func = (ftrace_func_t) fork_entry_handler,
			.flags = FTRACE_OPS_FL_SAVE_REGS,
		},
		.fn = 0,
		.name = "proc_fork_connector",
		.registered = false,
	},
	{
		.ops = {
			.func = (ftrace_func_t) audit_entry_handler,
			.flags = FTRACE_OPS_FL_SAVE_REGS,
		},
		.fn = 0,
		.name = "audit_receive",
		.registered = false,
	},
	{
		.ops = {
			.func = (ftrace_func_t) rw_verify_area_entry_handler,
			.flags = FTRACE_OPS_FL_SAVE_REGS,
		},
		// rw_verify_area is a public symbol in modern kernels but not in the older ones.
		// TODO: 'rw_verify_area' misses 'remap_verify_area' case so cloning is not handled.
		//       Perhaps a simple solution could be hooking the clone functions like
		//       'vfs_clone_file_range' and 'vfs_dedupe_file_range_one'?
		// themselves?
#if LINUX_VERSION_CODE >= KERNEL_VERSION(5, 18, 0)
		.fn = (unsigned long) rw_verify_area,
		.name = NULL,
#else
		.fn = 0,
		.name = "rw_verify_area",
#endif
		.registered = false,
	},
	{
#ifdef HAVE_VFS_CLONE_FILE_RANGE
		.ops = {
			.func = (ftrace_func_t) clone_file_range_entry_handler,
			.flags = FTRACE_OPS_FL_SAVE_REGS,
		},
		.fn = (unsigned long) vfs_clone_file_range,
		.name = NULL,
		.registered = false,
#endif
	},
	{
#ifdef HAVE_VFS_COPY_FILE_RANGE
		.ops = {
			.func = (ftrace_func_t) copy_file_range_entry_handler,
			.flags = FTRACE_OPS_FL_SAVE_REGS,
		},
		.fn = (unsigned long) vfs_copy_file_range,
		.name = NULL,
		.registered = false,
#endif
	},
	{
		.ops = {
			.func = (ftrace_func_t) do_splice_direct_entry_handler,
			.flags = FTRACE_OPS_FL_SAVE_REGS,
		},
		// public symbol since 3.18
#if LINUX_VERSION_CODE >= KERNEL_VERSION(3, 18, 0)
		.fn = (unsigned long) do_splice_direct,
		.name = NULL,
#else
		.fn = 0,
		.name = "do_splice_direct",
#endif
		.registered = false,
	},
};

void register_ftrace_post_events(void)
{
	int i = 0;

	for (; i < (int) ARRAY_SIZE(g_probes); i++)
	{
		fp_probe_t* probe = &g_probes[i];
		struct ftrace_ops *ops = &probe->ops;
		int ret;

#ifdef HAVE_FSNOTIFY_PUBLIC_API
		if (i == FTRACE_POST_EVENT_FSNOTIFY)
		{
			IPRINTF("fsnotify public API is used instead of ftrace");
			continue;
		}
#endif

		if (!probe->fn && !probe->name)
		{
			continue;
		}

		if (probe->name) {
			probe->fn = compat_kallsyms_lookup_name(probe->name);
			if (!probe->fn) {
				EPRINTF("Failed to find %s", probe->name);
				continue;
			}

			DPRINTF("%s -> %lu", probe->name, probe->fn);
		}

		ret = ftrace_set_filter_ip(ops, probe->fn, 0, 0);
		if (ret < 0)
		{
			EPRINTF("Failed to set filter ip");
			continue;
		}

		ret = register_ftrace_function(ops);
		if (ret < 0)
		{
			EPRINTF("Failed to register ftrace");
			continue;
		}

		IPRINTF("Planted ftrace %ps", (void*) probe->fn);
		g_probes[i].registered = true;
	}
}

void unregister_ftrace_post_events(void)
{
	int i = 0;

	for (; i < (int) ARRAY_SIZE(g_probes); i++)
	{
		if (g_probes[i].registered)
		{
			fp_probe_t* probe = &g_probes[i];
			struct ftrace_ops *ops = &probe->ops;
			IPRINTF("Remove ftrace for %ps", (void*) probe->fn);
			unregister_ftrace_function(ops);
			ftrace_set_filter_ip(ops, probe->fn, 1, 0);
			g_probes[i].registered = false;
		}
	}
}

bool ftrace_post_event_have(ftrace_post_event_type_t ev)
{
	if (ev == FTRACE_POST_EVENT_FSNOTIFY && fsnotify_events_listener_registered())
		return true;

	return g_probes[ev].registered;
}

#else

void register_ftrace_post_events(void)
{
}

void unregister_ftrace_post_events(void)
{
}

bool ftrace_post_event_have(ftrace_post_event_type_t ev)
{
	return false;
}

#endif

© 2025 Cubjrnet7