diff options
| author | Paul Buetow <paul@buetow.org> | 2026-05-21 08:16:08 +0300 |
|---|---|---|
| committer | Paul Buetow <paul@buetow.org> | 2026-05-21 08:16:08 +0300 |
| commit | be8735fe701f7398c19c17c394f4827614eab875 (patch) | |
| tree | aba59890563edb6e03f2eb82fee5d89b49fa2c81 /internal/tracepoints | |
| parent | 3a5706f21d30258577a5934efb93c400dad723db (diff) | |
p7 add attach-time trace dimension gating
Diffstat (limited to 'internal/tracepoints')
| -rw-r--r-- | internal/tracepoints/dimension_selector.go | 219 | ||||
| -rw-r--r-- | internal/tracepoints/dimension_selector_test.go | 147 | ||||
| -rw-r--r-- | internal/tracepoints/generated_tracepoints.go | 740 | ||||
| -rw-r--r-- | internal/tracepoints/selector.go | 46 | ||||
| -rw-r--r-- | internal/tracepoints/selector_test.go | 14 |
5 files changed, 1162 insertions, 4 deletions
diff --git a/internal/tracepoints/dimension_selector.go b/internal/tracepoints/dimension_selector.go new file mode 100644 index 0000000..22a70fb --- /dev/null +++ b/internal/tracepoints/dimension_selector.go @@ -0,0 +1,219 @@ +package tracepoints + +import ( + "fmt" + "sort" + "strings" + + "ior/internal/types" +) + +// DimensionSelectorConfig holds attach-time syscall-dimension selection inputs. +// Each field accepts comma-separated values. +type DimensionSelectorConfig struct { + TraceFamilies string + TraceKinds string + TraceSyscalls string + NoTraceFamilies string + NoTraceKinds string + NoTraceSyscalls string +} + +// ParseSelectorWithDimensions compiles regex-based attach/exclude filters and +// applies attach-time syscall dimension gating. +func ParseSelectorWithDimensions(attach, exclude string, dims DimensionSelectorConfig) (Selector, error) { + sel, err := ParseSelector(attach, exclude) + if err != nil { + return Selector{}, err + } + + allow, err := buildAllowedSyscalls(dims) + if err != nil { + return Selector{}, err + } + sel.RestrictSyscalls = true + sel.Syscalls = allow + return sel, nil +} + +func buildAllowedSyscalls(dims DimensionSelectorConfig) (map[string]struct{}, error) { + knownSyscalls := allKnownSyscalls() + knownKinds := allKnownKinds() + + includeFamilies, familyFilterProvided, err := parseFamiliesCSV(dims.TraceFamilies) + if err != nil { + return nil, err + } + includeKinds, kindFilterProvided, err := parseKindsCSV(dims.TraceKinds, knownKinds) + if err != nil { + return nil, err + } + includeSyscalls, syscallFilterProvided, err := parseSyscallsCSV(dims.TraceSyscalls, knownSyscalls) + if err != nil { + return nil, err + } + + allow := make(map[string]struct{}) + hasPositive := familyFilterProvided || kindFilterProvided || syscallFilterProvided + if hasPositive { + for syscall, family := range syscallFamilies { + if _, ok := includeFamilies[family]; ok { + allow[syscall] = struct{}{} + } + } + for syscall, kind := range syscallKinds { + if _, ok := includeKinds[kind]; ok { + allow[syscall] = struct{}{} + } + } + for syscall := range includeSyscalls { + allow[syscall] = struct{}{} + } + } else { + // Backward compatibility default: keep existing file-I/O coverage on and + // leave newly-expanded non-IO families disabled unless explicitly opted in. + for syscall, family := range syscallFamilies { + if family == string(types.FamilyFS) { + allow[syscall] = struct{}{} + } + } + } + + excludeFamilies, _, err := parseFamiliesCSV(dims.NoTraceFamilies) + if err != nil { + return nil, err + } + excludeKinds, _, err := parseKindsCSV(dims.NoTraceKinds, knownKinds) + if err != nil { + return nil, err + } + excludeSyscalls, _, err := parseSyscallsCSV(dims.NoTraceSyscalls, knownSyscalls) + if err != nil { + return nil, err + } + + for syscall := range allow { + if _, ok := excludeSyscalls[syscall]; ok { + delete(allow, syscall) + continue + } + if family, ok := syscallFamilies[syscall]; ok { + if _, excluded := excludeFamilies[family]; excluded { + delete(allow, syscall) + continue + } + } + if kind, ok := syscallKinds[syscall]; ok { + if _, excluded := excludeKinds[kind]; excluded { + delete(allow, syscall) + } + } + } + + return allow, nil +} + +func parseFamiliesCSV(raw string) (map[string]struct{}, bool, error) { + values, provided := splitCSV(raw) + if !provided { + return map[string]struct{}{}, false, nil + } + out := make(map[string]struct{}, len(values)) + for _, value := range values { + family, ok := types.ParseSyscallFamily(value) + if !ok { + return nil, false, fmt.Errorf("invalid syscall family in trace selector: %q", value) + } + out[string(family)] = struct{}{} + } + return out, true, nil +} + +func parseKindsCSV(raw string, knownKinds map[string]struct{}) (map[string]struct{}, bool, error) { + values, provided := splitCSV(raw) + if !provided { + return map[string]struct{}{}, false, nil + } + out := make(map[string]struct{}, len(values)) + for _, value := range values { + kind := normalizeKind(value) + if _, ok := knownKinds[kind]; !ok { + return nil, false, fmt.Errorf("invalid syscall kind in trace selector: %q", value) + } + out[kind] = struct{}{} + } + return out, true, nil +} + +func parseSyscallsCSV(raw string, knownSyscalls map[string]struct{}) (map[string]struct{}, bool, error) { + values, provided := splitCSV(raw) + if !provided { + return map[string]struct{}{}, false, nil + } + out := make(map[string]struct{}, len(values)) + for _, value := range values { + syscall := strings.ToLower(strings.TrimSpace(value)) + if _, ok := knownSyscalls[syscall]; !ok { + return nil, false, fmt.Errorf("invalid syscall in trace selector: %q", value) + } + out[syscall] = struct{}{} + } + return out, true, nil +} + +func splitCSV(raw string) ([]string, bool) { + raw = strings.TrimSpace(raw) + if raw == "" { + return nil, false + } + parts := strings.Split(raw, ",") + values := make([]string, 0, len(parts)) + for _, part := range parts { + part = strings.TrimSpace(part) + if part == "" { + continue + } + values = append(values, part) + } + if len(values) == 0 { + return nil, false + } + return values, true +} + +func normalizeKind(raw string) string { + normalized := strings.ToLower(strings.TrimSpace(raw)) + normalized = strings.ReplaceAll(normalized, "_", "-") + return normalized +} + +func allKnownSyscalls() map[string]struct{} { + out := make(map[string]struct{}, len(syscallFamilies)) + for syscall := range syscallFamilies { + out[syscall] = struct{}{} + } + return out +} + +func allKnownKinds() map[string]struct{} { + out := make(map[string]struct{}) + for _, kind := range syscallKinds { + if kind == "" { + continue + } + out[kind] = struct{}{} + } + return out +} + +// KnownKinds returns the sorted, normalized attach-time kind names accepted by +// -trace-kinds and -no-trace-kinds. +func KnownKinds() []string { + kindSet := allKnownKinds() + kinds := make([]string, 0, len(kindSet)) + for kind := range kindSet { + kinds = append(kinds, kind) + } + sort.Strings(kinds) + return kinds +} diff --git a/internal/tracepoints/dimension_selector_test.go b/internal/tracepoints/dimension_selector_test.go new file mode 100644 index 0000000..cd7b0f8 --- /dev/null +++ b/internal/tracepoints/dimension_selector_test.go @@ -0,0 +1,147 @@ +package tracepoints + +import ( + "strings" + "testing" +) + +func TestParseSelectorWithDimensionsDefaultFSOnly(t *testing.T) { + sel, err := ParseSelectorWithDimensions("", "", DimensionSelectorConfig{}) + if err != nil { + t.Fatalf("unexpected error: %v", err) + } + if !sel.ShouldAttach("sys_enter_openat") { + t.Fatal("expected FS syscall openat to be attached by default") + } + if sel.ShouldAttach("sys_enter_nanosleep") { + t.Fatal("expected non-FS syscall nanosleep to be excluded by default") + } +} + +func TestParseSelectorWithDimensionsFamilyOnly(t *testing.T) { + sel, err := ParseSelectorWithDimensions("", "", DimensionSelectorConfig{ + TraceFamilies: "Time", + }) + if err != nil { + t.Fatalf("unexpected error: %v", err) + } + if !sel.ShouldAttach("sys_enter_nanosleep") { + t.Fatal("expected nanosleep to be attached when Time family is enabled") + } + if sel.ShouldAttach("sys_enter_openat") { + t.Fatal("expected openat to be excluded when only Time family is enabled") + } +} + +func TestParseSelectorWithDimensionsKindOnly(t *testing.T) { + sel, err := ParseSelectorWithDimensions("", "", DimensionSelectorConfig{ + TraceKinds: "sleep", + }) + if err != nil { + t.Fatalf("unexpected error: %v", err) + } + if !sel.ShouldAttach("sys_enter_nanosleep") { + t.Fatal("expected nanosleep to be attached for sleep kind") + } + if sel.ShouldAttach("sys_enter_openat") { + t.Fatal("expected openat to be excluded when only sleep kind is enabled") + } +} + +func TestParseSelectorWithDimensionsSyscallOnly(t *testing.T) { + sel, err := ParseSelectorWithDimensions("", "", DimensionSelectorConfig{ + TraceSyscalls: "openat", + }) + if err != nil { + t.Fatalf("unexpected error: %v", err) + } + if !sel.ShouldAttach("sys_enter_openat") || !sel.ShouldAttach("sys_exit_openat") { + t.Fatal("expected both openat enter/exit tracepoints to be attached") + } + if sel.ShouldAttach("sys_enter_write") { + t.Fatal("expected write to be excluded when only openat is selected") + } +} + +func TestParseSelectorWithDimensionsUnionSemantics(t *testing.T) { + sel, err := ParseSelectorWithDimensions("", "", DimensionSelectorConfig{ + TraceFamilies: "Time", + TraceSyscalls: "openat", + }) + if err != nil { + t.Fatalf("unexpected error: %v", err) + } + if !sel.ShouldAttach("sys_enter_openat") { + t.Fatal("expected openat from syscall selector") + } + if !sel.ShouldAttach("sys_enter_nanosleep") { + t.Fatal("expected nanosleep from family selector") + } +} + +func TestParseSelectorWithDimensionsExclusionsOverridePositives(t *testing.T) { + sel, err := ParseSelectorWithDimensions("", "", DimensionSelectorConfig{ + TraceFamilies: "FS", + NoTraceSyscalls: "openat", + }) + if err != nil { + t.Fatalf("unexpected error: %v", err) + } + if sel.ShouldAttach("sys_enter_openat") { + t.Fatal("expected openat to be excluded by -no-trace-syscalls") + } + if !sel.ShouldAttach("sys_enter_read") { + t.Fatal("expected other FS syscall (read) to remain attached") + } +} + +func TestParseSelectorWithDimensionsRegexStillApplies(t *testing.T) { + sel, err := ParseSelectorWithDimensions("^sys_enter_openat$,^sys_exit_openat$", "", DimensionSelectorConfig{ + TraceFamilies: "FS", + }) + if err != nil { + t.Fatalf("unexpected error: %v", err) + } + if !sel.ShouldAttach("sys_enter_openat") { + t.Fatal("expected openat to pass regex+dimension filters") + } + if sel.ShouldAttach("sys_enter_read") { + t.Fatal("expected read to fail regex attach filters") + } +} + +func TestParseSelectorWithDimensionsRejectsInvalidFamily(t *testing.T) { + _, err := ParseSelectorWithDimensions("", "", DimensionSelectorConfig{ + TraceFamilies: "Nope", + }) + if err == nil { + t.Fatal("expected error") + } + if !strings.Contains(err.Error(), "invalid syscall family") { + t.Fatalf("unexpected error: %v", err) + } +} + +func TestParseSelectorWithDimensionsRejectsInvalidKind(t *testing.T) { + _, err := ParseSelectorWithDimensions("", "", DimensionSelectorConfig{ + TraceKinds: "not-a-kind", + }) + if err == nil { + t.Fatal("expected error") + } + if !strings.Contains(err.Error(), "invalid syscall kind") { + t.Fatalf("unexpected error: %v", err) + } +} + +func TestParseSelectorWithDimensionsRejectsInvalidSyscall(t *testing.T) { + _, err := ParseSelectorWithDimensions("", "", DimensionSelectorConfig{ + TraceSyscalls: "not_a_syscall", + }) + if err == nil { + t.Fatal("expected error") + } + if !strings.Contains(err.Error(), "invalid syscall in trace selector") { + t.Fatalf("unexpected error: %v", err) + } +} diff --git a/internal/tracepoints/generated_tracepoints.go b/internal/tracepoints/generated_tracepoints.go index 8277cbf..11913c8 100644 --- a/internal/tracepoints/generated_tracepoints.go +++ b/internal/tracepoints/generated_tracepoints.go @@ -737,3 +737,743 @@ var List = []string{ "sys_enter_rt_sigreturn", "sys_exit_rt_sigreturn", } + +var syscallFamilies = map[string]string{ + "accept": "Network", + "accept4": "Network", + "access": "FS", + "acct": "Misc", + "add_key": "Security", + "adjtimex": "Misc", + "alarm": "Misc", + "arch_prctl": "Process", + "bind": "Network", + "bpf": "Security", + "brk": "Memory", + "cachestat": "FS", + "capget": "Security", + "capset": "Security", + "chdir": "FS", + "chmod": "FS", + "chown": "FS", + "chroot": "FS", + "clock_adjtime": "Time", + "clock_getres": "Time", + "clock_gettime": "Time", + "clock_nanosleep": "Time", + "clock_settime": "Time", + "clone": "Process", + "clone3": "Process", + "close": "FS", + "close_range": "FS", + "connect": "Network", + "copy_file_range": "FS", + "creat": "FS", + "delete_module": "Security", + "dup": "FS", + "dup2": "FS", + "dup3": "FS", + "epoll_create": "Polling", + "epoll_create1": "Polling", + "epoll_ctl": "Polling", + "epoll_pwait": "Polling", + "epoll_pwait2": "Polling", + "epoll_wait": "Polling", + "eventfd": "IPC", + "eventfd2": "IPC", + "execve": "Process", + "execveat": "Process", + "exit": "Process", + "exit_group": "Process", + "faccessat": "FS", + "faccessat2": "FS", + "fadvise64": "FS", + "fallocate": "FS", + "fanotify_init": "Misc", + "fanotify_mark": "Misc", + "fchdir": "FS", + "fchmod": "FS", + "fchmodat": "FS", + "fchmodat2": "FS", + "fchown": "FS", + "fchownat": "FS", + "fcntl": "FS", + "fdatasync": "FS", + "fgetxattr": "FS", + "file_getattr": "Misc", + "file_setattr": "Misc", + "finit_module": "Security", + "flistxattr": "FS", + "flock": "FS", + "fork": "Process", + "fremovexattr": "FS", + "fsconfig": "FS", + "fsetxattr": "FS", + "fsmount": "FS", + "fsopen": "FS", + "fspick": "FS", + "fstatfs": "FS", + "fsync": "FS", + "ftruncate": "FS", + "futex": "Misc", + "futex_requeue": "Misc", + "futex_wait": "Misc", + "futex_waitv": "Misc", + "futex_wake": "Misc", + "futimesat": "FS", + "get_mempolicy": "Security", + "get_robust_list": "Misc", + "getcpu": "Misc", + "getcwd": "FS", + "getdents": "FS", + "getdents64": "FS", + "getegid": "Process", + "geteuid": "Process", + "getgid": "Process", + "getgroups": "Process", + "getitimer": "Time", + "getpeername": "Network", + "getpgid": "Process", + "getpgrp": "Process", + "getpid": "Process", + "getppid": "Process", + "getpriority": "Process", + "getrandom": "Security", + "getresgid": "Process", + "getresuid": "Process", + "getrlimit": "Process", + "getrusage": "Process", + "getsid": "Process", + "getsockname": "Network", + "getsockopt": "Network", + "gettid": "Process", + "gettimeofday": "Time", + "getuid": "Process", + "getxattr": "FS", + "getxattrat": "FS", + "init_module": "Security", + "inotify_add_watch": "IPC", + "inotify_init": "IPC", + "inotify_init1": "IPC", + "inotify_rm_watch": "IPC", + "io_cancel": "AIO", + "io_destroy": "AIO", + "io_getevents": "AIO", + "io_pgetevents": "AIO", + "io_setup": "AIO", + "io_submit": "AIO", + "io_uring_enter": "AIO", + "io_uring_register": "AIO", + "io_uring_setup": "AIO", + "ioctl": "FS", + "ioperm": "Misc", + "iopl": "Misc", + "ioprio_get": "Misc", + "ioprio_set": "Misc", + "kcmp": "Process", + "kexec_file_load": "Security", + "kexec_load": "Misc", + "keyctl": "Security", + "kill": "Signals", + "landlock_add_rule": "Security", + "landlock_create_ruleset": "Security", + "landlock_restrict_self": "Security", + "lchown": "FS", + "lgetxattr": "FS", + "link": "FS", + "linkat": "FS", + "listen": "Network", + "listmount": "FS", + "listns": "FS", + "listxattr": "FS", + "listxattrat": "FS", + "llistxattr": "FS", + "lremovexattr": "FS", + "lseek": "FS", + "lsetxattr": "FS", + "lsm_get_self_attr": "Misc", + "lsm_list_modules": "Misc", + "lsm_set_self_attr": "Misc", + "madvise": "Memory", + "map_shadow_stack": "Memory", + "mbind": "Memory", + "membarrier": "Memory", + "memfd_create": "IPC", + "memfd_secret": "IPC", + "migrate_pages": "Memory", + "mincore": "Memory", + "mkdir": "FS", + "mkdirat": "FS", + "mknod": "FS", + "mknodat": "FS", + "mlock": "Memory", + "mlock2": "Memory", + "mlockall": "Memory", + "mmap": "Memory", + "modify_ldt": "Misc", + "mount": "FS", + "mount_setattr": "FS", + "move_mount": "FS", + "move_pages": "Memory", + "mprotect": "Memory", + "mq_getsetattr": "IPC", + "mq_notify": "IPC", + "mq_open": "IPC", + "mq_timedreceive": "IPC", + "mq_timedsend": "IPC", + "mq_unlink": "IPC", + "mremap": "Memory", + "mseal": "Memory", + "msgctl": "IPC", + "msgget": "IPC", + "msgrcv": "IPC", + "msgsnd": "IPC", + "msync": "FS", + "munlock": "Memory", + "munlockall": "Memory", + "munmap": "Memory", + "name_to_handle_at": "FS", + "nanosleep": "Time", + "newfstat": "FS", + "newfstatat": "FS", + "newlstat": "FS", + "newstat": "FS", + "newuname": "Misc", + "open": "FS", + "open_by_handle_at": "FS", + "open_tree": "FS", + "open_tree_attr": "FS", + "openat": "FS", + "openat2": "FS", + "pause": "Signals", + "perf_event_open": "Security", + "personality": "Process", + "pidfd_getfd": "IPC", + "pidfd_open": "IPC", + "pidfd_send_signal": "IPC", + "pipe": "IPC", + "pipe2": "IPC", + "pivot_root": "Process", + "pkey_alloc": "Memory", + "pkey_free": "Memory", + "pkey_mprotect": "Memory", + "poll": "Polling", + "ppoll": "Polling", + "prctl": "Process", + "pread64": "FS", + "preadv": "FS", + "preadv2": "FS", + "prlimit64": "Process", + "process_madvise": "Memory", + "process_mrelease": "Memory", + "process_vm_readv": "Memory", + "process_vm_writev": "Memory", + "pselect6": "Polling", + "ptrace": "Security", + "pwrite64": "FS", + "pwritev": "FS", + "pwritev2": "FS", + "quotactl": "FS", + "quotactl_fd": "FS", + "read": "FS", + "readahead": "FS", + "readlink": "FS", + "readlinkat": "FS", + "readv": "FS", + "reboot": "Process", + "recvfrom": "Network", + "recvmmsg": "Network", + "recvmsg": "Network", + "remap_file_pages": "Memory", + "removexattr": "FS", + "removexattrat": "FS", + "rename": "FS", + "renameat": "FS", + "renameat2": "FS", + "request_key": "Security", + "restart_syscall": "Process", + "rmdir": "FS", + "rseq": "Misc", + "rt_sigaction": "Signals", + "rt_sigpending": "Signals", + "rt_sigprocmask": "Signals", + "rt_sigqueueinfo": "Signals", + "rt_sigreturn": "Signals", + "rt_sigsuspend": "Signals", + "rt_sigtimedwait": "Signals", + "rt_tgsigqueueinfo": "Signals", + "sched_get_priority_max": "Sched", + "sched_get_priority_min": "Sched", + "sched_getaffinity": "Sched", + "sched_getattr": "Sched", + "sched_getparam": "Sched", + "sched_getscheduler": "Sched", + "sched_rr_get_interval": "Sched", + "sched_setaffinity": "Sched", + "sched_setattr": "Sched", + "sched_setparam": "Sched", + "sched_setscheduler": "Sched", + "sched_yield": "Sched", + "seccomp": "Security", + "select": "Polling", + "semctl": "IPC", + "semget": "IPC", + "semop": "IPC", + "semtimedop": "IPC", + "sendfile64": "Network", + "sendmmsg": "Network", + "sendmsg": "Network", + "sendto": "Network", + "set_mempolicy": "Memory", + "set_mempolicy_home_node": "Memory", + "set_robust_list": "Misc", + "set_tid_address": "Process", + "setdomainname": "Misc", + "setfsgid": "Process", + "setfsuid": "Process", + "setgid": "Process", + "setgroups": "Process", + "sethostname": "Misc", + "setitimer": "Time", + "setns": "Process", + "setpgid": "Process", + "setpriority": "Process", + "setregid": "Process", + "setresgid": "Process", + "setresuid": "Process", + "setreuid": "Process", + "setrlimit": "Process", + "setsid": "Process", + "setsockopt": "Network", + "settimeofday": "Time", + "setuid": "Process", + "setxattr": "FS", + "setxattrat": "FS", + "shmat": "IPC", + "shmctl": "IPC", + "shmdt": "IPC", + "shmget": "IPC", + "shutdown": "Network", + "sigaltstack": "Signals", + "signalfd": "IPC", + "signalfd4": "IPC", + "socket": "Network", + "socketpair": "Network", + "splice": "Network", + "statfs": "FS", + "statmount": "FS", + "statx": "FS", + "swapoff": "FS", + "swapon": "FS", + "symlink": "FS", + "symlinkat": "FS", + "sync": "FS", + "sync_file_range": "FS", + "syncfs": "FS", + "sysfs": "Misc", + "sysinfo": "Misc", + "syslog": "Misc", + "tee": "Network", + "tgkill": "Signals", + "time": "Time", + "timer_create": "Time", + "timer_delete": "Time", + "timer_getoverrun": "Time", + "timer_gettime": "Time", + "timer_settime": "Time", + "timerfd_create": "IPC", + "timerfd_gettime": "IPC", + "timerfd_settime": "IPC", + "times": "Time", + "tkill": "Signals", + "truncate": "FS", + "umask": "Process", + "umount": "FS", + "unlink": "FS", + "unlinkat": "FS", + "unshare": "Process", + "uprobe": "Misc", + "uretprobe": "Misc", + "userfaultfd": "IPC", + "ustat": "FS", + "utime": "Misc", + "utimensat": "FS", + "utimes": "Misc", + "vfork": "Process", + "vhangup": "Process", + "vmsplice": "Misc", + "wait4": "Process", + "waitid": "Process", + "write": "FS", + "writev": "FS", +} + +var syscallKinds = map[string]string{ + "accept": "accept", + "accept4": "accept", + "access": "path", + "acct": "null", + "add_key": "keyctl", + "adjtimex": "null", + "alarm": "null", + "arch_prctl": "null", + "bind": "fd", + "bpf": "null", + "brk": "null", + "cachestat": "fd", + "capget": "null", + "capset": "null", + "chdir": "path", + "chmod": "path", + "chown": "path", + "chroot": "path", + "clock_adjtime": "null", + "clock_getres": "null", + "clock_gettime": "null", + "clock_nanosleep": "sleep", + "clock_settime": "null", + "clone": "null", + "clone3": "null", + "close": "fd", + "close_range": "fd", + "connect": "fd", + "copy_file_range": "fd", + "creat": "path", + "delete_module": "null", + "dup": "fd", + "dup2": "fd", + "dup3": "dup3", + "epoll_create": "null", + "epoll_create1": "null", + "epoll_ctl": "epoll-ctl", + "epoll_pwait": "fd", + "epoll_pwait2": "fd", + "epoll_wait": "fd", + "eventfd": "eventfd", + "eventfd2": "eventfd", + "execve": "exec", + "execveat": "exec", + "exit": "null", + "exit_group": "null", + "faccessat": "path", + "faccessat2": "path", + "fadvise64": "fd", + "fallocate": "fd", + "fanotify_init": "null", + "fanotify_mark": "path", + "fchdir": "fd", + "fchmod": "fd", + "fchmodat": "path", + "fchmodat2": "path", + "fchown": "fd", + "fchownat": "path", + "fcntl": "fcntl", + "fdatasync": "fd", + "fgetxattr": "fd", + "file_getattr": "path", + "file_setattr": "path", + "finit_module": "fd", + "flistxattr": "fd", + "flock": "fd", + "fork": "null", + "fremovexattr": "fd", + "fsconfig": "fd", + "fsetxattr": "fd", + "fsmount": "eventfd", + "fsopen": "null", + "fspick": "path", + "fstatfs": "fd", + "fsync": "fd", + "ftruncate": "fd", + "futex": "null", + "futex_requeue": "null", + "futex_wait": "null", + "futex_waitv": "null", + "futex_wake": "null", + "futimesat": "path", + "get_mempolicy": "null", + "get_robust_list": "null", + "getcpu": "null", + "getcwd": "null", + "getdents": "fd", + "getdents64": "fd", + "getegid": "null", + "geteuid": "null", + "getgid": "null", + "getgroups": "null", + "getitimer": "null", + "getpeername": "fd", + "getpgid": "null", + "getpgrp": "null", + "getpid": "null", + "getppid": "null", + "getpriority": "null", + "getrandom": "null", + "getresgid": "null", + "getresuid": "null", + "getrlimit": "null", + "getrusage": "null", + "getsid": "null", + "getsockname": "fd", + "getsockopt": "fd", + "gettid": "null", + "gettimeofday": "null", + "getuid": "null", + "getxattr": "path", + "getxattrat": "path", + "init_module": "null", + "inotify_add_watch": "fd", + "inotify_init": "null", + "inotify_init1": "null", + "inotify_rm_watch": "fd", + "io_cancel": "null", + "io_destroy": "null", + "io_getevents": "null", + "io_pgetevents": "null", + "io_setup": "null", + "io_submit": "null", + "io_uring_enter": "fd", + "io_uring_register": "fd", + "io_uring_setup": "null", + "ioctl": "fd", + "ioperm": "null", + "iopl": "null", + "ioprio_get": "null", + "ioprio_set": "null", + "kcmp": "null", + "kexec_file_load": "null", + "kexec_load": "null", + "keyctl": "keyctl", + "kill": "null", + "landlock_add_rule": "null", + "landlock_create_ruleset": "null", + "landlock_restrict_self": "null", + "lchown": "path", + "lgetxattr": "path", + "link": "name", + "linkat": "name", + "listen": "fd", + "listmount": "null", + "listns": "null", + "listxattr": "path", + "listxattrat": "path", + "llistxattr": "path", + "lremovexattr": "path", + "lseek": "fd", + "lsetxattr": "path", + "lsm_get_self_attr": "null", + "lsm_list_modules": "null", + "lsm_set_self_attr": "null", + "madvise": "null", + "map_shadow_stack": "null", + "mbind": "null", + "membarrier": "null", + "memfd_create": "null", + "memfd_secret": "null", + "migrate_pages": "null", + "mincore": "null", + "mkdir": "path", + "mkdirat": "path", + "mknod": "path", + "mknodat": "path", + "mlock": "null", + "mlock2": "null", + "mlockall": "null", + "mmap": "fd", + "modify_ldt": "null", + "mount": "path", + "mount_setattr": "path", + "move_mount": "two-fd", + "move_pages": "null", + "mprotect": "null", + "mq_getsetattr": "fd", + "mq_notify": "fd", + "mq_open": "open", + "mq_timedreceive": "fd", + "mq_timedsend": "fd", + "mq_unlink": "path", + "mremap": "mem", + "mseal": "null", + "msgctl": "null", + "msgget": "null", + "msgrcv": "null", + "msgsnd": "null", + "msync": "null", + "munlock": "null", + "munlockall": "null", + "munmap": "mem", + "name_to_handle_at": "path", + "nanosleep": "sleep", + "newfstat": "fd", + "newfstatat": "path", + "newlstat": "path", + "newstat": "path", + "newuname": "null", + "open": "open", + "open_by_handle_at": "open-by-handle-at", + "open_tree": "open", + "open_tree_attr": "open", + "openat": "open", + "openat2": "open", + "pause": "null", + "perf_event_open": "perf-open", + "personality": "null", + "pidfd_getfd": "fd", + "pidfd_open": "null", + "pidfd_send_signal": "null", + "pipe": "pipe", + "pipe2": "pipe", + "pivot_root": "path", + "pkey_alloc": "null", + "pkey_free": "null", + "pkey_mprotect": "null", + "poll": "poll", + "ppoll": "poll", + "prctl": "null", + "pread64": "fd", + "preadv": "fd", + "preadv2": "fd", + "prlimit64": "null", + "process_madvise": "null", + "process_mrelease": "null", + "process_vm_readv": "null", + "process_vm_writev": "null", + "pselect6": "poll", + "ptrace": "ptrace", + "pwrite64": "fd", + "pwritev": "fd", + "pwritev2": "fd", + "quotactl": "path", + "quotactl_fd": "fd", + "read": "fd", + "readahead": "fd", + "readlink": "path", + "readlinkat": "path", + "readv": "fd", + "reboot": "null", + "recvfrom": "fd", + "recvmmsg": "fd", + "recvmsg": "fd", + "remap_file_pages": "null", + "removexattr": "path", + "removexattrat": "path", + "rename": "name", + "renameat": "name", + "renameat2": "name", + "request_key": "keyctl", + "restart_syscall": "null", + "rmdir": "path", + "rseq": "null", + "rt_sigaction": "null", + "rt_sigpending": "null", + "rt_sigprocmask": "null", + "rt_sigqueueinfo": "null", + "rt_sigreturn": "null", + "rt_sigsuspend": "null", + "rt_sigtimedwait": "null", + "rt_tgsigqueueinfo": "null", + "sched_get_priority_max": "null", + "sched_get_priority_min": "null", + "sched_getaffinity": "null", + "sched_getattr": "null", + "sched_getparam": "null", + "sched_getscheduler": "null", + "sched_rr_get_interval": "null", + "sched_setaffinity": "null", + "sched_setattr": "null", + "sched_setparam": "null", + "sched_setscheduler": "null", + "sched_yield": "null", + "seccomp": "null", + "select": "poll", + "semctl": "null", + "semget": "null", + "semop": "null", + "semtimedop": "null", + "sendfile64": "null", + "sendmmsg": "fd", + "sendmsg": "fd", + "sendto": "fd", + "set_mempolicy": "null", + "set_mempolicy_home_node": "null", + "set_robust_list": "null", + "set_tid_address": "null", + "setdomainname": "null", + "setfsgid": "null", + "setfsuid": "null", + "setgid": "null", + "setgroups": "null", + "sethostname": "null", + "setitimer": "null", + "setns": "fd", + "setpgid": "null", + "setpriority": "null", + "setregid": "null", + "setresgid": "null", + "setresuid": "null", + "setreuid": "null", + "setrlimit": "null", + "setsid": "null", + "setsockopt": "fd", + "settimeofday": "null", + "setuid": "null", + "setxattr": "path", + "setxattrat": "path", + "shmat": "null", + "shmctl": "null", + "shmdt": "null", + "shmget": "null", + "shutdown": "fd", + "sigaltstack": "null", + "signalfd": "null", + "signalfd4": "null", + "socket": "socket", + "socketpair": "socketpair", + "splice": "null", + "statfs": "path", + "statmount": "null", + "statx": "path", + "swapoff": "path", + "swapon": "path", + "symlink": "name", + "symlinkat": "name", + "sync": "null", + "sync_file_range": "fd", + "syncfs": "fd", + "sysfs": "null", + "sysinfo": "null", + "syslog": "null", + "tee": "null", + "tgkill": "null", + "time": "null", + "timer_create": "null", + "timer_delete": "null", + "timer_getoverrun": "null", + "timer_gettime": "null", + "timer_settime": "null", + "timerfd_create": "null", + "timerfd_gettime": "null", + "timerfd_settime": "null", + "times": "null", + "tkill": "null", + "truncate": "path", + "umask": "null", + "umount": "path", + "unlink": "path", + "unlinkat": "path", + "unshare": "null", + "uprobe": "null", + "uretprobe": "null", + "userfaultfd": "null", + "ustat": "null", + "utime": "path", + "utimensat": "path", + "utimes": "path", + "vfork": "null", + "vhangup": "null", + "vmsplice": "fd", + "wait4": "null", + "waitid": "null", + "write": "fd", + "writev": "fd", +} diff --git a/internal/tracepoints/selector.go b/internal/tracepoints/selector.go index af2f39e..91df58f 100644 --- a/internal/tracepoints/selector.go +++ b/internal/tracepoints/selector.go @@ -2,6 +2,7 @@ package tracepoints import ( "fmt" + "maps" "regexp" "slices" "strings" @@ -18,6 +19,12 @@ type Selector struct { // Exclude is the list of compiled regexes that suppress specific // tracepoints even when they match the Attach list. Exclude []*regexp.Regexp + // Syscalls optionally restricts attach to an explicit syscall allowlist. + // Keys are bare syscall names (for example "openat", not "sys_enter_openat"). + // When RestrictSyscalls is true, only entries in this map are attached. + Syscalls map[string]struct{} + // RestrictSyscalls gates whether Syscalls should be enforced. + RestrictSyscalls bool } // ParseSelector parses the comma-separated regex strings for the -tps and @@ -65,11 +72,27 @@ func (s Selector) ShouldAttach(tracepointName string) bool { } } if len(s.Attach) == 0 { - return true + if !s.RestrictSyscalls { + return true + } + syscall, ok := SyscallNameFromTracepoint(tracepointName) + if !ok { + return false + } + _, allowed := s.Syscalls[syscall] + return allowed } for _, re := range s.Attach { if re.MatchString(tracepointName) { - return true + if !s.RestrictSyscalls { + return true + } + syscall, ok := SyscallNameFromTracepoint(tracepointName) + if !ok { + return false + } + _, allowed := s.Syscalls[syscall] + return allowed } } return false @@ -79,7 +102,22 @@ func (s Selector) ShouldAttach(tracepointName string) bool { // copy's slices do not affect the original. func (s Selector) Clone() Selector { return Selector{ - Attach: slices.Clone(s.Attach), - Exclude: slices.Clone(s.Exclude), + Attach: slices.Clone(s.Attach), + Exclude: slices.Clone(s.Exclude), + Syscalls: maps.Clone(s.Syscalls), + RestrictSyscalls: s.RestrictSyscalls, + } +} + +// SyscallNameFromTracepoint returns the bare syscall name for a tracepoint +// (for example "openat" from "sys_enter_openat"). +func SyscallNameFromTracepoint(tracepointName string) (string, bool) { + switch { + case strings.HasPrefix(tracepointName, "sys_enter_"): + return strings.TrimPrefix(tracepointName, "sys_enter_"), true + case strings.HasPrefix(tracepointName, "sys_exit_"): + return strings.TrimPrefix(tracepointName, "sys_exit_"), true + default: + return "", false } } diff --git a/internal/tracepoints/selector_test.go b/internal/tracepoints/selector_test.go index d12f24b..dd9084b 100644 --- a/internal/tracepoints/selector_test.go +++ b/internal/tracepoints/selector_test.go @@ -82,3 +82,17 @@ func TestSelectorCloneIsIndependent(t *testing.T) { t.Error("original Selector was mutated through clone") } } + +func TestSelectorCloneCopiesSyscallAllowlist(t *testing.T) { + sel := Selector{ + Syscalls: map[string]struct{}{ + "openat": {}, + }, + RestrictSyscalls: true, + } + clone := sel.Clone() + delete(clone.Syscalls, "openat") + if !sel.ShouldAttach("sys_enter_openat") { + t.Fatal("original syscall allowlist mutated through clone") + } +} |
