diff --git a/pkg/ebpf/c/common/buffer.h b/pkg/ebpf/c/common/buffer.h index bbcd8d7e2730..cf78af096534 100644 --- a/pkg/ebpf/c/common/buffer.h +++ b/pkg/ebpf/c/common/buffer.h @@ -17,9 +17,10 @@ statfunc int save_u64_arr_to_buf(args_buffer_t *, const u64 __user *, int, u8); statfunc int save_str_arr_to_buf(args_buffer_t *, const char __user *const __user *, u8); statfunc int save_args_str_arr_to_buf(args_buffer_t *, const char *, const char *, int, u8); statfunc int save_sockaddr_to_buf(args_buffer_t *, struct socket *, u8); +statfunc int save_args_to_submit_buf(event_data_t *, args_t *); +statfunc void *buffer_memcpy(void *, const void *, size_t); statfunc int events_perf_submit(program_data_t *, u32 id, long); statfunc int signal_perf_submit(void *, controlplane_signal_t *sig, u32 id); -statfunc int save_args_to_submit_buf(event_data_t *, args_t *); // FUNCTIONS @@ -336,51 +337,6 @@ statfunc int save_sockaddr_to_buf(args_buffer_t *buf, struct socket *sock, u8 in return 0; } -statfunc int events_perf_submit(program_data_t *p, u32 id, long ret) -{ - p->event->context.eventid = id; - p->event->context.retval = ret; - - // KEEP THIS FOR DEBUGGING (until process tree is fully implemented) - // u32 hash = (u64) hash_u32_and_u64(p->event->context.task.pid, p->event->context.task.task_start_time); - // bpf_printk("hash: %u\n", hash); - - // Get Stack trace - if (p->config->options & OPT_CAPTURE_STACK_TRACES) { - int stack_id = bpf_get_stackid(p->ctx, &stack_addresses, BPF_F_USER_STACK); - if (stack_id >= 0) { - p->event->context.stack_id = stack_id; - } - } - - u32 size = sizeof(event_context_t) + sizeof(u8) + - p->event->args_buf.offset; // context + argnum + arg buffer size - - // inline bounds check to force compiler to use the register of size - asm volatile("if %[size] < %[max_size] goto +1;\n" - "%[size] = %[max_size];\n" - : - : [size] "r"(size), [max_size] "i"(MAX_EVENT_SIZE)); - - return bpf_perf_event_output(p->ctx, &events, BPF_F_CURRENT_CPU, p->event, size); -} - -statfunc int signal_perf_submit(void *ctx, controlplane_signal_t *sig, u32 id) -{ - sig->event_id = id; - - u32 size = - sizeof(u32) + sizeof(u8) + sig->args_buf.offset; // signal id + argnum + arg buffer size - - // inline bounds check to force compiler to use the register of size - asm volatile("if %[size] < %[max_size] goto +1;\n" - "%[size] = %[max_size];\n" - : - : [size] "r"(size), [max_size] "i"(MAX_SIGNAL_SIZE)); - - return bpf_perf_event_output(ctx, &signals, BPF_F_CURRENT_CPU, sig, size); -} - #define DEC_ARG(n, enc_arg) ((enc_arg >> (8 * n)) & 0xFF) statfunc int save_args_to_submit_buf(event_data_t *event, args_t *args) @@ -488,4 +444,60 @@ statfunc int save_args_to_submit_buf(event_data_t *event, args_t *args) return arg_num; } +statfunc void *buffer_memcpy(void *dest, const void *src, size_t n) +{ + u8 *cdest = (u8 *) dest; + const u8 *csrc = (const u8 *) src; + + for (size_t i = 0; i < n; i++) { + cdest[i] = csrc[i]; + } + + return dest; +} + +// ATTENTION: Keep the embedded asm code at the end due to parsing errors (vscode) after they +// happen. + +statfunc int events_perf_submit(program_data_t *p, u32 id, long ret) +{ + p->event->context.eventid = id; + p->event->context.retval = ret; + + // Get Stack trace + if (p->config->options & OPT_CAPTURE_STACK_TRACES) { + int stack_id = bpf_get_stackid(p->ctx, &stack_addresses, BPF_F_USER_STACK); + if (stack_id >= 0) { + p->event->context.stack_id = stack_id; + } + } + + // context + argnum + arg buffer size + u32 size = sizeof(event_context_t) + sizeof(u8) + p->event->args_buf.offset; + + // inline bounds check to force compiler to use the register of size + asm volatile("if %[size] < %[max_size] goto +1;\n" + "%[size] = %[max_size];\n" + : + : [size] "r"(size), [max_size] "i"(MAX_EVENT_SIZE)); + + return bpf_perf_event_output(p->ctx, &events, BPF_F_CURRENT_CPU, p->event, size); +} + +statfunc int signal_perf_submit(void *ctx, controlplane_signal_t *sig, u32 id) +{ + sig->event_id = id; + + // signal id + argnum + arg buffer size + u32 size = sizeof(u32) + sizeof(u8) + sig->args_buf.offset; + + // inline bounds check to force compiler to use the register of size + asm volatile("if %[size] < %[max_size] goto +1;\n" + "%[size] = %[max_size];\n" + : + : [size] "r"(size), [max_size] "i"(MAX_SIGNAL_SIZE)); + + return bpf_perf_event_output(ctx, &signals, BPF_F_CURRENT_CPU, sig, size); +} + #endif diff --git a/pkg/ebpf/c/tracee.bpf.c b/pkg/ebpf/c/tracee.bpf.c index 337cba4e722b..9ab3762afa1c 100644 --- a/pkg/ebpf/c/tracee.bpf.c +++ b/pkg/ebpf/c/tracee.bpf.c @@ -507,6 +507,7 @@ int tracepoint__sched__sched_process_fork(struct bpf_raw_tracepoint_args *ctx) u64 parent_start_time = get_task_start_time(parent); u64 task_start_time = get_task_start_time(child); + // Update task_info_map with the new task (child) task_info_t task = {}; __builtin_memcpy(&task, p.task_info, sizeof(task_info_t)); task.recompute_scope = true; @@ -518,27 +519,26 @@ int tracepoint__sched__sched_process_fork(struct bpf_raw_tracepoint_args *ctx) if (ret < 0) tracee_log(ctx, BPF_LOG_LVL_DEBUG, BPF_LOG_ID_MAP_UPDATE_ELEM, ret); + // Pick new values for the child int parent_pid = get_task_host_pid(parent); - int child_pid = get_task_host_pid(child); - int parent_tgid = get_task_host_tgid(parent); + int child_pid = get_task_host_pid(child); int child_tgid = get_task_host_tgid(child); + // Check if this is a new process (and not just another thread) and add it to proc_info_map proc_info_t *c_proc_info = bpf_map_lookup_elem(&proc_info_map, &child_tgid); if (c_proc_info == NULL) { - // this is a new process (and not just another thread) - add it to proc_info_map - proc_info_t *p_proc_info = bpf_map_lookup_elem(&proc_info_map, &parent_tgid); if (unlikely(p_proc_info == NULL)) { - // parent proc should exist in proc_map (init_program_data should have set it) + // init_program_data should have set proc_info for the parent already tracee_log(ctx, BPF_LOG_LVL_WARN, BPF_LOG_ID_MAP_LOOKUP_ELEM, 0); return 0; } + // Update the child proc_info_map entry with same values as parent bpf_map_update_elem(&proc_info_map, &child_tgid, p_proc_info, BPF_NOEXIST); c_proc_info = bpf_map_lookup_elem(&proc_info_map, &child_tgid); - // appease the verifier - if (unlikely(c_proc_info == NULL)) { + if (unlikely(c_proc_info == NULL)) { // verifier needs tracee_log(ctx, BPF_LOG_LVL_WARN, BPF_LOG_ID_MAP_LOOKUP_ELEM, 0); return 0; } @@ -547,7 +547,7 @@ int tracepoint__sched__sched_process_fork(struct bpf_raw_tracepoint_args *ctx) c_proc_info->new_proc = true; } - // update process tree map if the parent has an entry + // If process tree filter is enabled, update the child with the parent's scopes if (p.config->proc_tree_filter_enabled_scopes) { u32 *tgid_filtered = bpf_map_lookup_elem(&process_tree_map, &parent_tgid); if (tgid_filtered) { @@ -557,32 +557,51 @@ int tracepoint__sched__sched_process_fork(struct bpf_raw_tracepoint_args *ctx) } } + // TODO: decide if SCHED_PROCESS_FORK signal should be always submitted or just for traced evts if (!should_trace(&p)) return 0; // follow every pid that passed the should_trace() checks (used by the follow filter) c_proc_info->follow_in_scopes = p.task_info->matched_scopes; - if (should_submit(SCHED_PROCESS_FORK, p.event) || p.config->options & OPT_PROCESS_INFO) { - int parent_ns_pid = get_task_ns_pid(parent); - int parent_ns_tgid = get_task_ns_tgid(parent); - int child_ns_pid = get_task_ns_pid(child); - int child_ns_tgid = get_task_ns_tgid(child); - - save_to_submit_buf(&p.event->args_buf, (void *) &parent_pid, sizeof(int), 0); - save_to_submit_buf(&p.event->args_buf, (void *) &parent_ns_pid, sizeof(int), 1); - save_to_submit_buf(&p.event->args_buf, (void *) &parent_tgid, sizeof(int), 2); - save_to_submit_buf(&p.event->args_buf, (void *) &parent_ns_tgid, sizeof(int), 3); - save_to_submit_buf(&p.event->args_buf, (void *) &child_pid, sizeof(int), 4); - save_to_submit_buf(&p.event->args_buf, (void *) &child_ns_pid, sizeof(int), 5); - save_to_submit_buf(&p.event->args_buf, (void *) &child_tgid, sizeof(int), 6); - save_to_submit_buf(&p.event->args_buf, (void *) &child_ns_tgid, sizeof(int), 7); - save_to_submit_buf(&p.event->args_buf, (void *) &task_start_time, sizeof(u64), 8); + int parent_ns_pid = get_task_ns_pid(parent); + int parent_ns_tgid = get_task_ns_tgid(parent); + int child_ns_pid = get_task_ns_pid(child); + int child_ns_tgid = get_task_ns_tgid(child); + + // parent + save_to_submit_buf(&p.event->args_buf, (void *) &parent_pid, sizeof(int), 0); + save_to_submit_buf(&p.event->args_buf, (void *) &parent_ns_pid, sizeof(int), 1); + save_to_submit_buf(&p.event->args_buf, (void *) &parent_tgid, sizeof(int), 2); + save_to_submit_buf(&p.event->args_buf, (void *) &parent_ns_tgid, sizeof(int), 3); + save_to_submit_buf(&p.event->args_buf, (void *) &parent_start_time, sizeof(u64), 4); + // child + save_to_submit_buf(&p.event->args_buf, (void *) &child_pid, sizeof(int), 5); + save_to_submit_buf(&p.event->args_buf, (void *) &child_ns_pid, sizeof(int), 6); + save_to_submit_buf(&p.event->args_buf, (void *) &child_tgid, sizeof(int), 7); + save_to_submit_buf(&p.event->args_buf, (void *) &child_ns_tgid, sizeof(int), 8); + save_to_submit_buf(&p.event->args_buf, (void *) &task_start_time, sizeof(u64), 9); + + // Submit the regular SCHED_PROCESS_FORK event + if (should_submit(SCHED_PROCESS_FORK, p.event) || p.config->options & OPT_PROCESS_INFO) { events_perf_submit(&p, SCHED_PROCESS_FORK, 0); } - return 0; + // Submit SCHED_PROCESS_FORK signal event now (only args, other perfbuffer) + + controlplane_signal_t *signal = init_controlplane_signal(); + if (unlikely(signal == NULL)) + return 0; + + // ATTENTION: + // + // 1. __builtin_memcpy won't work in between 2 eBPF maps + // 2. calling save_to_submit_buf(signal) would raise instr count too much + // + buffer_memcpy(&signal->args_buf, &p.event->args_buf, sizeof(signal->args_buf)); + + return signal_perf_submit(ctx, signal, SCHED_PROCESS_FORK); } // number of iterations - value that the verifier was seen to cope with - the higher, the better @@ -1082,7 +1101,6 @@ int lkm_seeker_new_mod_only_tail(struct pt_regs *ctx) return 0; } -// trace/events/sched.h: TP_PROTO(struct task_struct *p, pid_t old_pid, struct linux_binprm *bprm) SEC("raw_tracepoint/sched_process_exec") int tracepoint__sched__sched_process_exec(struct bpf_raw_tracepoint_args *ctx) { @@ -1090,13 +1108,14 @@ int tracepoint__sched__sched_process_exec(struct bpf_raw_tracepoint_args *ctx) if (!init_program_data(&p, ctx)) return 0; - // Perform the following checks before should_trace() so we can filter by newly created - // containers/processes. We assume that a new container/pod has started when a process of a - // newly created cgroup and mount ns executed a binary + // Perform the checks below, before should_trace(), so we can filter by newly created + // containers/processes. Assume a new container/pod has started when a process of a newly + // created cgroup and mount ns executed a binary. if (p.task_info->container_state == CONTAINER_CREATED) { u32 mntns = get_task_mnt_ns_id(p.event->task); struct task_struct *parent = get_parent_task(p.event->task); u32 parent_mntns = get_task_mnt_ns_id(parent); + if (mntns != parent_mntns) { u32 cgroup_id_lsb = p.event->context.task.cgroup_id; u8 state = CONTAINER_STARTED; @@ -1110,41 +1129,36 @@ int tracepoint__sched__sched_process_exec(struct bpf_raw_tracepoint_args *ctx) p.task_info->recompute_scope = true; struct linux_binprm *bprm = (struct linux_binprm *) ctx->args[2]; - if (bprm == NULL) { + if (bprm == NULL) return -1; - } - struct file *file = get_file_ptr_from_bprm(bprm); - void *file_path = get_path_str(__builtin_preserve_access_index(&file->f_path)); - proc_info_t *proc_info = bpf_map_lookup_elem(&proc_info_map, &p.event->context.task.host_pid); - if (proc_info == NULL) { + proc_info_t *pinfo = bpf_map_lookup_elem(&proc_info_map, &p.event->context.task.host_pid); + if (pinfo == NULL) { // entry should exist in proc_map (init_program_data should have set it otherwise) tracee_log(ctx, BPF_LOG_LVL_WARN, BPF_LOG_ID_MAP_LOOKUP_ELEM, 0); return 0; } - proc_info->new_proc = true; + pinfo->new_proc = true; - // extract the binary name to be used in should_trace - __builtin_memset(proc_info->binary.path, 0, MAX_BIN_PATH_SIZE); - bpf_probe_read_str(proc_info->binary.path, MAX_BIN_PATH_SIZE, file_path); - proc_info->binary.mnt_id = p.event->context.task.mnt_id; + struct file *file = get_file_ptr_from_bprm(bprm); + void *file_path = get_path_str(__builtin_preserve_access_index(&file->f_path)); + + // Extract the binary name to be used in should_trace + __builtin_memset(pinfo->binary.path, 0, MAX_BIN_PATH_SIZE); + bpf_probe_read_str(pinfo->binary.path, MAX_BIN_PATH_SIZE, file_path); + pinfo->binary.mnt_id = p.event->context.task.mnt_id; if (!should_trace(&p)) return 0; // Follow this task for matched scopes - proc_info->follow_in_scopes = p.task_info->matched_scopes; + pinfo->follow_in_scopes = p.task_info->matched_scopes; - if (!should_submit(SCHED_PROCESS_EXEC, p.event) && - (p.config->options & OPT_PROCESS_INFO) != OPT_PROCESS_INFO) - return 0; + // NOTE: In >= v5.9, there are two new fields in bprm to be considered: + // 1. struct file *executable - gives the executable name passed to an interpreter + // 2. fdpath - gives generated fname for execveat (after resolving dirfd) - // Note: Starting from kernel 5.9, there are two new interesting fields in bprm that we - // should consider adding: - // 1. struct file *executable - can be used to get the executable name passed to an - // interpreter - // 2. fdpath - generated filename for execveat (after resolving dirfd) const char *filename = get_binprm_filename(bprm); dev_t s_dev = get_dev_from_file(file); unsigned long inode_nr = get_inode_nr_from_file(file); @@ -1157,18 +1171,20 @@ int tracepoint__sched__sched_process_exec(struct bpf_raw_tracepoint_args *ctx) save_to_submit_buf(&p.event->args_buf, &inode_nr, sizeof(unsigned long), 3); save_to_submit_buf(&p.event->args_buf, &ctime, sizeof(u64), 4); save_to_submit_buf(&p.event->args_buf, &inode_mode, sizeof(umode_t), 5); + // If the interpreter file is the same as the executed one, it means that there is no // interpreter. For more information, see the load_elf_phdrs kprobe program. - if (proc_info->interpreter.id.inode != 0 && (proc_info->interpreter.id.device != s_dev || - proc_info->interpreter.id.inode != inode_nr)) { - save_str_to_buf(&p.event->args_buf, &proc_info->interpreter.pathname, 6); - save_to_submit_buf(&p.event->args_buf, &proc_info->interpreter.id.device, sizeof(dev_t), 7); - save_to_submit_buf( - &p.event->args_buf, &proc_info->interpreter.id.inode, sizeof(unsigned long), 8); - save_to_submit_buf(&p.event->args_buf, &proc_info->interpreter.id.ctime, sizeof(u64), 9); + + file_info_t *interp = &pinfo->interpreter; + if (interp->id.inode != 0 && (interp->id.device != s_dev || interp->id.inode != inode_nr)) { + save_str_to_buf(&p.event->args_buf, &interp->pathname, 6); + save_to_submit_buf(&p.event->args_buf, &interp->id.device, sizeof(dev_t), 7); + save_to_submit_buf(&p.event->args_buf, &interp->id.inode, sizeof(unsigned long), 8); + save_to_submit_buf(&p.event->args_buf, &interp->id.ctime, sizeof(u64), 9); } bpf_tail_call(ctx, &prog_array_tp, TAIL_SCHED_PROCESS_EXEC_EVENT_SUBMIT); + return -1; } @@ -1208,6 +1224,7 @@ int sched_process_exec_event_submit_tail(struct bpf_raw_tracepoint_args *ctx) save_to_submit_buf(&p.event->args_buf, &stdin_type, sizeof(unsigned short), 12); save_str_to_buf(&p.event->args_buf, stdin_path, 13); save_to_submit_buf(&p.event->args_buf, &invoked_from_kernel, sizeof(int), 14); + if (p.config->options & OPT_EXEC_ENV) { unsigned long env_start, env_end; env_start = get_env_start_from_mm(mm); @@ -1218,11 +1235,29 @@ int sched_process_exec_event_submit_tail(struct bpf_raw_tracepoint_args *ctx) &p.event->args_buf, (void *) env_start, (void *) env_end, envc, 15); } - events_perf_submit(&p, SCHED_PROCESS_EXEC, 0); - return 0; + bool sb = should_submit(SCHED_PROCESS_EXEC, p.event); + bool opt = (p.config->options & OPT_PROCESS_INFO) == OPT_PROCESS_INFO; + bool should_submit = (sb || opt); + + if (should_submit) + events_perf_submit(&p, SCHED_PROCESS_EXEC, 0); + + // Submit SCHED_PROCESS_EXEC signal event now (only args, other perfbuffer) + + controlplane_signal_t *signal = init_controlplane_signal(); + if (unlikely(signal == NULL)) + return 0; + + // ATTENTION: + // + // 1. __builtin_memcpy won't work in between 2 eBPF maps + // 2. calling save_to_submit_buf(signal) would raise instr count too much + // + buffer_memcpy(&signal->args_buf, &p.event->args_buf, sizeof(signal->args_buf)); + + return signal_perf_submit(ctx, signal, SCHED_PROCESS_EXEC); } -// trace/events/sched.h: TP_PROTO(struct task_struct *p) SEC("raw_tracepoint/sched_process_exit") int tracepoint__sched__sched_process_exit(struct bpf_raw_tracepoint_args *ctx) { @@ -1237,28 +1272,46 @@ int tracepoint__sched__sched_process_exit(struct bpf_raw_tracepoint_args *ctx) bool group_dead = false; struct task_struct *task = p.event->task; - struct signal_struct *signal = BPF_CORE_READ(task, signal); - atomic_t live = BPF_CORE_READ(signal, live); + struct signal_struct *sigstruct = BPF_CORE_READ(task, signal); + atomic_t live = BPF_CORE_READ(sigstruct, live); + // This check could be true for multiple thread exits if the thread count was 0 when the hooks // were triggered. This could happen for example if the threads performed exit in different CPUs // simultaneously. - if (live.counter == 0) { + if (!live.counter) group_dead = true; + + long exit_code = get_task_exit_code(p.event->task); + save_to_submit_buf(&p.event->args_buf, (void *) &exit_code, sizeof(long), 0); + save_to_submit_buf(&p.event->args_buf, (void *) &group_dead, sizeof(bool), 1); + + if (traced) { + if (should_submit(SCHED_PROCESS_EXIT, p.event) || p.config->options & OPT_PROCESS_INFO) { + events_perf_submit(&p, SCHED_PROCESS_EXIT, 0); + } } - if (!traced) - return 0; + // Submit SCHED_PROCESS_EXIT signal event now (only args, other perfbuffer) - long exit_code = get_task_exit_code(p.event->task); + // args: host_pid and exit_time used by control plane only - if (should_submit(SCHED_PROCESS_EXIT, p.event) || p.config->options & OPT_PROCESS_INFO) { - save_to_submit_buf(&p.event->args_buf, (void *) &exit_code, sizeof(long), 0); - save_to_submit_buf(&p.event->args_buf, (void *) &group_dead, sizeof(bool), 1); + u64 exit_time = bpf_ktime_get_ns(); + save_to_submit_buf( + &p.event->args_buf, (void *) &p.event->context.task.host_pid, sizeof(u32), 2); + save_to_submit_buf(&p.event->args_buf, (void *) &exit_time, sizeof(u64), 3); - events_perf_submit(&p, SCHED_PROCESS_EXIT, 0); - } + controlplane_signal_t *signal = init_controlplane_signal(); + if (unlikely(signal == NULL)) + return 0; - return 0; + // ATTENTION: + // + // 1. __builtin_memcpy won't work in between 2 eBPF maps + // 2. calling save_to_submit_buf(signal) would raise instr count too much + // + buffer_memcpy(&signal->args_buf, &p.event->args_buf, sizeof(signal->args_buf)); + + return signal_perf_submit(ctx, signal, SCHED_PROCESS_EXIT); } // trace/events/sched.h: TP_PROTO(struct task_struct *p) @@ -5941,12 +5994,17 @@ CGROUP_SKB_HANDLE_FUNCTION(proto_tcp_http) return 1; // NOTE: might block HTTP here if needed (return 0) } +// clang-format on + +// END OF Network Packets + // // Control Plane Programs // -// Control Plane programs are almost duplicate programs of select events which we send as -// direct signals to tracee in a separate buffer. -// This is done to mitigate the consenquences of losing these events in the main perf buffer. +// Control Plane programs are almost duplicate programs of select events which we send as direct +// signals to tracee in a separate buffer. This is done to mitigate the consenquences of losing +// these events in the main perf buffer. +// SEC("raw_tracepoint/cgroup_mkdir_signal") int cgroup_mkdir_signal(struct bpf_raw_tracepoint_args *ctx) @@ -5955,6 +6013,7 @@ int cgroup_mkdir_signal(struct bpf_raw_tracepoint_args *ctx) config_entry_t *cfg = bpf_map_lookup_elem(&config_map, &zero); if (unlikely(cfg == NULL)) return 0; + controlplane_signal_t *signal = init_controlplane_signal(); if (unlikely(signal == NULL)) return 0; @@ -5979,9 +6038,8 @@ int cgroup_mkdir_signal(struct bpf_raw_tracepoint_args *ctx) save_to_submit_buf(&signal->args_buf, &cgroup_id, sizeof(u64), 0); save_str_to_buf(&signal->args_buf, path, 1); save_to_submit_buf(&signal->args_buf, &hierarchy_id, sizeof(u32), 2); - signal_perf_submit(ctx, signal, CGROUP_MKDIR); - return 0; + return signal_perf_submit(ctx, signal, CGROUP_MKDIR); } SEC("raw_tracepoint/cgroup_rmdir_signal") @@ -5991,6 +6049,7 @@ int cgroup_rmdir_signal(struct bpf_raw_tracepoint_args *ctx) config_entry_t *cfg = bpf_map_lookup_elem(&config_map, &zero); if (unlikely(cfg == NULL)) return 0; + controlplane_signal_t *signal = init_controlplane_signal(); if (unlikely(signal == NULL)) return 0; @@ -6012,9 +6071,22 @@ int cgroup_rmdir_signal(struct bpf_raw_tracepoint_args *ctx) save_to_submit_buf(&signal->args_buf, &cgroup_id, sizeof(u64), 0); save_str_to_buf(&signal->args_buf, path, 1); save_to_submit_buf(&signal->args_buf, &hierarchy_id, sizeof(u32), 2); - signal_perf_submit(ctx, signal, CGROUP_RMDIR); - return 0; + return signal_perf_submit(ctx, signal, CGROUP_RMDIR); } -// clang-format on +// +// NOTE: The sched_process_{fork,exec,exit} control events are submitted by the +// the regular programs, since those programs are too complex to be duplicated +// and would cause overhead in execution as well. +// +// SEC("raw_tracepoint/sched_process_fork_signal") +// int sched_process_fork_signal(struct bpf_raw_tracepoint_args *ctx) +// +// SEC("raw_tracepoint/sched_process_exit_signal") +// int sched_process_exit_signal(struct bpf_raw_tracepoint_args *ctx) +// +// SEC("raw_tracepoint/sched_process_exec_signal") +// int sched_process_exec_signal(struct bpf_raw_tracepoint_args *ctx) + +// END OF Control Plane Programs diff --git a/pkg/ebpf/c/types.h b/pkg/ebpf/c/types.h index 9fe71228cd1c..cd7b2cce8fb0 100644 --- a/pkg/ebpf/c/types.h +++ b/pkg/ebpf/c/types.h @@ -13,32 +13,32 @@ // by their pid, but also by their start time). typedef struct task_context { - u64 task_start_time; // task's start time - u64 parent_start_time; // task's parent start time - u64 cgroup_id; // control group ID - u32 pid; // PID as in the userspace term - u32 tid; // TID as in the userspace term - u32 ppid; // Parent PID as in the userspace term - u32 host_pid; // PID in host pid namespace - u32 host_tid; // TID in host pid namespace - u32 host_ppid; // Parent PID in host pid namespace - u32 uid; // task's effective UID - u32 mnt_id; // task's mount namespace ID - u32 pid_id; // task's pid namespace ID - char comm[TASK_COMM_LEN]; // task's comm - char uts_name[TASK_COMM_LEN]; // task's uts name - u32 flags; // task's status flags (see context_flags_e) + u64 task_start_time; // task's start time + u64 parent_start_time; // task's parent start time + u64 cgroup_id; // control group ID + u32 pid; // PID as in the userspace term + u32 tid; // TID as in the userspace term + u32 ppid; // Parent PID as in the userspace term + u32 host_pid; // PID in host pid namespace + u32 host_tid; // TID in host pid namespace + u32 host_ppid; // Parent PID in host pid namespace + u32 uid; // task's effective UID + u32 mnt_id; // task's mount namespace ID + u32 pid_id; // task's pid namespace ID + char comm[TASK_COMM_LEN]; // task's comm + char uts_name[TASK_COMM_LEN]; // task's uts name + u32 flags; // task's status flags (see context_flags_e) } task_context_t; typedef struct event_context { - u64 ts; // timestamp + u64 ts; // timestamp task_context_t task; u32 eventid; - s32 syscall; // syscall that triggered the event + s32 syscall; // syscall that triggered the event u64 matched_policies; - s64 retval; // syscall return value + s64 retval; // syscall return value u32 stack_id; - u16 processor_id; // ID of the processor that processed the event + u16 processor_id; // ID of the processor that processed the event } event_context_t; enum event_id_e @@ -359,13 +359,14 @@ typedef struct event_data { u64 param_types; } event_data_t; -// A control plane signal - sent to indicate some critical event which should be processed -// with priority. +// Control Plane Signal +// +// Higher priority (and, most of the times, simpler) events to be processed faster by userland. // -// Signals currently consist of shortened events sent only with their arguments. -// As such, they consist of an event id and an argument buffer. -// If we ever require a signal independent of an event, the event_id field should change -// accordingly. +// Signals currently consist of shortened events sent only with their arguments. As such, they +// consist of an event id and an argument buffer. If we ever require a signal independent of an +// event, the event_id field should change accordingly. + typedef struct controlplane_signal { u32 event_id; args_buffer_t args_buf; diff --git a/pkg/ebpf/controlplane/controller.go b/pkg/ebpf/controlplane/controller.go index 0d6800fdcab4..9098f9d9aaf4 100644 --- a/pkg/ebpf/controlplane/controller.go +++ b/pkg/ebpf/controlplane/controller.go @@ -3,6 +3,7 @@ package controlplane import ( "context" "fmt" + "os" "github.com/aquasecurity/libbpfgo" @@ -24,19 +25,35 @@ const ( cgroupRmdirControlProbe ) +// These events are needed for some of the control plane events. At the eBPF side, they also submit +// events in the control plane perfbuffer. There isn't a need to set EventState for them (to +// configure Submit to a policy number), but there IS a need for dealing with their dependencies: +// making sure their probes are attached, make sure their dependencies are satisfied, make sure +// their tailCalls are indexed, etc. +var CoreEventsNeeded = []events.ID{ + events.SchedProcessFork, + events.SchedProcessExec, + events.SchedProcessExit, +} + type Controller struct { - ctx context.Context - signalChan chan []byte - lostSignalChan chan uint64 - bpfModule *libbpfgo.Module - probeGroup *probes.ProbeGroup - signalBuffer *libbpfgo.PerfBuffer - cgroupManager *containers.Containers - enrichEnabled bool + ctx context.Context + signalChan chan []byte + lostSignalChan chan uint64 + bpfModule *libbpfgo.Module + controlProbeGroup *probes.ProbeGroup + signalBuffer *libbpfgo.PerfBuffer + cgroupManager *containers.Containers + enrichEnabled bool } -func NewController(bpfModule *libbpfgo.Module, cgroupManager *containers.Containers, enrichEnabled bool) (*Controller, error) { +func NewController( + bpfModule *libbpfgo.Module, + cgroupManager *containers.Containers, + enrichEnabled bool, +) (*Controller, error) { var err error + p := &Controller{ signalChan: make(chan []byte, 100), lostSignalChan: make(chan uint64), @@ -44,36 +61,48 @@ func NewController(bpfModule *libbpfgo.Module, cgroupManager *containers.Contain cgroupManager: cgroupManager, enrichEnabled: enrichEnabled, } + p.signalBuffer, err = bpfModule.InitPerfBuf("signals", p.signalChan, p.lostSignalChan, 1024) if err != nil { return nil, err } - p.probeGroup = probes.NewProbeGroup(bpfModule, map[probes.Handle]probes.Probe{ - cgroupMkdirControlProbe: probes.NewTraceProbe( - probes.RawTracepoint, - "cgroup:cgroup_mkdir", - "cgroup_mkdir_signal", - ), - cgroupRmdirControlProbe: probes.NewTraceProbe( - probes.RawTracepoint, - "cgroup:cgroup_rmdir", - "cgroup_rmdir_signal", - ), - }) + p.controlProbeGroup = probes.NewProbeGroup( + bpfModule, map[probes.Handle]probes.Probe{ + cgroupMkdirControlProbe: probes.NewTraceProbe( + probes.RawTracepoint, + "cgroup:cgroup_mkdir", + "cgroup_mkdir_signal", + ), + cgroupRmdirControlProbe: probes.NewTraceProbe( + probes.RawTracepoint, + "cgroup:cgroup_rmdir", + "cgroup_rmdir_signal", + ), + }, + ) return p, nil } func (p *Controller) Attach() error { - err := p.probeGroup.Attach(cgroupMkdirControlProbe) - if err != nil { - return fmt.Errorf("failed to attach cgroup_mkdir probe in control plane: %v", err) + controlProbes := []probes.Handle{ + cgroupMkdirControlProbe, + cgroupRmdirControlProbe, } - err = p.probeGroup.Attach(cgroupRmdirControlProbe) - if err != nil { - return fmt.Errorf("failed to attach cgroup_rmdir probe in control plane: %v", err) + + // Attach the control probes + for _, probeHandle := range controlProbes { + err := p.controlProbeGroup.Attach(probeHandle) + if err != nil { + return fmt.Errorf( + "failed to attach control probe (program: %v): %v", + p.controlProbeGroup.GetProgramNameByHandle(probeHandle), + err, + ) + } } + return nil } @@ -107,7 +136,7 @@ func (p *Controller) Run(ctx context.Context) { func (p *Controller) Stop() error { p.signalBuffer.Stop() - return p.probeGroup.DetachAll() + return p.controlProbeGroup.DetachAll() } func (p *Controller) processSignal(signal signal) error { @@ -116,10 +145,27 @@ func (p *Controller) processSignal(signal signal) error { return p.processCgroupMkdir(signal.args) case events.CgroupRmdir: return p.processCgroupRmdir(signal.args) + case events.SchedProcessFork: + return p.processSchedProcessFork(signal.args) + case events.SchedProcessExec: + return p.processSchedProcessExec(signal.args) + case events.SchedProcessExit: + return p.processSchedProcessExit(signal.args) } return nil } +// +// TODO: +// +// If we agree that all signal events are just regular events without the event_context_t prefixing +// them, like the existing cases below, then we should create specific parsing functions for the +// arguments (instead of having a single parsing function to each signal type (just like we do in +// the regular pipeline). OR at least we should have a generic parsing function WHEN that is true +// (and allow other parsing functions in the case the signal event use other arguments than a +// regular existing event arguments. +// + func (p *Controller) processCgroupMkdir(args []trace.Argument) error { cgroupId, err := parse.ArgVal[uint64](args, "cgroup_id") if err != nil { @@ -137,21 +183,20 @@ func (p *Controller) processCgroupMkdir(args []trace.Argument) error { if err != nil { return errfmt.WrapError(err) } + if info.Container.ContainerId == "" && !info.Dead { - // If cgroupId is from a regular cgroup directory, and not the - // container base directory (from known runtimes), it should be - // removed from the containers bpf map. + // If cgroupId is from a regular cgroup directory, and not the container base directory + // (from known runtimes), it should be removed from the containers bpf map. err := capabilities.GetInstance().EBPF( func() error { return p.cgroupManager.RemoveFromBPFMap(p.bpfModule, cgroupId, hId) }, ) if err != nil { - // If the cgroupId was not found in bpf map, this could mean that - // it is not a container cgroup and, as a systemd cgroup, could have been - // created and removed very quickly. + // If the cgroupId was not found in bpf map, this could mean that it is not a container + // cgroup and, as a systemd cgroup, could have been created and removed very quickly. // In this case, we don't want to return an error. - logger.Debugw("Failed to remove entry from containers bpf map", "error", err) + logger.Debugw("failed to remove entry from containers bpf map", "error", err) } return errfmt.WrapError(err) } @@ -172,13 +217,245 @@ func (p *Controller) processCgroupMkdir(args []trace.Argument) error { func (p *Controller) processCgroupRmdir(args []trace.Argument) error { cgroupId, err := parse.ArgVal[uint64](args, "cgroup_id") if err != nil { - return errfmt.Errorf("error parsing cgroup_rmdir args: %v", err) + return errfmt.Errorf("error parsing cgroup_rmdir signal args: %v", err) } - hId, err := parse.ArgVal[uint32](args, "hierarchy_id") if err != nil { - return errfmt.Errorf("error parsing cgroup_rmdir args: %v", err) + return errfmt.Errorf("error parsing cgroup_rmdir signal args: %v", err) } p.cgroupManager.CgroupRemove(cgroupId, hId) + + return nil +} + +func (p *Controller) processSchedProcessFork(args []trace.Argument) error { + var err error + var file *os.File + + var parentTid, parentNsTid, parentPid, parentNsPid int32 + var childTid, childNsTid, childPid, childNsPid int32 + var parentStartTime, startTime uint64 + + if len(args) != 10 { + err = errfmt.Errorf("got %d args instead of %d", len(args), 10) + goto failed + } + + // Parent + parentTid, err = parse.ArgVal[int32](args, "parent_tid") + if err != nil { + goto failed + } + parentNsTid, err = parse.ArgVal[int32](args, "parent_ns_tid") + if err != nil { + goto failed + } + parentPid, err = parse.ArgVal[int32](args, "parent_pid") + if err != nil { + goto failed + } + parentNsPid, err = parse.ArgVal[int32](args, "parent_ns_pid") + if err != nil { + goto failed + } + // Child + childTid, err = parse.ArgVal[int32](args, "child_tid") + if err != nil { + goto failed + } + childNsTid, err = parse.ArgVal[int32](args, "child_ns_tid") + if err != nil { + goto failed + } + childPid, err = parse.ArgVal[int32](args, "child_pid") + if err != nil { + goto failed + } + childNsPid, err = parse.ArgVal[int32](args, "child_ns_pid") + if err != nil { + goto failed + } + // Times + parentStartTime, err = parse.ArgVal[uint64](args, "parent_start_time") + if err != nil { + goto failed + } + startTime, err = parse.ArgVal[uint64](args, "start_time") + if err != nil { + goto failed + } + + // DEBUG (remove only when process tree is implemented) + file, _ = os.Open("/dev/null") + // file = os.Stdout + fmt.Fprintf(file, "--\nFork event received:\n") + fmt.Fprintf(file, "Parent: tid=%d ns_tid=%d pid=%d ns_pid=%d\n", parentTid, parentNsTid, parentPid, parentNsPid) + fmt.Fprintf(file, "Child: tid=%d ns_tid=%d pid=%d ns_pid=%d\n", childTid, childNsTid, childPid, childNsPid) + fmt.Fprintf(file, "parentStartTime=%d\n", parentStartTime) + fmt.Fprintf(file, "startTime=%d\n", startTime) + fmt.Fprintf(file, "--\n") + // END OF DEBUG + + return nil + +failed: + return errfmt.Errorf("error parsing SchedProcessFork signal args: %v", err) +} + +func (p *Controller) processSchedProcessExec(args []trace.Argument) error { + var err error + var file *os.File + + var cmdPath, pathName, interPathName, interpreter, stdinPath string + var dev, interDev uint32 + var inode, ctime, interInode, interCtime uint64 + var inodeMode, stdinType uint16 + var invokedFromKernel int32 + + if len(args) != 16 { + err = errfmt.Errorf("got %d args instead of %d", len(args), 16) + goto failed + } + + // string + cmdPath, err = parse.ArgVal[string](args, "cmdpath") + if err != nil { + goto failed + } + pathName, err = parse.ArgVal[string](args, "pathname") + if err != nil { + goto failed + } + interPathName, err = parse.ArgVal[string](args, "interpreter_pathname") + if err != nil { + goto failed + } + interpreter, err = parse.ArgVal[string](args, "interp") + if err != nil { + goto failed + } + stdinPath, err = parse.ArgVal[string](args, "stdin_path") + if err != nil { + goto failed + } + // dev_t(uint32) + dev, err = parse.ArgVal[uint32](args, "dev") + if err != nil { + goto failed + } + interDev, err = parse.ArgVal[uint32](args, "interpreter_dev") + if err != nil { + goto failed + } + // uint64 + inode, err = parse.ArgVal[uint64](args, "inode") + if err != nil { + goto failed + } + ctime, err = parse.ArgVal[uint64](args, "ctime") + if err != nil { + goto failed + } + interInode, err = parse.ArgVal[uint64](args, "interpreter_inode") + if err != nil { + goto failed + } + interCtime, err = parse.ArgVal[uint64](args, "interpreter_ctime") + if err != nil { + goto failed + } + // uint16 + inodeMode, err = parse.ArgVal[uint16](args, "inode_mode") + if err != nil { + goto failed + } + stdinType, err = parse.ArgVal[uint16](args, "stdin_type") + if err != nil { + goto failed + } + // int32 + invokedFromKernel, err = parse.ArgVal[int32](args, "invoked_from_kernel") + if err != nil { + goto failed + } + + // TODO: deal with argv and envp if ever needed + + // DEBUG (remove only when process tree is implemented) + file, _ = os.Open("/dev/null") + // file = os.Stdout + fmt.Fprintf(file, "--\nExec event received:\n") + fmt.Fprintf(file, "cmdPath=%s\n", cmdPath) + fmt.Fprintf(file, "pathName=%s\n", pathName) + fmt.Fprintf(file, "interPathName=%s\n", interPathName) + fmt.Fprintf(file, "interpreter=%s\n", interpreter) + fmt.Fprintf(file, "stdinPath=%s\n", stdinPath) + fmt.Fprintf(file, "dev=%d\n", dev) + fmt.Fprintf(file, "interDev=%d\n", interDev) + fmt.Fprintf(file, "inode=%d\n", inode) + fmt.Fprintf(file, "ctime=%d\n", ctime) + fmt.Fprintf(file, "interInode=%d\n", interInode) + fmt.Fprintf(file, "interCtime=%d\n", interCtime) + fmt.Fprintf(file, "inodeMode=%d\n", inodeMode) + fmt.Fprintf(file, "stdinType=%d\n", stdinType) + fmt.Fprintf(file, "invokedFromKernel=%d\n", invokedFromKernel) + fmt.Fprintf(file, "--\n") + // END OF DEBUG + + return nil + +failed: + return errfmt.Errorf("error parsing SchedProcessExec signal args: %v", err) +} + +func (p *Controller) processSchedProcessExit(args []trace.Argument) error { + var err error + var file *os.File + + var exitCode int64 + var exitTime uint64 + var hostPid int32 + var groupExit bool + + if len(args) != 4 { + err = errfmt.Errorf("got %d args instead of %d", len(args), 4) + goto failed + } + + // int64 + exitCode, err = parse.ArgVal[int64](args, "exit_code") + if err != nil { + goto failed + } + // uint64 + exitTime, err = parse.ArgVal[uint64](args, "exit_time") + if err != nil { + goto failed + } + // int32 + hostPid, err = parse.ArgVal[int32](args, "host_pid") + if err != nil { + goto failed + } + // bool + groupExit, err = parse.ArgVal[bool](args, "process_group_exit") + if err != nil { + goto failed + } + + // DEBUG (remove only when process tree is implemented) + file, _ = os.Open("/dev/null") + // file = os.Stdout + fmt.Fprintf(file, "--\nExit event received:\n") + fmt.Fprintf(file, "exitCode=%d\n", exitCode) + fmt.Fprintf(file, "groupExit=%t\n", groupExit) + fmt.Fprintf(file, "exitTime=%d\n", exitTime) + fmt.Fprintf(file, "hostPid=%d\n", hostPid) + fmt.Fprintf(file, "--\n") + // END OF DEBUG + return nil + +failed: + return errfmt.Errorf("error parsing SchedProcessExit signal args: %v", err) } diff --git a/pkg/ebpf/probes/probe_group.go b/pkg/ebpf/probes/probe_group.go index 81a800f416c8..3f3905d3dadf 100644 --- a/pkg/ebpf/probes/probe_group.go +++ b/pkg/ebpf/probes/probe_group.go @@ -12,6 +12,8 @@ import ( // // ProbeGroup // +// ATTENTION: Do not implement GetProbe() as the synchronization is handled by the ProbeGroup. +// // ProbeGroup is a collection of probes. type ProbeGroup struct { @@ -30,7 +32,7 @@ func NewProbeGroup(m *bpf.Module, p map[Handle]Probe) *ProbeGroup { } // GetProbe returns a probe type by its handle. -func (p *ProbeGroup) GetProbeType(handle Handle) string { +func (p *ProbeGroup) GetProbeTypeByHandle(handle Handle) string { p.probesLock.Lock() defer p.probesLock.Unlock() @@ -52,6 +54,24 @@ func (p *ProbeGroup) GetProbeType(handle Handle) string { return "" } +// GetProbeNmae returns a probe name by its handle. +func (p *ProbeGroup) GetProgramNameByHandle(handle Handle) string { + p.probesLock.Lock() + defer p.probesLock.Unlock() + + switch p.probes[handle].(type) { + case *TraceProbe: + return p.probes[handle].(*TraceProbe).programName + case *Uprobe: + return p.probes[handle].(*Uprobe).programName + case *CgroupProbe: + return p.probes[handle].(*CgroupProbe).programName + } + + // default + return "" +} + // Attach attaches a probe's program to its hook, by given handle. func (p *ProbeGroup) Attach(handle Handle, args ...interface{}) error { p.probesLock.Lock() diff --git a/pkg/ebpf/tracee.go b/pkg/ebpf/tracee.go index 7165d45b0bf1..71881acd0559 100644 --- a/pkg/ebpf/tracee.go +++ b/pkg/ebpf/tracee.go @@ -122,16 +122,6 @@ func (t *Tracee) Stats() *metrics.Stats { return &t.stats } -// GetEssentialEventsList sets the default events used by tracee -func GetEssentialEventsList() map[events.ID]events.EventState { - // Set essential events - return map[events.ID]events.EventState{ - events.SchedProcessExec: {}, - events.SchedProcessExit: {}, - events.SchedProcessFork: {}, - } -} - // GetCaptureEventsList sets events used to capture data func GetCaptureEventsList(cfg config.Config) map[events.ID]events.EventState { captureEvents := make(map[events.ID]events.EventState) @@ -178,19 +168,23 @@ func GetCaptureEventsList(cfg config.Config) map[events.ID]events.EventState { return captureEvents } -func (t *Tracee) handleEventsDependencies(givenEventId events.ID, submitMap uint64) { - givenEventDefinition := events.Core.GetDefinitionByID(givenEventId) - for _, depEventId := range givenEventDefinition.GetDependencies().GetIDs() { +// handleEventsDependencies handles all events dependencies recursively. +func (t *Tracee) handleEventsDependencies(givenEvtId events.ID, givenEvtState events.EventState) { + givenEvtDefinition := events.Core.GetDefinitionByID(givenEvtId) + + for _, depEventId := range givenEvtDefinition.GetDependencies().GetIDs() { depEventState, ok := t.eventsState[depEventId] if !ok { depEventState = events.EventState{} - t.handleEventsDependencies(depEventId, submitMap) + t.handleEventsDependencies(depEventId, givenEvtState) // dependencies of dependencies } - depEventState.Submit |= submitMap + depEventState.Submit |= givenEvtState.Submit + depEventState.Control = givenEvtState.Control + t.eventsState[depEventId] = depEventState - if events.Core.GetDefinitionByID(givenEventId).IsSignature() { + if events.Core.GetDefinitionByID(givenEvtId).IsSignature() { t.eventSignatures[depEventId] = true } } @@ -213,10 +207,20 @@ func New(cfg config.Config) (*Tracee, error) { writtenFiles: make(map[string]string), readFiles: make(map[string]string), capturedFiles: make(map[string]int64), - eventsState: GetEssentialEventsList(), + eventsState: make(map[events.ID]events.EventState), eventSignatures: make(map[events.ID]bool), } + // SchedProcessFork, ShedProcessExec and SchedProcessExit are needed not only for the control + // plane, but also for events processed by regular pipeline. In the future, it is possible that + // all events like that will be processed by the control plane, and not by the regular pipeline. + // For now, we need to make sure that these events are processed by both. + for _, evtID := range controlplane.CoreEventsNeeded { + t.eventsState[evtID] = events.EventState{ + Control: true, + } + } + // Initialize capabilities rings soon err = capabilities.Initialize(t.config.Capabilities.BypassCaps) @@ -248,8 +252,8 @@ func New(cfg config.Config) (*Tracee, error) { // Handle all essential events dependencies - for id, evt := range t.eventsState { - t.handleEventsDependencies(id, evt.Submit) + for id, state := range t.eventsState { + t.handleEventsDependencies(id, state) } // Update capabilities rings with all events dependencies @@ -1226,7 +1230,11 @@ func (t *Tracee) initBPF() error { } // Initialize control plane - t.controlPlane, err = controlplane.NewController(t.bpfModule, t.containers, t.config.ContainersEnrich) + t.controlPlane, err = controlplane.NewController( + t.bpfModule, + t.containers, + t.config.ContainersEnrich, + ) if err != nil { return errfmt.WrapError(err) } diff --git a/pkg/events/core.go b/pkg/events/core.go index 58d0ec3ba384..52084a6bcac1 100644 --- a/pkg/events/core.go +++ b/pkg/events/core.go @@ -8895,6 +8895,7 @@ var CoreEvents = map[ID]Definition{ {Type: "int", Name: "parent_ns_tid"}, {Type: "int", Name: "parent_pid"}, {Type: "int", Name: "parent_ns_pid"}, + {Type: "unsigned long", Name: "parent_start_time"}, {Type: "int", Name: "child_tid"}, {Type: "int", Name: "child_ns_tid"}, {Type: "int", Name: "child_pid"}, @@ -8957,12 +8958,14 @@ var CoreEvents = map[ID]Definition{ }, }, sets: []string{"proc", "proc_life"}, + // The "process_group_exit" field value represents that all threads exited at the event + // time. Multiple exits of threads of the same process group at the same time could result + // that all threads exit events would have 'true' value in this field altogether. params: []trace.ArgMeta{ {Type: "long", Name: "exit_code"}, - // The field value represents that all threads exited at the event time. - // Multiple exits of threads of the same process group at the same time could result that all threads exit - // events would have 'true' value in this field altogether. {Type: "bool", Name: "process_group_exit"}, + {Type: "int", Name: "host_pid"}, // used by control plane + {Type: "unsigned long", Name: "exit_time"}, // used by control plane }, }, SchedSwitch: { diff --git a/pkg/events/definition_group.go b/pkg/events/definition_group.go index 07bc126de381..3253805e8903 100644 --- a/pkg/events/definition_group.go +++ b/pkg/events/definition_group.go @@ -11,8 +11,9 @@ import ( // TODO: add states to the EventGroup struct (to keep states of events from that group) type EventState struct { - Submit uint64 // should be submitted to userspace (by policies bitmap) - Emit uint64 // should be emitted to the user (by policies bitmap) + Submit uint64 // should be submitted to userspace (by policies bitmap) + Emit uint64 // should be emitted to the user (by policies bitmap) + Control bool // needed by the control plane (submits independently of policies bitmap) } // ATTENTION: the definition group is instantiable (all the rest is immutable) @@ -185,7 +186,7 @@ func (d *DefinitionGroup) GetTailCalls(state map[ID]EventState) []TailCall { var tailCalls []TailCall for evtDefID, evtDef := range d.definitions { - if state[evtDefID].Submit > 0 { // only traced events to provide their tailcalls + if state[evtDefID].Submit > 0 || state[evtDefID].Control { tailCalls = append(tailCalls, evtDef.GetDependencies().GetTailCalls()...) } } diff --git a/pkg/events/parse/params.go b/pkg/events/parse/params.go index 7060910affe4..cc504f1acd9a 100644 --- a/pkg/events/parse/params.go +++ b/pkg/events/parse/params.go @@ -11,7 +11,12 @@ func ArgVal[T any](args []trace.Argument, argName string) (T, error) { val, ok := arg.Value.(T) if !ok { zeroVal := *new(T) - return zeroVal, errfmt.Errorf("argument %s is not of type %T", argName, zeroVal) + return zeroVal, errfmt.Errorf( + "argument %s is not of type %T, is of type %T", + argName, + zeroVal, + arg.Value, + ) } return val, nil }