/** @file @brief 'exec', 'exit' and 'fork' tracepoints @details Copyright (c) 2017-2021 Acronis International GmbH @author Mikhail Krivtsov ([email protected]) @since $Id: $ */ #include "tracepoints.h" #include "compat.h" #include "debug.h" #include "exit_event.h" #include "exec_event.h" #include "ftrace_hooks/ftrace_events.h" #include "fork_event.h" #include "memory.h" #include "message.h" #include <linux/binfmts.h> #include <linux/dcache.h> // d_path #include <linux/file.h> // fput() #include <linux/fs.h> // struct file #include <linux/limits.h> // PATH_MAX #include <linux/mm.h> // get_task_exe_file() #include <linux/mm_types.h> // struct mm_struct #include <linux/path.h> // struct path #ifndef KERNEL_MOCK #include <linux/sched.h> // struct task_struct #else #include <mock/mock_sched.h> #endif #include <linux/tracepoint.h> #include <linux/version.h> // LINUX_VERSION_CODE, KERNEL_VERSION() #include <trace/events/sched.h> // TRACE_EVENT(sched_*) #ifdef HAVE_SCHED_PROCESS_EXEC_TRACEPOINT static bool g_registered_exec = false; #endif static bool g_registered_exit = false; static bool g_registered_fork = false; static TRACE_CB_PROTO(sched_process_exit, TP_PROTO(struct task_struct *p)) { DPRINTF("exit() p=%p { pid=%d tgid=%d }", p, p->pid, p->tgid); exit_event_nowait(p); } /* * Here the caller only guarantees locking for struct file and struct inode. * Locking must therefore be done in the probe to use the dentry. */ static TRACE_CB_PROTO(sched_process_fork, TP_PROTO(struct task_struct *current_macro, struct task_struct *p)) { DPRINTF("fork() current=%p { pid=%d tgid=%d comm='%s' } " "p=%p { pid=%d tgid=%d comm='%s' }", current_macro, current_macro->pid, current_macro->tgid, current_macro->comm, p, p->pid, p->tgid, p->comm); fork_event_nowait(current_macro, p); } #ifdef HAVE_SCHED_PROCESS_EXEC_TRACEPOINT static TRACE_CB_PROTO(sched_process_exec, TP_PROTO(struct task_struct *p, pid_t old_pid, struct linux_binprm *bprm)) { DPRINTF("exec() p=%p { pid=%d tgid=%d comm='%s' }", p, p->pid, p->tgid, p->comm); exec_event_nowait(p, bprm); } #endif int tracepoints_attach(void) { int ret; if (!ftrace_post_event_have(FTRACE_POST_EVENT_EXIT)) { ret = REGISTER_TRACE(sched_process_exit, TRACE_CB_NAME(sched_process_exit)); if (ret) { EPRINTF("'register_trace_sched_process_exit()' failure %i", ret); goto fail; } g_registered_exit = true; } if (!ftrace_post_event_have(FTRACE_POST_EVENT_FORK)) { ret = REGISTER_TRACE(sched_process_fork, TRACE_CB_NAME(sched_process_fork)); if (ret) { EPRINTF("'register_trace_sched_process_fork()' failure %i", ret); goto fail; } g_registered_fork = true; } #ifdef HAVE_SCHED_PROCESS_EXEC_TRACEPOINT if (!ftrace_post_event_have(FTRACE_POST_EVENT_EXEC)) { ret = REGISTER_TRACE(sched_process_exec, TRACE_CB_NAME(sched_process_exec)); if (ret) { EPRINTF("'register_trace_sched_process_exec()' failure %i", ret); goto fail; } g_registered_exec = true; } #endif IPRINTF("tracepoints attached"); return 0; fail: tracepoints_detach(); return ret; } void tracepoints_detach(void) { if (g_registered_fork) { UNREGISTER_TRACE(sched_process_fork, TRACE_CB_NAME(sched_process_fork)); g_registered_fork = false; } if (g_registered_exit) { UNREGISTER_TRACE(sched_process_exit, TRACE_CB_NAME(sched_process_exit)); g_registered_exit = false; } #ifdef HAVE_SCHED_PROCESS_EXEC_TRACEPOINT if (g_registered_exec) { UNREGISTER_TRACE(sched_process_exec, TRACE_CB_NAME(sched_process_exec)); g_registered_exec = false; } #endif tracepoint_synchronize_unregister(); IPRINTF("tracepoints detached"); }