Skip to content
New issue

Have a question about this project? Sign up for a free GitHub account to open an issue and contact its maintainers and the community.

By clicking “Sign up for GitHub”, you agree to our terms of service and privacy statement. We’ll occasionally send you account related emails.

Already on GitHub? Sign in to your account

[WIP] Collect correct arguments when setproctitle is called #976

Open
wants to merge 1 commit into
base: master
Choose a base branch
from
Open
Show file tree
Hide file tree
Changes from all commits
Commits
File filter

Filter by extension

Filter by extension

Conversations
Failed to load comments.
Loading
Jump to
Jump to file
Failed to load files.
Loading
Diff view
Diff view
234 changes: 130 additions & 104 deletions driver/bpf/fillers.h
Original file line number Diff line number Diff line change
Expand Up @@ -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() */
Copy link
Contributor

Choose a reason for hiding this comment

The reason will be displayed to describe this comment to others. Learn more.

Just to better highlight the algo here: https://elixir.bootlin.com/linux/latest/source/mm/util.c#L965

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;
}
}

/*
Expand Down
6 changes: 6 additions & 0 deletions driver/bpf/ring_helpers.h
Original file line number Diff line number Diff line change
Expand Up @@ -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)
Expand Down
Original file line number Diff line number Diff line change
Expand Up @@ -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
{
Expand Down
Loading