Skip to content
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
44 changes: 20 additions & 24 deletions internal/ebpf/c/clone.bpf.c
Original file line number Diff line number Diff line change
@@ -1,19 +1,20 @@
//go:build ignore

#include "common/events.h"
#include "bpf_core_read.h"
#include "bpf_tracing.h"

char LICENSE[] SEC("license") = "Dual MIT/GPL";

static __always_inline int handle_clone_enter(u64 flags, u32 syscall_id)
{
u64 key = bpf_get_current_pid_tgid();
struct scratch_value val = {};
struct scratch_value *val;

val.arg0 = flags;
val.arg1 = syscall_id;
bpf_map_update_elem(&scratch, &key, &val, BPF_ANY);
val = borrow_scratch();
if (!val)
return 0;
val->flags = flags;
val->arg1 = syscall_id;
store_scratch(val);
return 0;
}

Expand All @@ -22,39 +23,32 @@ static __always_inline int handle_clone_exit(long ret, u32 syscall_id)
u64 key = bpf_get_current_pid_tgid();
struct scratch_value *val;
struct syscall_event *e;
struct task_struct *task;
struct task_struct *parent;
const struct cred *cred;

/* Successful clones are emitted from sched_process_fork with child identity.
* Leave scratch in place so the fork handler can recover clone flags.
*/
if (ret >= 0)
return 0;

if (!type_enabled(EVT_TYPE_CLONE)) {
bpf_map_delete_elem(&scratch, &key);
return 0;
}

e = reserve_event();
if (!e)
if (!e) {
bpf_map_delete_elem(&scratch, &key);
return 0;
}

e->type = EVT_TYPE_CLONE;
e->syscall_id = syscall_id;
e->retval = ret;
fill_current_ids(e);

task = (struct task_struct *)bpf_get_current_task();
e->start_boottime = BPF_CORE_READ(task, start_boottime);
parent = BPF_CORE_READ(task, real_parent);
if (parent)
e->ppid = BPF_CORE_READ(parent, tgid);
cred = BPF_CORE_READ(task, real_cred);
if (cred) {
e->uid = BPF_CORE_READ(cred, euid.val);
e->gid = BPF_CORE_READ(cred, egid.val);
}
fill_current_task(e);

