diff --git a/bpf/enhancedrecording/command.bpf.c b/bpf/enhancedrecording/command.bpf.c index f9b6f81f7f4bc..f8708feb46744 100644 --- a/bpf/enhancedrecording/command.bpf.c +++ b/bpf/enhancedrecording/command.bpf.c @@ -8,25 +8,10 @@ // Size, in bytes, of the ring buffer used to report // audit events to userspace. This is the default, // the userspace can adjust this value based on config. -#define EVENTS_BUF_SIZE (4096*8) +#define EVENTS_BUF_SIZE (4096 * 128) char LICENSE[] SEC("license") = "Dual BSD/GPL"; -enum event_type { - EVENT_ARG, - EVENT_RET, -}; - -// common_data_t is a struct used to store fields that are common across -// multiple events. Those fields are the same as `data_t`. -struct common_data_t { - u64 pid; - u64 ppid; - u8 command[TASK_COMM_LEN]; - u64 cgroup; - u32 audit_session_id; -}; - struct data_t { // pid as in the userspace term (i.e. task->tgid in kernel). u64 pid; @@ -34,12 +19,14 @@ struct data_t { u64 ppid; // Command is the executable. u8 command[TASK_COMM_LEN]; - // Type is the type of event. - enum event_type type; - // Argv is the list of arguments to the program. - u8 argv[ARGSIZE]; - // ReturnCode is the return code of execve. - int retval; + // Filename is the path of the executable. + u8 filename[FILENAMESIZE]; + // Args is the list of arguments to the program. + u8 args[ARGBUFSIZE]; + // ArgsLen is the length of the args. + u64 args_len; + // ArgsTruncated is true if the args were truncated. + bool args_truncated; // CgroupID is the internal cgroupv2 ID of the event. u64 cgroup; // AuditSessionID is the audit session ID that is used to correlate @@ -59,45 +46,8 @@ BPF_RING_BUF(execve_events, EVENTS_BUF_SIZE); BPF_COUNTER(lost); -static int __submit_arg(void *ptr, struct common_data_t *common) +static int enter_execve(struct trace_event_raw_sched_process_exec *tp) { - struct data_t *data = bpf_ringbuf_reserve(&execve_events, sizeof(*data), 0); - if (!data) { - return -1; - } - - if (bpf_probe_read_user(data->argv, sizeof(data->argv), ptr) < 0) { - bpf_ringbuf_discard(data, 0); - return -1; - } - - data->type = EVENT_ARG; - data->pid = common->pid; - data->cgroup = common->cgroup; - data->audit_session_id = common->audit_session_id; - for (int i = 0; i < TASK_COMM_LEN; i++) - data->command[i] = common->command[i]; - - bpf_ringbuf_submit(data, 0); - return 1; -} - -static int submit_arg(void *ptr, struct common_data_t *common) -{ - const char *argp = 0; - bpf_probe_read_user(&argp, sizeof(argp), ptr); - if (argp) { - return __submit_arg((void *)(argp), common); - } - return 0; -} - -static int enter_execve(const char *filename, - const char *const *argv, - const char *const *envp) -{ - struct common_data_t common = {}; - struct task_struct *task = (struct task_struct *)bpf_get_current_task(); u32 session_id = BPF_CORE_READ(task, sessionid); u8 *is_monitored = bpf_map_lookup_elem(&monitored_sessionids, &session_id); @@ -105,98 +55,46 @@ static int enter_execve(const char *filename, return 0; } - print_command_event(task, filename, argv); - - common.pid = bpf_get_current_pid_tgid() >> 32; - common.cgroup = bpf_get_current_cgroup_id(); - common.audit_session_id = session_id; - - common.ppid = BPF_CORE_READ(task, real_parent, tgid); - bpf_get_current_comm(&common.command, sizeof(common.command)); - - if(__submit_arg((void *)filename, &common) < 0) { + struct data_t *data = bpf_ringbuf_reserve(&execve_events, sizeof(*data), 0); + if (!data) { INCR_COUNTER(lost); - goto out; + bpf_printk("execve_events ring buffer full"); + return 0; } - for (int i = 1; i < MAXARGS; i++) { - int res = submit_arg((void *)&argv[i], &common); - if (res < 0) { - INCR_COUNTER(lost); - goto out; - } - - // If no arguments were sent, we reached the end of the arguments list. - if (res == 0) - goto out; + u64 filename_loc = BPF_CORE_READ(tp, __data_loc_filename) & 0xFFFF; + bpf_probe_read_str(&data->filename, sizeof(data->filename), (void *)tp + filename_loc); + + void *arg_start = (void *)BPF_CORE_READ(task, mm, arg_start); + void *arg_end = (void *)BPF_CORE_READ(task, mm, arg_end); + data->args_len = arg_end - arg_start; + + data->args_truncated = false; + if (data->args_len > ARGBUFSIZE) { + data->args_len = ARGBUFSIZE; + data->args_truncated = true; } - - // handle truncated argument list - char ellipsis[] = "..."; - if (__submit_arg((void *)ellipsis, &common) < 0) - INCR_COUNTER(lost); -out: - return 0; -} - -static int exit_execve(int ret) -{ - struct task_struct *task = (struct task_struct *)bpf_get_current_task(); - u32 session_id = BPF_CORE_READ(task, sessionid); - u8 *is_monitored = bpf_map_lookup_elem(&monitored_sessionids, &session_id); - if (is_monitored == NULL) { - return 0; + int read_ret = bpf_probe_read_user(&data->args, data->args_len, arg_start); + if (read_ret < 0) { + data->args_len = 0; } - struct data_t *data = bpf_ringbuf_reserve(&execve_events, sizeof(*data), 0); - if (!data) { - INCR_COUNTER(lost); - return 0; - } + print_command_event(task, data->filename, data->args); data->pid = bpf_get_current_pid_tgid() >> 32; data->cgroup = bpf_get_current_cgroup_id(); data->audit_session_id = session_id; - task = (struct task_struct *)bpf_get_current_task(); data->ppid = BPF_CORE_READ(task, real_parent, tgid); - bpf_get_current_comm(&data->command, sizeof(data->command)); - data->type = EVENT_RET; - data->retval = ret; bpf_ringbuf_submit(data, 0); - return 0; -} - -SEC("tp/syscalls/sys_execve") -int tracepoint__syscalls__sys_enter_execve(struct syscall_trace_enter *tp) -{ - const char *filename = (const char *)tp->args[0]; - const char *const *argv = (const char *const *)tp->args[1]; - const char *const *envp = (const char *const *)tp->args[2]; - return enter_execve(filename, argv, envp); -} - -SEC("tp/syscalls/sys_exit_execve") -int tracepoint__syscalls__sys_exit_execve(struct syscall_trace_exit *tp) -{ - return exit_execve(tp->ret); -} - -SEC("tp/syscalls/sys_execveat") -int tracepoint__syscalls__sys_enter_execveat(struct syscall_trace_enter *tp) -{ - const char *filename = (const char *)tp->args[1]; - const char *const *argv = (const char *const *)tp->args[2]; - const char *const *envp = (const char *const *)tp->args[3]; - - return enter_execve(filename, argv, envp); + return 0; } -SEC("tp/syscalls/sys_exit_execveat") -int tracepoint__syscalls__sys_exit_execveat(struct syscall_trace_exit *tp) +SEC("tracepoint/sched/sched_process_exec") +int tracepoint__sched__sched_process_exec(struct trace_event_raw_sched_process_exec *tp) { - return exit_execve(tp->ret); + return enter_execve(tp); } diff --git a/bpf/enhancedrecording/common.h b/bpf/enhancedrecording/common.h index cfbd9d524540e..797cd501e7977 100644 --- a/bpf/enhancedrecording/common.h +++ b/bpf/enhancedrecording/common.h @@ -11,9 +11,12 @@ // Maximum monitored sessions. #define MAX_MONITORED_SESSIONS 1024 +#define FILENAMESIZE 512 + // ARGSIZE specifies the max argument size read. -#define ARGSIZE 1024 -#define MAXARGS 20 +#define _ARGSIZE 1024 +#define _MAXARGS 20 +#define ARGBUFSIZE (_ARGSIZE * _MAXARGS) // Easier to use bpf_printk taken from https://nakryiko.com/posts/bpf-tips-printk/ @@ -49,19 +52,13 @@ static void print_event(struct task_struct *task) { bpf_printk(" session ID: %lu", session_id); } -static void print_command_event(struct task_struct *task, const char *filename, const char *const *argv) { +static void print_command_event(struct task_struct *task, const u8 filename[FILENAMESIZE], const u8 argv[ARGBUFSIZE]) { const char *arg = NULL; - + bpf_printk("command:"); print_event(task); bpf_printk(" filename: %s", filename); - for (int i = 1; i < MAXARGS; i++) { - bpf_probe_read_user_str(&arg, MAXARGS, (void*)&argv[i]); - if (arg == NULL){ - break; - } - bpf_printk(" argv[%d]: %s", i, arg); - } + bpf_printk(" argv[0]: %s", argv); } static void print_disk_event(struct task_struct *task, const char *path) { @@ -72,7 +69,7 @@ static void print_disk_event(struct task_struct *task, const char *path) { #else #define bpf_printk(fmt, ...) static void print_event(struct task_struct *task) {} -static void print_command_event(struct task_struct *task, const char *filename, const char *const *argv) {} +static void print_command_event(struct task_struct *task, const u8 filename[FILENAMESIZE], const u8 argv[ARGBUFSIZE]) {} static void print_disk_event(struct task_struct *task, const char *path) {} #endif diff --git a/bpf/enhancedrecording/disk.bpf.c b/bpf/enhancedrecording/disk.bpf.c index acf401ff5be0c..010b973e890db 100644 --- a/bpf/enhancedrecording/disk.bpf.c +++ b/bpf/enhancedrecording/disk.bpf.c @@ -12,17 +12,10 @@ // Size, in bytes, of the ring buffer used to report // audit events to userspace. This is the default, // the userspace can adjust this value based on config. -#define EVENTS_BUF_SIZE (4096*128) - +#define EVENTS_BUF_SIZE (4096 * 2048) char LICENSE[] SEC("license") = "Dual BSD/GPL"; -struct val_t { - u64 pid; - const char *fname; - int flags; -}; - struct data_t { // CgroupID is the internal cgroupv2 ID of the event. u64 cgroup; @@ -36,7 +29,7 @@ struct data_t { // Command is name of the executable opening the file. u8 command[TASK_COMM_LEN]; // File_path is the full path to the file being opened. - u8 file_path[NAME_MAX]; + u8 file_path[PATH_MAX]; // Flags are the flags passed to open. int flags; }; @@ -49,14 +42,12 @@ const struct data_t *unused __attribute__((unused)); // by Teleport. BPF_HASH(monitored_sessionids, u32, u8, MAX_MONITORED_SESSIONS); -BPF_HASH(infotmp, u64, struct val_t, INFLIGHT_MAX); - // open_events ring buffer BPF_RING_BUF(open_events, EVENTS_BUF_SIZE); BPF_COUNTER(lost); -static int enter_open(const char *filename, int flags) { +static int handle_open(struct file *f) { struct task_struct *task = (struct task_struct *)bpf_get_current_task(); u32 session_id = BPF_CORE_READ(task, sessionid); u8 *is_monitored = bpf_map_lookup_elem(&monitored_sessionids, &session_id); @@ -64,108 +55,29 @@ static int enter_open(const char *filename, int flags) { return 0; } - print_disk_event(task, filename); - - struct val_t val = {}; - u64 id = bpf_get_current_pid_tgid(); - - val.pid = id >> 32; - val.fname = filename; - val.flags = flags; - bpf_map_update_elem(&infotmp, &id, &val, 0); - - return 0; -} - -static int exit_open(int ret) { - struct val_t *valp; - u64 id = bpf_get_current_pid_tgid(); - - valp = bpf_map_lookup_elem(&infotmp, &id); - if (valp == NULL) { - // Missed entry. + struct data_t *data = bpf_ringbuf_reserve(&open_events, sizeof(*data), 0); + if (!data) { + INCR_COUNTER(lost); + bpf_printk("open_events ring buffer full"); return 0; } - struct data_t data = {}; - if (bpf_get_current_comm(&data.command, sizeof(data.command)) != 0) { - data.command[0] = '\0'; - } + bpf_d_path(&f->f_path, (char *)data->file_path, sizeof(data->file_path)); + print_disk_event(task, (char *)data->file_path); - bpf_probe_read_user(&data.file_path, sizeof(data.file_path), (void *)valp->fname); + bpf_get_current_comm(&data->command, sizeof(data->command)); - data.pid = valp->pid; - data.flags = valp->flags; - data.return_code = ret; - data.cgroup = bpf_get_current_cgroup_id(); - - struct task_struct *task = (struct task_struct *)bpf_get_current_task(); - data.audit_session_id = BPF_CORE_READ(task, sessionid); - - if (bpf_ringbuf_output(&open_events, &data, sizeof(data), 0) != 0) - INCR_COUNTER(lost); + data->pid = bpf_get_current_pid_tgid() >> 32; + data->flags = BPF_CORE_READ(f, f_flags); + data->cgroup = bpf_get_current_cgroup_id(); + data->audit_session_id = BPF_CORE_READ(task, sessionid); - bpf_map_delete_elem(&infotmp, &id); + bpf_ringbuf_submit(data, 0); return 0; } - -SEC("tp/syscalls/sys_enter_creat") -int tracepoint__syscalls__sys_enter_creat(struct syscall_trace_enter *tp) -{ - const char *filename = (const char*) tp->args[0]; - - return enter_open(filename, 0); -} - -SEC("tp/syscalls/sys_exit_creat") -int tracepoint__syscalls__sys_exit_creat(struct syscall_trace_exit *tp) -{ - return exit_open(tp->ret); -} - -SEC("tp/syscalls/sys_enter_open") -int tracepoint__syscalls__sys_enter_open(struct syscall_trace_enter *tp) -{ - const char *filename = (const char*) tp->args[0]; - int flags = tp->args[1]; - - return enter_open(filename, flags); -}; - -SEC("tp/syscalls/sys_exit_open") -int tracepoint__syscalls__sys_exit_open(struct syscall_trace_exit *tp) -{ - return exit_open(tp->ret); -} - -SEC("tp/syscalls/sys_enter_openat") -int tracepoint__syscalls__sys_enter_openat(struct syscall_trace_enter *tp) -{ - const char *filename = (const char*) tp->args[1]; - int flags = tp->args[2]; - - return enter_open(filename, flags); -}; - -SEC("tp/syscalls/sys_exit_openat") -int tracepoint__syscalls__sys_exit_openat(struct syscall_trace_exit *tp) -{ - return exit_open(tp->ret); -} - -SEC("tp/syscalls/sys_enter_openat2") -int tracepoint__syscalls__sys_enter_openat2(struct syscall_trace_enter *tp) -{ - const char *filename = (const char*) tp->args[1]; - struct open_how *how = (struct open_how *) tp->args[2]; - - return enter_open(filename, BPF_CORE_READ(how, flags)); -}; - -SEC("tp/syscalls/sys_exit_openat2") -int tracepoint__syscalls__sys_exit_openat2(struct syscall_trace_exit *tp) -{ - return exit_open(tp->ret); +SEC("fentry/security_file_open") +int BPF_PROG(security_file_open, struct file *f) { + return handle_open(f); } diff --git a/bpf/helpers.h b/bpf/helpers.h index 6dc3537584f10..c531ac6214eae 100644 --- a/bpf/helpers.h +++ b/bpf/helpers.h @@ -1,6 +1,8 @@ #ifndef HELPERS_H #define HELPERS_H +#include "vmlinux.h" + #define BPF_ARRAY(name, val_type, size) \ struct { \ __uint(type, BPF_MAP_TYPE_ARRAY); \ @@ -9,8 +11,6 @@ __type(value, val_type); \ } name SEC(".maps") -#include "vmlinux.h" - #define BPF_HASH(name, key_type, val_type, size) \ struct { \ __uint(type, BPF_MAP_TYPE_HASH); \ diff --git a/lib/bpf/bpf.go b/lib/bpf/bpf.go index c290a96fe976d..ffe55d1989995 100644 --- a/lib/bpf/bpf.go +++ b/lib/bpf/bpf.go @@ -28,7 +28,6 @@ import ( "math" "net" "slices" - "strconv" "time" "github.com/cilium/ebpf/ringbuf" @@ -44,9 +43,8 @@ import ( "github.com/gravitational/teleport/lib/utils" ) -// ArgsCacheSize is the number of args events to store before dropping args -// events. -const ArgsCacheSize = len(commandDataT{}.Argv) +// CommandArgsBufferSize is the size of a command event args buffer. +const CommandArgsBufferSize = len(commandDataT{}.Args) type sessionHandler interface { startSession(auditSessionID uint32) error @@ -61,10 +59,6 @@ type Service struct { // watching and emitting events for. sessions utils.SyncMap[uint32, *SessionContext] - // argsCache holds the arguments to execve because they come a different - // event than the result. - argsCache *utils.FnCache - // closeContext is used to signal the BPF service is shutting down to all // goroutines. closeContext context.Context @@ -121,13 +115,6 @@ func New(config *servicecfg.BPFConfig) (bpf BPF, err error) { } }() - s.argsCache, err = utils.NewFnCache(utils.FnCacheConfig{ - TTL: 24 * time.Hour, - }) - if err != nil { - return nil, trace.Wrap(err) - } - start := time.Now() logger.DebugContext(closeContext, "Starting enhanced session recording") @@ -346,77 +333,72 @@ func (s *Service) emitCommandEvent(eventBytes []byte) { return } - switch event.Type { - // Args are sent in their own event by execsnoop to save stack space. Store - // the args in a ttlmap, so they can be retrieved when the return event arrives. - case eventArg: - key := strconv.FormatUint(event.Pid, 10) + args := convertArgs(event.Args[:event.ArgsLen], event.ArgsTruncated) - args, err := utils.FnCacheGet(s.closeContext, s.argsCache, key, func(ctx context.Context) ([]string, error) { - return make([]string, 0), nil - }) - if err != nil { - logger.WarnContext(s.closeContext, "Unable to retrieve args from FnCache - this is a bug!", "error", err) - args = []string{} - } + // Emit "command" event. + sessionCommandEvent := &apievents.SessionCommand{ + Metadata: apievents.Metadata{ + Type: events.SessionCommandEvent, + Code: events.SessionCommandCode, + }, + ServerMetadata: apievents.ServerMetadata{ + ServerVersion: ossteleport.Version, + ServerID: ctx.ServerID, + ServerHostname: ctx.ServerHostname, + ServerNamespace: ctx.Namespace, + }, + SessionMetadata: apievents.SessionMetadata{ + SessionID: ctx.SessionID, + }, + UserMetadata: apievents.UserMetadata{ + User: ctx.User, + Login: ctx.Login, + UserClusterName: ctx.UserOriginClusterName, + UserRoles: slices.Clone(ctx.UserRoles), + UserTraits: ctx.UserTraits.Clone(), + }, + BPFMetadata: apievents.BPFMetadata{ + CgroupID: event.Cgroup, + AuditSessionID: event.AuditSessionId, + Program: unix.ByteSliceToString(event.Command[:]), + PID: event.Pid, + }, + PPID: event.Ppid, + Path: unix.ByteSliceToString(event.Filename[:]), + Argv: args, + } + if err := ctx.Emitter.EmitAuditEvent(ctx.Context, sessionCommandEvent); err != nil { + logger.WarnContext(ctx.Context, "Failed to emit command event", "error", err) + } +} - args = append(args, ConvertString(event.Argv[:])) +// convertArgs converts a large buffer of null-terminated strings into +// a slice of strings. +func convertArgs(rawArgs []byte, truncated bool) []string { + if len(rawArgs) == 0 { + return nil + } - s.argsCache.SetWithTTL(key, args, 24*time.Hour) - // The event has returned, emit the fully parsed event. - case eventRet: - // The args should have come in a previous event, find them by PID. - key := strconv.FormatUint(event.Pid, 10) + argc := bytes.Count(rawArgs, []byte{0x0}) + args := make([]string, 0, argc) - args, err := utils.FnCacheGet(s.closeContext, s.argsCache, key, func(ctx context.Context) ([]string, error) { - return nil, trace.NotFound("args missing") - }) - if err != nil { - logger.DebugContext(s.closeContext, "Got event with missing args, skipping") - lostCommandEvents.Add(float64(1)) - return + parts := bytes.Split(rawArgs, []byte{0x0}) + for _, part := range parts { + if len(part) == 0 { + continue } + args = append(args, string(part)) + } - // Emit "command" event. - sessionCommandEvent := &apievents.SessionCommand{ - Metadata: apievents.Metadata{ - Type: events.SessionCommandEvent, - Code: events.SessionCommandCode, - }, - ServerMetadata: apievents.ServerMetadata{ - ServerVersion: ossteleport.Version, - ServerID: ctx.ServerID, - ServerHostname: ctx.ServerHostname, - ServerNamespace: ctx.Namespace, - }, - SessionMetadata: apievents.SessionMetadata{ - SessionID: ctx.SessionID, - }, - UserMetadata: apievents.UserMetadata{ - User: ctx.User, - Login: ctx.Login, - UserClusterName: ctx.UserOriginClusterName, - UserRoles: slices.Clone(ctx.UserRoles), - UserTraits: ctx.UserTraits.Clone(), - }, - BPFMetadata: apievents.BPFMetadata{ - CgroupID: event.Cgroup, - AuditSessionID: event.AuditSessionId, - Program: ConvertString(event.Command[:]), - PID: event.Pid, - }, - PPID: event.Ppid, - ReturnCode: event.Retval, - Path: args[0], - Argv: args[1:], - } - if err := ctx.Emitter.EmitAuditEvent(ctx.Context, sessionCommandEvent); err != nil { - logger.WarnContext(ctx.Context, "Failed to emit command event", "error", err) - } + if len(args) == 1 { + return nil + } - // Now that the event has been processed, remove from cache. - s.argsCache.Remove(key) + if truncated { + args = append(args, "...") } + + return args[1:] } // emitDiskEvent will parse and emit disk events to the Audit Log. @@ -466,11 +448,11 @@ func (s *Service) emitDiskEvent(eventBytes []byte) { BPFMetadata: apievents.BPFMetadata{ CgroupID: event.Cgroup, AuditSessionID: event.AuditSessionId, - Program: ConvertString(event.Command[:]), + Program: unix.ByteSliceToString(event.Command[:]), PID: event.Pid, }, Flags: event.Flags, - Path: ConvertString(event.FilePath[:]), + Path: unix.ByteSliceToString(event.FilePath[:]), ReturnCode: event.ReturnCode, } // Logs can be DoS by event failures here @@ -526,7 +508,7 @@ func (s *Service) emit4NetworkEvent(eventBytes []byte) { BPFMetadata: apievents.BPFMetadata{ CgroupID: event.Cgroup, AuditSessionID: event.AuditSessionId, - Program: ConvertString(event.Command[:]), + Program: unix.ByteSliceToString(event.Command[:]), PID: uint64(event.Pid), }, DstPort: int32(event.Dport), @@ -588,7 +570,7 @@ func (s *Service) emit6NetworkEvent(eventBytes []byte) { BPFMetadata: apievents.BPFMetadata{ CgroupID: event.Cgroup, AuditSessionID: event.AuditSessionId, - Program: ConvertString(event.Command[:]), + Program: unix.ByteSliceToString(event.Command[:]), PID: uint64(event.Pid), }, DstPort: int32(event.Dport), @@ -616,11 +598,6 @@ func unmarshalEvent(data []byte, v interface{}) error { return nil } -// ConvertString converts a NUL-terminated string to a Go string. -func ConvertString(s []byte) string { - return unix.ByteSliceToString(s) -} - // SystemHasBPF returns true if the binary was build with support for BPF // compiled in. func SystemHasBPF() bool { diff --git a/lib/bpf/command.go b/lib/bpf/command.go index 5367b148c32b5..741504ddf219a 100644 --- a/lib/bpf/command.go +++ b/lib/bpf/command.go @@ -88,25 +88,13 @@ func startExec(bufferSize int) (*exec, error) { tracepoint *ebpf.Program }{ { - name: "sys_enter_execve", - tracepoint: objs.TracepointSyscallsSysEnterExecve, - }, - { - name: "sys_exit_execve", - tracepoint: objs.TracepointSyscallsSysExitExecve, - }, - { - name: "sys_enter_execveat", - tracepoint: objs.TracepointSyscallsSysEnterExecveat, - }, - { - name: "sys_exit_execveat", - tracepoint: objs.TracepointSyscallsSysExitExecveat, + name: "sched_process_exec", + tracepoint: objs.TracepointSchedSchedProcessExec, }, } for _, tp := range tracePoints { - tp, err := link.Tracepoint("syscalls", tp.name, tp.tracepoint, nil) + tp, err := link.Tracepoint("sched", tp.name, tp.tracepoint, nil) if err != nil { return nil, trace.Wrap(err) } diff --git a/lib/bpf/command_arm64_bpfel.go b/lib/bpf/command_arm64_bpfel.go index d45213ca3dfc3..54cef4674fe77 100644 --- a/lib/bpf/command_arm64_bpfel.go +++ b/lib/bpf/command_arm64_bpfel.go @@ -18,9 +18,11 @@ type commandDataT struct { Pid uint64 Ppid uint64 Command [16]uint8 - Type uint32 - Argv [1024]uint8 - Retval int32 + Filename [512]uint8 + Args [20480]uint8 + ArgsLen uint64 + ArgsTruncated bool + _ [7]byte Cgroup uint64 AuditSessionId uint32 _ [4]byte @@ -68,10 +70,7 @@ type commandSpecs struct { // // It can be passed ebpf.CollectionSpec.Assign. type commandProgramSpecs struct { - TracepointSyscallsSysEnterExecve *ebpf.ProgramSpec `ebpf:"tracepoint__syscalls__sys_enter_execve"` - TracepointSyscallsSysEnterExecveat *ebpf.ProgramSpec `ebpf:"tracepoint__syscalls__sys_enter_execveat"` - TracepointSyscallsSysExitExecve *ebpf.ProgramSpec `ebpf:"tracepoint__syscalls__sys_exit_execve"` - TracepointSyscallsSysExitExecveat *ebpf.ProgramSpec `ebpf:"tracepoint__syscalls__sys_exit_execveat"` + TracepointSchedSchedProcessExec *ebpf.ProgramSpec `ebpf:"tracepoint__sched__sched_process_exec"` } // commandMapSpecs contains maps before they are loaded into the kernel. @@ -137,18 +136,12 @@ type commandVariables struct { // // It can be passed to loadCommandObjects or ebpf.CollectionSpec.LoadAndAssign. type commandPrograms struct { - TracepointSyscallsSysEnterExecve *ebpf.Program `ebpf:"tracepoint__syscalls__sys_enter_execve"` - TracepointSyscallsSysEnterExecveat *ebpf.Program `ebpf:"tracepoint__syscalls__sys_enter_execveat"` - TracepointSyscallsSysExitExecve *ebpf.Program `ebpf:"tracepoint__syscalls__sys_exit_execve"` - TracepointSyscallsSysExitExecveat *ebpf.Program `ebpf:"tracepoint__syscalls__sys_exit_execveat"` + TracepointSchedSchedProcessExec *ebpf.Program `ebpf:"tracepoint__sched__sched_process_exec"` } func (p *commandPrograms) Close() error { return _CommandClose( - p.TracepointSyscallsSysEnterExecve, - p.TracepointSyscallsSysEnterExecveat, - p.TracepointSyscallsSysExitExecve, - p.TracepointSyscallsSysExitExecveat, + p.TracepointSchedSchedProcessExec, ) } diff --git a/lib/bpf/command_arm64_bpfel.o b/lib/bpf/command_arm64_bpfel.o index a5ccac38c1e30..d75f8f0789bb8 100644 Binary files a/lib/bpf/command_arm64_bpfel.o and b/lib/bpf/command_arm64_bpfel.o differ diff --git a/lib/bpf/command_x86_bpfel.go b/lib/bpf/command_x86_bpfel.go index fc1e508d78dbc..3a4a7e340c88a 100644 --- a/lib/bpf/command_x86_bpfel.go +++ b/lib/bpf/command_x86_bpfel.go @@ -18,9 +18,11 @@ type commandDataT struct { Pid uint64 Ppid uint64 Command [16]uint8 - Type uint32 - Argv [1024]uint8 - Retval int32 + Filename [512]uint8 + Args [20480]uint8 + ArgsLen uint64 + ArgsTruncated bool + _ [7]byte Cgroup uint64 AuditSessionId uint32 _ [4]byte @@ -68,10 +70,7 @@ type commandSpecs struct { // // It can be passed ebpf.CollectionSpec.Assign. type commandProgramSpecs struct { - TracepointSyscallsSysEnterExecve *ebpf.ProgramSpec `ebpf:"tracepoint__syscalls__sys_enter_execve"` - TracepointSyscallsSysEnterExecveat *ebpf.ProgramSpec `ebpf:"tracepoint__syscalls__sys_enter_execveat"` - TracepointSyscallsSysExitExecve *ebpf.ProgramSpec `ebpf:"tracepoint__syscalls__sys_exit_execve"` - TracepointSyscallsSysExitExecveat *ebpf.ProgramSpec `ebpf:"tracepoint__syscalls__sys_exit_execveat"` + TracepointSchedSchedProcessExec *ebpf.ProgramSpec `ebpf:"tracepoint__sched__sched_process_exec"` } // commandMapSpecs contains maps before they are loaded into the kernel. @@ -137,18 +136,12 @@ type commandVariables struct { // // It can be passed to loadCommandObjects or ebpf.CollectionSpec.LoadAndAssign. type commandPrograms struct { - TracepointSyscallsSysEnterExecve *ebpf.Program `ebpf:"tracepoint__syscalls__sys_enter_execve"` - TracepointSyscallsSysEnterExecveat *ebpf.Program `ebpf:"tracepoint__syscalls__sys_enter_execveat"` - TracepointSyscallsSysExitExecve *ebpf.Program `ebpf:"tracepoint__syscalls__sys_exit_execve"` - TracepointSyscallsSysExitExecveat *ebpf.Program `ebpf:"tracepoint__syscalls__sys_exit_execveat"` + TracepointSchedSchedProcessExec *ebpf.Program `ebpf:"tracepoint__sched__sched_process_exec"` } func (p *commandPrograms) Close() error { return _CommandClose( - p.TracepointSyscallsSysEnterExecve, - p.TracepointSyscallsSysEnterExecveat, - p.TracepointSyscallsSysExitExecve, - p.TracepointSyscallsSysExitExecveat, + p.TracepointSchedSchedProcessExec, ) } diff --git a/lib/bpf/command_x86_bpfel.o b/lib/bpf/command_x86_bpfel.o index b9053d1501c37..c5990f82736f7 100644 Binary files a/lib/bpf/command_x86_bpfel.o and b/lib/bpf/command_x86_bpfel.o differ diff --git a/lib/bpf/common.go b/lib/bpf/common.go index 1c71222986922..a546486f5fcb6 100644 --- a/lib/bpf/common.go +++ b/lib/bpf/common.go @@ -123,7 +123,7 @@ func IsHostCompatible() error { if err != nil { return trace.Wrap(err) } - minKernelVersion := semver.Version{Major: 5, Minor: 8, Patch: 0} + minKernelVersion := semver.Version{Major: 5, Minor: 10, Patch: 0} if version.LessThan(minKernelVersion) { return trace.BadParameter("incompatible kernel found, minimum supported kernel is %v", minKernelVersion) } diff --git a/lib/bpf/disk.go b/lib/bpf/disk.go index 498e13d63d7bb..347bce5002936 100644 --- a/lib/bpf/disk.go +++ b/lib/bpf/disk.go @@ -23,7 +23,6 @@ package bpf import ( "context" "io" - "runtime" "sync" "github.com/cilium/ebpf" @@ -80,61 +79,12 @@ func startOpen(bufferSize int) (*open, error) { return nil, trace.Wrap(err) } - trs := []struct { - name string - prog *ebpf.Program - }{ - { - name: "sys_enter_openat", - prog: objs.TracepointSyscallsSysEnterOpenat, - }, - { - name: "sys_exit_openat", - prog: objs.TracepointSyscallsSysExitOpenat, - }, - { - name: "sys_enter_openat2", - prog: objs.TracepointSyscallsSysEnterOpenat2, - }, - { - name: "sys_exit_openat2", - prog: objs.TracepointSyscallsSysExitOpenat2, - }, - } - - if runtime.GOARCH != "arm64" { - // creat and open are not implemented on arm64. - trs = append(trs, []struct { - name string - prog *ebpf.Program - }{ - { - name: "sys_enter_creat", - prog: objs.TracepointSyscallsSysEnterCreat, - }, - { - name: "sys_exit_creat", - prog: objs.TracepointSyscallsSysExitCreat, - }, - { - name: "sys_enter_open", - prog: objs.TracepointSyscallsSysEnterOpen, - }, - { - name: "sys_exit_open", - prog: objs.TracepointSyscallsSysExitOpen, - }, - }...) - } - - toClose := make([]io.Closer, 0, len(trs)) - for _, tr := range trs { - tp, err := link.Tracepoint("syscalls", tr.name, tr.prog, nil) - if err != nil { - return nil, trace.Wrap(err, "linking %q tracepoint: %v", tr.name, err) - } - - toClose = append(toClose, tp) + tp, err := link.AttachTracing(link.TracingOptions{ + Program: objs.SecurityFileOpen, + AttachType: ebpf.AttachTraceFEntry, + }) + if err != nil { + return nil, trace.Wrap(err) } eventBuf, err := ringbuf.NewReader(objs.OpenEvents) @@ -148,7 +98,7 @@ func startOpen(bufferSize int) (*open, error) { return &open{ objs: objs, eventBuf: bpfEvents, - toClose: toClose, + toClose: []io.Closer{tp}, lostCounter: lostCtr, }, nil } diff --git a/lib/bpf/disk_arm64_bpfel.go b/lib/bpf/disk_arm64_bpfel.go index 22be5621fc3e7..730413778eb3d 100644 --- a/lib/bpf/disk_arm64_bpfel.go +++ b/lib/bpf/disk_arm64_bpfel.go @@ -21,8 +21,7 @@ type diskDataT struct { Pid uint64 ReturnCode int32 Command [16]uint8 - FilePath [255]uint8 - _ [1]byte + FilePath [4096]uint8 Flags int32 } @@ -68,21 +67,13 @@ type diskSpecs struct { // // It can be passed ebpf.CollectionSpec.Assign. type diskProgramSpecs struct { - TracepointSyscallsSysEnterCreat *ebpf.ProgramSpec `ebpf:"tracepoint__syscalls__sys_enter_creat"` - TracepointSyscallsSysEnterOpen *ebpf.ProgramSpec `ebpf:"tracepoint__syscalls__sys_enter_open"` - TracepointSyscallsSysEnterOpenat *ebpf.ProgramSpec `ebpf:"tracepoint__syscalls__sys_enter_openat"` - TracepointSyscallsSysEnterOpenat2 *ebpf.ProgramSpec `ebpf:"tracepoint__syscalls__sys_enter_openat2"` - TracepointSyscallsSysExitCreat *ebpf.ProgramSpec `ebpf:"tracepoint__syscalls__sys_exit_creat"` - TracepointSyscallsSysExitOpen *ebpf.ProgramSpec `ebpf:"tracepoint__syscalls__sys_exit_open"` - TracepointSyscallsSysExitOpenat *ebpf.ProgramSpec `ebpf:"tracepoint__syscalls__sys_exit_openat"` - TracepointSyscallsSysExitOpenat2 *ebpf.ProgramSpec `ebpf:"tracepoint__syscalls__sys_exit_openat2"` + SecurityFileOpen *ebpf.ProgramSpec `ebpf:"security_file_open"` } // diskMapSpecs contains maps before they are loaded into the kernel. // // It can be passed ebpf.CollectionSpec.Assign. type diskMapSpecs struct { - Infotmp *ebpf.MapSpec `ebpf:"infotmp"` LostCounter *ebpf.MapSpec `ebpf:"lost_counter"` LostDoorbell *ebpf.MapSpec `ebpf:"lost_doorbell"` MonitoredSessionids *ebpf.MapSpec `ebpf:"monitored_sessionids"` @@ -116,7 +107,6 @@ func (o *diskObjects) Close() error { // // It can be passed to loadDiskObjects or ebpf.CollectionSpec.LoadAndAssign. type diskMaps struct { - Infotmp *ebpf.Map `ebpf:"infotmp"` LostCounter *ebpf.Map `ebpf:"lost_counter"` LostDoorbell *ebpf.Map `ebpf:"lost_doorbell"` MonitoredSessionids *ebpf.Map `ebpf:"monitored_sessionids"` @@ -125,7 +115,6 @@ type diskMaps struct { func (m *diskMaps) Close() error { return _DiskClose( - m.Infotmp, m.LostCounter, m.LostDoorbell, m.MonitoredSessionids, @@ -144,26 +133,12 @@ type diskVariables struct { // // It can be passed to loadDiskObjects or ebpf.CollectionSpec.LoadAndAssign. type diskPrograms struct { - TracepointSyscallsSysEnterCreat *ebpf.Program `ebpf:"tracepoint__syscalls__sys_enter_creat"` - TracepointSyscallsSysEnterOpen *ebpf.Program `ebpf:"tracepoint__syscalls__sys_enter_open"` - TracepointSyscallsSysEnterOpenat *ebpf.Program `ebpf:"tracepoint__syscalls__sys_enter_openat"` - TracepointSyscallsSysEnterOpenat2 *ebpf.Program `ebpf:"tracepoint__syscalls__sys_enter_openat2"` - TracepointSyscallsSysExitCreat *ebpf.Program `ebpf:"tracepoint__syscalls__sys_exit_creat"` - TracepointSyscallsSysExitOpen *ebpf.Program `ebpf:"tracepoint__syscalls__sys_exit_open"` - TracepointSyscallsSysExitOpenat *ebpf.Program `ebpf:"tracepoint__syscalls__sys_exit_openat"` - TracepointSyscallsSysExitOpenat2 *ebpf.Program `ebpf:"tracepoint__syscalls__sys_exit_openat2"` + SecurityFileOpen *ebpf.Program `ebpf:"security_file_open"` } func (p *diskPrograms) Close() error { return _DiskClose( - p.TracepointSyscallsSysEnterCreat, - p.TracepointSyscallsSysEnterOpen, - p.TracepointSyscallsSysEnterOpenat, - p.TracepointSyscallsSysEnterOpenat2, - p.TracepointSyscallsSysExitCreat, - p.TracepointSyscallsSysExitOpen, - p.TracepointSyscallsSysExitOpenat, - p.TracepointSyscallsSysExitOpenat2, + p.SecurityFileOpen, ) } diff --git a/lib/bpf/disk_arm64_bpfel.o b/lib/bpf/disk_arm64_bpfel.o index 2fbe3ebf3c426..c171c7dcbd9aa 100644 Binary files a/lib/bpf/disk_arm64_bpfel.o and b/lib/bpf/disk_arm64_bpfel.o differ diff --git a/lib/bpf/disk_x86_bpfel.go b/lib/bpf/disk_x86_bpfel.go index 35181787a0275..40b035cff8a11 100644 --- a/lib/bpf/disk_x86_bpfel.go +++ b/lib/bpf/disk_x86_bpfel.go @@ -21,8 +21,7 @@ type diskDataT struct { Pid uint64 ReturnCode int32 Command [16]uint8 - FilePath [255]uint8 - _ [1]byte + FilePath [4096]uint8 Flags int32 } @@ -68,21 +67,13 @@ type diskSpecs struct { // // It can be passed ebpf.CollectionSpec.Assign. type diskProgramSpecs struct { - TracepointSyscallsSysEnterCreat *ebpf.ProgramSpec `ebpf:"tracepoint__syscalls__sys_enter_creat"` - TracepointSyscallsSysEnterOpen *ebpf.ProgramSpec `ebpf:"tracepoint__syscalls__sys_enter_open"` - TracepointSyscallsSysEnterOpenat *ebpf.ProgramSpec `ebpf:"tracepoint__syscalls__sys_enter_openat"` - TracepointSyscallsSysEnterOpenat2 *ebpf.ProgramSpec `ebpf:"tracepoint__syscalls__sys_enter_openat2"` - TracepointSyscallsSysExitCreat *ebpf.ProgramSpec `ebpf:"tracepoint__syscalls__sys_exit_creat"` - TracepointSyscallsSysExitOpen *ebpf.ProgramSpec `ebpf:"tracepoint__syscalls__sys_exit_open"` - TracepointSyscallsSysExitOpenat *ebpf.ProgramSpec `ebpf:"tracepoint__syscalls__sys_exit_openat"` - TracepointSyscallsSysExitOpenat2 *ebpf.ProgramSpec `ebpf:"tracepoint__syscalls__sys_exit_openat2"` + SecurityFileOpen *ebpf.ProgramSpec `ebpf:"security_file_open"` } // diskMapSpecs contains maps before they are loaded into the kernel. // // It can be passed ebpf.CollectionSpec.Assign. type diskMapSpecs struct { - Infotmp *ebpf.MapSpec `ebpf:"infotmp"` LostCounter *ebpf.MapSpec `ebpf:"lost_counter"` LostDoorbell *ebpf.MapSpec `ebpf:"lost_doorbell"` MonitoredSessionids *ebpf.MapSpec `ebpf:"monitored_sessionids"` @@ -116,7 +107,6 @@ func (o *diskObjects) Close() error { // // It can be passed to loadDiskObjects or ebpf.CollectionSpec.LoadAndAssign. type diskMaps struct { - Infotmp *ebpf.Map `ebpf:"infotmp"` LostCounter *ebpf.Map `ebpf:"lost_counter"` LostDoorbell *ebpf.Map `ebpf:"lost_doorbell"` MonitoredSessionids *ebpf.Map `ebpf:"monitored_sessionids"` @@ -125,7 +115,6 @@ type diskMaps struct { func (m *diskMaps) Close() error { return _DiskClose( - m.Infotmp, m.LostCounter, m.LostDoorbell, m.MonitoredSessionids, @@ -144,26 +133,12 @@ type diskVariables struct { // // It can be passed to loadDiskObjects or ebpf.CollectionSpec.LoadAndAssign. type diskPrograms struct { - TracepointSyscallsSysEnterCreat *ebpf.Program `ebpf:"tracepoint__syscalls__sys_enter_creat"` - TracepointSyscallsSysEnterOpen *ebpf.Program `ebpf:"tracepoint__syscalls__sys_enter_open"` - TracepointSyscallsSysEnterOpenat *ebpf.Program `ebpf:"tracepoint__syscalls__sys_enter_openat"` - TracepointSyscallsSysEnterOpenat2 *ebpf.Program `ebpf:"tracepoint__syscalls__sys_enter_openat2"` - TracepointSyscallsSysExitCreat *ebpf.Program `ebpf:"tracepoint__syscalls__sys_exit_creat"` - TracepointSyscallsSysExitOpen *ebpf.Program `ebpf:"tracepoint__syscalls__sys_exit_open"` - TracepointSyscallsSysExitOpenat *ebpf.Program `ebpf:"tracepoint__syscalls__sys_exit_openat"` - TracepointSyscallsSysExitOpenat2 *ebpf.Program `ebpf:"tracepoint__syscalls__sys_exit_openat2"` + SecurityFileOpen *ebpf.Program `ebpf:"security_file_open"` } func (p *diskPrograms) Close() error { return _DiskClose( - p.TracepointSyscallsSysEnterCreat, - p.TracepointSyscallsSysEnterOpen, - p.TracepointSyscallsSysEnterOpenat, - p.TracepointSyscallsSysEnterOpenat2, - p.TracepointSyscallsSysExitCreat, - p.TracepointSyscallsSysExitOpen, - p.TracepointSyscallsSysExitOpenat, - p.TracepointSyscallsSysExitOpenat2, + p.SecurityFileOpen, ) } diff --git a/lib/bpf/disk_x86_bpfel.o b/lib/bpf/disk_x86_bpfel.o index c503a7205cd12..19d44725cd0a3 100644 Binary files a/lib/bpf/disk_x86_bpfel.o and b/lib/bpf/disk_x86_bpfel.o differ diff --git a/lib/srv/bpf_test.go b/lib/srv/bpf_test.go index a15cd1abb2250..662c2fbded8d7 100644 --- a/lib/srv/bpf_test.go +++ b/lib/srv/bpf_test.go @@ -53,23 +53,17 @@ import ( ) const ( - // the maximum number of arguments that will be emitted in a event, - // including argv[0] - maxArgs = 20 - - // the maximum length of a path, anything longer will be truncated - maxPathLength = 255 - longArgBase = "averylongargument" // number of commands that will be run in parallel during the // stress test test case - stressTestRunCount = 10 + // TODO: bump back up once event flakiness is addressed + stressTestRunCount = 3 ) var ( // the maximum length of a single argument, anything longer will be truncated - maxArgLength = bpf.ArgsCacheSize + maxArgLength = bpf.CommandArgsBufferSize - 4 longArg = strings.Repeat(longArgBase, (maxArgLength/4)/len(longArgBase)) overMaxArg = strings.Repeat(longArgBase, (maxArgLength)/len(longArgBase)+1) @@ -207,20 +201,6 @@ eval $(echo %s | base64 --decode)`, err = os.WriteFile(obfScriptPath, []byte(obfScript), 0o700) require.NoError(t, err) - // Create a slice of arguments over the maximum length. - overMaxArgs := make([]string, maxArgs+3) - for i := range overMaxArgs { - overMaxArgs[i] = strconv.Itoa(i) + overMaxArg - } - atMaxArgs := slices.Clone(overMaxArgs) - for i := range atMaxArgs { - atMaxArgs[i] = atMaxArgs[i][:maxArgLength] - } - maxArgPaths := slices.Clone(atMaxArgs) - for i := range maxArgPaths { - maxArgPaths[i] = maxArgPaths[i][:maxPathLength] - } - // Define the test cases. tests := []struct { name string @@ -242,7 +222,6 @@ eval $(echo %s | base64 --decode)`, }, paths: []string{ "/proc/filesystems", - ".", cmdDir, }, }, @@ -307,7 +286,6 @@ eval $(echo %s | base64 --decode)`, "/etc/nsswitch.conf", "/etc/passwd", "/etc/group", - "/etc/localtime", cmdDir, }, }, @@ -324,7 +302,7 @@ eval $(echo %s | base64 --decode)`, }, paths: []string{ "/etc/bash.bashrc", - cmdDir + string(filepath.Separator), + cmdDir, }, }, { @@ -353,6 +331,7 @@ eval $(echo %s | base64 --decode)`, program: obfScriptName, interpreter: "bash", scriptPath: obfScriptPath, + args: []string{obfScriptPath}, }, }, { @@ -368,7 +347,6 @@ eval $(echo %s | base64 --decode)`, }, paths: []string{ "/proc/filesystems", - ".", }, }, }, @@ -428,7 +406,6 @@ eval $(echo %s | base64 --decode)`, // the argument isn't an existing file on disk expectedFail: true, }, - paths: []string{longArg}, }, }, }, @@ -439,72 +416,10 @@ eval $(echo %s | base64 --decode)`, { cmdInfo: commandInfo{ program: "cat", - args: []string{overMaxArg[:maxArgLength]}, + args: []string{overMaxArg[:maxArgLength], "..."}, // the argument isn't an existing file on disk expectedFail: true, }, - paths: []string{overMaxArg[:maxPathLength]}, - }, - }, - }, - { - name: "max amount of args", - command: "cat " + strings.Repeat(tempFilePath+" ", maxArgs), - eventInfos: []expectedEvents{ - { - cmdInfo: commandInfo{ - program: "cat", - // argv[0] is counted, so we expect MAXARGS-1 args - args: slices.Repeat([]string{tempFilePath}, maxArgs-1), - }, - paths: []string{tempFilePath}, - }, - }, - }, - // TODO(capnspacehook): bpf C code seems to want to add '...' if arguments are truncated but doesn't - { - name: "over max amount of args", - command: "cat " + strings.Repeat(tempFilePath+" ", maxArgs+3), - eventInfos: []expectedEvents{ - { - cmdInfo: commandInfo{ - program: "cat", - // argv[0] is counted, so we expect MAXARGS-1 args - args: slices.Repeat([]string{tempFilePath}, maxArgs-1), - }, - paths: []string{tempFilePath}, - }, - }, - }, - { - name: "max amount of args over max length", - command: "cat " + strings.Join(overMaxArgs[:maxArgs], " "), - eventInfos: []expectedEvents{ - { - cmdInfo: commandInfo{ - program: "cat", - // argv[0] is counted, so we expect MAXARGS-1 args - args: atMaxArgs[:maxArgs-1], - // the arguments aren't existing files on disk - expectedFail: true, - }, - paths: maxArgPaths[:maxArgs], - }, - }, - }, - { - name: "over max amount of args over max length", - command: "cat " + strings.Join(overMaxArgs, " "), - eventInfos: []expectedEvents{ - { - cmdInfo: commandInfo{ - program: "cat", - // argv[0] is counted, so we expect MAXARGS-1 args - args: atMaxArgs[:maxArgs-1], - // the arguments aren't existing files on disk - expectedFail: true, - }, - paths: maxArgPaths, }, }, }, @@ -519,11 +434,9 @@ eval $(echo %s | base64 --decode)`, }, paths: []string{ "/proc/filesystems", - ".", "/etc/nsswitch.conf", "/etc/passwd", "/etc/group", - "/etc/localtime", }, count: stressTestRunCount, }, @@ -1021,6 +934,8 @@ func runCommand(t *testing.T, srv Server, bpfSrv bpf.BPF, command string, expect } func getProgramLibs(t *testing.T, path string, count int) []countedValue[string] { + t.Helper() + elfFile, err := elf.Open(path) require.NoError(t, err) importedLibs, err := elfFile.ImportedLibraries() @@ -1057,12 +972,31 @@ func checkCommandEvent(t *testing.T, e *apievents.SessionCommand, cmdPaths map[s // checkDiskEvent returns true if the given disk event matches an // expected disk event. If the event is an expected event, the matched // path will be removed from the expected paths map. -func checkDiskEvent(t *testing.T, e *apievents.SessionDisk, expectedPaths map[string][]countedValue[string], matchBase bool) bool { +func checkDiskEvent(t *testing.T, e *apievents.SessionDisk, expectedPaths map[string][]countedValue[string], isLib bool) bool { t.Helper() path := e.Path - if matchBase { - path = filepath.Base(e.Path) + // We have the basenames of libraries to check against, and on top + // of that some libraries are symlinks to other libraries; so we + // check if the basename of the path contains the expected library + // as a prefix. For example if we expect to see librtmp.so.1 and + // it's a symlink to librtmp.so.1.2.3 then using this logic the path + // /usr/lib/librtmp.so.1.2.3 will be considered a match. + if isLib { + expectedLibs, ok := expectedPaths[e.BPFMetadata.Program] + if !ok { + return false + } + + lib := filepath.Base(e.Path) + for i := range expectedLibs { + if strings.HasPrefix(lib, expectedLibs[i].value) { + expectedLibs[i].count-- + expectedPaths[e.BPFMetadata.Program] = expectedLibs + t.Log("disk event is expected!") + return true + } + } } if checkEvent(e.BPFMetadata.Program, path, expectedPaths) {