diff --git a/driver/feature_gates.h b/driver/feature_gates.h index bcc1da3eb7..db494285d3 100644 --- a/driver/feature_gates.h +++ b/driver/feature_gates.h @@ -15,7 +15,8 @@ or GPL2.txt for full copies of the license. * These feature gates are used by: * - kernel module * - BPF probe - * - userspace + * - userspace + * - modern BPF probe * to compile out some features. The userspace is in charge of * filling the BPF maps that's why it also needs these macros. * @@ -131,6 +132,24 @@ or GPL2.txt for full copies of the license. #define CAPTURE_PAGE_FAULTS #endif +#elif defined(__USE_VMLINUX__) /* modern BPF probe */ + +/////////////////////////////// +// CAPTURE_SCHED_PROC_EXEC +/////////////////////////////// + +#if defined(__TARGET_ARCH_arm64) + #define CAPTURE_SCHED_PROC_EXEC +#endif + +/////////////////////////////// +// CAPTURE_SCHED_PROC_FORK +/////////////////////////////// + +#if defined(__TARGET_ARCH_arm64) || defined(__TARGET_ARCH_s390) + #define CAPTURE_SCHED_PROC_FORK +#endif + #else /* Userspace */ /* Please note: the userspace loads the filler table for the bpf probe diff --git a/driver/modern_bpf/definitions/vmlinux.h b/driver/modern_bpf/definitions/vmlinux.h index aabda206a6..e4348edc53 100644 --- a/driver/modern_bpf/definitions/vmlinux.h +++ b/driver/modern_bpf/definitions/vmlinux.h @@ -2692,6 +2692,10 @@ enum rseq_cs_flags_bit { RSEQ_CS_FLAG_NO_RESTART_ON_MIGRATE_BIT = 2, }; +enum { + TASK_COMM_LEN = 16, +}; + struct rseq { __u32 cpu_id_start; __u32 cpu_id; @@ -136555,6 +136559,10 @@ enum rseq_cs_flags_bit { RSEQ_CS_FLAG_NO_RESTART_ON_MIGRATE_BIT = 2, }; +enum { + TASK_COMM_LEN = 16, +}; + enum uclamp_id { UCLAMP_MIN = 0, UCLAMP_MAX = 1, diff --git a/driver/modern_bpf/helpers/extract/extract_from_kernel.h b/driver/modern_bpf/helpers/extract/extract_from_kernel.h index 79ddaf3062..8720583227 100644 --- a/driver/modern_bpf/helpers/extract/extract_from_kernel.h +++ b/driver/modern_bpf/helpers/extract/extract_from_kernel.h @@ -210,39 +210,6 @@ static __always_inline u64 extract__capability(struct task_struct *task, enum ca return capabilities_to_scap(((unsigned long)cap_struct.cap[1] << 32) | cap_struct.cap[0]); } -/////////////////////////// -// CHARBUF EXTRACION -/////////////////////////// - -/** - * @brief Extract a specif charbuf pointer from an array of charbuf pointers - * using `index`. - * - * Please note: Here we don't care about the result of `bpf_probe_read_...()` - * if we obtain a not-valid pointer we will manage it in the caller - * functions. - * - * @param array charbuf pointers array. - * @param index at which we want to extract the charbuf pointer. - * @param mem from which memory we need to read: user-space or kernel-space. - * @return unsigned long return the extracted charbuf pointer or an invalid pointer in - * case of failure. - */ -static __always_inline unsigned long extract__charbuf_pointer_from_array(unsigned long array, u16 index, enum read_memory mem) -{ - char **charbuf_array = (char **)array; - char *charbuf_pointer = NULL; - if(mem == KERNEL) - { - bpf_probe_read_kernel(&charbuf_pointer, sizeof(charbuf_pointer), &charbuf_array[index]); - } - else - { - bpf_probe_read_user(&charbuf_pointer, sizeof(charbuf_pointer), &charbuf_array[index]); - } - return (unsigned long)charbuf_pointer; -} - ///////////////////////// // PIDS EXTRACION //////////////////////// diff --git a/driver/modern_bpf/helpers/store/auxmap_store_params.h b/driver/modern_bpf/helpers/store/auxmap_store_params.h index 0765e4e94d..aeab8617e0 100644 --- a/driver/modern_bpf/helpers/store/auxmap_store_params.h +++ b/driver/modern_bpf/helpers/store/auxmap_store_params.h @@ -10,7 +10,14 @@ #include #include -/* Right now a cgroup pathname can have at most 6 components. */ +/*=============================== FIXED CONSTRAINTS ===============================*/ + +/* These are some of the constraints we want to impose during our + * store operations. One day these could become const global variables + * that could be set by the userspace. + */ + +/* Right now a `cgroup` pathname can have at most 6 components. */ #define MAX_CGROUP_PATH_POINTERS 6 /* Right now a file path extracted from a file descriptor can @@ -18,15 +25,35 @@ */ #define MAX_PATH_POINTERS 8 -/* Maximum length of unix socket path. - * We can have at maximum 108 characters plus the `\0` terminator. +/* Maximum length of `unix` socket path. + * We can have a maximum of 108 characters plus the `\0` terminator. */ #define MAX_UNIX_SOCKET_PATH 108 + 1 -/* Max number of iovec structure that we can analize. */ +/* Maximum number of `iovec` structures that we can analyze. */ #define MAX_IOVCNT 32 -/* Conversion factors used in setsockopt val. */ +/* Maximum number of charbuf pointers that we assume an array can have. */ +#define MAX_CHARBUF_POINTERS 16 + +/* Proc name */ +#define MAX_PROC_EXE 4096 + +/* Proc arguments or environment variables. + * Must be always a power of 2 because we can also use it as a mask! + */ +#define MAX_PROC_ARG_ENV 4096 + +/* PATH_MAX supported by the operating system: 4096 */ +#define MAX_PATH 4096 + +/*=============================== FIXED CONSTRAINTS ===============================*/ + +/*=============================== COMMON DEFINITIONS ===============================*/ + +/* Some auxiliary definitions we use during our store operations */ + +/* Conversion factors used in `setsockopt` val. */ #define SEC_FACTOR 1000000000 #define USEC_FACTOR 1000 @@ -46,8 +73,7 @@ enum connection_direction INBOUND = 1, }; -/* Maximum number of charbuf pointers that we assume an array can have. */ -#define MAX_CHARBUF_POINTERS 16 +/*=============================== COMMON DEFINITIONS ===============================*/ /* Concept of auxamp (auxiliary map): * @@ -300,18 +326,27 @@ static __always_inline void auxmap__store_u64_param(struct auxiliary_map *auxmap /** * @brief This helper stores the charbuf pointed by `charbuf_pointer` - * into the auxmap. The charbuf can have a maximum length - * of `MAX_PARAM_SIZE`. For more details, look at the underlying + * into the auxmap. We read until we find a `\0`, if the charbuf length + * is greater than `len_to_read`, we read up to `len_to_read-1` bytes + * and add the `\0`. For more details, look at the underlying * `push__charbuf` method * * @param auxmap pointer to the auxmap in which we are storing the param. * @param charbuf_pointer pointer to the charbuf to store. + * @param len_to_read upper bound limit. * @param mem from which memory we need to read: user-space or kernel-space. * @return number of bytes read. */ -static __always_inline u16 auxmap__store_charbuf_param(struct auxiliary_map *auxmap, unsigned long charbuf_pointer, enum read_memory mem) +static __always_inline u16 auxmap__store_charbuf_param(struct auxiliary_map *auxmap, unsigned long charbuf_pointer, u16 len_to_read, enum read_memory mem) { - u16 charbuf_len = push__charbuf(auxmap->data, &auxmap->payload_pos, charbuf_pointer, MAX_PARAM_SIZE, mem); + u16 charbuf_len = 0; + /* This check is just for performance reasons. Is useless to check + * `len_to_read > 0` here, since `len_to_read` is just the upper bound. + */ + if(charbuf_pointer) + { + charbuf_len = push__charbuf(auxmap->data, &auxmap->payload_pos, charbuf_pointer, len_to_read, mem); + } /* If we are not able to push anything with `push__charbuf` * `charbuf_len` will be equal to `0` so we will send an * empty param to userspace. @@ -332,10 +367,11 @@ static __always_inline u16 auxmap__store_charbuf_param(struct auxiliary_map *aux * @param mem from which memory we need to read: user-space or kernel-space. * @return number of bytes read. */ -static __always_inline u16 auxmap__store_bytebuf_param(struct auxiliary_map *auxmap, unsigned long bytebuf_pointer, unsigned long len_to_read, enum read_memory mem) +static __always_inline u16 auxmap__store_bytebuf_param(struct auxiliary_map *auxmap, unsigned long bytebuf_pointer, u16 len_to_read, enum read_memory mem) { u16 bytebuf_len = 0; - if (len_to_read > 0) + /* This check is just for performance reasons. */ + if(bytebuf_pointer && len_to_read > 0) { bytebuf_len = push__bytebuf(auxmap->data, &auxmap->payload_pos, bytebuf_pointer, len_to_read, mem); } @@ -348,58 +384,66 @@ static __always_inline u16 auxmap__store_bytebuf_param(struct auxiliary_map *aux } /** - * @brief Use `auxmap__store_single_charbuf_param_from_array` when - * you have to store a charbuf from a charbuf pointer array. - * You have to provide the index of the charbuf pointer inside the - * array. Indexes start from '0' as usual. - * Once we obtain the pointer with `extract__charbuf_pointer_from_array`, - * we can store the charbuf with `auxmap__store_charbuf_param`. + * @brief Use `auxmap__store_execve_exe` when you have to store the + * `exe` name from an execve-family syscall. + * By convention, `exe` is `argv[0]`, this is the reason why here we pass the `argv` array. * * @param auxmap pointer to the auxmap in which we are storing the param. - * @param array charbuf pointer array. - * @param index position at which we want to extract our charbuf. - * @param mem from which memory we need to read: user-space or kernel-space. + * @param array charbuf pointer array, obtained directly from the syscall (`argv`). */ -static __always_inline void auxmap__store_single_charbuf_param_from_array(struct auxiliary_map *auxmap, unsigned long array, u16 index, enum read_memory mem) +static __always_inline void auxmap__store_execve_exe(struct auxiliary_map *auxmap, char **array) { - unsigned long charbuf_pointer = extract__charbuf_pointer_from_array(array, index, mem); - auxmap__store_charbuf_param(auxmap, charbuf_pointer, mem); + unsigned long charbuf_pointer = 0; + u16 exe_len = 0; + + if(bpf_probe_read_user(&charbuf_pointer, sizeof(charbuf_pointer), &array[0])) + { + push__param_len(auxmap->data, &auxmap->lengths_pos, exe_len); + return; + } + + exe_len = push__charbuf(auxmap->data, &auxmap->payload_pos, charbuf_pointer, MAX_PROC_EXE, USER); + push__param_len(auxmap->data, &auxmap->lengths_pos, exe_len); } /** - * @brief Use `auxmap__store_multiple_charbufs_param_from_array` when - * you have to store multiple charbufs from a charbuf pointer - * array. You have to provide an index that states where to start - * the charbuf collection. If you want to store all the charbufs - * pointed in the array, you can use '0' as 'index'. + * @brief Use `auxmap__store_execve_args` when you have to store + * `argv` or `envp` params from an execve-family syscall. + * You have to provide an index that states where to start + * the charbuf collection. This is becuase with `argv` we want to avoid + * the first param (`argv[0]`), since it is already collected with + * `auxmap__store_execve_exe`. * * Please note: right now we assume that our arrays have no more * than `MAX_CHARBUF_POINTERS` * * @param auxmap pointer to the auxmap in which we are storing the param. - * @param array charbuf pointer array + * @param array charbuf pointer array, obtained directly from the syscall (`argv` or `envp`). * @param index position at which we start to collect our charbufs. - * @param mem from which memory we need to read: user-space or kernel-space. */ -static __always_inline void auxmap__store_multiple_charbufs_param_from_array(struct auxiliary_map *auxmap, unsigned long array, u16 index, enum read_memory mem) +static __always_inline void auxmap__store_execve_args(struct auxiliary_map *auxmap, char **array, u16 index) { - unsigned long charbuf_pointer; - u16 charbuf_len = 0; + unsigned long charbuf_pointer = 0; + u16 arg_len = 0; u16 total_len = 0; - /* We push in the auxmap all the charbufs that we find. - * We push the overall length only at the end of the - * for loop with `push__param_len`. - */ + u64 initial_payload_pos = auxmap->payload_pos; + for(; index < MAX_CHARBUF_POINTERS; ++index) { - charbuf_pointer = extract__charbuf_pointer_from_array(array, index, mem); - charbuf_len = push__charbuf(auxmap->data, &auxmap->payload_pos, charbuf_pointer, MAX_PARAM_SIZE, mem); - if(!charbuf_len) + if(bpf_probe_read_user(&charbuf_pointer, sizeof(charbuf_pointer), &array[index])) + { + break; + } + arg_len = push__charbuf(auxmap->data, &auxmap->payload_pos, charbuf_pointer, MAX_PROC_ARG_ENV, USER); + if(!arg_len) { break; } - total_len += charbuf_len; + total_len += arg_len; } + /* the sum of all env variables lengths should be `<= MAX_PROC_ARG_ENV` */ + total_len = total_len & (MAX_PROC_ARG_ENV - 1); + auxmap->payload_pos = initial_payload_pos + total_len; push__param_len(auxmap->data, &auxmap->lengths_pos, total_len); } diff --git a/driver/modern_bpf/programs/attached/events/sched_process_exec.bpf.c b/driver/modern_bpf/programs/attached/events/sched_process_exec.bpf.c new file mode 100644 index 0000000000..b761a6f9e0 --- /dev/null +++ b/driver/modern_bpf/programs/attached/events/sched_process_exec.bpf.c @@ -0,0 +1,207 @@ +/* + * Copyright (C) 2022 The Falco Authors. + * + * This file is dual licensed under either the MIT or GPL 2. See MIT.txt + * or GPL2.txt for full copies of the license. + */ + +#include +#include + +/* From linux tree: /include/trace/events/sched.h + * TP_PROTO(struct task_struct *p, pid_t old_pid, + * struct linux_binprm *bprm) + */ +#ifdef CAPTURE_SCHED_PROC_EXEC +/* chose a short name for bpftool debugging*/ +SEC("tp_btf/sched_process_exec") +int BPF_PROG(sched_p_exec, + struct task_struct *p, pid_t old_pid, + struct linux_binprm *bprm) +{ + if(!attached_programs__capture_enabled()) + { + return 0; + } + + struct task_struct *task = get_current_task(); + uint32_t flags = 0; + READ_TASK_FIELD_INTO(&flags, task, flags); + + /* We are not interested in kernel threads. */ + if(flags & PF_KTHREAD) + { + return 0; + } + + struct auxiliary_map *auxmap = auxmap__get(); + if(!auxmap) + { + return 0; + } + auxmap__preload_event_header(auxmap, PPME_SYSCALL_EXECVE_19_X); + + /*=============================== COLLECT PARAMETERS ===========================*/ + + /* Parameter 1: res (type: PT_ERRNO) */ + /* Please note: if this tracepoint is called the `execve/execveat` call + * is correctly performed, so the return value will be always 0. + */ + auxmap__store_s64_param(auxmap, 0); + + unsigned long arg_start_pointer = 0; + unsigned long arg_end_pointer = 0; + + /* `arg_start` points to the memory area where arguments start. + * We directly read charbufs from there, not pointers to charbufs! + * We will store charbufs directly from memory. + */ + READ_TASK_FIELD_INTO(&arg_start_pointer, task, mm, arg_start); + READ_TASK_FIELD_INTO(&arg_end_pointer, task, mm, arg_end); + + unsigned long total_args_len = arg_end_pointer - arg_start_pointer; + + /* Parameter 2: exe (type: PT_CHARBUF) */ + /* We need to extract the len of `exe` arg so we can understand + * the overall length of the remaining args. + */ + u16 exe_arg_len = auxmap__store_charbuf_param(auxmap, arg_start_pointer, MAX_PROC_EXE, USER); + + /* Parameter 3: args (type: PT_CHARBUFARRAY) */ + /* Here we read the whole array starting from the pointer to the first + * element. We could also read the array element per element but + * since we know the total len we read it as a `bytebuf`. + * The `\0` after every argument is preserved. + */ + auxmap__store_bytebuf_param(auxmap, arg_start_pointer + exe_arg_len, (total_args_len - exe_arg_len) & (MAX_PROC_ARG_ENV - 1), USER); + + /* Parameter 4: tid (type: PT_PID) */ + /* this is called `tid` but it is the `pid`. */ + s64 pid = (s64)extract__task_xid_nr(task, PIDTYPE_PID); + auxmap__store_s64_param(auxmap, pid); + + /* Parameter 5: pid (type: PT_PID) */ + /* this is called `pid` but it is the `tgid`. */ + s64 tgid = (s64)extract__task_xid_nr(task, PIDTYPE_TGID); + auxmap__store_s64_param(auxmap, tgid); + + /* Parameter 6: ptid (type: PT_PID) */ + /* this is called `ptid` but it is the `pgid`. */ + s64 pgid = (s64)extract__task_xid_nr(task, PIDTYPE_PGID); + auxmap__store_s64_param(auxmap, pgid); + + /* Parameter 7: cwd (type: PT_CHARBUF) */ + /// TODO: right now we leave the current working directory empty like in the old probe. + auxmap__store_empty_param(auxmap); + + /* Parameter 8: fdlimit (type: PT_UINT64) */ + unsigned long fdlimit = 0; + extract__fdlimit(task, &fdlimit); + auxmap__store_u64_param(auxmap, fdlimit); + + /* Parameter 9: pgft_maj (type: PT_UINT64) */ + unsigned long pgft_maj = 0; + extract__pgft_maj(task, &pgft_maj); + auxmap__store_u64_param(auxmap, pgft_maj); + + /* Parameter 10: pgft_min (type: PT_UINT64) */ + unsigned long pgft_min = 0; + extract__pgft_min(task, &pgft_min); + auxmap__store_u64_param(auxmap, pgft_min); + + struct mm_struct *mm = NULL; + READ_TASK_FIELD_INTO(&mm, task, mm); + + /* Parameter 11: vm_size (type: PT_UINT32) */ + u32 vm_size = extract__vm_size(mm); + auxmap__store_u32_param(auxmap, vm_size); + + /* Parameter 12: vm_rss (type: PT_UINT32) */ + u32 vm_rss = extract__vm_rss(mm); + auxmap__store_u32_param(auxmap, vm_rss); + + /* Parameter 13: vm_swap (type: PT_UINT32) */ + u32 vm_swap = extract__vm_swap(mm); + auxmap__store_u32_param(auxmap, vm_swap); + + /* Parameter 14: comm (type: PT_CHARBUF) */ + auxmap__store_charbuf_param(auxmap, (unsigned long)task->comm, TASK_COMM_LEN, KERNEL); + + /*=============================== COLLECT PARAMETERS ===========================*/ + + bpf_tail_call(ctx, &extra_event_prog_tail_table, T1_SCHED_PROC_EXEC); + return 0; +} + +SEC("tp_btf/sched_process_exec") +int BPF_PROG(t1_sched_p_exec, + struct task_struct *p, pid_t old_pid, + struct linux_binprm *bprm) +{ + struct auxiliary_map *auxmap = auxmap__get(); + if(!auxmap) + { + return 0; + } + + /*=============================== COLLECT PARAMETERS ===========================*/ + + struct task_struct *task = get_current_task(); + + /* Parameter 15: cgroups (type: PT_CHARBUFARRAY) */ + auxmap__store_cgroups_param(auxmap, task); + + unsigned long env_start_pointer = 0; + unsigned long env_end_pointer = 0; + + READ_TASK_FIELD_INTO(&env_start_pointer, task, mm, env_start); + READ_TASK_FIELD_INTO(&env_end_pointer, task, mm, env_end); + + unsigned long total_env_len = env_end_pointer - env_start_pointer; + + /* Parameter 16: env (type: PT_CHARBUFARRAY) */ + /* Here we read all the array starting from the pointer to the first + * element. We could also read the array element per element but + * since we know the total len we read it as a `bytebuf`. + * The `\0` after every argument are preserved. + */ + auxmap__store_bytebuf_param(auxmap, env_start_pointer, total_env_len & (MAX_PROC_ARG_ENV - 1), USER); + + /* Parameter 17: tty (type: PT_INT32) */ + u32 tty = exctract__tty(task); + auxmap__store_s32_param(auxmap, (s32)tty); + + /* Parameter 18: pgid (type: PT_PID) */ + pid_t pgid = extract__task_xid_vnr(task, PIDTYPE_PGID); + auxmap__store_s64_param(auxmap, (s64)pgid); + + /* Parameter 19: loginuid (type: PT_INT32) */ + u32 loginuid; + extract__loginuid(task, &loginuid); + auxmap__store_s32_param(auxmap, (s32)loginuid); + + /* Parameter 20: flags (type: PT_FLAGS32) */ + /// TODO: still to implement! See what we do in the old probe. + u32 flags = 0; + auxmap__store_u32_param(auxmap, flags); + + /* Parameter 21: cap_inheritable (type: PT_UINT64) */ + u64 cap_inheritable = extract__capability(task, CAP_INHERITABLE); + auxmap__store_u64_param(auxmap, cap_inheritable); + + /* Parameter 22: cap_permitted (type: PT_UINT64) */ + u64 cap_permitted = extract__capability(task, CAP_PERMITTED); + auxmap__store_u64_param(auxmap, cap_permitted); + + /* Parameter 23: cap_effective (type: PT_UINT64) */ + u64 cap_effective = extract__capability(task, CAP_EFFECTIVE); + auxmap__store_u64_param(auxmap, cap_effective); + + /*=============================== COLLECT PARAMETERS ===========================*/ + + auxmap__finalize_event_header(auxmap); + + auxmap__submit_event(auxmap); + return 0; +} +#endif /* CAPTURE_SCHED_PROC_EXEC */ diff --git a/driver/modern_bpf/programs/attached/events/sched_process_fork.bpf.c b/driver/modern_bpf/programs/attached/events/sched_process_fork.bpf.c new file mode 100644 index 0000000000..bac5f982fc --- /dev/null +++ b/driver/modern_bpf/programs/attached/events/sched_process_fork.bpf.c @@ -0,0 +1,229 @@ +/* + * Copyright (C) 2022 The Falco Authors. + * + * This file is dual licensed under either the MIT or GPL 2. See MIT.txt + * or GPL2.txt for full copies of the license. + */ + +#include +#include + +/* From linux tree: /include/trace/events/sched.h + * TP_PROTO(struct task_struct *parent, + * struct task_struct *child) + */ + +#ifdef CAPTURE_SCHED_PROC_FORK +/* chose a short name for bpftool debugging*/ +SEC("tp_btf/sched_process_fork") +int BPF_PROG(sched_p_fork, + struct task_struct *parent, struct task_struct *child) +{ + if(!attached_programs__capture_enabled()) + { + return 0; + } + + struct task_struct *task = get_current_task(); + uint32_t flags = 0; + READ_TASK_FIELD_INTO(&flags, task, flags); + + /* We are not interested in kernel threads. */ + if(flags & PF_KTHREAD) + { + return 0; + } + + struct auxiliary_map *auxmap = auxmap__get(); + if(!auxmap) + { + return 0; + } + auxmap__preload_event_header(auxmap, PPME_SYSCALL_CLONE_20_X); + + /*=============================== COLLECT PARAMETERS ===========================*/ + + /* First of all we need to update the event header with the child tid. + * The clone child exit event must be generated by the child but while + * we are sending this event, we are still the parent so we have to + * modify the event header to simulate it. + */ + pid_t child_pid = 0; + READ_TASK_FIELD_INTO(&child_pid, child, pid); + struct ppm_evt_hdr *hdr = (struct ppm_evt_hdr *)auxmap->data; + hdr->tid = (u64)child_pid; + + /* Parameter 1: res (type: PT_ERRNO) */ + /* Please note: here we are in the clone child exit + * event, so the return value will be always 0. + */ + auxmap__store_s64_param(auxmap, 0); + + unsigned long arg_start_pointer = 0; + unsigned long arg_end_pointer = 0; + + /* `arg_start` points to the memory area where arguments start. + * We directly read charbufs from there, not pointers to charbufs! + * We will store charbufs directly from memory. + */ + READ_TASK_FIELD_INTO(&arg_start_pointer, child, mm, arg_start); + READ_TASK_FIELD_INTO(&arg_end_pointer, child, mm, arg_end); + + unsigned long total_args_len = arg_end_pointer - arg_start_pointer; + + /* Parameter 2: exe (type: PT_CHARBUF) */ + /* We need to extract the len of `exe` arg so we can understand + * the overall length of the remaining args. + */ + u16 exe_arg_len = auxmap__store_charbuf_param(auxmap, arg_start_pointer, MAX_PROC_EXE, USER); + + /* Parameter 3: args (type: PT_CHARBUFARRAY) */ + /* Here we read the whole array starting from the pointer to the first + * element. We could also read the array element per element but + * since we know the total len we read it as a `bytebuf`. + * The `\0` after every argument is preserved. + */ + auxmap__store_bytebuf_param(auxmap, arg_start_pointer + exe_arg_len, (total_args_len - exe_arg_len) & (MAX_PROC_ARG_ENV - 1), USER); + + /* Parameter 4: tid (type: PT_PID) */ + /* this is called `tid` but it is the `pid`. */ + s64 pid = (s64)extract__task_xid_nr(child, PIDTYPE_PID); + auxmap__store_s64_param(auxmap, pid); + + /* Parameter 5: pid (type: PT_PID) */ + /* this is called `pid` but it is the `tgid`. */ + s64 tgid = (s64)extract__task_xid_nr(child, PIDTYPE_TGID); + auxmap__store_s64_param(auxmap, tgid); + + /* Parameter 6: ptid (type: PT_PID) */ + /* this is called `ptid` but it is the `pgid`. */ + s64 pgid = (s64)extract__task_xid_nr(child, PIDTYPE_PGID); + auxmap__store_s64_param(auxmap, pgid); + + /* Parameter 7: cwd (type: PT_CHARBUF) */ + /// TODO: right now we leave the current working directory empty like in the old probe. + auxmap__store_empty_param(auxmap); + + /* Parameter 8: fdlimit (type: PT_UINT64) */ + unsigned long fdlimit = 0; + extract__fdlimit(child, &fdlimit); + auxmap__store_u64_param(auxmap, fdlimit); + + /* Parameter 9: pgft_maj (type: PT_UINT64) */ + unsigned long pgft_maj = 0; + extract__pgft_maj(child, &pgft_maj); + auxmap__store_u64_param(auxmap, pgft_maj); + + /* Parameter 10: pgft_min (type: PT_UINT64) */ + unsigned long pgft_min = 0; + extract__pgft_min(child, &pgft_min); + auxmap__store_u64_param(auxmap, pgft_min); + + struct mm_struct *mm = NULL; + READ_TASK_FIELD_INTO(&mm, child, mm); + + /* Parameter 11: vm_size (type: PT_UINT32) */ + u32 vm_size = extract__vm_size(mm); + auxmap__store_u32_param(auxmap, vm_size); + + /* Parameter 12: vm_rss (type: PT_UINT32) */ + u32 vm_rss = extract__vm_rss(mm); + auxmap__store_u32_param(auxmap, vm_rss); + + /* Parameter 13: vm_swap (type: PT_UINT32) */ + u32 vm_swap = extract__vm_swap(mm); + auxmap__store_u32_param(auxmap, vm_swap); + + /* Parameter 14: comm (type: PT_CHARBUF) */ + auxmap__store_charbuf_param(auxmap, (unsigned long)child->comm, TASK_COMM_LEN, KERNEL); + + /*=============================== COLLECT PARAMETERS ===========================*/ + + bpf_tail_call(ctx, &extra_event_prog_tail_table, T1_SCHED_PROC_FORK); + return 0; +} + +SEC("tp_btf/sched_process_fork") +int BPF_PROG(t1_sched_p_fork, + struct task_struct *parent, struct task_struct *child) +{ + struct auxiliary_map *auxmap = auxmap__get(); + if(!auxmap) + { + return 0; + } + + /*=============================== COLLECT PARAMETERS ===========================*/ + + /* Parameter 15: cgroups (type: PT_CHARBUFARRAY) */ + auxmap__store_cgroups_param(auxmap, child); + + /* Parameter 16: flags (type: PT_FLAGS32) */ + u32 flags = 0; + + /* Since Linux 2.5.35, the flags mask must also include + * CLONE_SIGHAND if CLONE_THREAD is specified (and note that, + * since Linux 2.6.0, CLONE_SIGHAND also requires CLONE_VM to + * be included). + * Taken from https://man7.org/linux/man-pages/man2/clone.2.html + */ + pid_t tid = READ_TASK_FIELD(child, pid); + pid_t tgid = READ_TASK_FIELD(child, tgid); + if(tid != tgid) + { + flags |= PPM_CL_CLONE_THREAD | PPM_CL_CLONE_SIGHAND | PPM_CL_CLONE_VM; + } + + /* If CLONE_FILES is set, the calling process and the child + * process share the same file descriptor table. + * Taken from https://man7.org/linux/man-pages/man2/clone.2.html + */ + struct files_struct *file_struct = NULL; + struct files_struct *parent_file_struct = NULL; + READ_TASK_FIELD_INTO(&file_struct, child, files); + READ_TASK_FIELD_INTO(&parent_file_struct, parent, files); + if(parent_file_struct == file_struct) + { + flags |= PPM_CL_CLONE_FILES; + } + auxmap__store_u32_param(auxmap, flags); + + + /* It's possible to have a process in a PID namespace that + * nevertheless has tid == vtid, so we need to generate this + * custom flag `PPM_CL_CHILD_IN_PIDNS`. + */ + struct pid *pid_struct = extract__task_pid_struct(child, PIDTYPE_PID); + struct pid_namespace *pid_namespace_struct = extract__namespace_of_pid(pid_struct); + int pidns_level = BPF_CORE_READ(pid_namespace_struct, level); + if(pidns_level != 0) + { + flags |= PPM_CL_CHILD_IN_PIDNS; + } + + /* Parameter 17: uid (type: PT_UINT32) */ + u32 euid = 0; + extract__euid(child, &euid); + auxmap__store_u32_param(auxmap, euid); + + /* Parameter 18: gid (type: PT_UINT32) */ + u32 egid = 0; + extract__egid(child, &egid); + auxmap__store_u32_param(auxmap, egid); + + /* Parameter 19: vtid (type: PT_PID) */ + pid_t vtid = extract__task_xid_vnr(child, PIDTYPE_PID); + auxmap__store_s64_param(auxmap, (s64)vtid); + + /* Parameter 20: vpid (type: PT_PID) */ + pid_t vpid = extract__task_xid_vnr(child, PIDTYPE_TGID); + auxmap__store_s64_param(auxmap, (s64)vpid); + + /*=============================== COLLECT PARAMETERS ===========================*/ + + auxmap__finalize_event_header(auxmap); + + auxmap__submit_event(auxmap); + return 0; +} +#endif /* CAPTURE_SCHED_PROC_EXEC */ diff --git a/driver/modern_bpf/programs/tail_called/events/syscall_dispatched_events/chdir.bpf.c b/driver/modern_bpf/programs/tail_called/events/syscall_dispatched_events/chdir.bpf.c index 60f672c577..20672a0da7 100644 --- a/driver/modern_bpf/programs/tail_called/events/syscall_dispatched_events/chdir.bpf.c +++ b/driver/modern_bpf/programs/tail_called/events/syscall_dispatched_events/chdir.bpf.c @@ -58,7 +58,7 @@ int BPF_PROG(chdir_x, /* Parameter 2: path (type: PT_CHARBUF) */ unsigned long path_pointer = extract__syscall_argument(regs, 0); - auxmap__store_charbuf_param(auxmap, path_pointer, USER); + auxmap__store_charbuf_param(auxmap, path_pointer, MAX_PATH, USER); /*=============================== COLLECT PARAMETERS ===========================*/ diff --git a/driver/modern_bpf/programs/tail_called/events/syscall_dispatched_events/chmod.bpf.c b/driver/modern_bpf/programs/tail_called/events/syscall_dispatched_events/chmod.bpf.c index 226c13059b..30aff9f1ca 100644 --- a/driver/modern_bpf/programs/tail_called/events/syscall_dispatched_events/chmod.bpf.c +++ b/driver/modern_bpf/programs/tail_called/events/syscall_dispatched_events/chmod.bpf.c @@ -58,7 +58,7 @@ int BPF_PROG(chmod_x, /* Parameter 2: filename (type: PT_FSPATH) */ unsigned long path_pointer = extract__syscall_argument(regs, 0); - auxmap__store_charbuf_param(auxmap, path_pointer, USER); + auxmap__store_charbuf_param(auxmap, path_pointer, MAX_PATH, USER); /* Parameter 3: mode (type: PT_MODE) */ unsigned long mode = extract__syscall_argument(regs, 1); diff --git a/driver/modern_bpf/programs/tail_called/events/syscall_dispatched_events/chroot.bpf.c b/driver/modern_bpf/programs/tail_called/events/syscall_dispatched_events/chroot.bpf.c index 42fa6ba4e3..13434a6bd0 100644 --- a/driver/modern_bpf/programs/tail_called/events/syscall_dispatched_events/chroot.bpf.c +++ b/driver/modern_bpf/programs/tail_called/events/syscall_dispatched_events/chroot.bpf.c @@ -58,7 +58,7 @@ int BPF_PROG(chroot_x, /* Parameter 2: path (type: PT_FSPATH) */ unsigned long path_pointer = extract__syscall_argument(regs, 0); - auxmap__store_charbuf_param(auxmap, path_pointer, USER); + auxmap__store_charbuf_param(auxmap, path_pointer, MAX_PATH, USER); /*=============================== COLLECT PARAMETERS ===========================*/ diff --git a/driver/modern_bpf/programs/tail_called/events/syscall_dispatched_events/clone.bpf.c b/driver/modern_bpf/programs/tail_called/events/syscall_dispatched_events/clone.bpf.c index 6a4a4d40dd..11c590166d 100644 --- a/driver/modern_bpf/programs/tail_called/events/syscall_dispatched_events/clone.bpf.c +++ b/driver/modern_bpf/programs/tail_called/events/syscall_dispatched_events/clone.bpf.c @@ -43,6 +43,19 @@ int BPF_PROG(clone_x, struct pt_regs *regs, long ret) { + +/* We already catch the clone child event with our `sched_process_fork` tracepoint, + * for this reason we don't need also this instrumentation. Please note that we use + * the aforementioned tracepoint only for the child event but we need to catch also + * the father event or the failure case, for this reason we check the `ret==0` + */ +#ifdef CAPTURE_SCHED_PROC_FORK + if(ret == 0) + { + return 0; + } +#endif + struct auxiliary_map *auxmap = auxmap__get(); if(!auxmap) { @@ -78,7 +91,7 @@ int BPF_PROG(clone_x, /* We need to extract the len of `exe` arg so we can understand * the overall length of the remaining args. */ - u16 exe_arg_len = auxmap__store_charbuf_param(auxmap, arg_start_pointer, USER); + u16 exe_arg_len = auxmap__store_charbuf_param(auxmap, arg_start_pointer, MAX_PROC_EXE, USER); /* Parameter 3: args (type: PT_CHARBUFARRAY) */ /* Here we read all the array starting from the pointer to the first @@ -86,7 +99,7 @@ int BPF_PROG(clone_x, * since we know the total len we read it as a `bytebuf`. * The `\0` after every argument are preserved. */ - auxmap__store_bytebuf_param(auxmap, arg_start_pointer + exe_arg_len, total_args_len - exe_arg_len, USER); + auxmap__store_bytebuf_param(auxmap, arg_start_pointer + exe_arg_len, (total_args_len - exe_arg_len) & (MAX_PROC_ARG_ENV - 1), USER); } else { @@ -147,7 +160,7 @@ int BPF_PROG(clone_x, auxmap__store_u32_param(auxmap, vm_swap); /* Parameter 14: comm (type: PT_CHARBUF) */ - auxmap__store_charbuf_param(auxmap, (unsigned long)task->comm, KERNEL); + auxmap__store_charbuf_param(auxmap, (unsigned long)task->comm, TASK_COMM_LEN, KERNEL); /*=============================== COLLECT PARAMETERS ===========================*/ diff --git a/driver/modern_bpf/programs/tail_called/events/syscall_dispatched_events/clone3.bpf.c b/driver/modern_bpf/programs/tail_called/events/syscall_dispatched_events/clone3.bpf.c index 108b13ce97..0aba85312c 100644 --- a/driver/modern_bpf/programs/tail_called/events/syscall_dispatched_events/clone3.bpf.c +++ b/driver/modern_bpf/programs/tail_called/events/syscall_dispatched_events/clone3.bpf.c @@ -43,6 +43,19 @@ int BPF_PROG(clone3_x, struct pt_regs *regs, long ret) { + +/* We already catch the clone3 child event with our `sched_process_fork` tracepoint, + * for this reason we don't need also this instrumentation. Please note that we use + * the aforementioned tracepoint only for the child event but we need to catch also + * the father event or the failure case, for this reason we check the `ret==0` + */ +#ifdef CAPTURE_SCHED_PROC_FORK + if(ret == 0) + { + return 0; + } +#endif + struct auxiliary_map *auxmap = auxmap__get(); if(!auxmap) { @@ -78,7 +91,7 @@ int BPF_PROG(clone3_x, /* We need to extract the len of `exe` arg so we can understand * the overall length of the remaining args. */ - u16 exe_arg_len = auxmap__store_charbuf_param(auxmap, arg_start_pointer, USER); + u16 exe_arg_len = auxmap__store_charbuf_param(auxmap, arg_start_pointer, MAX_PROC_EXE, USER); /* Parameter 3: args (type: PT_CHARBUFARRAY) */ /* Here we read all the array starting from the pointer to the first @@ -86,7 +99,7 @@ int BPF_PROG(clone3_x, * since we know the total len we read it as a `bytebuf`. * The `\0` after every argument are preserved. */ - auxmap__store_bytebuf_param(auxmap, arg_start_pointer + exe_arg_len, total_args_len - exe_arg_len, USER); + auxmap__store_bytebuf_param(auxmap, arg_start_pointer + exe_arg_len, (total_args_len - exe_arg_len) & (MAX_PROC_ARG_ENV - 1), USER); } else { @@ -147,7 +160,7 @@ int BPF_PROG(clone3_x, auxmap__store_u32_param(auxmap, vm_swap); /* Parameter 14: comm (type: PT_CHARBUF) */ - auxmap__store_charbuf_param(auxmap, (unsigned long)task->comm, KERNEL); + auxmap__store_charbuf_param(auxmap, (unsigned long)task->comm, TASK_COMM_LEN, KERNEL); /*=============================== COLLECT PARAMETERS ===========================*/ diff --git a/driver/modern_bpf/programs/tail_called/events/syscall_dispatched_events/creat.bpf.c b/driver/modern_bpf/programs/tail_called/events/syscall_dispatched_events/creat.bpf.c index 36f892be30..6d9eb96e21 100644 --- a/driver/modern_bpf/programs/tail_called/events/syscall_dispatched_events/creat.bpf.c +++ b/driver/modern_bpf/programs/tail_called/events/syscall_dispatched_events/creat.bpf.c @@ -26,7 +26,7 @@ int BPF_PROG(creat_e, /* Parameter 1: name (type: PT_FSPATH) */ unsigned long name_pointer = extract__syscall_argument(regs, 0); - auxmap__store_charbuf_param(auxmap, name_pointer, USER); + auxmap__store_charbuf_param(auxmap, name_pointer, MAX_PATH, USER); /* Parameter 2: mode (type: PT_UINT32) */ unsigned long mode = extract__syscall_argument(regs, 1); @@ -65,7 +65,7 @@ int BPF_PROG(creat_x, /* Parameter 2: name (type: PT_FSPATH) */ unsigned long name_pointer = extract__syscall_argument(regs, 0); - auxmap__store_charbuf_param(auxmap, name_pointer, USER); + auxmap__store_charbuf_param(auxmap, name_pointer, MAX_PATH, USER); /* Parameter 3: mode (type: PT_UINT32) */ unsigned long mode = extract__syscall_argument(regs, 1); diff --git a/driver/modern_bpf/programs/tail_called/events/syscall_dispatched_events/execve.bpf.c b/driver/modern_bpf/programs/tail_called/events/syscall_dispatched_events/execve.bpf.c index 6f3c87081e..ce08c68937 100644 --- a/driver/modern_bpf/programs/tail_called/events/syscall_dispatched_events/execve.bpf.c +++ b/driver/modern_bpf/programs/tail_called/events/syscall_dispatched_events/execve.bpf.c @@ -25,7 +25,7 @@ int BPF_PROG(execve_e, /* Parameter 1: filename (type: PT_FSPATH) */ unsigned long filename_pointer = extract__syscall_argument(regs, 0); - auxmap__store_charbuf_param(auxmap, filename_pointer, USER); + auxmap__store_charbuf_param(auxmap, filename_pointer, MAX_PATH, USER); /*=============================== COLLECT PARAMETERS ===========================*/ @@ -44,6 +44,19 @@ int BPF_PROG(execve_x, struct pt_regs *regs, long ret) { + +/* On some recent kernels the execve/execveat issue is solved: + * https://git.kernel.org/pub/scm/linux/kernel/git/stable/linux.git/commit/?h=linux-5.15.y&id=42eede3ae05bbf32cb0d87940b466ec5a76aca3f + * BTW we already catch the event with our `sched_process_exec` tracepoint, for this reason we don't need also this instrumentation. + * Please note that we still need to catch the syscall failure for this reason we check the `ret==0`. + */ +#ifdef CAPTURE_SCHED_PROC_EXEC + if(ret == 0) + { + return 0; + } +#endif + struct auxiliary_map *auxmap = auxmap__get(); if(!auxmap) { @@ -79,7 +92,7 @@ int BPF_PROG(execve_x, /* We need to extract the len of `exe` arg so we can undestand * the overall length of the remaining args. */ - u16 exe_arg_len = auxmap__store_charbuf_param(auxmap, arg_start_pointer, USER); + u16 exe_arg_len = auxmap__store_charbuf_param(auxmap, arg_start_pointer, MAX_PROC_EXE, USER); /* Parameter 3: args (type: PT_CHARBUFARRAY) */ /* Here we read the whole array starting from the pointer to the first @@ -87,7 +100,7 @@ int BPF_PROG(execve_x, * since we know the total len we read it as a `bytebuf`. * The `\0` after every argument are preserved. */ - auxmap__store_bytebuf_param(auxmap, arg_start_pointer + exe_arg_len, total_args_len - exe_arg_len, USER); + auxmap__store_bytebuf_param(auxmap, arg_start_pointer + exe_arg_len, (total_args_len - exe_arg_len) & (MAX_PROC_ARG_ENV - 1), USER); } else { @@ -99,10 +112,10 @@ int BPF_PROG(execve_x, unsigned long argv = extract__syscall_argument(regs, 1); /* Parameter 2: exe (type: PT_CHARBUF) */ - auxmap__store_single_charbuf_param_from_array(auxmap, argv, 0, USER); + auxmap__store_execve_exe(auxmap, (char **)argv); /* Parameter 3: args (type: PT_CHARBUFARRAY) */ - auxmap__store_multiple_charbufs_param_from_array(auxmap, argv, 1, USER); + auxmap__store_execve_args(auxmap, (char **)argv, 1); } /* Parameter 4: tid (type: PT_PID) */ @@ -155,7 +168,7 @@ int BPF_PROG(execve_x, auxmap__store_u32_param(auxmap, vm_swap); /* Parameter 14: comm (type: PT_CHARBUF) */ - auxmap__store_charbuf_param(auxmap, (unsigned long)task->comm, KERNEL); + auxmap__store_charbuf_param(auxmap, (unsigned long)task->comm, TASK_COMM_LEN, KERNEL); /*=============================== COLLECT PARAMETERS ===========================*/ @@ -203,13 +216,13 @@ int BPF_PROG(t1_execve_x, * since we know the total len we read it as a `bytebuf`. * The `\0` after every argument are preserved. */ - auxmap__store_bytebuf_param(auxmap, env_start_pointer, total_env_len, USER); + auxmap__store_bytebuf_param(auxmap, env_start_pointer, total_env_len & (MAX_PROC_ARG_ENV - 1), USER); } else { /* Parameter 16: env (type: PT_CHARBUFARRAY) */ unsigned long envp = extract__syscall_argument(regs, 2); - auxmap__store_multiple_charbufs_param_from_array(auxmap, envp, 0, USER); + auxmap__store_execve_args(auxmap, (char **)envp, 0); } /* Parameter 17: tty (type: PT_INT32) */ diff --git a/driver/modern_bpf/programs/tail_called/events/syscall_dispatched_events/execveat.bpf.c b/driver/modern_bpf/programs/tail_called/events/syscall_dispatched_events/execveat.bpf.c index ebaafc25c6..7b74e62c91 100644 --- a/driver/modern_bpf/programs/tail_called/events/syscall_dispatched_events/execveat.bpf.c +++ b/driver/modern_bpf/programs/tail_called/events/syscall_dispatched_events/execveat.bpf.c @@ -33,7 +33,7 @@ int BPF_PROG(execveat_e, /* Parameter 2: pathname (type: PT_FSRELPATH) */ unsigned long pathname_pointer = extract__syscall_argument(regs, 1); - auxmap__store_charbuf_param(auxmap, pathname_pointer, USER); + auxmap__store_charbuf_param(auxmap, pathname_pointer, MAX_PATH, USER); /* Parameter 3: flags (type: PT_FLAGS32) */ unsigned long flags = extract__syscall_argument(regs, 4); @@ -51,11 +51,29 @@ int BPF_PROG(execveat_e, /*=============================== EXIT EVENT ===========================*/ +/* Note: On aarch64 and x86 architectures this BPF program is called only when + * `execveat` fails, in case of success the `execve_x` bpf program is called. + * The exception is `s390x` where this program (`execveat_x`) is called also when + * the call is successful. + */ SEC("tp_btf/sys_exit") int BPF_PROG(execveat_x, struct pt_regs *regs, long ret) { + +/* On some recent kernels the execve/execveat issue is solved: + * https://git.kernel.org/pub/scm/linux/kernel/git/stable/linux.git/commit/?h=linux-5.15.y&id=42eede3ae05bbf32cb0d87940b466ec5a76aca3f + * BTW we already catch the event with our `sched_process_exec` tracepoint, for this reason we don't need also this instrumentation. + * Please note that we still need to catch the syscall failure for this reason we check the `ret==0`. + */ +#ifdef CAPTURE_SCHED_PROC_EXEC + if(ret == 0) + { + return 0; + } +#endif + struct auxiliary_map *auxmap = auxmap__get(); if(!auxmap) { @@ -70,21 +88,52 @@ int BPF_PROG(execveat_x, struct task_struct *task = get_current_task(); - /* This is a charbuf pointer array. - * Every element of `argv` array is a pointer to a charbuf. - * Here the first pointer points to `exe` param while all - * the others point to the different args. - * - * Please note: this bpf program is called only if the `execveat` fails - * so we cannot get arguments from the kernel memory. + /* In case of success we take `exe` and `args` directly from the kernel + * otherwise we get them from the syscall arguments. */ - unsigned long argv = extract__syscall_argument(regs, 2); - - /* Parameter 2: exe (type: PT_CHARBUF) */ - auxmap__store_single_charbuf_param_from_array(auxmap, argv, 0, USER); - - /* Parameter 3: args (type: PT_CHARBUFARRAY) */ - auxmap__store_multiple_charbufs_param_from_array(auxmap, argv, 1, USER); + if(ret == 0) + { + unsigned long arg_start_pointer = 0; + unsigned long arg_end_pointer = 0; + + /* `arg_start` points to the memory area where arguments start. + * We directly read charbufs from there, not pointers to charbufs! + * We will store charbufs directly from memory. + */ + READ_TASK_FIELD_INTO(&arg_start_pointer, task, mm, arg_start); + READ_TASK_FIELD_INTO(&arg_end_pointer, task, mm, arg_end); + + unsigned long total_args_len = arg_end_pointer - arg_start_pointer; + + /* Parameter 2: exe (type: PT_CHARBUF) */ + /* We need to extract the len of `exe` arg so we can undestand + * the overall length of the remaining args. + */ + u16 exe_arg_len = auxmap__store_charbuf_param(auxmap, arg_start_pointer, MAX_PROC_EXE, USER); + + /* Parameter 3: args (type: PT_CHARBUFARRAY) */ + /* Here we read the whole array starting from the pointer to the first + * element. We could also read the array element per element but + * since we know the total len we read it as a `bytebuf`. + * The `\0` after every argument are preserved. + */ + auxmap__store_bytebuf_param(auxmap, arg_start_pointer + exe_arg_len, (total_args_len - exe_arg_len) & (MAX_PROC_ARG_ENV - 1), USER); + } + else + { + /* This is a charbuf pointer array. + * Every element of `argv` array is a pointer to a charbuf. + * Here the first pointer points to `exe` param while all + * the others point to the different args. + */ + unsigned long argv = extract__syscall_argument(regs, 2); + + /* Parameter 2: exe (type: PT_CHARBUF) */ + auxmap__store_execve_exe(auxmap, (char **)argv); + + /* Parameter 3: args (type: PT_CHARBUFARRAY) */ + auxmap__store_execve_args(auxmap, (char **)argv, 1); + } /* Parameter 4: tid (type: PT_PID) */ /* this is called `tid` but it is the `pid`. */ @@ -136,7 +185,7 @@ int BPF_PROG(execveat_x, auxmap__store_u32_param(auxmap, vm_swap); /* Parameter 14: comm (type: PT_CHARBUF) */ - auxmap__store_charbuf_param(auxmap, (unsigned long)task->comm, KERNEL); + auxmap__store_charbuf_param(auxmap, (unsigned long)task->comm, TASK_COMM_LEN, KERNEL); /*=============================== COLLECT PARAMETERS ===========================*/ @@ -165,9 +214,33 @@ int BPF_PROG(t1_execveat_x, /* Parameter 15: cgroups (type: PT_CHARBUFARRAY) */ auxmap__store_cgroups_param(auxmap, task); - /* Parameter 16: env (type: PT_CHARBUFARRAY) */ - unsigned long envp = extract__syscall_argument(regs, 3); - auxmap__store_multiple_charbufs_param_from_array(auxmap, envp, 0, USER); + /* In case of success we take `env` directly from the kernel + * otherwise we get them from the syscall arguments. + */ + if(ret == 0) + { + unsigned long env_start_pointer = 0; + unsigned long env_end_pointer = 0; + + READ_TASK_FIELD_INTO(&env_start_pointer, task, mm, env_start); + READ_TASK_FIELD_INTO(&env_end_pointer, task, mm, env_end); + + unsigned long total_env_len = env_end_pointer - env_start_pointer; + + /* Parameter 16: env (type: PT_CHARBUFARRAY) */ + /* Here we read all the array starting from the pointer to the first + * element. We could also read the array element per element but + * since we know the total len we read it as a `bytebuf`. + * The `\0` after every argument are preserved. + */ + auxmap__store_bytebuf_param(auxmap, env_start_pointer, total_env_len & (MAX_PROC_ARG_ENV - 1), USER); + } + else + { + /* Parameter 16: env (type: PT_CHARBUFARRAY) */ + unsigned long envp = extract__syscall_argument(regs, 3); + auxmap__store_execve_args(auxmap, (char **)envp, 0); + } /* Parameter 17: tty (type: PT_INT32) */ u32 tty = exctract__tty(task); diff --git a/driver/modern_bpf/programs/tail_called/events/syscall_dispatched_events/fchmodat.bpf.c b/driver/modern_bpf/programs/tail_called/events/syscall_dispatched_events/fchmodat.bpf.c index 2551109e07..9f675d0452 100644 --- a/driver/modern_bpf/programs/tail_called/events/syscall_dispatched_events/fchmodat.bpf.c +++ b/driver/modern_bpf/programs/tail_called/events/syscall_dispatched_events/fchmodat.bpf.c @@ -66,7 +66,7 @@ int BPF_PROG(fchmodat_x, /* Parameter 3: filename (type: PT_FSRELPATH) */ unsigned long path_pointer = extract__syscall_argument(regs, 1); - auxmap__store_charbuf_param(auxmap, path_pointer, USER); + auxmap__store_charbuf_param(auxmap, path_pointer, MAX_PATH, USER); /* Parameter 4: mode (type: PT_MODE) */ unsigned long mode = extract__syscall_argument(regs, 2); @@ -81,5 +81,4 @@ int BPF_PROG(fchmodat_x, return 0; } - /*=============================== EXIT EVENT ===========================*/ diff --git a/driver/modern_bpf/programs/tail_called/events/syscall_dispatched_events/fork.bpf.c b/driver/modern_bpf/programs/tail_called/events/syscall_dispatched_events/fork.bpf.c index bd18e9b9cd..4100ed7e61 100644 --- a/driver/modern_bpf/programs/tail_called/events/syscall_dispatched_events/fork.bpf.c +++ b/driver/modern_bpf/programs/tail_called/events/syscall_dispatched_events/fork.bpf.c @@ -43,6 +43,19 @@ int BPF_PROG(fork_x, struct pt_regs *regs, long ret) { + +/* We already catch the fork child event with our `sched_process_fork` tracepoint, + * for this reason we don't need also this instrumentation. Please note that we use + * the aforementioned tracepoint only for the child event but we need to catch also + * the father event or the failure case, for this reason we check the `ret==0` + */ +#ifdef CAPTURE_SCHED_PROC_FORK + if(ret == 0) + { + return 0; + } +#endif + struct auxiliary_map *auxmap = auxmap__get(); if(!auxmap) { @@ -80,7 +93,7 @@ int BPF_PROG(fork_x, /* We need to extract the len of `exe` arg so we can undestand * the overall length of the remaining args. */ - u16 exe_arg_len = auxmap__store_charbuf_param(auxmap, arg_start_pointer, USER); + u16 exe_arg_len = auxmap__store_charbuf_param(auxmap, arg_start_pointer, MAX_PROC_EXE, USER); /* Parameter 3: args (type: PT_CHARBUFARRAY) */ /* Here we read all the array starting from the pointer to the first @@ -88,7 +101,7 @@ int BPF_PROG(fork_x, * since we know the total len we read it as a `bytebuf`. * The `\0` after every argument are preserved. */ - auxmap__store_bytebuf_param(auxmap, arg_start_pointer + exe_arg_len, total_args_len - exe_arg_len, USER); + auxmap__store_bytebuf_param(auxmap, arg_start_pointer + exe_arg_len, (total_args_len - exe_arg_len) & (MAX_PROC_ARG_ENV - 1), USER); } else { @@ -149,7 +162,7 @@ int BPF_PROG(fork_x, auxmap__store_u32_param(auxmap, vm_swap); /* Parameter 14: comm (type: PT_CHARBUF) */ - auxmap__store_charbuf_param(auxmap, (unsigned long)task->comm, KERNEL); + auxmap__store_charbuf_param(auxmap, (unsigned long)task->comm, TASK_COMM_LEN, KERNEL); /*=============================== COLLECT PARAMETERS ===========================*/ diff --git a/driver/modern_bpf/programs/tail_called/events/syscall_dispatched_events/fsconfig.bpf.c b/driver/modern_bpf/programs/tail_called/events/syscall_dispatched_events/fsconfig.bpf.c index 833f16ac84..b85318dfb6 100644 --- a/driver/modern_bpf/programs/tail_called/events/syscall_dispatched_events/fsconfig.bpf.c +++ b/driver/modern_bpf/programs/tail_called/events/syscall_dispatched_events/fsconfig.bpf.c @@ -68,7 +68,7 @@ int BPF_PROG(fsconfig_x, /* Parameter 4: key (type: PT_CHARBUF) */ unsigned long key_pointer = extract__syscall_argument(regs, 2); - auxmap__store_charbuf_param(auxmap, key_pointer, USER); + auxmap__store_charbuf_param(auxmap, key_pointer, MAX_PARAM_SIZE, USER); int aux = extract__syscall_argument(regs, 4); @@ -114,7 +114,7 @@ int BPF_PROG(fsconfig_x, auxmap__store_empty_param(auxmap); /* Parameter 6: value_charbuf (type: PT_CHARBUF) */ - auxmap__store_charbuf_param(auxmap, value_pointer, USER); + auxmap__store_charbuf_param(auxmap, value_pointer, MAX_PATH, USER); break; case PPM_FSCONFIG_SET_BINARY: diff --git a/driver/modern_bpf/programs/tail_called/events/syscall_dispatched_events/link.bpf.c b/driver/modern_bpf/programs/tail_called/events/syscall_dispatched_events/link.bpf.c index c06a097602..6249f92417 100644 --- a/driver/modern_bpf/programs/tail_called/events/syscall_dispatched_events/link.bpf.c +++ b/driver/modern_bpf/programs/tail_called/events/syscall_dispatched_events/link.bpf.c @@ -58,11 +58,11 @@ int BPF_PROG(link_x, /* Parameter 2: oldpath (type: PT_FSPATH) */ unsigned long old_path_pointer = extract__syscall_argument(regs, 0); - auxmap__store_charbuf_param(auxmap, old_path_pointer, USER); + auxmap__store_charbuf_param(auxmap, old_path_pointer, MAX_PATH, USER); /* Parameter 3: newpath (type: PT_FSPATH) */ unsigned long new_path_pointer = extract__syscall_argument(regs, 1); - auxmap__store_charbuf_param(auxmap, new_path_pointer, USER); + auxmap__store_charbuf_param(auxmap, new_path_pointer, MAX_PATH, USER); /*=============================== COLLECT PARAMETERS ===========================*/ diff --git a/driver/modern_bpf/programs/tail_called/events/syscall_dispatched_events/linkat.bpf.c b/driver/modern_bpf/programs/tail_called/events/syscall_dispatched_events/linkat.bpf.c index 948bac8350..fa29aac448 100644 --- a/driver/modern_bpf/programs/tail_called/events/syscall_dispatched_events/linkat.bpf.c +++ b/driver/modern_bpf/programs/tail_called/events/syscall_dispatched_events/linkat.bpf.c @@ -66,7 +66,7 @@ int BPF_PROG(linkat_x, /* Parameter 3: oldpath (type: PT_FSRELPATH) */ unsigned long old_path_pointer = extract__syscall_argument(regs, 1); - auxmap__store_charbuf_param(auxmap, old_path_pointer, USER); + auxmap__store_charbuf_param(auxmap, old_path_pointer, MAX_PATH, USER); /* Parameter 4: newdirfd (type: PT_FD) */ s32 newdirfd = (s32)extract__syscall_argument(regs, 2); @@ -78,7 +78,7 @@ int BPF_PROG(linkat_x, /* Parameter 5: newpath (type: PT_FSRELPATH) */ unsigned long new_path_pointer = extract__syscall_argument(regs, 3); - auxmap__store_charbuf_param(auxmap, new_path_pointer, USER); + auxmap__store_charbuf_param(auxmap, new_path_pointer, MAX_PATH, USER); /* Parameter 6: flags (type: PT_FLAGS32) */ unsigned long flags = extract__syscall_argument(regs, 4); diff --git a/driver/modern_bpf/programs/tail_called/events/syscall_dispatched_events/mkdir.bpf.c b/driver/modern_bpf/programs/tail_called/events/syscall_dispatched_events/mkdir.bpf.c index 9ef5864022..04f09ffc28 100644 --- a/driver/modern_bpf/programs/tail_called/events/syscall_dispatched_events/mkdir.bpf.c +++ b/driver/modern_bpf/programs/tail_called/events/syscall_dispatched_events/mkdir.bpf.c @@ -60,7 +60,7 @@ int BPF_PROG(mkdir_x, /* Parameter 2: path (type: PT_FSPATH) */ unsigned long path_pointer = extract__syscall_argument(regs, 0); - auxmap__store_charbuf_param(auxmap, path_pointer, USER); + auxmap__store_charbuf_param(auxmap, path_pointer, MAX_PATH, USER); /*=============================== COLLECT PARAMETERS ===========================*/ diff --git a/driver/modern_bpf/programs/tail_called/events/syscall_dispatched_events/mkdirat.bpf.c b/driver/modern_bpf/programs/tail_called/events/syscall_dispatched_events/mkdirat.bpf.c index 2366eb69a0..7263fc11e7 100644 --- a/driver/modern_bpf/programs/tail_called/events/syscall_dispatched_events/mkdirat.bpf.c +++ b/driver/modern_bpf/programs/tail_called/events/syscall_dispatched_events/mkdirat.bpf.c @@ -66,7 +66,7 @@ int BPF_PROG(mkdirat_x, /* Parameter 3: path (type: PT_FSRELPATH) */ unsigned long path_pointer = extract__syscall_argument(regs, 1); - auxmap__store_charbuf_param(auxmap, path_pointer, USER); + auxmap__store_charbuf_param(auxmap, path_pointer, MAX_PATH, USER); /* Parameter 4: mode (type: PT_UINT32) */ u32 mode = (u32)extract__syscall_argument(regs, 2); diff --git a/driver/modern_bpf/programs/tail_called/events/syscall_dispatched_events/mount.bpf.c b/driver/modern_bpf/programs/tail_called/events/syscall_dispatched_events/mount.bpf.c index 535d1e5511..61c4e7cd53 100644 --- a/driver/modern_bpf/programs/tail_called/events/syscall_dispatched_events/mount.bpf.c +++ b/driver/modern_bpf/programs/tail_called/events/syscall_dispatched_events/mount.bpf.c @@ -73,15 +73,15 @@ int BPF_PROG(mount_x, /* Parameter 2: dev (type: PT_CHARBUF) */ unsigned long source_pointer = extract__syscall_argument(regs, 0); - auxmap__store_charbuf_param(auxmap, source_pointer, USER); + auxmap__store_charbuf_param(auxmap, source_pointer, MAX_PATH, USER); /* Parameter 3: dir (type: PT_FSPATH) */ unsigned long target_pointer = extract__syscall_argument(regs, 1); - auxmap__store_charbuf_param(auxmap, target_pointer, USER); + auxmap__store_charbuf_param(auxmap, target_pointer, MAX_PATH, USER); /* Parameter 4: type (type: PT_CHARBUF) */ unsigned long fstype_pointer = extract__syscall_argument(regs, 2); - auxmap__store_charbuf_param(auxmap, fstype_pointer, USER); + auxmap__store_charbuf_param(auxmap, fstype_pointer, MAX_PARAM_SIZE, USER); /*=============================== COLLECT PARAMETERS ===========================*/ diff --git a/driver/modern_bpf/programs/tail_called/events/syscall_dispatched_events/open.bpf.c b/driver/modern_bpf/programs/tail_called/events/syscall_dispatched_events/open.bpf.c index ea12c15190..dc30be7f94 100644 --- a/driver/modern_bpf/programs/tail_called/events/syscall_dispatched_events/open.bpf.c +++ b/driver/modern_bpf/programs/tail_called/events/syscall_dispatched_events/open.bpf.c @@ -26,7 +26,7 @@ int BPF_PROG(open_e, /* Parameter 1: name (type: PT_FSPATH) */ unsigned long name_pointer = extract__syscall_argument(regs, 0); - auxmap__store_charbuf_param(auxmap, name_pointer, USER); + auxmap__store_charbuf_param(auxmap, name_pointer, MAX_PATH, USER); /* Parameter 2: flags (type: PT_FLAGS32) */ u32 flags = (u32)extract__syscall_argument(regs, 1); @@ -69,7 +69,7 @@ int BPF_PROG(open_x, /* Parameter 2: name (type: PT_FSPATH) */ unsigned long name_pointer = extract__syscall_argument(regs, 0); - auxmap__store_charbuf_param(auxmap, name_pointer, USER); + auxmap__store_charbuf_param(auxmap, name_pointer, MAX_PATH, USER); /* Parameter 3: flags (type: PT_FLAGS32) */ u32 flags = (u32)extract__syscall_argument(regs, 1); diff --git a/driver/modern_bpf/programs/tail_called/events/syscall_dispatched_events/openat.bpf.c b/driver/modern_bpf/programs/tail_called/events/syscall_dispatched_events/openat.bpf.c index b794af720f..39c4f1bf08 100644 --- a/driver/modern_bpf/programs/tail_called/events/syscall_dispatched_events/openat.bpf.c +++ b/driver/modern_bpf/programs/tail_called/events/syscall_dispatched_events/openat.bpf.c @@ -34,7 +34,7 @@ int BPF_PROG(openat_e, /* Parameter 2: name (type: PT_FSRELPATH) */ unsigned long path_pointer = extract__syscall_argument(regs, 1); - auxmap__store_charbuf_param(auxmap, path_pointer, USER); + auxmap__store_charbuf_param(auxmap, path_pointer, MAX_PATH, USER); /* Parameter 3: flags (type: PT_FLAGS32) */ u32 flags = (u32)extract__syscall_argument(regs, 2); @@ -85,7 +85,7 @@ int BPF_PROG(openat_x, /* Parameter 3: name (type: PT_FSRELPATH) */ unsigned long path_pointer = extract__syscall_argument(regs, 1); - auxmap__store_charbuf_param(auxmap, path_pointer, USER); + auxmap__store_charbuf_param(auxmap, path_pointer, MAX_PATH, USER); /* Parameter 4: flags (type: PT_FLAGS32) */ u32 flags = (u32)extract__syscall_argument(regs, 2); diff --git a/driver/modern_bpf/programs/tail_called/events/syscall_dispatched_events/openat2.bpf.c b/driver/modern_bpf/programs/tail_called/events/syscall_dispatched_events/openat2.bpf.c index 63d34c1a8e..9c9a75e378 100644 --- a/driver/modern_bpf/programs/tail_called/events/syscall_dispatched_events/openat2.bpf.c +++ b/driver/modern_bpf/programs/tail_called/events/syscall_dispatched_events/openat2.bpf.c @@ -34,7 +34,7 @@ int BPF_PROG(openat2_e, /* Parameter 2: name (type: PT_FSRELPATH) */ unsigned long path_pointer = extract__syscall_argument(regs, 1); - auxmap__store_charbuf_param(auxmap, path_pointer, USER); + auxmap__store_charbuf_param(auxmap, path_pointer, MAX_PATH, USER); /* the `open_how` struct is defined since kernel version 5.6 */ unsigned long open_how_pointer = extract__syscall_argument(regs, 2); @@ -91,7 +91,7 @@ int BPF_PROG(openat2_x, /* Parameter 3: name (type: PT_FSRELPATH) */ unsigned long path_pointer = extract__syscall_argument(regs, 1); - auxmap__store_charbuf_param(auxmap, path_pointer, USER); + auxmap__store_charbuf_param(auxmap, path_pointer, MAX_PATH, USER); /* the `open_how` struct is defined since kernel version 5.6 */ unsigned long open_how_pointer = extract__syscall_argument(regs, 2); diff --git a/driver/modern_bpf/programs/tail_called/events/syscall_dispatched_events/quotactl.bpf.c b/driver/modern_bpf/programs/tail_called/events/syscall_dispatched_events/quotactl.bpf.c index 82b565abe0..94fa055560 100644 --- a/driver/modern_bpf/programs/tail_called/events/syscall_dispatched_events/quotactl.bpf.c +++ b/driver/modern_bpf/programs/tail_called/events/syscall_dispatched_events/quotactl.bpf.c @@ -91,7 +91,7 @@ int BPF_PROG(quotactl_x, * the filesystem being manipulated. */ unsigned long special_pointer = extract__syscall_argument(regs, 1); - auxmap__store_charbuf_param(auxmap, special_pointer, USER); + auxmap__store_charbuf_param(auxmap, special_pointer, MAX_PATH, USER); uint32_t cmd = (uint32_t)extract__syscall_argument(regs, 0); u16 scap_cmd = quotactl_cmd_to_scap(cmd); @@ -106,7 +106,7 @@ int BPF_PROG(quotactl_x, if(scap_cmd == PPM_Q_QUOTAON) { /* Parameter 3: quotafilepath (type: PT_CHARBUF) */ - auxmap__store_charbuf_param(auxmap, addr_pointer, USER); + auxmap__store_charbuf_param(auxmap, addr_pointer, MAX_PATH, USER); } else { diff --git a/driver/modern_bpf/programs/tail_called/events/syscall_dispatched_events/rename.bpf.c b/driver/modern_bpf/programs/tail_called/events/syscall_dispatched_events/rename.bpf.c index 52116b84c9..3fbf1dd36b 100644 --- a/driver/modern_bpf/programs/tail_called/events/syscall_dispatched_events/rename.bpf.c +++ b/driver/modern_bpf/programs/tail_called/events/syscall_dispatched_events/rename.bpf.c @@ -58,11 +58,11 @@ int BPF_PROG(rename_x, /* Parameter 2: oldpath (type: PT_FSPATH) */ unsigned long old_path_pointer = extract__syscall_argument(regs, 0); - auxmap__store_charbuf_param(auxmap, old_path_pointer, USER); + auxmap__store_charbuf_param(auxmap, old_path_pointer, MAX_PATH, USER); /* Parameter 3: newpath (type: PT_FSPATH) */ unsigned long new_path_pointer = extract__syscall_argument(regs, 1); - auxmap__store_charbuf_param(auxmap, new_path_pointer, USER); + auxmap__store_charbuf_param(auxmap, new_path_pointer, MAX_PATH, USER); /*=============================== COLLECT PARAMETERS ===========================*/ diff --git a/driver/modern_bpf/programs/tail_called/events/syscall_dispatched_events/renameat.bpf.c b/driver/modern_bpf/programs/tail_called/events/syscall_dispatched_events/renameat.bpf.c index 3f87bd6c01..c47dee84be 100644 --- a/driver/modern_bpf/programs/tail_called/events/syscall_dispatched_events/renameat.bpf.c +++ b/driver/modern_bpf/programs/tail_called/events/syscall_dispatched_events/renameat.bpf.c @@ -66,7 +66,7 @@ int BPF_PROG(renameat_x, /* Parameter 3: oldpath (type: PT_FSRELPATH) */ unsigned long old_path_pointer = extract__syscall_argument(regs, 1); - auxmap__store_charbuf_param(auxmap, old_path_pointer, USER); + auxmap__store_charbuf_param(auxmap, old_path_pointer, MAX_PATH, USER); /* Parameter 4: newdirfd (type: PT_FD) */ s32 newdirfd = (s32)extract__syscall_argument(regs, 2); @@ -78,7 +78,7 @@ int BPF_PROG(renameat_x, /* Parameter 5: newpath (type: PT_FSRELPATH) */ unsigned long new_path_pointer = extract__syscall_argument(regs, 3); - auxmap__store_charbuf_param(auxmap, new_path_pointer, USER); + auxmap__store_charbuf_param(auxmap, new_path_pointer, MAX_PATH, USER); /*=============================== COLLECT PARAMETERS ===========================*/ diff --git a/driver/modern_bpf/programs/tail_called/events/syscall_dispatched_events/renameat2.bpf.c b/driver/modern_bpf/programs/tail_called/events/syscall_dispatched_events/renameat2.bpf.c index b9d5d53df2..36a007ae2c 100644 --- a/driver/modern_bpf/programs/tail_called/events/syscall_dispatched_events/renameat2.bpf.c +++ b/driver/modern_bpf/programs/tail_called/events/syscall_dispatched_events/renameat2.bpf.c @@ -66,7 +66,7 @@ int BPF_PROG(renameat2_x, /* Parameter 3: oldpath (type: PT_FSRELPATH) */ unsigned long old_path_pointer = extract__syscall_argument(regs, 1); - auxmap__store_charbuf_param(auxmap, old_path_pointer, USER); + auxmap__store_charbuf_param(auxmap, old_path_pointer, MAX_PATH, USER); /* Parameter 4: newdirfd (type: PT_FD) */ s32 newdirfd = (s32)extract__syscall_argument(regs, 2); @@ -78,7 +78,7 @@ int BPF_PROG(renameat2_x, /* Parameter 5: newpath (type: PT_FSRELPATH) */ unsigned long new_path_pointer = extract__syscall_argument(regs, 3); - auxmap__store_charbuf_param(auxmap, new_path_pointer, USER); + auxmap__store_charbuf_param(auxmap, new_path_pointer, MAX_PATH, USER); /* Parameter 6: flags (type: PT_FLAGS32) */ u32 flags = (u32)extract__syscall_argument(regs, 4); diff --git a/driver/modern_bpf/programs/tail_called/events/syscall_dispatched_events/rmdir.bpf.c b/driver/modern_bpf/programs/tail_called/events/syscall_dispatched_events/rmdir.bpf.c index 3106baa66c..8c99b3824b 100644 --- a/driver/modern_bpf/programs/tail_called/events/syscall_dispatched_events/rmdir.bpf.c +++ b/driver/modern_bpf/programs/tail_called/events/syscall_dispatched_events/rmdir.bpf.c @@ -58,7 +58,7 @@ int BPF_PROG(rmdir_x, /* Parameter 2: path (type: PT_CHARBUF) */ unsigned long path_pointer = extract__syscall_argument(regs, 0); - auxmap__store_charbuf_param(auxmap, path_pointer, USER); + auxmap__store_charbuf_param(auxmap, path_pointer, MAX_PATH, USER); /*=============================== COLLECT PARAMETERS ===========================*/ diff --git a/driver/modern_bpf/programs/tail_called/events/syscall_dispatched_events/symlink.bpf.c b/driver/modern_bpf/programs/tail_called/events/syscall_dispatched_events/symlink.bpf.c index e677f200e8..ab9bb423df 100644 --- a/driver/modern_bpf/programs/tail_called/events/syscall_dispatched_events/symlink.bpf.c +++ b/driver/modern_bpf/programs/tail_called/events/syscall_dispatched_events/symlink.bpf.c @@ -58,11 +58,11 @@ int BPF_PROG(symlink_x, /* Parameter 2: target (type: PT_CHARBUF) */ unsigned long target_pointer = extract__syscall_argument(regs, 0); - auxmap__store_charbuf_param(auxmap, target_pointer, USER); + auxmap__store_charbuf_param(auxmap, target_pointer, MAX_PATH, USER); /* Parameter 3: linkpath (type: PT_FSPATH) */ unsigned long linkpath_pointer = extract__syscall_argument(regs, 1); - auxmap__store_charbuf_param(auxmap, linkpath_pointer, USER); + auxmap__store_charbuf_param(auxmap, linkpath_pointer, MAX_PATH, USER); /*=============================== COLLECT PARAMETERS ===========================*/ diff --git a/driver/modern_bpf/programs/tail_called/events/syscall_dispatched_events/symlinkat.bpf.c b/driver/modern_bpf/programs/tail_called/events/syscall_dispatched_events/symlinkat.bpf.c index 6c38b8260f..08af3cf4c8 100644 --- a/driver/modern_bpf/programs/tail_called/events/syscall_dispatched_events/symlinkat.bpf.c +++ b/driver/modern_bpf/programs/tail_called/events/syscall_dispatched_events/symlinkat.bpf.c @@ -58,7 +58,7 @@ int BPF_PROG(symlinkat_x, /* Parameter 2: target (type: PT_CHARBUF) */ unsigned long target_pointer = extract__syscall_argument(regs, 0); - auxmap__store_charbuf_param(auxmap, target_pointer, USER); + auxmap__store_charbuf_param(auxmap, target_pointer, MAX_PATH, USER); /* Parameter 3: linkdirfd (type: PT_FD) */ s32 linkdirfd = (s32)extract__syscall_argument(regs, 1); @@ -70,7 +70,7 @@ int BPF_PROG(symlinkat_x, /* Parameter 4: linkpath (type: PT_FSRELPATH) */ unsigned long linkpath_pointer = extract__syscall_argument(regs, 2); - auxmap__store_charbuf_param(auxmap, linkpath_pointer, USER); + auxmap__store_charbuf_param(auxmap, linkpath_pointer, MAX_PATH, USER); /*=============================== COLLECT PARAMETERS ===========================*/ diff --git a/driver/modern_bpf/programs/tail_called/events/syscall_dispatched_events/umount2.bpf.c b/driver/modern_bpf/programs/tail_called/events/syscall_dispatched_events/umount2.bpf.c index 064d7ec3d5..aa2c9b19b1 100644 --- a/driver/modern_bpf/programs/tail_called/events/syscall_dispatched_events/umount2.bpf.c +++ b/driver/modern_bpf/programs/tail_called/events/syscall_dispatched_events/umount2.bpf.c @@ -62,7 +62,7 @@ int BPF_PROG(umount2_x, /* Parameter 2: name (type: PT_FSPATH) */ unsigned long target_pointer = extract__syscall_argument(regs, 0); - auxmap__store_charbuf_param(auxmap, target_pointer, USER); + auxmap__store_charbuf_param(auxmap, target_pointer, MAX_PATH, USER); /*=============================== COLLECT PARAMETERS ===========================*/ diff --git a/driver/modern_bpf/programs/tail_called/events/syscall_dispatched_events/unlink.bpf.c b/driver/modern_bpf/programs/tail_called/events/syscall_dispatched_events/unlink.bpf.c index 4332c877a7..33062bb68d 100644 --- a/driver/modern_bpf/programs/tail_called/events/syscall_dispatched_events/unlink.bpf.c +++ b/driver/modern_bpf/programs/tail_called/events/syscall_dispatched_events/unlink.bpf.c @@ -58,7 +58,7 @@ int BPF_PROG(unlink_x, /* Parameter 2: path (type: PT_FSPATH) */ unsigned long path_pointer = extract__syscall_argument(regs, 0); - auxmap__store_charbuf_param(auxmap, path_pointer, USER); + auxmap__store_charbuf_param(auxmap, path_pointer, MAX_PATH, USER); /*=============================== COLLECT PARAMETERS ===========================*/ diff --git a/driver/modern_bpf/programs/tail_called/events/syscall_dispatched_events/unlinkat.bpf.c b/driver/modern_bpf/programs/tail_called/events/syscall_dispatched_events/unlinkat.bpf.c index 3882e9b7df..db5313bfc8 100644 --- a/driver/modern_bpf/programs/tail_called/events/syscall_dispatched_events/unlinkat.bpf.c +++ b/driver/modern_bpf/programs/tail_called/events/syscall_dispatched_events/unlinkat.bpf.c @@ -66,7 +66,7 @@ int BPF_PROG(unlinkat_x, /* Parameter 3: path (type: PT_FSRELPATH) */ unsigned long path_pointer = extract__syscall_argument(regs, 1); - auxmap__store_charbuf_param(auxmap, path_pointer, USER); + auxmap__store_charbuf_param(auxmap, path_pointer, MAX_PATH, USER); /* Parameter 4: flags (type: PT_FLAGS32) */ unsigned long flags = extract__syscall_argument(regs, 2); diff --git a/driver/modern_bpf/programs/tail_called/events/syscall_dispatched_events/vfork.bpf.c b/driver/modern_bpf/programs/tail_called/events/syscall_dispatched_events/vfork.bpf.c index 4ece4c0b21..06eb5b1e03 100644 --- a/driver/modern_bpf/programs/tail_called/events/syscall_dispatched_events/vfork.bpf.c +++ b/driver/modern_bpf/programs/tail_called/events/syscall_dispatched_events/vfork.bpf.c @@ -80,7 +80,7 @@ int BPF_PROG(vfork_x, /* We need to extract the len of `exe` arg so we can undestand * the overall length of the remaining args. */ - u16 exe_arg_len = auxmap__store_charbuf_param(auxmap, arg_start_pointer, USER); + u16 exe_arg_len = auxmap__store_charbuf_param(auxmap, arg_start_pointer, MAX_PROC_EXE, USER); /* Parameter 3: args (type: PT_CHARBUFARRAY) */ /* Here we read all the array starting from the pointer to the first @@ -88,7 +88,7 @@ int BPF_PROG(vfork_x, * since we know the total len we read it as a `bytebuf`. * The `\0` after every argument are preserved. */ - auxmap__store_bytebuf_param(auxmap, arg_start_pointer + exe_arg_len, total_args_len - exe_arg_len, USER); + auxmap__store_bytebuf_param(auxmap, arg_start_pointer + exe_arg_len, (total_args_len - exe_arg_len) & (MAX_PROC_ARG_ENV - 1), USER); } else { @@ -147,7 +147,7 @@ int BPF_PROG(vfork_x, auxmap__store_u32_param(auxmap, vm_swap); /* Parameter 14: comm (type: PT_CHARBUF) */ - auxmap__store_charbuf_param(auxmap, (unsigned long)task->comm, KERNEL); + auxmap__store_charbuf_param(auxmap, (unsigned long)task->comm, TASK_COMM_LEN, KERNEL); /*=============================== COLLECT PARAMETERS ===========================*/ @@ -163,6 +163,19 @@ int BPF_PROG(t1_vfork_x, struct pt_regs *regs, long ret) { + +/* We already catch the vfork child event with our `sched_process_fork` tracepoint, + * for this reason we don't need also this instrumentation. Please note that we use + * the aforementioned tracepoint only for the child event but we need to catch also + * the father event or the failure case, for this reason we check the `ret==0` + */ +#ifdef CAPTURE_SCHED_PROC_FORK + if(ret == 0) + { + return 0; + } +#endif + struct auxiliary_map *auxmap = auxmap__get(); if(!auxmap) { diff --git a/driver/ppm_events_public.h b/driver/ppm_events_public.h index fced75210d..5fb5340215 100644 --- a/driver/ppm_events_public.h +++ b/driver/ppm_events_public.h @@ -1191,7 +1191,9 @@ enum extra_event_prog_code T1_CLONE3_X = 3, T1_FORK_X = 4, T1_VFORK_X = 5, - TAIL_EXTRA_EVENT_PROG_MAX = 6 + T1_SCHED_PROC_EXEC = 6, + T1_SCHED_PROC_FORK = 7, + TAIL_EXTRA_EVENT_PROG_MAX = 8 }; /* diff --git a/test/modern_bpf/event_class/event_class.cpp b/test/modern_bpf/event_class/event_class.cpp index cedb4d5fae..cf2281272f 100644 --- a/test/modern_bpf/event_class/event_class.cpp +++ b/test/modern_bpf/event_class/event_class.cpp @@ -100,6 +100,12 @@ event_test::~event_test() */ pman_detach_sched_proc_exit(); pman_detach_sched_switch(); +#ifdef CAPTURE_SCHED_PROC_EXEC + pman_detach_sched_proc_exec(); +#endif +#ifdef CAPTURE_SCHED_PROC_FORK + pman_detach_sched_proc_fork(); +#endif } /* This constructor must be used with generic tracepoints @@ -119,6 +125,18 @@ event_test::event_test(ppm_event_type event_type) pman_attach_sched_switch(); break; + case PPME_SYSCALL_EXECVE_19_X: +#ifdef CAPTURE_SCHED_PROC_EXEC + pman_attach_sched_proc_exec(); +#endif + break; + + case PPME_SYSCALL_CLONE_20_X: +#ifdef CAPTURE_SCHED_PROC_FORK + pman_attach_sched_proc_fork(); +#endif + break; + default: std::cout << " Unable to find the correct BPF program to attach" << std::endl; } @@ -396,44 +414,12 @@ void event_test::connect_unix_client_to_server(int32_t* client_socket, struct so void event_test::assert_event_presence(pid_t pid_to_search, int event_to_search) { - int16_t cpu_id = 0; - pid_t pid = 0; - uint16_t evt_type = 0; - - if(pid_to_search == CURRENT_PID) - { - pid = ::getpid(); - } - else - { - pid = pid_to_search; - } - - if(event_to_search == CURRENT_EVENT_TYPE) - { - evt_type = m_event_type; - } - else - { - evt_type = event_to_search; - } + assert_event_in_buffers(pid_to_search, event_to_search, true); +} - /* We need the while loop because in the buffers there could be different events - * with the type we are searching for. Even if we explicitly create only one event - * of this type, the system could create other events of the same type during the test! - */ - while(true) - { - get_event_from_ringbuffer(&cpu_id); - if(m_event_header == NULL) - { - FAIL() << "There is no event '" << evt_type << "' in the buffer." << std::endl; - } - if(m_event_header->tid == (uint64_t)pid && m_event_header->type == evt_type) - { - break; - } - } +void event_test::assert_event_absence(pid_t pid_to_search, int event_to_search) +{ + assert_event_in_buffers(pid_to_search, event_to_search, false); } void event_test::assert_header() @@ -840,3 +826,59 @@ void event_test::assert_unix_path(const char* desired_path, int starting_index) const char* unix_path = m_event_params[m_current_param].valptr + starting_index; ASSERT_STREQ(unix_path, desired_path) << VALUE_NOT_CORRECT << m_current_param; } + +void event_test::assert_event_in_buffers(pid_t pid_to_search, int event_to_search, bool presence) +{ + int16_t cpu_id = 0; + pid_t pid = 0; + uint16_t evt_type = 0; + + if(pid_to_search == CURRENT_PID) + { + pid = ::getpid(); + } + else + { + pid = pid_to_search; + } + + if(event_to_search == CURRENT_EVENT_TYPE) + { + evt_type = m_event_type; + } + else + { + evt_type = event_to_search; + } + + /* We need the while loop because in the buffers there could be different events + * with the type we are searching for. Even if we explicitly create only one event + * of this type, the system could create other events of the same type during the test! + */ + while(true) + { + get_event_from_ringbuffer(&cpu_id); + if(m_event_header == NULL) + { + if(presence) + { + FAIL() << "There is no event '" << evt_type << "' in the buffers." << std::endl; + } + else + { + break; + } + } + if(m_event_header->tid == (uint64_t)pid && m_event_header->type == evt_type) + { + if(presence) + { + break; + } + else + { + FAIL() << "There is an event '" << evt_type << "' in the buffers, but it shouldn't be there" << std::endl; + } + } + } +} diff --git a/test/modern_bpf/event_class/event_class.h b/test/modern_bpf/event_class/event_class.h index 23a8ecc4fd..fb9b39cb59 100644 --- a/test/modern_bpf/event_class/event_class.h +++ b/test/modern_bpf/event_class/event_class.h @@ -235,26 +235,49 @@ class event_test void connect_ipv4_client_to_server(int32_t* client_socket, struct sockaddr_in* client_sockaddr, int32_t* server_socket, struct sockaddr_in* server_sockaddr); void connect_ipv6_client_to_server(int32_t* client_socket, struct sockaddr_in6* client_sockaddr, int32_t* server_socket, struct sockaddr_in6* server_sockaddr); void connect_unix_client_to_server(int32_t* client_socket, struct sockaddr_un* client_sockaddr, int32_t* server_socket, struct sockaddr_un* server_sockaddr); + ///////////////////////////////// // GENERIC EVENT ASSERTIONS ///////////////////////////////// /** - * @brief Assert if in our buffers there is an event generated by `pid_to_search`. - * If no `pid_to_search` is specified we search for an event generated by - * the process that calls this method. + * @brief Assert if our buffers contain an event: + * + * 1. generated by `pid_to_search`. If no `pid_to_search` is specified we search + * for an event generated by the process that calls this method. This is the meaning of + * the `CURRENT_PID` macro. * - * We search the event saved in the `event_test` object in case `CURRENT_EVENT_TYPE` - * is specified, otherwise we search the passed event_type. + * 2. of type `event_to_search`. If no `event_to_search` is specified we search + * for the event type saved in the `event_test` object. This is the meaning of + * the `CURRENT_EVENT_TYPE` macro. * - * Search until all buffers are empty. If we don't find any event of this type, - * generated by the specified `pid_to_search`, we fail. + * Search until all buffers are empty. If we don't find any event + * with these requirements, we fail. * * @param pid_to_search pid that generated the event we are looking for. * @param event_to_search event type we are looking for. */ void assert_event_presence(pid_t pid_to_search = CURRENT_PID, int event_to_search = CURRENT_EVENT_TYPE); + /** + * @brief Assert if our buffers *don't* contain an event: + * + * 1. generated by `pid_to_search`. If no `pid_to_search` is specified we search + * for an event generated by the process that calls this method. This is the meaning of + * the `CURRENT_PID` macro. + * + * 2. of type `event_to_search`. If no `event_to_search` is specified we search + * for the event type saved in the `event_test` object. This is the meaning of + * the `CURRENT_EVENT_TYPE` macro. + * + * Search until all buffers are empty. If we find any event + * with these requirements, we fail. + * + * @param pid_to_search pid that generated the event we are looking for. + * @param event_to_search event type we are looking for. + */ + void assert_event_absence(pid_t pid_to_search = CURRENT_PID, int event_to_search = CURRENT_EVENT_TYPE); + /** * @brief Assert some fields of the event header: * - the event params num must match the number of parameters in the event table. @@ -582,6 +605,18 @@ class event_test * @param starting_index index inside the param where we can find the unix path. */ void assert_unix_path(const char* desired_path, int starting_index); + + /** + * @brief Assert if our buffers contain or not an event according to the + * `presence` bool. + * This method is used by `assert_event_presence` and `assert_event_absence`, if you + * need more info about the usage look at these methods. + * + * @param pid_to_search pid that generated the event we are looking for. + * @param event_to_search event type we are looking for. + * @param presence true if we want to assert the event presence. + */ + void assert_event_in_buffers(pid_t pid_to_search, int event_to_search, bool presence); }; ///////////////////////////////// diff --git a/test/modern_bpf/test_suites/generic_tracepoints_suite/sched_process_exec.cpp b/test/modern_bpf/test_suites/generic_tracepoints_suite/sched_process_exec.cpp new file mode 100644 index 0000000000..fbb2c8c18a --- /dev/null +++ b/test/modern_bpf/test_suites/generic_tracepoints_suite/sched_process_exec.cpp @@ -0,0 +1,105 @@ +#include "../../event_class/event_class.h" +#include "../../helpers/proc_parsing.h" + +#if defined(CAPTURE_SCHED_PROC_EXEC) && defined(__NR_clone3) && defined(__NR_wait4) && defined(__NR_execve) + +#include + +TEST(GenericTracepoints, sched_proc_exec) +{ + auto evt_test = get_generic_event_test(PPME_SYSCALL_EXECVE_19_X); + + evt_test->enable_capture(); + + /*=============================== TRIGGER SYSCALL ===========================*/ + + /* Prepare the execve args */ + const char *pathname = "/usr/bin/echo"; + const char *comm = "echo"; + const char *argv[] = {pathname, "[OUTPUT] GenericTracepoints.sched_proc_exec test", NULL}; + const char *envp[] = {"IN_TEST=yes", "3_ARGUMENT=yes", "2_ARGUMENT=no", NULL}; + + /* We need to use `SIGCHLD` otherwise the parent won't receive any signal + * when the child terminates. + */ + struct clone_args cl_args = {0}; + cl_args.exit_signal = SIGCHLD; + pid_t ret_pid = syscall(__NR_clone3, &cl_args, sizeof(cl_args)); + + if(ret_pid == 0) + { + syscall(__NR_execve, pathname, argv, envp); + exit(EXIT_FAILURE); + } + + assert_syscall_state(SYSCALL_SUCCESS, "clone3", ret_pid, NOT_EQUAL, -1); + + /* Catch the child before doing anything else. */ + int status = 0; + int options = 0; + assert_syscall_state(SYSCALL_SUCCESS, "wait4", syscall(__NR_wait4, ret_pid, &status, options, NULL), NOT_EQUAL, -1); + + if(__WEXITSTATUS(status) == EXIT_FAILURE) + { + FAIL() << "The child execve failed." << std::endl; + } + + /*=============================== TRIGGER SYSCALL ===========================*/ + + evt_test->disable_capture(); + + /* We search for a child event. */ + evt_test->assert_event_presence(ret_pid); + + if(HasFatalFailure()) + { + return; + } + + evt_test->parse_event(); + + evt_test->assert_header(); + + /*=============================== ASSERT PARAMETERS ===========================*/ + + /* Please note here we cannot assert all the params, we check only the possible ones. */ + + /* Parameter 1: res (type: PT_ERRNO)*/ + evt_test->assert_numeric_param(1, (int64_t)0); + + /* Parameter 2: exe (type: PT_CHARBUF) */ + evt_test->assert_charbuf_param(2, pathname); + + /* Parameter 3: args (type: PT_CHARBUFARRAY) */ + /* Starting from `1` because the first is `exe`. */ + evt_test->assert_charbuf_array_param(3, &argv[1]); + + /* Parameter 4: tid (type: PT_PID) */ + evt_test->assert_numeric_param(4, (int64_t)ret_pid); + + /* Parameter 5: pid (type: PT_PID) */ + /* We are the main thread of the process so it's equal to `tid`. */ + evt_test->assert_numeric_param(5, (int64_t)ret_pid); + + /* Parameter 6: ptid (type: PT_PID) */ + evt_test->assert_numeric_param(6, (int64_t)::getpid()); + + /* Parameter 7: cwd (type: PT_CHARBUF) */ + /* leave the current working directory empty like in the old probe. */ + evt_test->assert_empty_param(7); + + /* Parameter 14: comm (type: PT_CHARBUF) */ + evt_test->assert_charbuf_param(14, comm); + + /* Parameter 16: env (type: PT_CHARBUFARRAY) */ + evt_test->assert_charbuf_array_param(16, &envp[0]); + + /* Parameter 20: flags (type: PT_UINT32) */ + /* Right now we send always `0`. */ + evt_test->assert_numeric_param(20, (uint32_t)0); + + /*=============================== ASSERT PARAMETERS ===========================*/ + + evt_test->assert_num_params_pushed(23); +} +#endif diff --git a/test/modern_bpf/test_suites/generic_tracepoints_suite/sched_process_fork.cpp b/test/modern_bpf/test_suites/generic_tracepoints_suite/sched_process_fork.cpp new file mode 100644 index 0000000000..b254cc9772 --- /dev/null +++ b/test/modern_bpf/test_suites/generic_tracepoints_suite/sched_process_fork.cpp @@ -0,0 +1,445 @@ +#include "../../event_class/event_class.h" +#include "../../helpers/proc_parsing.h" + +#if defined(CAPTURE_SCHED_PROC_FORK) && defined(__NR_wait4) + +#include + +#ifdef __NR_clone3 +TEST(GenericTracepoints, sched_proc_fork_case_clone3) +{ + auto evt_test = get_generic_event_test(PPME_SYSCALL_CLONE_20_X); + + evt_test->enable_capture(); + + /*=============================== TRIGGER SYSCALL ===========================*/ + + /* We need to use `SIGCHLD` otherwise the parent won't receive any signal + * when the child terminates. We use `CLONE_FILES` just to test the flags. + */ + struct clone_args cl_args = {0}; + cl_args.flags = CLONE_FILES; + cl_args.exit_signal = SIGCHLD; + pid_t ret_pid = syscall(__NR_clone3, &cl_args, sizeof(cl_args)); + + /* Child performs assertions on itself. */ + if(ret_pid == 0) + { + /* Please note: in the child, we cannot have a fatal failure otherwise all the following + * tests will crash. So we add a simple failure and the father will check for failures in + * the child process. + */ + + /* We scan proc after the BPF event is caught so we have + * to use `LESS_EQUAL` in the assertions. + */ + struct proc_info info = {0}; + pid_t pid = ::getpid(); + if(!get_proc_info(pid, &info)) + { + ADD_FAILURE() << "Unable to get all the info from proc" << std::endl; + exit(EXIT_FAILURE); + } + + evt_test->disable_capture(); + + evt_test->assert_event_presence(pid); + + if(HasFatalFailure()) + { + ADD_FAILURE() << "Problem during initialization"; + exit(EXIT_FAILURE); + } + + evt_test->parse_event(); + + evt_test->assert_header(); + + /*=============================== ASSERT PARAMETERS ===========================*/ + + /* Parameter 1: res (type: PT_PID)*/ + evt_test->assert_numeric_param(1, (int64_t)ret_pid); + + /* Parameter 2: exe (type: PT_CHARBUF) */ + evt_test->assert_charbuf_param(2, info.args[0]); + + /* Parameter 3: args (type: PT_CHARBUFARRAY) */ + /* Starting from `1` because the first is `exe`. */ + evt_test->assert_charbuf_array_param(3, &info.args[1]); + + /* Parameter 4: tid (type: PT_PID) */ + evt_test->assert_numeric_param(4, (int64_t)pid); + + /* Parameter 5: pid (type: PT_PID) */ + /* We are the main thread of the process so it's equal to `tid`. */ + evt_test->assert_numeric_param(5, (int64_t)pid); + + /* Parameter 6: ptid (type: PT_PID) */ + evt_test->assert_numeric_param(6, (int64_t)info.ppid); + + /* Parameter 7: cwd (type: PT_CHARBUF) */ + /* leave the current working directory empty like in the old probe. */ + evt_test->assert_empty_param(7); + + /* Parameter 8: fdlimit (type: PT_UINT64) */ + evt_test->assert_numeric_param(8, (uint64_t)info.file_rlimit.rlim_cur); + + /* Parameter 9: pgft_maj (type: PT_UINT64) */ + evt_test->assert_numeric_param(9, (uint64_t)0, GREATER_EQUAL); + + /* Parameter 10: pgft_min (type: PT_UINT64) */ + evt_test->assert_numeric_param(10, (uint64_t)0, GREATER_EQUAL); + + /* Parameter 11: vm_size (type: PT_UINT32) */ + evt_test->assert_numeric_param(11, (uint32_t)info.vm_size, LESS_EQUAL); + + /* Parameter 12: vm_rss (type: PT_UINT32) */ + evt_test->assert_numeric_param(12, (uint32_t)info.vm_rss, LESS_EQUAL); + + /* Parameter 13: vm_swap (type: PT_UINT32) */ + evt_test->assert_numeric_param(13, (uint32_t)info.vm_swap, LESS_EQUAL); + + /* Parameter 14: comm (type: PT_CHARBUF) */ + evt_test->assert_charbuf_param(14, TEST_EXECUTABLE_NAME); + + /* Parameter 15: cgroups (type: PT_CHARBUFARRAY) */ + evt_test->assert_cgroup_param(15); + + /* Parameter 16: flags (type: PT_FLAGS32) */ + evt_test->assert_numeric_param(16, (uint32_t)PPM_CL_CLONE_FILES); + + /* Parameter 17: uid (type: PT_UINT32) */ + evt_test->assert_numeric_param(17, (uint32_t)info.uid); + + /* Parameter 18: gid (type: PT_UINT32) */ + evt_test->assert_numeric_param(18, (uint32_t)info.gid); + + /* Parameter 19: vtid (type: PT_PID) */ + evt_test->assert_numeric_param(19, (int64_t)info.vtid); + + /* Parameter 20: vpid (type: PT_PID) */ + evt_test->assert_numeric_param(20, (int64_t)info.vpid); + + /*=============================== ASSERT PARAMETERS ===========================*/ + + evt_test->assert_num_params_pushed(20); + + if(HasFailure()) + { + exit(EXIT_FAILURE); + } + else + { + exit(EXIT_SUCCESS); + } + } + + assert_syscall_state(SYSCALL_SUCCESS, "clone3", ret_pid, NOT_EQUAL, -1); + + int status = 0; + int options = 0; + assert_syscall_state(SYSCALL_SUCCESS, "wait4", syscall(__NR_wait4, ret_pid, &status, options, NULL), NOT_EQUAL, -1); + + if(__WEXITSTATUS(status) == EXIT_FAILURE) + { + FAIL() << "Something in the child failed." << std::endl; + } + + /*=============================== TRIGGER SYSCALL ===========================*/ +} +#endif /* __NR_clone3 */ + + +#ifdef __NR_clone +TEST(GenericTracepoints, sched_proc_fork_case_clone) +{ + auto evt_test = get_generic_event_test(PPME_SYSCALL_CLONE_20_X); + + evt_test->enable_capture(); + + /*=============================== TRIGGER SYSCALL ===========================*/ + + /* We need to use `SIGCHLD` otherwise the parent won't receive any signal + * when the child terminates. We use `CLONE_FILES` just to test the flags. + */ + unsigned long clone_flags = CLONE_FILES | SIGCHLD; + int parent_tid = 0; + unsigned long newsp = 0; + int child_tid = 0; + unsigned long tls = 0; + pid_t ret_pid = 0; + +#ifdef __s390x__ + ret_pid = syscall(__NR_clone, newsp, clone_flags, &parent_tid, &child_tid, tls); +#elif __aarch64__ + ret_pid = syscall(__NR_clone, clone_flags, newsp, &parent_tid, tls, &child_tid); +#else + ret_pid = syscall(__NR_clone, clone_flags, newsp, &parent_tid, &child_tid, tls); +#endif + + /* Child performs assertions on itself. */ + if(ret_pid == 0) + { + /* Please note: in the child, we cannot have a fatal failure otherwise all the following + * tests will crash. So we add a simple failure and the father will check for failures in + * the child process. + */ + + /* We scan proc after the BPF event is caught so we have + * to use `LESS_EQUAL` in the assertions. + */ + struct proc_info info = {0}; + pid_t pid = ::getpid(); + if(!get_proc_info(pid, &info)) + { + ADD_FAILURE() << "Unable to get all the info from proc" << std::endl; + exit(EXIT_FAILURE); + } + + evt_test->disable_capture(); + + evt_test->assert_event_presence(pid); + + if(HasFatalFailure()) + { + ADD_FAILURE() << "Problem during initialization"; + exit(EXIT_FAILURE); + } + + evt_test->parse_event(); + + evt_test->assert_header(); + + /*=============================== ASSERT PARAMETERS ===========================*/ + + /* Parameter 1: res (type: PT_PID)*/ + evt_test->assert_numeric_param(1, (int64_t)ret_pid); + + /* Parameter 2: exe (type: PT_CHARBUF) */ + evt_test->assert_charbuf_param(2, info.args[0]); + + /* Parameter 3: args (type: PT_CHARBUFARRAY) */ + /* Starting from `1` because the first is `exe`. */ + evt_test->assert_charbuf_array_param(3, &info.args[1]); + + /* Parameter 4: tid (type: PT_PID) */ + evt_test->assert_numeric_param(4, (int64_t)pid); + + /* Parameter 5: pid (type: PT_PID) */ + /* We are the main thread of the process so it's equal to `tid`. */ + evt_test->assert_numeric_param(5, (int64_t)pid); + + /* Parameter 6: ptid (type: PT_PID) */ + evt_test->assert_numeric_param(6, (int64_t)info.ppid); + + /* Parameter 7: cwd (type: PT_CHARBUF) */ + /* leave the current working directory empty like in the old probe. */ + evt_test->assert_empty_param(7); + + /* Parameter 8: fdlimit (type: PT_UINT64) */ + evt_test->assert_numeric_param(8, (uint64_t)info.file_rlimit.rlim_cur); + + /* Parameter 9: pgft_maj (type: PT_UINT64) */ + evt_test->assert_numeric_param(9, (uint64_t)0, GREATER_EQUAL); + + /* Parameter 10: pgft_min (type: PT_UINT64) */ + evt_test->assert_numeric_param(10, (uint64_t)0, GREATER_EQUAL); + + /* Parameter 11: vm_size (type: PT_UINT32) */ + evt_test->assert_numeric_param(11, (uint32_t)info.vm_size, LESS_EQUAL); + + /* Parameter 12: vm_rss (type: PT_UINT32) */ + evt_test->assert_numeric_param(12, (uint32_t)info.vm_rss, LESS_EQUAL); + + /* Parameter 13: vm_swap (type: PT_UINT32) */ + evt_test->assert_numeric_param(13, (uint32_t)info.vm_swap, LESS_EQUAL); + + /* Parameter 14: comm (type: PT_CHARBUF) */ + evt_test->assert_charbuf_param(14, TEST_EXECUTABLE_NAME); + + /* Parameter 15: cgroups (type: PT_CHARBUFARRAY) */ + evt_test->assert_cgroup_param(15); + + /* Parameter 16: flags (type: PT_FLAGS32) */ + evt_test->assert_numeric_param(16, (uint32_t)PPM_CL_CLONE_FILES); + + /* Parameter 17: uid (type: PT_UINT32) */ + evt_test->assert_numeric_param(17, (uint32_t)info.uid); + + /* Parameter 18: gid (type: PT_UINT32) */ + evt_test->assert_numeric_param(18, (uint32_t)info.gid); + + /* Parameter 19: vtid (type: PT_PID) */ + evt_test->assert_numeric_param(19, (int64_t)info.vtid); + + /* Parameter 20: vpid (type: PT_PID) */ + evt_test->assert_numeric_param(20, (int64_t)info.vpid); + + /*=============================== ASSERT PARAMETERS ===========================*/ + + evt_test->assert_num_params_pushed(20); + + if(HasFailure()) + { + exit(EXIT_FAILURE); + } + else + { + exit(EXIT_SUCCESS); + } + } + + assert_syscall_state(SYSCALL_SUCCESS, "clone", ret_pid, NOT_EQUAL, -1); + + int status = 0; + int options = 0; + assert_syscall_state(SYSCALL_SUCCESS, "wait4", syscall(__NR_wait4, ret_pid, &status, options, NULL), NOT_EQUAL, -1); + + if(__WEXITSTATUS(status) == EXIT_FAILURE) + { + FAIL() << "Something in the child failed." << std::endl; + } + + /*=============================== TRIGGER SYSCALL ===========================*/ +} +#endif /* __NR_clone */ + +#ifdef __NR_fork +TEST(GenericTracepoints, sched_proc_fork_case_fork) +{ + auto evt_test = get_generic_event_test(PPME_SYSCALL_CLONE_20_X); + + evt_test->enable_capture(); + + /*=============================== TRIGGER SYSCALL ===========================*/ + + pid_t ret_pid = syscall(__NR_fork); + + /* Child performs assertions on itself. */ + if(ret_pid == 0) + { + /* Please note: in the child, we cannot have a fatal failure otherwise all the following + * tests will crash. So we add a simple failure and the father will check for failures in + * the child process. + */ + + /* We scan proc after the BPF event is caught so we have + * to use `LESS_EQUAL` in the assertions. + */ + struct proc_info info = {0}; + pid_t pid = ::getpid(); + if(!get_proc_info(pid, &info)) + { + ADD_FAILURE() << "Unable to get all the info from proc" << std::endl; + exit(EXIT_FAILURE); + } + + evt_test->disable_capture(); + + evt_test->assert_event_presence(pid); + + if(HasFatalFailure()) + { + ADD_FAILURE() << "Problem during initialization"; + exit(EXIT_FAILURE); + } + + evt_test->parse_event(); + + evt_test->assert_header(); + + /*=============================== ASSERT PARAMETERS ===========================*/ + + /* Parameter 1: res (type: PT_PID)*/ + evt_test->assert_numeric_param(1, (int64_t)ret_pid); + + /* Parameter 2: exe (type: PT_CHARBUF) */ + evt_test->assert_charbuf_param(2, info.args[0]); + + /* Parameter 3: args (type: PT_CHARBUFARRAY) */ + /* Starting from `1` because the first is `exe`. */ + evt_test->assert_charbuf_array_param(3, &info.args[1]); + + /* Parameter 4: tid (type: PT_PID) */ + evt_test->assert_numeric_param(4, (int64_t)pid); + + /* Parameter 5: pid (type: PT_PID) */ + /* We are the main thread of the process so it's equal to `tid`. */ + evt_test->assert_numeric_param(5, (int64_t)pid); + + /* Parameter 6: ptid (type: PT_PID) */ + evt_test->assert_numeric_param(6, (int64_t)info.ppid); + + /* Parameter 7: cwd (type: PT_CHARBUF) */ + /* leave the current working directory empty like in the old probe. */ + evt_test->assert_empty_param(7); + + /* Parameter 8: fdlimit (type: PT_UINT64) */ + evt_test->assert_numeric_param(8, (uint64_t)info.file_rlimit.rlim_cur); + + /* Parameter 9: pgft_maj (type: PT_UINT64) */ + evt_test->assert_numeric_param(9, (uint64_t)0, GREATER_EQUAL); + + /* Parameter 10: pgft_min (type: PT_UINT64) */ + evt_test->assert_numeric_param(10, (uint64_t)0, GREATER_EQUAL); + + /* Parameter 11: vm_size (type: PT_UINT32) */ + evt_test->assert_numeric_param(11, (uint32_t)info.vm_size, LESS_EQUAL); + + /* Parameter 12: vm_rss (type: PT_UINT32) */ + evt_test->assert_numeric_param(12, (uint32_t)info.vm_rss, LESS_EQUAL); + + /* Parameter 13: vm_swap (type: PT_UINT32) */ + evt_test->assert_numeric_param(13, (uint32_t)info.vm_swap, LESS_EQUAL); + + /* Parameter 14: comm (type: PT_CHARBUF) */ + evt_test->assert_charbuf_param(14, TEST_EXECUTABLE_NAME); + + /* Parameter 15: cgroups (type: PT_CHARBUFARRAY) */ + evt_test->assert_cgroup_param(15); + + /* Parameter 16: flags (type: PT_FLAGS32) */ + evt_test->assert_numeric_param(16, (uint32_t)0); + + /* Parameter 17: uid (type: PT_UINT32) */ + evt_test->assert_numeric_param(17, (uint32_t)info.uid); + + /* Parameter 18: gid (type: PT_UINT32) */ + evt_test->assert_numeric_param(18, (uint32_t)info.gid); + + /* Parameter 19: vtid (type: PT_PID) */ + evt_test->assert_numeric_param(19, (int64_t)info.vtid); + + /* Parameter 20: vpid (type: PT_PID) */ + evt_test->assert_numeric_param(20, (int64_t)info.vpid); + + /*=============================== ASSERT PARAMETERS ===========================*/ + + evt_test->assert_num_params_pushed(20); + + if(HasFailure()) + { + exit(EXIT_FAILURE); + } + else + { + exit(EXIT_SUCCESS); + } + } + + assert_syscall_state(SYSCALL_SUCCESS, "fork", ret_pid, NOT_EQUAL, -1); + + int status = 0; + int options = 0; + assert_syscall_state(SYSCALL_SUCCESS, "wait4", syscall(__NR_wait4, ret_pid, &status, options, NULL), NOT_EQUAL, -1); + + if(__WEXITSTATUS(status) == EXIT_FAILURE) + { + FAIL() << "Something in the child failed." << std::endl; + } + + /*=============================== TRIGGER SYSCALL ===========================*/ +} +#endif /* __NR_fork */ + +#endif diff --git a/test/modern_bpf/test_suites/syscall_exit_suite/clone3_x.cpp b/test/modern_bpf/test_suites/syscall_exit_suite/clone3_x.cpp index 9ee30a4c42..5ab716126d 100644 --- a/test/modern_bpf/test_suites/syscall_exit_suite/clone3_x.cpp +++ b/test/modern_bpf/test_suites/syscall_exit_suite/clone3_x.cpp @@ -130,12 +130,6 @@ TEST(SyscallExit, clone3X_father) evt_test->assert_num_params_pushed(20); } -/* In some architectures we are not able to catch the `clone exit child - * event` from the `sys_exit` tracepoint. This is because there is no - * default behavior among different architectures... you can find more - * info in `driver/feature_gates.h`. - */ -#ifndef CAPTURE_SCHED_PROC_FORK TEST(SyscallExit, clone3X_child) { auto evt_test = get_syscall_event_test(__NR_clone3, EXIT_EVENT); @@ -173,6 +167,14 @@ TEST(SyscallExit, clone3X_child) evt_test->disable_capture(); +/* In some architectures we are not able to catch the `clone exit child + * event` from the `sys_exit` tracepoint. This is because there is no + * default behavior among different architectures... you can find more + * info in `driver/feature_gates.h`. + */ +#ifdef CAPTURE_SCHED_PROC_FORK + evt_test->assert_event_absence(pid); +#else evt_test->assert_event_presence(pid); if(HasFatalFailure()) @@ -253,7 +255,7 @@ TEST(SyscallExit, clone3X_child) /*=============================== ASSERT PARAMETERS ===========================*/ evt_test->assert_num_params_pushed(20); - +#endif if(HasFailure()) { exit(EXIT_FAILURE); @@ -277,6 +279,4 @@ TEST(SyscallExit, clone3X_child) /*=============================== TRIGGER SYSCALL ===========================*/ } -#endif /* CAPTURE_SCHED_PROC_FORK */ - #endif diff --git a/test/modern_bpf/test_suites/syscall_exit_suite/clone_x.cpp b/test/modern_bpf/test_suites/syscall_exit_suite/clone_x.cpp index 81d79d09d3..f54c34cdb7 100644 --- a/test/modern_bpf/test_suites/syscall_exit_suite/clone_x.cpp +++ b/test/modern_bpf/test_suites/syscall_exit_suite/clone_x.cpp @@ -166,12 +166,6 @@ TEST(SyscallExit, cloneX_father) evt_test->assert_num_params_pushed(20); } -/* In some architectures we are not able to catch the `clone exit child - * event` from the `sys_exit` tracepoint. This is because there is no - * default behavior among different architectures... you can find more - * info in `driver/feature_gates.h`. - */ -#ifndef CAPTURE_SCHED_PROC_FORK TEST(SyscallExit, cloneX_child) { auto evt_test = get_syscall_event_test(__NR_clone, EXIT_EVENT); @@ -219,6 +213,14 @@ TEST(SyscallExit, cloneX_child) evt_test->disable_capture(); +/* In some architectures we are not able to catch the `clone exit child + * event` from the `sys_exit` tracepoint. This is because there is no + * default behavior among different architectures... you can find more + * info in `driver/feature_gates.h`. + */ +#ifdef CAPTURE_SCHED_PROC_FORK + evt_test->assert_event_absence(pid); +#else evt_test->assert_event_presence(pid); if(HasFatalFailure()) @@ -299,7 +301,7 @@ TEST(SyscallExit, cloneX_child) /*=============================== ASSERT PARAMETERS ===========================*/ evt_test->assert_num_params_pushed(20); - +#endif if(HasFailure()) { exit(EXIT_FAILURE); @@ -326,5 +328,3 @@ TEST(SyscallExit, cloneX_child) /*=============================== TRIGGER SYSCALL ===========================*/ } #endif /* CAPTURE_SCHED_PROC_FORK */ - -#endif diff --git a/test/modern_bpf/test_suites/syscall_exit_suite/execve_x.cpp b/test/modern_bpf/test_suites/syscall_exit_suite/execve_x.cpp index 9b71186825..7f6e9315d1 100644 --- a/test/modern_bpf/test_suites/syscall_exit_suite/execve_x.cpp +++ b/test/modern_bpf/test_suites/syscall_exit_suite/execve_x.cpp @@ -2,9 +2,11 @@ #include "../../flags/flags_definitions.h" #include "../../helpers/proc_parsing.h" -#if defined(__NR_execve) && defined(__NR_capget) +#if defined(__NR_execve) && defined(__NR_capget) && defined(__NR_clone3) && defined(__NR_wait4) -TEST(SyscallExit, execveX) +#include + +TEST(SyscallExit, execveX_failure) { auto evt_test = get_syscall_event_test(__NR_execve, EXIT_EVENT); @@ -38,8 +40,8 @@ TEST(SyscallExit, execveX) * Call the `execve` */ char pathname[] = "//**null-file-path**//"; - const char* newargv[] = {pathname, "first_argv", "second_argv", NULL}; - const char* newenviron[] = {"IN_TEST=yes", "3_ARGUMENT=yes", "2_ARGUMENT=no", NULL}; + const char *newargv[] = {pathname, "first_argv", "second_argv", NULL}; + const char *newenviron[] = {"IN_TEST=yes", "3_ARGUMENT=yes", "2_ARGUMENT=no", NULL}; assert_syscall_state(SYSCALL_FAILURE, "execve", syscall(__NR_execve, pathname, newargv, newenviron)); int64_t errno_value = -errno; @@ -139,4 +141,107 @@ TEST(SyscallExit, execveX) evt_test->assert_num_params_pushed(23); } + +TEST(SyscallExit, execveX_success) +{ + auto evt_test = get_syscall_event_test(__NR_execve, EXIT_EVENT); + + evt_test->enable_capture(); + + /*=============================== TRIGGER SYSCALL ===========================*/ + + /* Prepare the execve args */ + const char *pathname = "/usr/bin/echo"; + const char *comm = "echo"; + const char *argv[] = {pathname, "[OUTPUT] SyscallExit.execveX_success test", NULL}; + const char *envp[] = {"IN_TEST=yes", "3_ARGUMENT=yes", "2_ARGUMENT=no", NULL}; + + /* We need to use `SIGCHLD` otherwise the parent won't receive any signal + * when the child terminates. + */ + struct clone_args cl_args = {0}; + cl_args.exit_signal = SIGCHLD; + pid_t ret_pid = syscall(__NR_clone3, &cl_args, sizeof(cl_args)); + + if(ret_pid == 0) + { + syscall(__NR_execve, pathname, argv, envp); + exit(EXIT_FAILURE); + } + + assert_syscall_state(SYSCALL_SUCCESS, "clone3", ret_pid, NOT_EQUAL, -1); + + /* Catch the child before doing anything else. */ + int status = 0; + int options = 0; + assert_syscall_state(SYSCALL_SUCCESS, "wait4", syscall(__NR_wait4, ret_pid, &status, options, NULL), NOT_EQUAL, -1); + + if(__WEXITSTATUS(status) == EXIT_FAILURE) + { + FAIL() << "The child execve failed." << std::endl; + } + + /*=============================== TRIGGER SYSCALL ===========================*/ + + evt_test->disable_capture(); + +#ifdef CAPTURE_SCHED_PROC_EXEC + /* We search for a child event. */ + evt_test->assert_event_absence(ret_pid); +#else + /* We search for a child event. */ + evt_test->assert_event_presence(ret_pid); + + if(HasFatalFailure()) + { + return; + } + + evt_test->parse_event(); + + evt_test->assert_header(); + + /*=============================== ASSERT PARAMETERS ===========================*/ + + /* Please note here we cannot assert all the params, we check only the possible ones. */ + + /* Parameter 1: res (type: PT_ERRNO)*/ + evt_test->assert_numeric_param(1, (int64_t)0); + + /* Parameter 2: exe (type: PT_CHARBUF) */ + evt_test->assert_charbuf_param(2, pathname); + + /* Parameter 3: args (type: PT_CHARBUFARRAY) */ + /* Starting from `1` because the first is `exe`. */ + evt_test->assert_charbuf_array_param(3, &argv[1]); + + /* Parameter 4: tid (type: PT_PID) */ + evt_test->assert_numeric_param(4, (int64_t)ret_pid); + + /* Parameter 5: pid (type: PT_PID) */ + /* We are the main thread of the process so it's equal to `tid`. */ + evt_test->assert_numeric_param(5, (int64_t)ret_pid); + + /* Parameter 6: ptid (type: PT_PID) */ + evt_test->assert_numeric_param(6, (int64_t)::getpid()); + + /* Parameter 7: cwd (type: PT_CHARBUF) */ + /* leave the current working directory empty like in the old probe. */ + evt_test->assert_empty_param(7); + + /* Parameter 14: comm (type: PT_CHARBUF) */ + evt_test->assert_charbuf_param(14, comm); + + /* Parameter 16: env (type: PT_CHARBUFARRAY) */ + evt_test->assert_charbuf_array_param(16, &envp[0]); + + /* Parameter 20: flags (type: PT_UINT32) */ + /* Right now we send always `0`. */ + evt_test->assert_numeric_param(20, (uint32_t)0); + + /*=============================== ASSERT PARAMETERS ===========================*/ + + evt_test->assert_num_params_pushed(23); +#endif +} #endif diff --git a/test/modern_bpf/test_suites/syscall_exit_suite/execveat_x.cpp b/test/modern_bpf/test_suites/syscall_exit_suite/execveat_x.cpp index bfe0b80b0b..df64012d3e 100644 --- a/test/modern_bpf/test_suites/syscall_exit_suite/execveat_x.cpp +++ b/test/modern_bpf/test_suites/syscall_exit_suite/execveat_x.cpp @@ -2,9 +2,11 @@ #include "../../flags/flags_definitions.h" #include "../../helpers/proc_parsing.h" -#if defined(__NR_execveat) && defined(__NR_capget) +#if defined(__NR_execveat) && defined(__NR_capget) && defined(__NR_clone3) && defined(__NR_wait4) && defined(__NR_execve) -TEST(SyscallExit, execveatX) +#include + +TEST(SyscallExit, execveatX_failure) { auto evt_test = get_syscall_event_test(__NR_execveat, EXIT_EVENT); @@ -40,8 +42,8 @@ TEST(SyscallExit, execveatX) */ int dirfd = AT_FDCWD; char pathname[] = "//**null-file-path**//"; - const char* newargv[] = {pathname, "first_argv", "second_argv", NULL}; - const char* newenviron[] = {"IN_TEST=yes", "3_ARGUMENT=yes", "2_ARGUMENT=no", NULL}; + const char *newargv[] = {pathname, "first_argv", "second_argv", NULL}; + const char *newenviron[] = {"IN_TEST=yes", "3_ARGUMENT=yes", "2_ARGUMENT=no", NULL}; int flags = AT_SYMLINK_NOFOLLOW; assert_syscall_state(SYSCALL_FAILURE, "execveat", syscall(__NR_execveat, dirfd, pathname, newargv, newenviron, flags)); int64_t errno_value = -errno; @@ -143,4 +145,223 @@ TEST(SyscallExit, execveatX) evt_test->assert_num_params_pushed(23); } + +/* All architectures return an `EXECVEAT_X` event when the syscall fails, but only + * `s390x` seems to return an `EXECVEAT_X` event also when the syscall succeeds, other + * architectures like `x86_64` return an `EXECVE_X` event. + */ +TEST(SyscallExit, execveatX_correct_exit) +{ + auto evt_test = get_syscall_event_test(__NR_execveat, EXIT_EVENT); + + evt_test->enable_capture(); + + /*=============================== TRIGGER SYSCALL ===========================*/ + + /* Prepare the execve args */ + int dirfd = 0; + const char *pathname = "/usr/bin/echo"; + const char *comm = "echo"; + const char *argv[] = {pathname, "[OUTPUT] SyscallExit.execveatX_success test", NULL}; + const char *envp[] = {"IN_TEST=yes", "3_ARGUMENT=yes", "2_ARGUMENT=no", NULL}; + int flags = 0; + + /* We need to use `SIGCHLD` otherwise the parent won't receive any signal + * when the child terminates. + */ + struct clone_args cl_args = {0}; + cl_args.exit_signal = SIGCHLD; + pid_t ret_pid = syscall(__NR_clone3, &cl_args, sizeof(cl_args)); + + if(ret_pid == 0) + { + syscall(__NR_execveat, dirfd, pathname, argv, envp, flags); + exit(EXIT_FAILURE); + } + + assert_syscall_state(SYSCALL_SUCCESS, "clone3", ret_pid, NOT_EQUAL, -1); + + /* Catch the child before doing anything else. */ + int status = 0; + int options = 0; + assert_syscall_state(SYSCALL_SUCCESS, "wait4", syscall(__NR_wait4, ret_pid, &status, options, NULL), NOT_EQUAL, -1); + + if(__WEXITSTATUS(status) == EXIT_FAILURE) + { + FAIL() << "The child execveat failed." << std::endl; + } + + /*=============================== TRIGGER SYSCALL ===========================*/ + + evt_test->disable_capture(); + +#if __s390x__ + /* We search for a child event. */ + evt_test->assert_event_presence(ret_pid); + + if(HasFatalFailure()) + { + return; + } + + evt_test->parse_event(); + + evt_test->assert_header(); + + /*=============================== ASSERT PARAMETERS ===========================*/ + + /* Please note here we cannot assert all the params, we check only the possible ones. */ + + /* Parameter 1: res (type: PT_ERRNO)*/ + evt_test->assert_numeric_param(1, (int64_t)0); + + /* Parameter 2: exe (type: PT_CHARBUF) */ + evt_test->assert_charbuf_param(2, pathname); + + /* Parameter 3: args (type: PT_CHARBUFARRAY) */ + /* Starting from `1` because the first is `exe`. */ + evt_test->assert_charbuf_array_param(3, &argv[1]); + + /* Parameter 4: tid (type: PT_PID) */ + evt_test->assert_numeric_param(4, (int64_t)ret_pid); + + /* Parameter 5: pid (type: PT_PID) */ + /* We are the main thread of the process so it's equal to `tid`. */ + evt_test->assert_numeric_param(5, (int64_t)ret_pid); + + /* Parameter 6: ptid (type: PT_PID) */ + evt_test->assert_numeric_param(6, (int64_t)::getpid()); + + /* Parameter 7: cwd (type: PT_CHARBUF) */ + /* leave the current working directory empty like in the old probe. */ + evt_test->assert_empty_param(7); + + /* Parameter 14: comm (type: PT_CHARBUF) */ + evt_test->assert_charbuf_param(14, comm); + + /* Parameter 16: env (type: PT_CHARBUFARRAY) */ + evt_test->assert_charbuf_array_param(16, &envp[0]); + + /* Parameter 20: flags (type: PT_UINT32) */ + /* Right now we send always `0`. */ + evt_test->assert_numeric_param(20, (uint32_t)0); + + /*=============================== ASSERT PARAMETERS ===========================*/ + + evt_test->assert_num_params_pushed(23); +#else + /* We search for a child event. */ + evt_test->assert_event_absence(ret_pid); +#endif +} + +TEST(SyscallExit, execveatX_execve_exit) +{ + auto evt_test = get_syscall_event_test(__NR_execve, EXIT_EVENT); + + evt_test->enable_capture(); + + /*=============================== TRIGGER SYSCALL ===========================*/ + + /* Prepare the execve args */ + int dirfd = 0; + const char *pathname = "/usr/bin/echo"; + const char *comm = "echo"; + const char *argv[] = {pathname, "[OUTPUT] SyscallExit.execveatX_success test", NULL}; + const char *envp[] = {"IN_TEST=yes", "3_ARGUMENT=yes", "2_ARGUMENT=no", NULL}; + int flags = 0; + + /* We need to use `SIGCHLD` otherwise the parent won't receive any signal + * when the child terminates. + */ + struct clone_args cl_args = {0}; + cl_args.exit_signal = SIGCHLD; + pid_t ret_pid = syscall(__NR_clone3, &cl_args, sizeof(cl_args)); + + if(ret_pid == 0) + { + syscall(__NR_execveat, dirfd, pathname, argv, envp, flags); + exit(EXIT_FAILURE); + } + + assert_syscall_state(SYSCALL_SUCCESS, "clone3", ret_pid, NOT_EQUAL, -1); + + /* Catch the child before doing anything else. */ + int status = 0; + int options = 0; + assert_syscall_state(SYSCALL_SUCCESS, "wait4", syscall(__NR_wait4, ret_pid, &status, options, NULL), NOT_EQUAL, -1); + + if(__WEXITSTATUS(status) == EXIT_FAILURE) + { + FAIL() << "The child execveat failed." << std::endl; + } + + /*=============================== TRIGGER SYSCALL ===========================*/ + + evt_test->disable_capture(); + +/* Some architectures like aarch64 don't return either an `EXECVEAT_X` or + * an `EXECVE_X` event, this is a known issue you look at `feature_gates.h` + * for more info. `s390x` already returns an `EXECVEAT_X` event so here we shouldn't + * receive anything. + */ +#if defined(__s390x__) || defined(CAPTURE_SCHED_PROC_EXEC) + /* We search for a child event. */ + evt_test->assert_event_absence(ret_pid); +#else + /* We search for a child event. */ + evt_test->assert_event_presence(ret_pid); + + if(HasFatalFailure()) + { + return; + } + + evt_test->parse_event(); + + evt_test->assert_header(); + + /*=============================== ASSERT PARAMETERS ===========================*/ + + /* Please note here we cannot assert all the params, we check only the possible ones. */ + + /* Parameter 1: res (type: PT_ERRNO)*/ + evt_test->assert_numeric_param(1, (int64_t)0); + + /* Parameter 2: exe (type: PT_CHARBUF) */ + evt_test->assert_charbuf_param(2, pathname); + + /* Parameter 3: args (type: PT_CHARBUFARRAY) */ + /* Starting from `1` because the first is `exe`. */ + evt_test->assert_charbuf_array_param(3, &argv[1]); + + /* Parameter 4: tid (type: PT_PID) */ + evt_test->assert_numeric_param(4, (int64_t)ret_pid); + + /* Parameter 5: pid (type: PT_PID) */ + /* We are the main thread of the process so it's equal to `tid`. */ + evt_test->assert_numeric_param(5, (int64_t)ret_pid); + + /* Parameter 6: ptid (type: PT_PID) */ + evt_test->assert_numeric_param(6, (int64_t)::getpid()); + + /* Parameter 7: cwd (type: PT_CHARBUF) */ + /* leave the current working directory empty like in the old probe. */ + evt_test->assert_empty_param(7); + + /* Parameter 14: comm (type: PT_CHARBUF) */ + evt_test->assert_charbuf_param(14, comm); + + /* Parameter 16: env (type: PT_CHARBUFARRAY) */ + evt_test->assert_charbuf_array_param(16, &envp[0]); + + /* Parameter 20: flags (type: PT_UINT32) */ + /* Right now we send always `0`. */ + evt_test->assert_numeric_param(20, (uint32_t)0); + + /*=============================== ASSERT PARAMETERS ===========================*/ + + evt_test->assert_num_params_pushed(23); +#endif +} #endif diff --git a/test/modern_bpf/test_suites/syscall_exit_suite/fork_x.cpp b/test/modern_bpf/test_suites/syscall_exit_suite/fork_x.cpp index 5e1cd8e7b8..fd08f94e00 100644 --- a/test/modern_bpf/test_suites/syscall_exit_suite/fork_x.cpp +++ b/test/modern_bpf/test_suites/syscall_exit_suite/fork_x.cpp @@ -123,12 +123,6 @@ TEST(SyscallExit, forkX_father) evt_test->assert_num_params_pushed(20); } -/* In some architectures we are not able to catch the `clone exit child - * event` from the `sys_exit` tracepoint. This is because there is no - * default behavior among different architectures... you can find more - * info in `driver/feature_gates.h`. - */ -#ifndef CAPTURE_SCHED_PROC_FORK TEST(SyscallExit, forkX_child) { auto evt_test = new event_test(__NR_fork, EXIT_EVENT); @@ -160,6 +154,9 @@ TEST(SyscallExit, forkX_child) evt_test->disable_capture(); +#ifdef CAPTURE_SCHED_PROC_FORK + evt_test->assert_event_absence(pid); +#else evt_test->assert_event_presence(pid); if(HasFatalFailure()) @@ -240,7 +237,7 @@ TEST(SyscallExit, forkX_child) /*=============================== ASSERT PARAMETERS ===========================*/ evt_test->assert_num_params_pushed(20); - +#endif if(HasFailure()) { exit(EXIT_FAILURE); @@ -266,6 +263,4 @@ TEST(SyscallExit, forkX_child) /*=============================== TRIGGER SYSCALL ===========================*/ } -#endif /* CAPTURE_SCHED_PROC_FORK */ - #endif diff --git a/userspace/libpman/include/libpman.h b/userspace/libpman/include/libpman.h index df1be700b5..32438f48be 100644 --- a/userspace/libpman/include/libpman.h +++ b/userspace/libpman/include/libpman.h @@ -161,6 +161,34 @@ extern "C" */ int pman_detach_sched_switch(void); + /** + * @brief Attach only the sched_proc_exec tracepoint + * + * @return `0` on success, `errno` in case of error. + */ + int pman_attach_sched_proc_exec(void); + + /** + * @brief Detach only the sched_proc_exec tracepoint + * + * @return `0` on success, `errno` in case of error. + */ + int pman_detach_sched_proc_exec(void); + + /** + * @brief Attach only the sched_proc_fork tracepoint + * + * @return `0` on success, `errno` in case of error. + */ + int pman_attach_sched_proc_fork(void); + + /** + * @brief Detach only the sched_proc_fork tracepoint + * + * @return `0` on success, `errno` in case of error. + */ + int pman_detach_sched_proc_fork(void); + ///////////////////////////// // MANAGE RINGBUFFERS ///////////////////////////// diff --git a/userspace/libpman/src/events_prog_names.h b/userspace/libpman/src/events_prog_names.h index a5ba0bf825..cae00e93ee 100644 --- a/userspace/libpman/src/events_prog_names.h +++ b/userspace/libpman/src/events_prog_names.h @@ -18,6 +18,7 @@ limitations under the License. #pragma once #include +#include /* For every event here we have the name of the corresponding bpf program. */ static const char* event_prog_names[PPM_EVENT_MAX] = { @@ -186,4 +187,10 @@ static const char* extra_event_prog_names[TAIL_EXTRA_EVENT_PROG_MAX] = { [T1_CLONE3_X] = "t1_clone3_x", [T1_FORK_X] = "t1_fork_x", [T1_VFORK_X] = "t1_vfork_x", +#ifdef CAPTURE_SCHED_PROC_EXEC + [T1_SCHED_PROC_EXEC] = "t1_sched_p_exec", +#endif +#ifdef CAPTURE_SCHED_PROC_FORK + [T1_SCHED_PROC_FORK] = "t1_sched_p_fork", +#endif }; diff --git a/userspace/libpman/src/programs.c b/userspace/libpman/src/programs.c index d71a39c7de..9e282b4e2a 100644 --- a/userspace/libpman/src/programs.c +++ b/userspace/libpman/src/programs.c @@ -16,6 +16,7 @@ limitations under the License. */ #include "state.h" +#include /* Some notes about how a bpf program must be detached without unloading it: * https://lore.kernel.org/bpf/CAEf4BzZ8=dV0wvggAKnD64yXnhcXhdf1ovCT_LBd17RtJJXrdA@mail.gmail.com/T/ @@ -91,6 +92,44 @@ int pman_attach_sched_switch() return 0; } +#ifdef CAPTURE_SCHED_PROC_EXEC +int pman_attach_sched_proc_exec() +{ + /* The program is already attached. */ + if(g_state.skel->links.sched_p_exec != NULL) + { + return 0; + } + + g_state.skel->links.sched_p_exec = bpf_program__attach(g_state.skel->progs.sched_p_exec); + if(!g_state.skel->links.sched_p_exec) + { + pman_print_error("failed to attach the 'sched_proc_exec' program"); + return errno; + } + return 0; +} +#endif + +#ifdef CAPTURE_SCHED_PROC_FORK +int pman_attach_sched_proc_fork() +{ + /* The program is already attached. */ + if(g_state.skel->links.sched_p_fork != NULL) + { + return 0; + } + + g_state.skel->links.sched_p_fork = bpf_program__attach(g_state.skel->progs.sched_p_fork); + if(!g_state.skel->links.sched_p_fork) + { + pman_print_error("failed to attach the 'sched_proc_fork' program"); + return errno; + } + return 0; +} +#endif + int pman_attach_all_programs() { int err; @@ -98,6 +137,12 @@ int pman_attach_all_programs() err = err ?: pman_attach_syscall_exit_dispatcher(); err = err ?: pman_attach_sched_proc_exit(); err = err ?: pman_attach_sched_switch(); +#ifdef CAPTURE_SCHED_PROC_EXEC + err = err ?: pman_attach_sched_proc_exec(); +#endif +#ifdef CAPTURE_SCHED_PROC_FORK + err = err ?: pman_attach_sched_proc_fork(); +#endif /* add all other programs. */ return err; } @@ -150,6 +195,32 @@ int pman_detach_sched_switch() return 0; } +#ifdef CAPTURE_SCHED_PROC_EXEC +int pman_detach_sched_proc_exec() +{ + if(g_state.skel->links.sched_p_exec && bpf_link__destroy(g_state.skel->links.sched_p_exec)) + { + pman_print_error("failed to detach the 'sched_proc_exec' program"); + return errno; + } + g_state.skel->links.sched_p_exec = NULL; + return 0; +} +#endif + +#ifdef CAPTURE_SCHED_PROC_FORK +int pman_detach_sched_proc_fork() +{ + if(g_state.skel->links.sched_p_fork && bpf_link__destroy(g_state.skel->links.sched_p_fork)) + { + pman_print_error("failed to detach the 'sched_proc_fork' program"); + return errno; + } + g_state.skel->links.sched_p_fork = NULL; + return 0; +} +#endif + int pman_detach_all_programs() { int err; @@ -157,6 +228,12 @@ int pman_detach_all_programs() err = err ?: pman_detach_syscall_exit_dispatcher(); err = err ?: pman_detach_sched_proc_exit(); err = err ?: pman_detach_sched_switch(); +#ifdef CAPTURE_SCHED_PROC_EXEC + err = err ?: pman_detach_sched_proc_exec(); +#endif +#ifdef CAPTURE_SCHED_PROC_FORK + err = err ?: pman_detach_sched_proc_fork(); +#endif /* add all other programs. */ return err; } diff --git a/userspace/libscap/engine/modern_bpf/scap_modern_bpf.c b/userspace/libscap/engine/modern_bpf/scap_modern_bpf.c index 9b396cf01b..a4a1766484 100644 --- a/userspace/libscap/engine/modern_bpf/scap_modern_bpf.c +++ b/userspace/libscap/engine/modern_bpf/scap_modern_bpf.c @@ -63,6 +63,18 @@ static int32_t attach_interesting_tracepoints(bool* tp_array) ret = pman_attach_sched_switch(); break; + case SCHED_PROC_EXEC: +#ifdef CAPTURE_SCHED_PROC_EXEC + ret = pman_attach_sched_proc_exec(); +#endif + break; + + case SCHED_PROC_FORK: +#ifdef CAPTURE_SCHED_PROC_FORK + ret = pman_attach_sched_proc_fork(); +#endif + break; + default: /* Do nothing right now. */ break; diff --git a/userspace/libsinsp/parsers.cpp b/userspace/libsinsp/parsers.cpp index ec42010d28..1159f4dea5 100644 --- a/userspace/libsinsp/parsers.cpp +++ b/userspace/libsinsp/parsers.cpp @@ -1711,9 +1711,9 @@ void sinsp_parser::parse_execve_exit(sinsp_evt *evt) ASSERT(parinfo->m_len == sizeof(int64_t)); retval = *(int64_t *)parinfo->m_val; - /* Please note here we will never parse an `PPME_SYSCALL_EXECVEAT_X` event since it is - * generated only in case of failure, here if `retval<0` we return immediately. - * If we remove this `if` we need to support also the `PPME_SYSCALL_EXECVEAT_X` event. + /* Some architectures like s390x send a `PPME_SYSCALL_EXECVEAT_X` exit event + * when the `execveat` syscall succeeds, for this reason, we need to manage also + * this event in the parser. */ if(retval < 0) { @@ -1721,7 +1721,7 @@ void sinsp_parser::parse_execve_exit(sinsp_evt *evt) } // - // We get here when `execve` return. The thread has already been added by a previous fork or clone, + // We get here when `execve` or `execveat` return. The thread has already been added by a previous fork or clone, // and we just update the entry with the new information. // if(!evt->m_tinfo) @@ -1754,6 +1754,7 @@ void sinsp_parser::parse_execve_exit(sinsp_evt *evt) case PPME_SYSCALL_EXECVE_17_X: case PPME_SYSCALL_EXECVE_18_X: case PPME_SYSCALL_EXECVE_19_X: + case PPME_SYSCALL_EXECVEAT_X: // Get the comm parinfo = evt->get_param(13); evt->m_tinfo->m_comm = parinfo->m_val; @@ -1799,6 +1800,7 @@ void sinsp_parser::parse_execve_exit(sinsp_evt *evt) case PPME_SYSCALL_EXECVE_17_X: case PPME_SYSCALL_EXECVE_18_X: case PPME_SYSCALL_EXECVE_19_X: + case PPME_SYSCALL_EXECVEAT_X: // Get the pgflt_maj parinfo = evt->get_param(8); ASSERT(parinfo->m_len == sizeof(uint64_t)); @@ -1847,6 +1849,7 @@ void sinsp_parser::parse_execve_exit(sinsp_evt *evt) case PPME_SYSCALL_EXECVE_17_X: case PPME_SYSCALL_EXECVE_18_X: case PPME_SYSCALL_EXECVE_19_X: + case PPME_SYSCALL_EXECVEAT_X: // Get the environment parinfo = evt->get_param(15); evt->m_tinfo->set_env(parinfo->m_val, parinfo->m_len); @@ -1884,6 +1887,7 @@ void sinsp_parser::parse_execve_exit(sinsp_evt *evt) case PPME_SYSCALL_EXECVE_17_X: case PPME_SYSCALL_EXECVE_18_X: case PPME_SYSCALL_EXECVE_19_X: + case PPME_SYSCALL_EXECVEAT_X: // Get the tty parinfo = evt->get_param(16); ASSERT(parinfo->m_len == sizeof(int32_t)); @@ -1903,7 +1907,8 @@ void sinsp_parser::parse_execve_exit(sinsp_evt *evt) * we can do nothing. */ if((etype == PPME_SYSCALL_EXECVE_18_X || - etype == PPME_SYSCALL_EXECVE_19_X) + etype == PPME_SYSCALL_EXECVE_19_X || + etype == PPME_SYSCALL_EXECVEAT_X) && retrieve_enter_event(enter_evt, evt)) { @@ -2036,6 +2041,7 @@ void sinsp_parser::parse_execve_exit(sinsp_evt *evt) case PPME_SYSCALL_EXECVE_18_X: break; case PPME_SYSCALL_EXECVE_19_X: + case PPME_SYSCALL_EXECVEAT_X: // Get the vpgid parinfo = evt->get_param(17); ASSERT(parinfo->m_len == sizeof(int64_t)); @@ -2077,7 +2083,7 @@ void sinsp_parser::parse_execve_exit(sinsp_evt *evt) // Get capabilities if(evt->get_num_params() > 22) { - if(etype == PPME_SYSCALL_EXECVE_19_X) + if(etype == PPME_SYSCALL_EXECVE_19_X || etype == PPME_SYSCALL_EXECVEAT_X) { parinfo = evt->get_param(20); ASSERT(parinfo->m_len == sizeof(uint64_t)); diff --git a/userspace/libsinsp/test/sinsp.ut.cpp b/userspace/libsinsp/test/sinsp.ut.cpp index 7803ee3fc0..f10fe25319 100644 --- a/userspace/libsinsp/test/sinsp.ut.cpp +++ b/userspace/libsinsp/test/sinsp.ut.cpp @@ -511,6 +511,159 @@ TEST_F(sinsp_with_test_input, execveat_invalid_path) } } +/* Same as `execveat_empty_path_flag` but with `PPME_SYSCALL_EXECVEAT_X` as exit event + * since on s390x architectures the `execveat` syscall correctly returns a `PPME_SYSCALL_EXECVEAT_X` + * exit event in case of success. + */ +TEST_F(sinsp_with_test_input, execveat_empty_path_flag_s390) +{ + add_default_init_thread(); + + open_inspector(); + sinsp_evt *evt = NULL; + + /* We generate a `dirfd` associated with the file that + * we want to run with the `execveat`, + */ + int64_t dirfd = 3; + const char *file_to_run = "/tmp/s390x/file_to_run"; + add_event_advance_ts(increasing_ts(), 1, PPME_SYSCALL_OPEN_E, 3, file_to_run, 0, 0); + add_event_advance_ts(increasing_ts(), 1, PPME_SYSCALL_OPEN_X, 6, dirfd, file_to_run, 0, 0, 0, 0); + + /* Now we call the `execveat_e` event,`sinsp` will store this enter + * event in the thread storage, in this way the exit event can use it. + */ + add_event_advance_ts(increasing_ts(), 1, PPME_SYSCALL_EXECVEAT_E, 3, dirfd, "", PPM_EXVAT_AT_EMPTY_PATH); + + struct scap_const_sized_buffer empty_bytebuf = {nullptr, 0}; + evt = add_event_advance_ts(increasing_ts(), 1, PPME_SYSCALL_EXECVEAT_X, 23, 0, "", empty_bytebuf, 1, 1, 1, "", 0, 0, 0, 0, 0, 0, "", empty_bytebuf, empty_bytebuf, 0, 0, 0, 0, 0, 0, 0); + + /* The `exepath` should be the file pointed by the `dirfd` since `execveat` is called with + * `AT_EMPTY_PATH` flag. + */ + if(evt->get_thread_info()) + { + ASSERT_STREQ(evt->get_thread_info()->m_exepath.c_str(), file_to_run); + } + else + { + FAIL(); + } +} + +/* Same as `execveat_relative_path` but with `PPME_SYSCALL_EXECVEAT_X` as exit event + * since on s390x architectures the `execveat` syscall correctly returns a `PPME_SYSCALL_EXECVEAT_X` + * exit event in case of success. + */ +TEST_F(sinsp_with_test_input, execveat_relative_path_s390) +{ + add_default_init_thread(); + + open_inspector(); + sinsp_evt *evt = NULL; + + /* We generate a `dirfd` associated with the directory that contains the file that + * we want to run with the `execveat`, + */ + int64_t dirfd = 3; + const char *directory = "/tmp/s390x/dir"; + add_event_advance_ts(increasing_ts(), 1, PPME_SYSCALL_OPEN_E, 3, directory, 0, 0); + add_event_advance_ts(increasing_ts(), 1, PPME_SYSCALL_OPEN_X, 6, dirfd, directory, 0, 0, 0, 0); + + /* Now we call the `execveat_e` event,`sinsp` will store this enter + * event in the thread storage, in this way the exit event can use it. + */ + add_event_advance_ts(increasing_ts(), 1, PPME_SYSCALL_EXECVEAT_E, 3, dirfd, "file", 0); + + struct scap_const_sized_buffer empty_bytebuf = {nullptr, 0}; + evt = add_event_advance_ts(increasing_ts(), 1, PPME_SYSCALL_EXECVEAT_X, 23, 0, "", empty_bytebuf, 1, 1, 1, "", 0, 0, 0, 0, 0, 0, "", empty_bytebuf, empty_bytebuf, 0, 0, 0, 0, 0, 0, 0); + + /* The `exepath` should be the directory pointed by the `dirfd` + the pathname + * specified in the `execveat` enter event. + */ + if(evt->get_thread_info()) + { + ASSERT_STREQ(evt->get_thread_info()->m_exepath.c_str(), "/tmp/s390x/dir/file"); + } + else + { + FAIL(); + } +} + +/* Same as `execveat_absolute_path` but with `PPME_SYSCALL_EXECVEAT_X` as exit event + * since on s390x architectures the `execveat` syscall correctly returns a `PPME_SYSCALL_EXECVEAT_X` + * exit event in case of success. + */ +TEST_F(sinsp_with_test_input, execveat_absolute_path_s390) +{ + add_default_init_thread(); + + open_inspector(); + sinsp_evt *evt = NULL; + + /* Now we call the `execveat_e` event,`sinsp` will store this enter + * event in the thread storage, in this way the exit event can use it. + */ + int invalid_dirfd = 0; + add_event_advance_ts(increasing_ts(), 1, PPME_SYSCALL_EXECVEAT_E, 3, invalid_dirfd, "/tmp/s390/file", 0); + + struct scap_const_sized_buffer empty_bytebuf = {nullptr, 0}; + evt = add_event_advance_ts(increasing_ts(), 1, PPME_SYSCALL_EXECVEAT_X, 23, 0, "", empty_bytebuf, 1, 1, 1, "", 0, 0, 0, 0, 0, 0, "", empty_bytebuf, empty_bytebuf, 0, 0, 0, 0, 0, 0, 0); + + /* The `exepath` should be the absolute file path that we passed in the + * `execveat` enter event. + */ + if(evt->get_thread_info()) + { + ASSERT_STREQ(evt->get_thread_info()->m_exepath.c_str(), "/tmp/s390/file"); + } + else + { + FAIL(); + } +} + +/* Same as `execveat_invalid_path` but with `PPME_SYSCALL_EXECVEAT_X` as exit event + * since on s390x architectures the `execveat` syscall correctly returns a `PPME_SYSCALL_EXECVEAT_X` + * exit event in case of success. + */ +TEST_F(sinsp_with_test_input, execveat_invalid_path_s390) +{ + add_default_init_thread(); + + open_inspector(); + sinsp_evt *evt = NULL; + + /* We generate a `dirfd` associated with the directory that contains the file that + * we want to run with the `execveat`, + */ + int64_t dirfd = 3; + const char *directory = "/tmp/s390/dir"; + add_event_advance_ts(increasing_ts(), 1, PPME_SYSCALL_OPEN_E, 3, directory, 0, 0); + add_event_advance_ts(increasing_ts(), 1, PPME_SYSCALL_OPEN_X, 6, dirfd, directory, 0, 0, 0, 0); + + /* Now we call the `execveat_e` event,`sinsp` will store this enter + * event in the thread storage, in this way the exit event can use it. + */ + add_event_advance_ts(increasing_ts(), 1, PPME_SYSCALL_EXECVEAT_E, 3, dirfd, "", 0); + + struct scap_const_sized_buffer empty_bytebuf = {nullptr, 0}; + evt = add_event_advance_ts(increasing_ts(), 1, PPME_SYSCALL_EXECVEAT_X, 23, 0, "", empty_bytebuf, 1, 1, 1, "", 0, 0, 0, 0, 0, 0, "", empty_bytebuf, empty_bytebuf, 0, 0, 0, 0, 0, 0, 0); + + /* The `exepath` should be ``, sinsp should recognize that the `pathname` + * is invalid and should set ``. + */ + if(evt->get_thread_info()) + { + ASSERT_STREQ(evt->get_thread_info()->m_exepath.c_str(), ""); + } + else + { + FAIL(); + } +} + TEST_F(sinsp_with_test_input, creates_fd_generic) { add_default_init_thread();