val = bpf_map_lookup_elem(&scratch, &key);
if (val) {
e->flags = val->arg0;
copy_scratch(e, val);
if (!e->syscall_id)
e->syscall_id = (u32)val->arg1;
}
Expand Down Expand Up @@ -126,6 +120,8 @@ int BPF_PROG(handle_sched_process_fork, struct task_struct *parent, struct task_

if (!child || !parent)
return 0;
if (!type_enabled(EVT_TYPE_CLONE))
return 0;

e = reserve_event();
if (!e)
Expand Down Expand Up @@ -154,7 +150,7 @@ int BPF_PROG(handle_sched_process_fork, struct task_struct *parent, struct task_
key = ((u64)parent->tgid << 32) | (u32)parent->pid;
val = bpf_map_lookup_elem(&scratch, &key);
if (val) {
e->flags = val->arg0;
copy_scratch(e, val);
e->syscall_id = (u32)val->arg1;
bpf_map_delete_elem(&scratch, &key);
}
Expand Down
225 changes: 221 additions & 4 deletions internal/ebpf/c/common/events.h
Original file line number Diff line number Diff line change
Expand Up @@ -4,12 +4,25 @@

#include "vmlinux.h"
#include "bpf_helpers.h"
#include "bpf_core_read.h"

#ifndef BPF_ANY
#define BPF_ANY 0
#endif

#define EVT_FILENAME_LEN 256
#define EVT_TYPE_MAX 32

/* Set when a userspace copy was longer than the destination and was cut off. */
#define TRUNC_FILENAME (1u << 0) /* filename[]: exec path, open/unlink/rename source */
#define TRUNC_AUX (1u << 1) /* aux[]: rename destination or sockaddr bytes */

/* UAPI openat2 argument; not a CO-RE kernel type. */
struct open_how {
u64 flags;
u64 mode;
u64 resolve;
};

struct trace_event_raw_sys_enter {
unsigned short common_type;
Expand Down Expand Up @@ -37,6 +50,17 @@ struct trace_event_raw_sys_exit {
#define EVT_TYPE_EXECVE 1
#define EVT_TYPE_EXIT 2
#define EVT_TYPE_CLONE 3
#define EVT_TYPE_OPENAT 4
#define EVT_TYPE_UNLINK 5
#define EVT_TYPE_RENAME 6
#define EVT_TYPE_CONNECT 7
#define EVT_TYPE_ACCEPT 8
#define EVT_TYPE_MMAP 9
#define EVT_TYPE_PROCESS_VM_READ 10
#define EVT_TYPE_PROCESS_VM_WRITE 11
#define EVT_TYPE_KILL 12
#define EVT_TYPE_PTRACE 13
#define EVT_TYPE_PRCTL 14

struct syscall_event {
u32 type;
Expand All @@ -48,18 +72,32 @@ struct syscall_event {
u32 gid;
u32 syscall_id;
s64 retval;
/* Interpreted per event type; clone stores the raw clone flags. */
/* Interpreted per event type: clone flags, open flags, mmap flags, accept flags. */
u64 flags;
/* Generic argument slots, interpreted per event type. */
u64 arg0;
Comment thread
rabbitstack marked this conversation as resolved.
u64 arg1;
u64 arg2;
u64 arg3;
u64 start_boottime;
u64 timestamp_ns;
u32 truncated;
Comment thread
rabbitstack marked this conversation as resolved.
u32 pad; /* keeps comm[] 8-byte aligned; no semantic value */
u8 comm[TASK_COMM_LEN];
u8 filename[EVT_FILENAME_LEN];
u8 filename[EVT_FILENAME_LEN]; /* exec path, open/unlink/rename source */
u8 aux[EVT_FILENAME_LEN]; /* rename destination or sockaddr bytes */
};

struct scratch_value {
u64 arg0;
u64 arg1;
u64 arg2;
u64 arg3;
u64 flags;
u32 truncated;
u32 pad;
u8 filename[EVT_FILENAME_LEN];
u8 aux[EVT_FILENAME_LEN];
};

struct {
Expand All @@ -74,13 +112,32 @@ struct {
__type(value, u64);
} drop_count SEC(".maps");

/* Correlates sys_enter state with the matching sys_exit. The task can
* migrate CPUs between entry and exit, so this must be a global hash, not
* a per-CPU one. LRU eviction reclaims entries whose exit never fired
* (e.g. the task was killed mid-syscall).
*/
struct {
__uint(type, BPF_MAP_TYPE_PERCPU_HASH);
__uint(max_entries, 8192);
__uint(type, BPF_MAP_TYPE_LRU_HASH);
__uint(max_entries, 2048);
__type(key, u64);
__type(value, struct scratch_value);
} scratch SEC(".maps");

struct {
__uint(type, BPF_MAP_TYPE_PERCPU_ARRAY);
__uint(max_entries, 1);
__type(key, u32);
__type(value, struct scratch_value);
} scratch_heap SEC(".maps");

struct {
__uint(type, BPF_MAP_TYPE_ARRAY);
__uint(max_entries, EVT_TYPE_MAX);
__type(key, u32);
__type(value, u8);
} enabled SEC(".maps");

static __always_inline void account_drop(void)
{
u32 key = 0;
Expand All @@ -92,6 +149,16 @@ static __always_inline void account_drop(void)
__sync_fetch_and_add(count, 1);
}

static __always_inline int type_enabled(u32 type)
{
u8 *on;

if (type >= EVT_TYPE_MAX)
return 0;
on = bpf_map_lookup_elem(&enabled, &type);
return on && *on;
}

static __always_inline struct syscall_event *reserve_event(void)
{
struct syscall_event *e;
Expand All @@ -118,3 +185,153 @@ static __always_inline void fill_current_ids(struct syscall_event *e)
e->gid = uidgid >> 32;
bpf_get_current_comm(&e->comm, sizeof(e->comm));
}

static __always_inline void fill_current_task(struct syscall_event *e)
{
struct task_struct *task;
struct task_struct *parent;
const struct cred *cred;

fill_current_ids(e);
task = (struct task_struct *)bpf_get_current_task();
e->start_boottime = BPF_CORE_READ(task, start_boottime);
parent = BPF_CORE_READ(task, real_parent);
if (parent)
e->ppid = BPF_CORE_READ(parent, tgid);
cred = BPF_CORE_READ(task, real_cred);
if (cred) {
e->uid = BPF_CORE_READ(cred, euid.val);
e->gid = BPF_CORE_READ(cred, egid.val);
}
}

static __always_inline u32 read_user_str(void *dst, u32 size, const char *src)
{
long n;

if (!src)
return 0;
n = bpf_probe_read_user_str(dst, size, src);
if (n == size)
return 1;
return 0;
}

static __always_inline void copy_scratch(struct syscall_event *e, struct scratch_value *val)
{
if (!val)
return;
e->arg0 = val->arg0;
e->arg1 = val->arg1;
e->arg2 = val->arg2;
e->arg3 = val->arg3;
if (!e->flags)
e->flags = val->flags;
e->truncated = val->truncated;
__builtin_memcpy(&e->filename, val->filename, sizeof(e->filename));
__builtin_memcpy(&e->aux, val->aux, sizeof(e->aux));
}

static __always_inline struct scratch_value *borrow_scratch(void)
{
u32 zero = 0;
struct scratch_value *val;

val = bpf_map_lookup_elem(&scratch_heap, &zero);
if (!val)
return NULL;
__builtin_memset(val, 0, sizeof(*val));
return val;
}

static __always_inline int store_scratch(struct scratch_value *val)
{
u64 key = bpf_get_current_pid_tgid();

return bpf_map_update_elem(&scratch, &key, val, BPF_ANY);
}

static __always_inline int submit_from_scratch(u32 type, long ret, u32 syscall_id)
{
u64 key = bpf_get_current_pid_tgid();
struct scratch_value *val;
struct syscall_event *e;

if (!type_enabled(type)) {
bpf_map_delete_elem(&scratch, &key);
return 0;
}

e = reserve_event();
if (!e) {
bpf_map_delete_elem(&scratch, &key);
return 0;
}

e->type = type;
e->syscall_id = syscall_id;
e->retval = ret;
fill_current_task(e);
val = bpf_map_lookup_elem(&scratch, &key);
if (val)
copy_scratch(e, val);
bpf_map_delete_elem(&scratch, &key);
bpf_ringbuf_submit(e, 0);
return 0;
}

/* x86-64 syscall ABI. arg3 is r10, not rcx. */
static __always_inline u64 syscall_nr(struct pt_regs *regs)
{
return BPF_CORE_READ(regs, orig_ax);
}

static __always_inline u64 syscall_arg0(struct pt_regs *regs)
{
return BPF_CORE_READ(regs, di);
}

static __always_inline u64 syscall_arg1(struct pt_regs *regs)
{
return BPF_CORE_READ(regs, si);
}

static __always_inline u64 syscall_arg2(struct pt_regs *regs)
{
return BPF_CORE_READ(regs, dx);
}

static __always_inline u64 syscall_arg3(struct pt_regs *regs)
{
return BPF_CORE_READ(regs, r10);
}

static __always_inline u64 syscall_arg4(struct pt_regs *regs)
{
return BPF_CORE_READ(regs, r8);
}

static __always_inline int submit_args(u32 type, long ret, u32 syscall_id,
u64 arg0, u64 arg1, u64 arg2, u64 arg3, u64 flags)
{
struct syscall_event *e;

if (!type_enabled(type))
return 0;

e = reserve_event();
if (!e)
return 0;

e->type = type;
e->syscall_id = syscall_id;
e->retval = ret;
e->arg0 = arg0;
e->arg1 = arg1;
e->arg2 = arg2;
e->arg3 = arg3;
e->flags = flags;
fill_current_task(e);
bpf_ringbuf_submit(e, 0);
return 0;
}
Loading
Loading