diff --git a/driver/bpf/fillers.h b/driver/bpf/fillers.h index deff436240..a157ebfc78 100644 --- a/driver/bpf/fillers.h +++ b/driver/bpf/fillers.h @@ -2314,138 +2314,164 @@ FILLER(proc_startupdate, true) { struct task_struct *real_parent; struct signal_struct *signal; - struct task_struct *task; unsigned long total_vm; unsigned long min_flt; unsigned long maj_flt; unsigned long fdlimit; - struct mm_struct *mm; long total_rss; char empty = 0; long args_len; - long retval; pid_t tgid; long swap; pid_t pid; - int res; - - /* - * Make sure the operation was successful - */ - retval = bpf_syscall_get_retval(data->ctx); - res = bpf_val_to_ring_type(data, retval, PT_ERRNO); - if (res != PPM_SUCCESS) - return res; - task = (struct task_struct *)bpf_get_current_task(); - mm = _READ(task->mm); - if (!mm) + struct task_struct *task = (struct task_struct *)bpf_get_current_task(); + struct mm_struct *mm = _READ(task->mm); + if(!mm) return PPM_FAILURE_BUG; - if (retval >= 0) { - /* - * The call succeeded. Get exe, args from the current - * process; put one \0-separated exe-args string into - * str_storage - */ - unsigned long arg_start; - unsigned long arg_end; - - arg_end = _READ(mm->arg_end); - if (!arg_end) - return PPM_FAILURE_BUG; - - arg_start = _READ(mm->arg_start); - args_len = arg_end - arg_start; + /* Parameter 1: res (type: PT_PID) */ + long retval = bpf_syscall_get_retval(data->ctx); + int res = bpf_val_to_ring_type(data, retval, PT_ERRNO); + CHECK_RES(res); - if (args_len > 0) { - if (args_len > ARGS_ENV_SIZE_MAX) - args_len = ARGS_ENV_SIZE_MAX; + /* To reduce a little bit the complexity we split the cases for different syscalls at least + * for the first 2 args `exe` and `args` + */ + const ppm_event_code type = data->state->tail_ctx.evt_type; -#ifdef BPF_FORBIDS_ZERO_ACCESS - if (bpf_probe_read_user(&data->buf[data->state->tail_ctx.curoff & SCRATCH_SIZE_HALF], - ((args_len - 1) & SCRATCH_SIZE_HALF) + 1, - (void *)arg_start)) -#else - if (bpf_probe_read_user(&data->buf[data->state->tail_ctx.curoff & SCRATCH_SIZE_HALF], - args_len & SCRATCH_SIZE_HALF, - (void *)arg_start)) -#endif - args_len = 0; - else - data->buf[(data->state->tail_ctx.curoff + args_len - 1) & SCRATCH_SIZE_MAX] = 0; + if((type == PPME_SYSCALL_EXECVE_19_X || + type == PPME_SYSCALL_EXECVEAT_X)) + { + /* Execve/execveat success */ + if(retval == 0) + { + unsigned long int arg_start_pointer = 0; + unsigned long int arg_end_pointer = 0; + bpf_probe_read_kernel(&arg_start_pointer, sizeof(arg_start_pointer), &mm->arg_start); + bpf_probe_read_kernel(&arg_end_pointer, sizeof(arg_end_pointer), &mm->arg_end); + unsigned long total_args_len = arg_end_pointer - arg_start_pointer; + + /* Parameter 2: exe (type: PT_CHARBUF) */ + res = __bpf_val_to_ring(data, arg_start_pointer, 0, PT_CHARBUF, -1, false, USER); + CHECK_RES(res); + + /* We get the len of the `exe` param */ + u16 exe_arg_len = get_param_len(data->buf, 1); + + /* Parameter 3: args (type: PT_CHARBUFARRAY) */ + res = __bpf_val_to_ring(data, arg_start_pointer + exe_arg_len, (total_args_len - exe_arg_len) & SCRATCH_SIZE_HALF, PT_BYTEBUF, -1, false, USER); + CHECK_RES(res); } - } else if (data->state->tail_ctx.evt_type == PPME_SYSCALL_EXECVE_19_X || - data->state->tail_ctx.evt_type == PPME_SYSCALL_EXECVEAT_X ) { - unsigned long val; - char **argv; - - switch (data->state->tail_ctx.evt_type) + else /* Failure */ { - case PPME_SYSCALL_EXECVE_19_X: - val = bpf_syscall_get_argument(data, 1); - break; + char **argv = NULL; + switch(type) + { + case PPME_SYSCALL_EXECVE_19_X: + argv = (char **)bpf_syscall_get_argument(data, 1); + break; - case PPME_SYSCALL_EXECVEAT_X: - val = bpf_syscall_get_argument(data, 2); - break; + case PPME_SYSCALL_EXECVEAT_X: + argv = (char **)bpf_syscall_get_argument(data, 2); + break; - default: - val = 0; - break; - } - argv = (char **)val; + default: + break; + } - res = bpf_accumulate_argv_or_env(data, argv, &args_len); - if (res != PPM_SUCCESS) - args_len = 0; - } else { - args_len = 0; + unsigned long total_args_len = 0; + /* Here we put already all we need into our auxmap */ + res = bpf_accumulate_argv_or_env(data, argv, &total_args_len); + if(res != PPM_SUCCESS) + total_args_len = 0; + + u16 exe_arg_len = bpf_probe_read_kernel_str(&data->buf[data->state->tail_ctx.curoff & SCRATCH_SIZE_HALF], + (total_args_len & SCRATCH_SIZE_HALF), + &data->buf[data->state->tail_ctx.curoff & SCRATCH_SIZE_HALF]); + + /* Parameter 2: exe (type: PT_CHARBUF) */ + data->curarg_already_on_frame = true; + res = __bpf_val_to_ring(data, 0, exe_arg_len, PT_CHARBUF, -1, false, KERNEL); + CHECK_RES(res); + + /* Parameter 3: args (type: PT_CHARBUFARRAY) */ + data->curarg_already_on_frame = true; + res = __bpf_val_to_ring(data, 0, (total_args_len - exe_arg_len) & SCRATCH_SIZE_HALF, PT_BYTEBUF, -1, false, KERNEL); + CHECK_RES(res); + } } + else + { + // PPME_SYSCALL_CLONE_20_X + // PPME_SYSCALL_FORK_20_X + // PPME_SYSCALL_VFORK_20_X + // PPME_SYSCALL_CLONE3_X + if(retval >= 0) + { + unsigned long int start_pointer = 0; + unsigned long int end_pointer = 0; + unsigned long int total_len = 0; + bpf_probe_read_kernel(&start_pointer, sizeof(start_pointer), &mm->arg_start); + bpf_probe_read_kernel(&end_pointer, sizeof(end_pointer), &mm->arg_end); + total_len = end_pointer - start_pointer; - if (args_len > 0) { - int exe_len; +#ifdef BPF_FORBIDS_ZERO_ACCESS + bpf_probe_read_user(&data->buf[data->state->tail_ctx.curoff & SCRATCH_SIZE_HALF], + ((total_len - 1) & SCRATCH_SIZE_HALF) + 1, + (void *)start_pointer); +#else + bpf_probe_read_user(&data->buf[data->state->tail_ctx.curoff & SCRATCH_SIZE_HALF], + total_len & SCRATCH_SIZE_HALF, + (void *)start_pointer); +#endif - exe_len = bpf_probe_read_kernel_str(&data->buf[data->state->tail_ctx.curoff & SCRATCH_SIZE_HALF], - SCRATCH_SIZE_HALF, - &data->buf[data->state->tail_ctx.curoff & SCRATCH_SIZE_HALF]); + /* if true application is using setproctitle() */ + if(data->buf[(data->state->tail_ctx.curoff + total_len - 1) & SCRATCH_SIZE_HALF] == '\0') + { + /* Parameter 2: exe (type: PT_CHARBUF) */ + res = __bpf_val_to_ring(data, start_pointer, 0, PT_CHARBUF, -1, false, USER); + CHECK_RES(res); - if (exe_len < 0) - return PPM_FAILURE_INVALID_USER_MEMORY; + /* We get the len of the `exe` param */ + u16 exe_arg_len = get_param_len(data->buf, 1); - /* - * exe - */ - data->curarg_already_on_frame = true; - res = __bpf_val_to_ring(data, 0, exe_len, PT_CHARBUF, -1, false, KERNEL); - if (res != PPM_SUCCESS) - return res; + /* Parameter 3: args (type: PT_CHARBUFARRAY) */ + res = __bpf_val_to_ring(data, start_pointer + exe_arg_len, (total_len - exe_arg_len) & SCRATCH_SIZE_HALF, PT_BYTEBUF, -1, false, USER); + CHECK_RES(res); + } + else + { + bpf_probe_read_kernel(&start_pointer, sizeof(start_pointer), &mm->env_start); + bpf_probe_read_kernel(&end_pointer, sizeof(end_pointer), &mm->env_end); + total_len = end_pointer - start_pointer; - args_len -= exe_len; - if (args_len < 0) - return PPM_FAILURE_INVALID_USER_MEMORY; + /* Parameter 2: exe (type: PT_CHARBUF) */ + res = __bpf_val_to_ring(data, start_pointer, 0, PT_CHARBUF, -1, false, USER); + CHECK_RES(res); - /* - * Args - */ - data->curarg_already_on_frame = true; - res = __bpf_val_to_ring(data, 0, args_len, PT_BYTEBUF, -1, false, KERNEL); - if (res != PPM_SUCCESS) - return res; - } else { - /* - * exe - */ - res = bpf_push_empty_param(data); - if (res != PPM_SUCCESS) - return res; + /* We get the len of the `exe` param */ + u16 exe_arg_len = get_param_len(data->buf, 1); - /* - * Args - */ - res = bpf_push_empty_param(data); - if (res != PPM_SUCCESS) - return res; + /* Parameter 3: args (type: PT_CHARBUFARRAY) */ + res = __bpf_val_to_ring(data, start_pointer + exe_arg_len, (total_len - exe_arg_len) & SCRATCH_SIZE_HALF, PT_BYTEBUF, -1, false, USER); + CHECK_RES(res); + } + } + else /* Failure */ + { + /* in clone/fork we cannot extract these params from the syscalls args */ + + /* Parameter 2: exe (type: PT_CHARBUF) */ + res = bpf_push_empty_param(data); + if(res != PPM_SUCCESS) + return res; + + /* Parameter 3: args (type: PT_CHARBUFARRAY) */ + res = bpf_push_empty_param(data); + if(res != PPM_SUCCESS) + return res; + } } /* diff --git a/driver/bpf/ring_helpers.h b/driver/bpf/ring_helpers.h index c65c6a40db..7de8175ce4 100644 --- a/driver/bpf/ring_helpers.h +++ b/driver/bpf/ring_helpers.h @@ -30,6 +30,12 @@ static __always_inline void fixup_evt_len(char *p, unsigned long len) evt_hdr->len = len; } +/* Should be used only on a param already pushed */ +static __always_inline u16 get_param_len(char *p, const unsigned int argnum) +{ + return *((u16 *)&p[sizeof(struct ppm_evt_hdr)] + (argnum & (PPM_MAX_EVENT_PARAMS - 1))); +} + static __always_inline void fixup_evt_arg_len(char *p, unsigned int argnum, unsigned int arglen) 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 7b9179960a..3043a3ab71 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 @@ -75,33 +75,39 @@ int BPF_PROG(fork_x, */ if(ret >= 0) { - unsigned long arg_start_pointer = 0; - unsigned long arg_end_pointer = 0; - - /* Please note: these are the arguments of the process, not the - * argument with which we call the `clone` syscall. - * `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 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, arg_start_pointer + exe_arg_len, (total_args_len - exe_arg_len) & (MAX_PROC_ARG_ENV - 1), USER); + unsigned long int start_pointer = 0; + unsigned long int end_pointer = 0; + unsigned long int total_len = 0; + + READ_TASK_FIELD_INTO(&start_pointer, task, mm, arg_start); + READ_TASK_FIELD_INTO(&end_pointer, task, mm, arg_end); + total_len = end_pointer - start_pointer; + + bpf_probe_read_user(&auxmap->data[SAFE_ACCESS(auxmap->payload_pos)], + (total_len & (MAX_PROC_ARG_ENV - 1)), + (void *)start_pointer); + + /* if true application is using setproctitle() */ + if(auxmap->data[SAFE_ACCESS(auxmap->payload_pos + total_len - 1)] == '\0') + { + /* Parameter 2: exe (type: PT_CHARBUF) */ + u16 exe_arg_len = auxmap__store_charbuf_param(auxmap, start_pointer, MAX_PROC_EXE, USER); + + /* Parameter 3: args (type: PT_CHARBUFARRAY) */ + auxmap__store_bytebuf_param(auxmap, start_pointer + exe_arg_len, (total_len - exe_arg_len) & (MAX_PROC_ARG_ENV - 1), USER); + } + else + { + READ_TASK_FIELD_INTO(&start_pointer, task, mm, env_start); + READ_TASK_FIELD_INTO(&end_pointer, task, mm, env_end); + total_len = end_pointer - start_pointer; + + /* Parameter 2: exe (type: PT_CHARBUF) */ + u16 exe_arg_len = auxmap__store_charbuf_param(auxmap, start_pointer, MAX_PROC_EXE, USER); + + /* Parameter 3: args (type: PT_CHARBUFARRAY) */ + auxmap__store_bytebuf_param(auxmap, start_pointer + exe_arg_len, (total_len - exe_arg_len) & (MAX_PROC_ARG_ENV - 1), USER); + } } else { diff --git a/driver/ppm_fillers.c b/driver/ppm_fillers.c index d7a229621b..e25ea4a126 100644 --- a/driver/ppm_fillers.c +++ b/driver/ppm_fillers.c @@ -842,8 +842,6 @@ int f_proc_startupdate(struct event_filler_arguments *args) #else /* UDIG */ unsigned long val = 0; int res = 0; - unsigned int exe_len = 0; /* the length of the executable string */ - int args_len = 0; /*the combined length of the arguments string + executable string */ struct mm_struct *mm = current->mm; int64_t retval; int ptid; @@ -853,87 +851,53 @@ int f_proc_startupdate(struct event_filler_arguments *args) int available = STR_STORAGE_SIZE; const struct cred *cred; u64 pidns_init_start_time = 0; - + bool empty_params = false; + unsigned int exe_len = 0; /* the length of the executable string */ + unsigned long int total_len = 0; /* the combined length of the arguments string + executable string */ #ifdef __NR_clone3 struct clone_args cl_args; #endif + const ppm_event_code type = args->event_type; - /* - * Make sure the operation was successful - */ - retval = (int64_t)syscall_get_return_value(current, args->regs); - res = val_to_ring(args, retval, 0, false, 0); - if (unlikely(res != PPM_SUCCESS)) - return res; - - if (unlikely(retval < 0 && - args->event_type != PPME_SYSCALL_EXECVE_19_X && - args->event_type != PPME_SYSCALL_EXECVEAT_X)) { - - /* The call failed, but this syscall has no exe, args - * anyway, so I report empty ones */ - *args->str_storage = 0; - - /* - * exe - */ - res = val_to_ring(args, (uint64_t)(long)args->str_storage, 0, false, 0); - if (unlikely(res != PPM_SUCCESS)) - return res; - - /* - * Args - */ - res = val_to_ring(args, (int64_t)(long)args->str_storage, 0, false, 0); - if (unlikely(res != PPM_SUCCESS)) - return res; - } else { + args->str_storage[0] = 0; - if (likely(retval >= 0)) { - /* - * The call succeeded. Get exe, args from the current - * process; put one \0-separated exe-args string into - * str_storage - */ + if(unlikely(!mm)) + { + pr_info("f_proc_startupdate drop, mm=NULL\n"); + return PPM_FAILURE_BUG; + } - if (unlikely(!mm)) { - args->str_storage[0] = 0; - pr_info("f_proc_startupdate drop, mm=NULL\n"); - return PPM_FAILURE_BUG; - } + /* Parameter 1: res (type: PT_PID) */ + retval = (int64_t)syscall_get_return_value(current, args->regs); + res = val_to_ring(args, retval, 0, false, 0); + CHECK_RES(res); - if (unlikely(!mm->arg_end)) { - args->str_storage[0] = 0; - pr_info("f_proc_startupdate drop, mm->arg_end=NULL\n"); - return PPM_FAILURE_BUG; + /* To reduce a little bit the complexity we split the cases for different syscalls at least + * for the first 2 args `exe` and `args` + */ + if(type == PPME_SYSCALL_EXECVE_19_X || + type == PPME_SYSCALL_EXECVEAT_X) + { + if(retval == 0) + { + total_len = (mm->arg_end - mm->arg_start) & (STR_STORAGE_SIZE-1); + if(unlikely(ppm_copy_from_user(args->str_storage, (const void __user *)mm->arg_start, total_len))) + { + empty_params = true; } - - args_len = mm->arg_end - mm->arg_start; - - if (args_len) { - if (args_len > PAGE_SIZE) - args_len = PAGE_SIZE; - - if (unlikely(ppm_copy_from_user(args->str_storage, (const void __user *)mm->arg_start, args_len))) - args_len = 0; - else - args->str_storage[args_len - 1] = 0; + else + { + empty_params = false; } - } else { - - /* - * The execve or execveat call failed. I get exe, args from the - * input args; put one \0-separated exe-args string into - * str_storage - */ - args->str_storage[0] = 0; - - switch (args->event_type) + } + else + { + switch(type) { case PPME_SYSCALL_EXECVE_19_X: syscall_get_arguments_deprecated(current, args->regs, 1, 1, &val); break; - + case PPME_SYSCALL_EXECVEAT_X: syscall_get_arguments_deprecated(current, args->regs, 2, 1, &val); break; @@ -943,41 +907,87 @@ int f_proc_startupdate(struct event_filler_arguments *args) break; } #ifdef CONFIG_COMPAT - if (unlikely(args->compat)) - args_len = compat_accumulate_argv_or_env((compat_uptr_t)val, - args->str_storage, available); + if(unlikely(args->compat)) + total_len = compat_accumulate_argv_or_env((compat_uptr_t)val, + args->str_storage, STR_STORAGE_SIZE); else #endif - args_len = accumulate_argv_or_env((const char __user * __user *)val, - args->str_storage, available); + total_len = accumulate_argv_or_env((const char __user *__user *)val, + args->str_storage, STR_STORAGE_SIZE); - if (unlikely(args_len < 0)) - args_len = 0; + if(unlikely(total_len < 0)) + { + empty_params = true; + } + empty_params = false; } + } + else + { + // PPME_SYSCALL_CLONE_20_X + // PPME_SYSCALL_FORK_20_X + // PPME_SYSCALL_VFORK_20_X + // PPME_SYSCALL_CLONE3_X + if(retval >= 0) + { + total_len = (mm->arg_end - mm->arg_start) & (STR_STORAGE_SIZE-1); - if (args_len == 0) - *args->str_storage = 0; + if(unlikely(ppm_copy_from_user(args->str_storage, (const void __user *)mm->arg_start, total_len))) + { + empty_params = true; + } + else if(args->str_storage[total_len - 1] == '\0') + { + empty_params = false; + } + else + { + /* Take params from the env */ + total_len = (mm->env_end - mm->env_start) & (STR_STORAGE_SIZE-1); + if(unlikely(ppm_copy_from_user(args->str_storage, (const void __user *)mm->env_start, total_len))) + { + empty_params = true; + } + else + { + empty_params = false; + } + } + } + else + { + empty_params = true; + } + } - exe_len = strnlen(args->str_storage, args_len); - if (exe_len < args_len) + if(empty_params) + { + /* Parameter 2: exe (type: PT_CHARBUF) */ + res = push_empty_param(args); + CHECK_RES(res); + + /* Parameter 3: args (type: PT_BYTEBUF) */ + res = push_empty_param(args); + CHECK_RES(res); + } + else + { + + args->str_storage[total_len - 1] = '\0'; + exe_len = strnlen(args->str_storage, total_len); + /* We need to include also the string terminator if there is space */ + if(exe_len < total_len) ++exe_len; - /* - * exe - */ + /* Parameter 2: exe (type: PT_CHARBUF) */ res = val_to_ring(args, (uint64_t)(long)args->str_storage, 0, false, 0); - if (unlikely(res != PPM_SUCCESS)) - return res; + CHECK_RES(res); - /* - * Args - */ - res = val_to_ring(args, (int64_t)(long)args->str_storage + exe_len, args_len - exe_len, false, 0); - if (unlikely(res != PPM_SUCCESS)) - return res; + /* Parameter 3: args (type: PT_BYTEBUF) */ + res = val_to_ring(args, (int64_t)(long)args->str_storage + exe_len, total_len - exe_len, false, 0); + CHECK_RES(res); } - /* * tid */ diff --git a/test/drivers/test_suites/syscall_exit_suite/fork_x.cpp b/test/drivers/test_suites/syscall_exit_suite/fork_x.cpp index 52ce118d02..2c5f07b3e3 100644 --- a/test/drivers/test_suites/syscall_exit_suite/fork_x.cpp +++ b/test/drivers/test_suites/syscall_exit_suite/fork_x.cpp @@ -231,4 +231,86 @@ TEST(SyscallExit, forkX_child) #endif } + +#ifdef __NR_prctl + +#include + +TEST(SyscallExit, forkX_father_setproctitle) +{ + auto evt_test = get_syscall_event_test(__NR_fork, EXIT_EVENT); + + evt_test->enable_capture(); + + /*=============================== TRIGGER SYSCALL ===========================*/ + + /* setproctitle uses the arg area for the title */ + char args[10] = "title"; + /* Remove the null terminator */ + args[9] = 'n'; + unsigned long arg_start = (unsigned long)args; + unsigned long arg_end = (unsigned long)args + 10; + + /* the env area contains the args */ + char env[] = "hello\0darkness\0my\0old\0friend"; + unsigned long env_start = (unsigned long)env; + unsigned long env_end = (unsigned long)env + sizeof(env); + + const char *expected_env[] = {"hello", "darkness", "my", "old", "friend", NULL}; + + assert_syscall_state(SYSCALL_SUCCESS, "prctl (1)", syscall(__NR_prctl, PR_SET_MM, PR_SET_MM_ARG_START, arg_start, 0, 0), NOT_EQUAL, -1); + assert_syscall_state(SYSCALL_SUCCESS, "prctl (2)", syscall(__NR_prctl, PR_SET_MM, PR_SET_MM_ARG_END, arg_end, 0, 0), NOT_EQUAL, -1); + assert_syscall_state(SYSCALL_SUCCESS, "prctl (3)", syscall(__NR_prctl, PR_SET_MM, PR_SET_MM_ENV_START, env_start, 0, 0), NOT_EQUAL, -1); + assert_syscall_state(SYSCALL_SUCCESS, "prctl (2)", syscall(__NR_prctl, PR_SET_MM, PR_SET_MM_ENV_END, env_end, 0, 0), NOT_EQUAL, -1); + + pid_t ret_pid = syscall(__NR_fork); + if(ret_pid == 0) + { + /* Child terminates immediately. */ + exit(EXIT_SUCCESS); + } + + assert_syscall_state(SYSCALL_SUCCESS, "fork", 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 || __WIFSIGNALED(status) != 0) + { + FAIL() << "Something in the child failed." << std::endl; + } + + /*=============================== TRIGGER SYSCALL ===========================*/ + + evt_test->disable_capture(); + + evt_test->assert_event_presence(); + + if(HasFatalFailure()) + { + return; + } + + 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, expected_env[0]); + + /* Parameter 3: args (type: PT_CHARBUFARRAY) */ + /* Starting from `1` because the first is `exe`. */ + evt_test->assert_charbuf_array_param(3, &expected_env[1]); + + /*=============================== ASSERT PARAMETERS ===========================*/ + + evt_test->assert_num_params_pushed(21); +} +#endif #endif