ebpf: fixed getting ppid, skip failed execve's

- Fixed getting ppid (precompiled .o won't probably work).
 - Skip failed execve* calls.

(cherry picked from commit 68c2c8ae1a)
This commit is contained in:
Gustavo Iñiguez Goia 2024-01-09 11:53:23 +01:00
parent eafb70fb55
commit 1ae20c3aa3
Failed to generate hash of commit
5 changed files with 119 additions and 50 deletions

View file

@ -8,7 +8,7 @@ import (
)
// NewExecEvent constructs a new execEvent from the arguments.
func NewExecEvent(pid, ppid, uid uint64, path string, comm [16]byte) *execEvent {
func NewExecEvent(pid, ppid, uid uint32, path string, comm [16]byte) *execEvent {
ev := &execEvent{
Type: EV_TYPE_EXEC,
PID: pid,
@ -31,18 +31,18 @@ type execEventItem struct {
}
type eventsStore struct {
execEvents map[uint64]*execEventItem
execEvents map[uint32]*execEventItem
sync.RWMutex
}
// NewEventsStore creates a new store of events.
func NewEventsStore() *eventsStore {
return &eventsStore{
execEvents: make(map[uint64]*execEventItem),
execEvents: make(map[uint32]*execEventItem),
}
}
func (e *eventsStore) add(key uint64, event execEvent, proc procmon.Process) {
func (e *eventsStore) add(key uint32, event execEvent, proc procmon.Process) {
e.Lock()
defer e.Unlock()
e.execEvents[key] = &execEventItem{
@ -51,14 +51,14 @@ func (e *eventsStore) add(key uint64, event execEvent, proc procmon.Process) {
}
}
func (e *eventsStore) isInStore(key uint64) (item *execEventItem, found bool) {
func (e *eventsStore) isInStore(key uint32) (item *execEventItem, found bool) {
e.RLock()
defer e.RUnlock()
item, found = e.execEvents[key]
return
}
func (e *eventsStore) delete(key uint64) {
func (e *eventsStore) delete(key uint32) {
e.Lock()
defer e.Unlock()
delete(e.execEvents, key)

View file

@ -30,14 +30,17 @@ const TaskCommLen = 16
type execEvent struct {
Type uint64
PID uint64
PPID uint64
UID uint64
ArgsCount uint64
ArgsPartial uint64
PID uint32
UID uint32
PPID uint32
RetCode uint32
ArgsCount uint8
ArgsPartial uint8
Filename [MaxPathLen]byte
Args [MaxArgs][MaxArgLen]byte
Comm [TaskCommLen]byte
Pad1 uint16
Pad2 uint32
}
// Struct that holds the metadata of a connection.
@ -91,6 +94,8 @@ func initEventsStreamer() {
"tracepoint/sched/sched_process_exit",
"tracepoint/syscalls/sys_enter_execve",
"tracepoint/syscalls/sys_enter_execveat",
"tracepoint/syscalls/sys_exit_execve",
"tracepoint/syscalls/sys_exit_execveat",
//"tracepoint/sched/sched_process_exec",
//"tracepoint/sched/sched_process_fork",
}
@ -198,6 +203,7 @@ func event2process(event *execEvent) (proc *procmon.Process) {
proc.ReadCwd()
proc.ReadEnv()
proc.UID = int(event.UID)
proc.PPID = int(event.PPID)
if event.ArgsPartial == 0 {
for i := 0; i < int(event.ArgsCount); i++ {

View file

@ -165,7 +165,7 @@ func findConnProcess(value *networkEventT, connKey string) (proc *procmon.Proces
proc.UID = int(value.UID)
err := proc.ReadPath()
if ev, found := execEvents.isInStore(value.Pid); found {
if ev, found := execEvents.isInStore(uint32(value.Pid)); found {
// use socket's UID. See above why ^
ev.Proc.UID = proc.UID
ev.Proc.ReadCmdline()
@ -186,8 +186,8 @@ func findConnProcess(value *networkEventT, connKey string) (proc *procmon.Proces
log.Debug("[ebpf conn] not in cache, NOR in execEvents: %s, %d -> %s", connKey, proc.ID, proc.Path)
// We'll end here if the events module has not been loaded, or if the process is not in cache.
proc.GetInfo()
execEvents.add(value.Pid,
*NewExecEvent(value.Pid, 0, value.UID, proc.Path, value.Comm),
execEvents.add(uint32(value.Pid),
*NewExecEvent(uint32(value.Pid), 0, uint32(value.UID), proc.Path, value.Comm),
*proc)
}

View file

@ -34,18 +34,55 @@ enum events_type {
EVENT_SCHED_EXIT,
};
struct trace_ev_common {
short common_type;
char common_flags;
char common_preempt_count;
int common_pid;
};
struct trace_sys_enter_execve {
struct trace_ev_common ext;
int __syscall_nr;
char *filename;
const char *const *argv;
const char *const *envp;
};
struct trace_sys_enter_execveat {
struct trace_ev_common ext;
int __syscall_nr;
char *filename;
const char *const *argv;
const char *const *envp;
int flags;
};
struct trace_sys_exit_execve {
struct trace_ev_common ext;
int __syscall_nr;
long ret;
};
struct data_t {
u64 type;
u64 pid; // PID as in the userspace term (i.e. task->tgid in kernel)
u64 ppid; // Parent PID as in the userspace term (i.e task->real_parent->tgid in kernel)
u64 uid;
u64 args_count;
u64 args_partial;
u32 pid; // PID as in the userspace term (i.e. task->tgid in kernel)
u32 uid;
// Parent PID as in the userspace term (i.e task->real_parent->tgid in kernel)
u32 ppid;
u32 ret_code;
u8 args_count;
u8 args_partial;
char filename[MAX_PATH_LEN];
char args[MAX_ARGS][MAX_ARG_SIZE];
char comm[TASK_COMM_LEN];
}__attribute__((packed));
u16 pad1;
u32 pad2;
};
//-----------------------------------------------------------------------------
// maps

View file

@ -10,6 +10,14 @@ struct bpf_map_def SEC("maps/proc-events") events = {
.max_entries = 256, // max cpus
};
struct bpf_map_def SEC("maps/execMap") execMap = {
.type = BPF_MAP_TYPE_HASH,
.key_size = sizeof(u32),
.value_size = sizeof(struct data_t),
.max_entries = 256,
};
static __always_inline void new_event(struct data_t* data)
{
// initializing variables with __builtin_memset() is required
@ -23,15 +31,28 @@ static __always_inline void new_event(struct data_t* data)
bpf_probe_read(&parent, sizeof(parent), &task->real_parent);
data->pid = bpf_get_current_pid_tgid() >> 32;
// FIXME: always 0?
#if !defined(__arm__) && !defined(__i386__)
// on i686 -> invalid read from stack
bpf_probe_read(&data->ppid, sizeof(data->ppid), &parent->tgid);
bpf_probe_read(&data->ppid, sizeof(u32), &parent->tgid);
#endif
data->uid = bpf_get_current_uid_gid() & 0xffffffff;
bpf_get_current_comm(&data->comm, sizeof(data->comm));
};
static __always_inline void __handle_exit_execve(struct trace_sys_exit_execve *ctx)
{
u64 pid_tgid = bpf_get_current_pid_tgid();
struct data_t *proc = bpf_map_lookup_elem(&execMap, &pid_tgid);
if (proc == NULL) { return; }
if (ctx->ret != 0) { goto out; }
proc->ret_code = ctx->ret;
bpf_perf_event_output(ctx, &events, BPF_F_CURRENT_CPU, proc, sizeof(*proc));
out:
bpf_map_delete_elem(&execMap, &pid_tgid);
}
// https://0xax.gitbooks.io/linux-insides/content/SysCall/linux-syscall-4.html
// bprm_execve REGS_PARM3
// https://elixir.bootlin.com/linux/latest/source/fs/exec.c#L1796
@ -50,16 +71,20 @@ int tracepoint__sched_sched_process_exit(struct pt_regs *ctx)
return 0;
};
struct trace_sys_enter_execve {
short common_type;
char common_flags;
char common_preempt_count;
int common_pid;
int __syscall_nr;
char *filename;
const char *const *argv;
const char *const *envp;
SEC("tracepoint/syscalls/sys_exit_execve")
int tracepoint__syscalls_sys_exit_execve(struct trace_sys_exit_execve *ctx)
{
__handle_exit_execve(ctx);
return 0;
};
SEC("tracepoint/syscalls/sys_exit_execveat")
int tracepoint__syscalls_sys_exit_execveat(struct trace_sys_exit_execve *ctx)
{
__handle_exit_execve(ctx);
return 0;
};
SEC("tracepoint/syscalls/sys_enter_execve")
int tracepoint__syscalls_sys_enter_execve(struct trace_sys_enter_execve* ctx)
{
@ -93,24 +118,19 @@ int tracepoint__syscalls_sys_enter_execve(struct trace_sys_enter_execve* ctx)
}
#endif
// With some commands, this helper fails with error -28 (ENOSPC). Misleading error? cmd failed maybe?
// BUG: after coming back from suspend state, this helper fails with error -95 (EOPNOTSUPP)
// Possible workaround: count -95 errors, and from userspace reinitialize the streamer if errors >= n-errors
bpf_perf_event_output(ctx, &events, BPF_F_CURRENT_CPU, data, sizeof(*data));
// in case of failure adding the item to the map, send it directly
u64 pid_tgid = bpf_get_current_pid_tgid();
if (bpf_map_update_elem(&execMap, &pid_tgid, data, BPF_ANY) != 0) {
// With some commands, this helper fails with error -28 (ENOSPC). Misleading error? cmd failed maybe?
// BUG: after coming back from suspend state, this helper fails with error -95 (EOPNOTSUPP)
// Possible workaround: count -95 errors, and from userspace reinitialize the streamer if errors >= n-errors
bpf_perf_event_output(ctx, &events, BPF_F_CURRENT_CPU, data, sizeof(*data));
}
return 0;
};
struct trace_sys_enter_execveat {
short common_type;
char common_flags;
char common_preempt_count;
int common_pid;
int __syscall_nr;
char *filename;
const char *const *argv;
const char *const *envp;
int flags;
};
SEC("tracepoint/syscalls/sys_enter_execveat")
int tracepoint__syscalls_sys_enter_execveat(struct trace_sys_enter_execveat* ctx)
{
@ -140,10 +160,16 @@ int tracepoint__syscalls_sys_enter_execveat(struct trace_sys_enter_execveat* ctx
data->args_count++;
}
// With some commands, this helper fails with error -28 (ENOSPC). Misleading error? cmd failed maybe?
// BUG: after coming back from suspend state, this helper fails with error -95 (EOPNOTSUPP)
// Possible workaround: count -95 errors, and from userspace reinitialize the streamer if errors >= n-errors
bpf_perf_event_output(ctx, &events, BPF_F_CURRENT_CPU, data, sizeof(*data));
// in case of failure adding the item to the map, send it directly
u64 pid_tgid = bpf_get_current_pid_tgid();
if (bpf_map_update_elem(&execMap, &pid_tgid, data, BPF_ANY) != 0) {
// With some commands, this helper fails with error -28 (ENOSPC). Misleading error? cmd failed maybe?
// BUG: after coming back from suspend state, this helper fails with error -95 (EOPNOTSUPP)
// Possible workaround: count -95 errors, and from userspace reinitialize the streamer if errors >= n-errors
bpf_perf_event_output(ctx, &events, BPF_F_CURRENT_CPU, data, sizeof(*data));
}
return 0;
};