Skip to content
Draft
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
70 changes: 53 additions & 17 deletions fact-ebpf/src/bpf/events.h
Original file line number Diff line number Diff line change
Expand Up @@ -3,47 +3,83 @@
// clang-format off
#include "vmlinux.h"

#include "inode.h"
#include "maps.h"
#include "process.h"
#include "types.h"
#include "raw_event.h"

#include <bpf/bpf_helpers.h>
// clang-format on

/**
* Format and submit an event to the ringbuffer.
*
* This method is responsible for using the provided values from
* different BPF programs, serialize this data alongside the current
* process information in a binary format and submit it as an event to
* the ringbuffer.
*
* The high level format for an event can be described as follows:
* |--|--------|---------------------------|---------------------------|
* | | | | ^ event end
* | | | ^ begin file data
* | | ^ begin process data
* | ^ timestamp
* ^ event type
*
* Event type: a 16 bit integer specifying the type of event this is.
* Timestamp: the amount of nano seconds since boot time.
* Process data: all the information collected from the current process.
* For more information on this field see the documentation for
* `process_fill`.
* File data: information collected about the file being acted upon.
*
* The file data field can be expanded as follows:
* |----|--------------|---|
* | | ^ Event specific data
* | ^ file path
* ^ inode information
*
* Inode information: Encoded as the inode and device numbers. Used for
* host path tracking.
* File path: The path to the file being acted upon, retrieved from
* d_path.
*/
__always_inline static void submit_event(struct metrics_by_hook_t* m,
file_activity_type_t event_type,
struct bound_path_t* path,
inode_key_t* inode,
bool use_bpf_d_path) {
struct event_t* event = bpf_ringbuf_reserve(&rb, sizeof(struct event_t), 0);
if (event == NULL) {
m->ringbuffer_full++;
unsigned int zero = 0;
struct raw_event_t raw_event = {
.buf = bpf_map_lookup_elem(&heap_map, &zero),
.len = 0,
};
if (raw_event.buf == NULL) {
m->error++;
return;
}

event->type = event_type;
event->timestamp = bpf_ktime_get_boot_ns();
inode_copy_or_reset(&event->inode, inode);
bpf_probe_read(event->filename, path->len & (PATH_MAX - 1), path->path);
event->filename_len = path->len;

struct helper_t* helper = get_helper();
if (helper == NULL) {
goto error;
}
raw_event_copy_u16(&raw_event, event_type);
raw_event_copy_uint(&raw_event, bpf_ktime_get_boot_ns());

int64_t err = process_fill(&event->process, use_bpf_d_path);
int64_t err = process_fill(&raw_event, use_bpf_d_path);
if (err) {
bpf_printk("Failed to fill process information: %d", err);
goto error;
}

// File data
raw_event_copy_inode(&raw_event, inode);
raw_event_copy_bound_path(&raw_event, path);

if (bpf_ringbuf_output(&rb, raw_event.buf, raw_event.len, 0) != 0) {
m->ringbuffer_full++;
return;
}
m->added++;
bpf_ringbuf_submit(event, 0);
return;

error:
m->error++;
bpf_ringbuf_discard(event, 0);
}
12 changes: 12 additions & 0 deletions fact-ebpf/src/bpf/maps.h
Original file line number Diff line number Diff line change
Expand Up @@ -7,6 +7,18 @@

#include <bpf/bpf_helpers.h>

#define MAX_EVENT_LEN ((1<< 15) - 1)
/**
* Raw buffer to encode events into prior to submitting to the
* ringbuffer
*/
struct {
__uint(type, BPF_MAP_TYPE_PERCPU_ARRAY);
__type(key, __u32);
__type(value, char[MAX_EVENT_LEN]);
__uint(max_entries, 1);
} heap_map SEC(".maps");

/**
* Helper struct with buffers for various operations
*/
Expand Down
99 changes: 74 additions & 25 deletions fact-ebpf/src/bpf/process.h
Original file line number Diff line number Diff line change
Expand Up @@ -6,6 +6,7 @@
#include "d_path.h"
#include "maps.h"
#include "types.h"
#include "raw_event.h"

#include <bpf/bpf_helpers.h>
#include <bpf/bpf_core_read.h>
Expand Down Expand Up @@ -77,52 +78,90 @@ __always_inline static const char* get_memory_cgroup(struct helper_t* helper) {
return helper->buf;
}

__always_inline static void process_fill_lineage(process_t* p, struct helper_t* helper, bool use_bpf_d_path) {
__always_inline static long process_fill_lineage(struct raw_event_t* event, struct helper_t* helper, bool use_bpf_d_path) {
struct task_struct* task = (struct task_struct*)bpf_get_current_task_btf();
p->lineage_len = 0;
uint16_t lineage_len_pos = event->len;
event->len += 2;

for (int i = 0; i < LINEAGE_MAX; i++) {
uint16_t i = 0;
for (; i < LINEAGE_MAX; i++) {
struct task_struct* parent = task->real_parent;

if (task == parent || parent->pid == 0) {
return;
break;
}
task = parent;

p->lineage[i].uid = task->cred->uid.val;

d_path(&task->mm->exe_file->f_path, p->lineage[i].exe_path, PATH_MAX, use_bpf_d_path);
p->lineage_len++;
raw_event_copy_uint(event, task->cred->uid.val);
long err = raw_event_d_path(event, &task->mm->exe_file->f_path, use_bpf_d_path);
if (err != 0) {
bpf_printk("Failed to read lineage exe_path");
return err;
}
}

// go back and set the amount of lineage processes in the buffer
uint16_t back = event->len;
event->len = lineage_len_pos;

raw_event_copy_uint(event, i);

event->len = back;
return 0;
}

__always_inline static unsigned long get_mount_ns() {
struct task_struct* task = (struct task_struct*)bpf_get_current_task_btf();
return task->nsproxy->mnt_ns->ns.inum;
}

__always_inline static int64_t process_fill(process_t* p, bool use_bpf_d_path) {
/**
* Fill in the information about the current process to the event
* buffer.
*
* This method serializes all required process information for the event
* as a binary blob into the provided event buffer. The serialized data
* will look something like this:
* |--|--|--|--|-------|--------------|-------------|------------|-|----|---|
* | | | | | | | | | | ^ grandparent lineage
* | | | | | | | | | ^ parent lineage
* | | | | | | | | ^ in_root_mount_ns
* | | | | | | | ^ cgroup
* | | | | | | ^ executable path
* | | | | | ^ arguments
* | | | | ^ comm
* | | | ^ pid
* | | ^ loginuid
* | ^ gid
* ^ uid
*/
__always_inline static int64_t process_fill(struct raw_event_t* event, bool use_bpf_d_path) {
struct task_struct* task = (struct task_struct*)bpf_get_current_task_btf();
uint32_t key = 0;
uint64_t uid_gid = bpf_get_current_uid_gid();
p->uid = uid_gid & 0xFFFFFFFF;
p->gid = (uid_gid >> 32) & 0xFFFFFFFF;
p->login_uid = task->loginuid.val;
p->pid = (bpf_get_current_pid_tgid() >> 32) & 0xFFFFFFFF;
u_int64_t err = bpf_get_current_comm(p->comm, TASK_COMM_LEN);
raw_event_copy_u32(event, uid_gid & 0xFFFFFFFF);
raw_event_copy_u32(event, ((uid_gid >> 32) & 0xFFFFFFFF));
raw_event_copy_uint(event, task->loginuid.val);
raw_event_copy_u32(event, (bpf_get_current_pid_tgid() >> 32) & 0xFFFFFFFF);
uint64_t err = raw_event_copy_comm(event);
if (err != 0) {
bpf_printk("Failed to fill task comm");
return err;
}

unsigned long arg_start = task->mm->arg_start;
unsigned long arg_end = task->mm->arg_end;
p->args_len = (arg_end - arg_start) & 0xFFF;
p->args[4095] = '\0'; // Ensure string termination at end of buffer
err = bpf_probe_read_user(p->args, p->args_len, (const char*)arg_start);
if (err != 0) {
bpf_printk("Failed to fill task args");
return err;
uint16_t args_len = (arg_end - arg_start) & 0xFFF;
err = raw_event_copy_buffer(event, (const void*)arg_start, args_len);
if (err < 0) {
bpf_printk("Failed to read process args");
return -1;
}

err = raw_event_d_path(event, &task->mm->exe_file->f_path, use_bpf_d_path);
if (err < 0) {
bpf_printk("Failed to read exe_path");
return -1;
}

struct helper_t* helper = bpf_map_lookup_elem(&helper_map, &key);
Expand All @@ -131,16 +170,26 @@ __always_inline static int64_t process_fill(process_t* p, bool use_bpf_d_path) {
return -1;
}

p->exe_path_len = d_path(&task->mm->exe_file->f_path, p->exe_path, PATH_MAX, use_bpf_d_path);

const char* cg = get_memory_cgroup(helper);
if (cg != NULL) {
bpf_probe_read_str(p->memory_cgroup, PATH_MAX, cg);
// Reserve space for the cgroup length
event->len += 2;
uint16_t cg_len = (uint16_t)bpf_probe_read_str(&event->buf[event->len], PATH_MAX, cg);

// Move back and fix the length
event->len -= 2;
raw_event_copy_u16(event, cg_len - 1);

// Forward past the cgroup
event->len += ((cg_len - 1) & (PATH_MAX - 1));
}

p->in_root_mount_ns = get_mount_ns() == host_mount_ns;
raw_event_copy_u8(event, get_mount_ns() == host_mount_ns);

process_fill_lineage(p, helper, use_bpf_d_path);
err = process_fill_lineage(event, helper, use_bpf_d_path);
if (err < 0) {
return -1;
}

return 0;
}
Loading
Loading