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

update(modern_bpf): reduce the execve instrumentation time with new APIs #648

Merged
merged 13 commits into from
Oct 25, 2022
Merged
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
21 changes: 20 additions & 1 deletion driver/feature_gates.h
Original file line number Diff line number Diff line change
Expand Up @@ -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.
*
Expand Down Expand Up @@ -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
Expand Down
8 changes: 8 additions & 0 deletions driver/modern_bpf/definitions/vmlinux.h
Original file line number Diff line number Diff line change
Expand Up @@ -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;
Expand Down Expand Up @@ -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,
Expand Down
33 changes: 0 additions & 33 deletions driver/modern_bpf/helpers/extract/extract_from_kernel.h
Original file line number Diff line number Diff line change
Expand Up @@ -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
////////////////////////
Expand Down
130 changes: 87 additions & 43 deletions driver/modern_bpf/helpers/store/auxmap_store_params.h
Original file line number Diff line number Diff line change
Expand Up @@ -10,23 +10,50 @@
#include <helpers/base/push_data.h>
#include <helpers/extract/extract_from_kernel.h>

/* 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
* have at most `MAX_PATH_POINTERS` components.
*/
#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
Andreagit97 marked this conversation as resolved.
Show resolved Hide resolved

/*=============================== 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

Expand All @@ -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):
*
Expand Down Expand Up @@ -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.
Expand All @@ -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);
}
Expand All @@ -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);
}

Expand Down
Loading