summaryrefslogtreecommitdiff
path: root/internal/generate
diff options
context:
space:
mode:
Diffstat (limited to 'internal/generate')
-rw-r--r--internal/generate/classify_test.go3149
-rw-r--r--internal/generate/codegen_test.go166
-rw-r--r--internal/generate/family_test.go365
-rw-r--r--internal/generate/retclassify_test.go185
4 files changed, 13 insertions, 3852 deletions
diff --git a/internal/generate/classify_test.go b/internal/generate/classify_test.go
index 2f74e8a..bd03af4 100644
--- a/internal/generate/classify_test.go
+++ b/internal/generate/classify_test.go
@@ -6,1315 +6,16 @@ import (
"testing"
)
-func classifyFromData(t *testing.T, data string) ClassificationResult {
- t.Helper()
- f := mustParseOne(t, data)
- return ClassifyFormat(&f)
-}
-
-func TestClassifyFdRead(t *testing.T) {
- r := classifyFromData(t, FormatRead)
- if r.Kind != KindFd {
- t.Errorf("read: got kind %d, want KindFd", r.Kind)
- }
-}
-
-func TestClassifyFdClose(t *testing.T) {
- r := classifyFromData(t, FormatClose)
- if r.Kind != KindFd {
- t.Errorf("close: got kind %d, want KindFd", r.Kind)
- }
-}
-
-func TestClassifyFdPread64(t *testing.T) {
- r := classifyFromData(t, FormatPread64)
- if r.Kind != KindFd {
- t.Errorf("pread64: got kind %d, want KindFd", r.Kind)
- }
-}
-
-func TestClassifyFdWrite(t *testing.T) {
- r := classifyFromData(t, FormatWrite)
- if r.Kind != KindFd {
- t.Errorf("write: got kind %d, want KindFd", r.Kind)
- }
-}
-
-// TestClassifyFdLseek pins lseek(2) as a single-fd KindFd event. lseek's
-// tracepoint exposes a generic "fd" field of an fd-like type at args[0], so it
-// classifies via classifyByField exactly like read/write — the fd is captured
-// from args[0], while the off_t offset and whence args are ignored. The return
-// value (resulting file offset) is asserted UNCLASSIFIED separately in
-// retclassify_test.go (TestClassifyRetUnclassified) and end-to-end in
-// TestClassifyRetExitLseek below.
-func TestClassifyFdLseek(t *testing.T) {
- r := classifyFromData(t, FormatLseek)
- if r.Kind != KindFd {
- t.Errorf("lseek: got kind %d, want KindFd", r.Kind)
- }
-}
-
-// TestClassifyRetExitLseek locks in that sys_exit_lseek is a plain ret_event
-// (KindRet) and that ClassifyRet keeps it UNCLASSIFIED. lseek returns the new
-// file OFFSET (bytes-from-start), not a transferred byte count, so it must
-// never be classified as READ/WRITE/TRANSFER — doing so would inflate I/O byte
-// accounting.
-func TestClassifyRetExitLseek(t *testing.T) {
- r := classifyFromData(t, FormatExitLseek)
- if r.Kind != KindRet {
- t.Errorf("lseek exit: got kind %d, want KindRet", r.Kind)
- }
- if got := ClassifyRet("sys_exit_lseek"); got != Unclassified {
- t.Errorf("lseek exit: ClassifyRet = %q, want UNCLASSIFIED", got)
- }
-}
-
-func TestClassifyFdPidfdGetfd(t *testing.T) {
- r := classifyFromData(t, FormatPidfdGetfd)
- if r.Kind != KindFd {
- t.Errorf("pidfd_getfd: got kind %d, want KindFd", r.Kind)
- }
-}
-
-func TestClassifyOpenOpenat(t *testing.T) {
- r := classifyFromData(t, FormatOpenat)
- if r.Kind != KindOpen {
- t.Errorf("openat: got kind %d, want KindOpen", r.Kind)
- }
-}
-
-func TestClassifyOpenOpen(t *testing.T) {
- r := classifyFromData(t, FormatOpen)
- if r.Kind != KindOpen {
- t.Errorf("open: got kind %d, want KindOpen", r.Kind)
- }
-}
-
-func TestClassifyOpenOpenat2(t *testing.T) {
- r := classifyFromData(t, FormatOpenat2)
- if r.Kind != KindOpen {
- t.Errorf("openat2: got kind %d, want KindOpen", r.Kind)
- }
-}
-
-func TestClassifyPathnameCreat(t *testing.T) {
- r := classifyFromData(t, FormatCreat)
- if r.Kind != KindPathname {
- t.Errorf("creat: got kind %d, want KindPathname", r.Kind)
- }
- if r.PathnameField != "pathname" {
- t.Errorf("creat: PathnameField = %q, want pathname", r.PathnameField)
- }
-}
-
-func TestClassifyPathnameUnlink(t *testing.T) {
- r := classifyFromData(t, FormatUnlink)
- if r.Kind != KindPathname {
- t.Errorf("unlink: got kind %d, want KindPathname", r.Kind)
- }
- if r.PathnameField != "pathname" {
- t.Errorf("unlink: PathnameField = %q, want pathname", r.PathnameField)
- }
-}
-
-// TestClassifyPathnameUtime locks in that utime's args[0] "filename" is
-// captured as a real path. utime(2) changes a file's access/modification
-// times; its filename argument is a genuine filesystem path (not a
-// domain/host name string), so it must classify as KindPathname with the
-// path wired to the "filename" field — matching siblings utimensat/futimesat.
-func TestClassifyPathnameUtime(t *testing.T) {
- r := classifyFromData(t, FormatUtime)
- if r.Kind != KindPathname {
- t.Errorf("utime: got kind %d, want KindPathname", r.Kind)
- }
- if r.PathnameField != "filename" {
- t.Errorf("utime: PathnameField = %q, want filename", r.PathnameField)
- }
-}
-
-// TestClassifyPathnameAccess locks in that access(2)'s args[0] argument
-// (kernel field "filename") is captured as a real filesystem path. access(2)
-// checks the calling process's permissions for a file by path; the path is at
-// args[0] (there is no dirfd), so it must classify as KindPathname with the
-// path wired to the "filename" field. If this regresses to a non-path kind,
-// access's pathname would silently stop being captured.
-func TestClassifyPathnameAccess(t *testing.T) {
- r := classifyFromData(t, FormatAccess)
- if r.Kind != KindPathname {
- t.Errorf("access: got kind %d, want KindPathname", r.Kind)
- }
- if r.PathnameField != "filename" {
- t.Errorf("access: PathnameField = %q, want filename", r.PathnameField)
- }
-}
-
-// TestClassifyPathnameFaccessat locks in that faccessat(2) — access(2)'s
-// dirfd-relative sibling — also classifies as KindPathname with the path wired
-// to the "filename" field. The path is at args[1] (args[0] is the dirfd); the
-// argument-index difference from access(2) is verified separately in the
-// codegen tests (TestGenerateAccessFaccessatHandlers).
-func TestClassifyPathnameFaccessat(t *testing.T) {
- r := classifyFromData(t, FormatFaccessat)
- if r.Kind != KindPathname {
- t.Errorf("faccessat: got kind %d, want KindPathname", r.Kind)
- }
- if r.PathnameField != "filename" {
- t.Errorf("faccessat: PathnameField = %q, want filename", r.PathnameField)
- }
-}
-
-func TestClassifyNameRename(t *testing.T) {
- r := classifyFromData(t, FormatRename)
- if r.Kind != KindName {
- t.Errorf("rename: got kind %d, want KindName", r.Kind)
- }
-}
-
-func TestClassifyNameLinkat(t *testing.T) {
- r := classifyFromData(t, FormatLinkat)
- if r.Kind != KindName {
- t.Errorf("linkat: got kind %d, want KindName", r.Kind)
- }
-}
-
-func TestClassifyNameSymlink(t *testing.T) {
- r := classifyFromData(t, FormatSymlink)
- if r.Kind != KindName {
- t.Errorf("symlink: got kind %d, want KindName", r.Kind)
- }
-}
-
-func TestClassifyFcntl(t *testing.T) {
- r := classifyFromData(t, FormatFcntl)
- if r.Kind != KindFcntl {
- t.Errorf("fcntl: got kind %d, want KindFcntl", r.Kind)
- }
-}
-
-func TestClassifyDup(t *testing.T) {
- r := classifyFromData(t, FormatDup)
- if r.Kind != KindFd {
- t.Errorf("dup: got kind %d, want KindFd", r.Kind)
- }
-}
-
-func TestClassifyDup2(t *testing.T) {
- r := classifyFromData(t, FormatDup2)
- if r.Kind != KindFd {
- t.Errorf("dup2: got kind %d, want KindFd", r.Kind)
- }
-}
-
-func TestClassifyDup3(t *testing.T) {
- r := classifyFromData(t, FormatDup3)
- if r.Kind != KindDup3 {
- t.Errorf("dup3: got kind %d, want KindDup3", r.Kind)
- }
-}
-
-func TestClassifyOpenByHandleAt(t *testing.T) {
- r := classifyFromData(t, FormatOpenByHandleAt)
- if r.Kind != KindOpenByHandleAt {
- t.Errorf("open_by_handle_at: got kind %d, want KindOpenByHandleAt", r.Kind)
- }
-}
-
-func TestClassifyNameToHandleAt(t *testing.T) {
- r := classifyFromData(t, FormatNameToHandleAt)
- if r.Kind != KindPathname {
- t.Errorf("name_to_handle_at: got kind %d, want KindPathname", r.Kind)
- }
- if r.PathnameField != "name" {
- t.Errorf("name_to_handle_at: PathnameField = %q, want name", r.PathnameField)
- }
-}
-
-func TestClassifyNullSync(t *testing.T) {
- r := classifyFromData(t, FormatSync)
- if r.Kind != KindNull {
- t.Errorf("sync: got kind %d, want KindNull", r.Kind)
- }
-}
-
-func TestClassifyNullSyslog(t *testing.T) {
- r := classifyFromData(t, FormatSyslog)
- if r.Kind != KindNull {
- t.Errorf("syslog: got kind %d, want KindNull", r.Kind)
- }
-}
-
-// TestClassifyNullGetcwd pins getcwd as KindNull at enter.
-//
-// getcwd's args[0] is `char *buf`, an OUTPUT buffer: the kernel writes the
-// absolute cwd path into it and the contents only become valid AFTER the
-// syscall returns (sys_exit). Reading buf at enter would capture an empty or
-// garbage string, so getcwd must NOT be classified as a path-input syscall.
-// KindNull is the correct enter kind; the cwd is resolved at exit from
-// /proc/<tid>/cwd (see eventLoop.handleNullExit). This test locks that in:
-// - the enter kind is KindNull (not KindPathname/KindName), and
-// - no pathname field is captured from the buffer at enter.
-func TestClassifyNullGetcwd(t *testing.T) {
- r := classifyFromData(t, FormatGetcwd)
- if r.Kind != KindNull {
- t.Errorf("getcwd: got kind %d, want KindNull", r.Kind)
- }
- if r.Kind == KindPathname || r.Kind == KindName {
- t.Errorf("getcwd: enter must not capture output buf as a path, got kind %d", r.Kind)
- }
- if r.PathnameField != "" {
- t.Errorf("getcwd: no enter-time pathname field expected, got %q", r.PathnameField)
- }
-}
-
-// TestClassifyByFieldGetcwdBufNotPath is a defense-in-depth lock-in: even if
-// the name-only KindNull override for getcwd were removed, the generic
-// field-based classifier must not treat `char *buf` as a pathname. Only the
-// field names pathname/path/filename/newname are path-like; "buf" is not, so
-// classifyByField must report no match for getcwd's output buffer.
-func TestClassifyByFieldGetcwdBufNotPath(t *testing.T) {
- if r, ok := classifyByField("char *", "buf"); ok {
- t.Errorf("getcwd buf: char *buf must not classify as a field kind, got %d", r.Kind)
- }
-}
-
-func TestClassifyNullIoUring(t *testing.T) {
- r := classifyFromData(t, FormatIoUringEnter)
- if r.Kind != KindFd {
- t.Errorf("io_uring_enter: got kind %d, want KindFd", r.Kind)
- }
-}
-
-func TestClassifyIoUringRegister(t *testing.T) {
- r := classifyFromData(t, FormatIoUringRegister)
- if r.Kind != KindFd {
- t.Errorf("io_uring_register: got kind %d, want KindFd", r.Kind)
- }
-}
-
-func TestClassifyRetExitRead(t *testing.T) {
- r := classifyFromData(t, FormatExitRead)
- if r.Kind != KindRet {
- t.Errorf("exit_read: got kind %d, want KindRet", r.Kind)
- }
-}
-
-func TestClassifyRetExitWrite(t *testing.T) {
- r := classifyFromData(t, FormatExitWrite)
- if r.Kind != KindRet {
- t.Errorf("exit_write: got kind %d, want KindRet", r.Kind)
- }
-}
-
-func TestClassifyRetExitOpenat(t *testing.T) {
- r := classifyFromData(t, FormatExitOpenat)
- if r.Kind != KindRet {
- t.Errorf("exit_openat: got kind %d, want KindRet", r.Kind)
- }
-}
-
-func TestClassifyRetExitPread64(t *testing.T) {
- r := classifyFromData(t, FormatExitPread64)
- if r.Kind != KindRet {
- t.Errorf("exit_pread64: got kind %d, want KindRet", r.Kind)
- }
-}
-
-func TestClassifyRetExitSymlink(t *testing.T) {
- r := classifyFromData(t, FormatExitSymlink)
- if r.Kind != KindRet {
- t.Errorf("exit_symlink: got kind %d, want KindRet", r.Kind)
- }
-}
-
-func TestClassifyPathnameMknod(t *testing.T) {
- r := classifyFromData(t, FormatMknod)
- if r.Kind != KindPathname {
- t.Errorf("mknod: got kind %d, want KindPathname", r.Kind)
- }
-}
-
-func TestClassifyExecExecve(t *testing.T) {
- r := classifyFromData(t, FormatExecve)
- if r.Kind != KindExec {
- t.Errorf("execve: got kind %d, want KindExec", r.Kind)
- }
-}
-
-func TestClassifyExecExecveat(t *testing.T) {
- r := classifyFromData(t, FormatExecveat)
- if r.Kind != KindExec {
- t.Errorf("execveat: got kind %d, want KindExec", r.Kind)
- }
-}
-
-func TestClassifyAccept(t *testing.T) {
- r := classifyFromData(t, FormatAccept)
- if r.Kind != KindAccept {
- t.Errorf("accept: got kind %d, want KindAccept", r.Kind)
- }
-}
-
-func TestClassifyAccept4(t *testing.T) {
- r := classifyFromData(t, FormatAccept4)
- if r.Kind != KindAccept {
- t.Errorf("accept4: got kind %d, want KindAccept", r.Kind)
- }
-}
-
-func TestClassifyExitAccept(t *testing.T) {
- r := classifyFromData(t, FormatExitAccept)
- if r.Kind != KindAccept {
- t.Errorf("exit_accept: got kind %d, want KindAccept", r.Kind)
- }
-}
-
-func TestClassifyExitAccept4(t *testing.T) {
- r := classifyFromData(t, FormatExitAccept4)
- if r.Kind != KindAccept {
- t.Errorf("exit_accept4: got kind %d, want KindAccept", r.Kind)
- }
-}
-
-func TestClassifySocketFdSyscallsByName(t *testing.T) {
- tests := []string{
- "bind",
- "connect",
- "listen",
- "shutdown",
- "getsockname",
- "getpeername",
- "getsockopt",
- "setsockopt",
- }
- for _, name := range tests {
- t.Run(name, func(t *testing.T) {
- r := ClassifyFormat(&Format{
- Name: "sys_enter_" + name,
- ExternalFields: []Field{
- {Type: "long", Name: "__syscall_nr"},
- {Type: "int", Name: "sockfd"},
- },
- })
- if r.Kind != KindFd {
- t.Errorf("%s: got kind %d, want KindFd", name, r.Kind)
- }
- })
- }
-}
-
-// TestClassifySyncFamilyFdSyscallsByName locks in that the filesystem-sync
-// family (fsync/fdatasync/syncfs/sync_file_range) is classified as KindFd on
-// enter. Each of these takes an open file descriptor as args[0]:
-// - int fsync(int fd)
-// - int fdatasync(int fd)
-// - int syncfs(int fd)
-// - int sync_file_range(int fd, off64_t offset, off64_t nbytes, unsigned flags)
-//
-// so their enter tracepoint carries a leading fd field and must capture
-// fd=args[0] into a fd_event (KindFd), matching the generated
-// handle_sys_enter_* handlers. (Plain sync() takes no args and is KindNull;
-// it is asserted separately in the classification table test.)
-func TestClassifySyncFamilyFdSyscallsByName(t *testing.T) {
- tests := []string{
- "fsync",
- "fdatasync",
- "syncfs",
- "sync_file_range",
- }
- for _, name := range tests {
- t.Run(name, func(t *testing.T) {
- r := ClassifyFormat(&Format{
- Name: "sys_enter_" + name,
- ExternalFields: []Field{
- {Type: "long", Name: "__syscall_nr"},
- {Type: "int", Name: "fd"},
- },
- })
- if r.Kind != KindFd {
- t.Errorf("%s: got kind %d, want KindFd", name, r.Kind)
- }
- })
- }
-}
-
-// TestClassifyExitSyncfs locks in that the syncfs exit tracepoint is classified
-// as KindRet. syncfs(2) returns int (0 on success, -1 on error) and transfers
-// no bytes, so its exit format carries a single "ret" field and must map to a
-// plain ret_event (KindRet, Unclassified) — matching the generated
-// sys_exit_syncfs handler and its fsync/fdatasync siblings.
-func TestClassifyExitSyncfs(t *testing.T) {
- r := ClassifyFormat(&Format{
- Name: "sys_exit_syncfs",
- ExternalFields: []Field{
- {Type: "long", Name: "__syscall_nr"},
- {Type: "long", Name: "ret"},
- },
- })
- if r.Kind != KindRet {
- t.Errorf("exit_syncfs: got kind %d, want KindRet", r.Kind)
- }
-}
-
-// TestClassifyFallocateEnterFd locks in that the fallocate enter tracepoint is
-// classified as KindFd with the fd captured at args[0].
-//
-// int fallocate(int fd, int mode, off_t offset, off_t len)
-//
-// fallocate(2) manipulates the allocated disk space for the file referred to
-// by fd (args[0]); the remaining mode/offset/len args are NOT captured, exactly
-// like its fd-based siblings fadvise64(2)/ftruncate(2)/sync_file_range(2) which
-// also carry trailing offset/len/advice args but only record args[0]. The
-// leading "fd" external field must select KindFd so the generated
-// handle_sys_enter_fallocate emits ev->fd = ctx->args[0] into a fd_event.
-func TestClassifyFallocateEnterFd(t *testing.T) {
- f := &Format{
- Name: "sys_enter_fallocate",
- ExternalFields: []Field{
- {Type: "long", Name: "__syscall_nr"},
- {Type: "int", Name: "fd"},
- {Type: "int", Name: "mode"},
- {Type: "loff_t", Name: "offset"},
- {Type: "loff_t", Name: "len"},
- },
- }
- r := ClassifyFormat(f)
- if r.Kind != KindFd {
- t.Fatalf("enter_fallocate: got kind %d, want KindFd", r.Kind)
- }
- // fd is the first real argument (args[0]); FieldNumber skips __syscall_nr.
- if got := f.FieldNumber("fd"); got != 0 {
- t.Errorf("enter_fallocate: fd field number = %d, want 0 (args[0])", got)
- }
-}
-
-// TestClassifyExitFallocateUnclassifiedRet locks in that the fallocate exit
-// tracepoint is classified as KindRet and Unclassified. fallocate(2) returns
-// int (0 on success, -1 on error) — that return is a status code, NOT a
-// transferred byte count, so its exit format carries a single "ret" field and
-// must map to a plain ret_event (KindRet) whose ret_type stays UNCLASSIFIED.
-// Misclassifying it as a READ/WRITE/TRANSFER byte count would be a real bug,
-// since fallocate allocates space but reports no transferred bytes.
-func TestClassifyExitFallocateUnclassifiedRet(t *testing.T) {
- r := ClassifyFormat(&Format{
- Name: "sys_exit_fallocate",
- ExternalFields: []Field{
- {Type: "long", Name: "__syscall_nr"},
- {Type: "long", Name: "ret"},
- },
- })
- if r.Kind != KindRet {
- t.Fatalf("exit_fallocate: got kind %d, want KindRet", r.Kind)
- }
- if got := ClassifyRet("sys_exit_fallocate"); got != Unclassified {
- t.Errorf("ClassifyRet(sys_exit_fallocate) = %q, want UNCLASSIFIED", got)
- }
-}
-
-// TestClassifySetuidNullEnter locks in that the setuid enter tracepoint is
-// classified as KindNull. setuid(2) is "int setuid(uid_t uid)" — its single
-// argument is a numeric user ID, NOT a file descriptor or a path. It must
-// therefore map to a null_event (no argument capture); misclassifying it as an
-// fd-bearing kind would be a real bug, since the uid is not an fd and capturing
-// it as one would attribute the credential change to a bogus file. The whole
-// credential-setting cluster (setuid/seteuid/setresuid/setreuid/setfsuid and
-// the gid analogues) shares this KindNull treatment with the getuid readers.
-func TestClassifySetuidNullEnter(t *testing.T) {
- r := ClassifyFormat(&Format{
- Name: "sys_enter_setuid",
- ExternalFields: []Field{
- {Type: "long", Name: "__syscall_nr"},
- {Type: "long", Name: "uid"},
- },
- })
- if r.Kind != KindNull {
- t.Fatalf("enter_setuid: got kind %d, want KindNull", r.Kind)
- }
- // The uid argument must never be captured as a file descriptor or path.
- if r.PathnameField != "" {
- t.Errorf("enter_setuid: unexpected PathnameField %q, want empty", r.PathnameField)
- }
-}
-
-// TestClassifyExitSetuidUnclassifiedRet locks in that the setuid exit
-// tracepoint is classified as KindRet and Unclassified. setuid(2) returns int
-// (0 on success, -1 on error) — that return is a status code, NOT a
-// transferred byte count, so its exit format carries a single "ret" field and
-// must map to a plain ret_event (KindRet) whose ret_type stays UNCLASSIFIED.
-// Misclassifying it as a READ/WRITE/TRANSFER byte count would be a real bug.
-func TestClassifyExitSetuidUnclassifiedRet(t *testing.T) {
- r := ClassifyFormat(&Format{
- Name: "sys_exit_setuid",
- ExternalFields: []Field{
- {Type: "long", Name: "__syscall_nr"},
- {Type: "long", Name: "ret"},
- },
- })
- if r.Kind != KindRet {
- t.Fatalf("exit_setuid: got kind %d, want KindRet", r.Kind)
- }
- if got := ClassifyRet("sys_exit_setuid"); got != Unclassified {
- t.Errorf("ClassifyRet(sys_exit_setuid) = %q, want UNCLASSIFIED", got)
- }
-}
-
-// TestClassifySetpgidNullEnter locks in the setpgid(2) enter classification
-// using the syscall's REAL tracepoint fields. setpgid(pid_t pid, pid_t pgid)
-// sets the process group ID of a process; both arguments are process/process-
-// group identifiers (the kernel tracepoint declares them as field type
-// "pid_t"), NOT file descriptors and NOT filesystem paths. The audit concern is
-// that args[0] ("pid") could be mistaken for an fd: it must not be. setpgid has
-// no fd or path argument, so its enter format must classify as KindNull
-// (null_event) — matching its session/process-group siblings setsid/getsid/
-// getpgid/getpgrp and the explicit name-only mapping in classify.go. Using the
-// real "pid"/"pgid" pid_t fields here (rather than a synthetic arg0) proves the
-// generic field heuristics never capture them: isFdType only matches int/
-// unsigned int/unsigned long (not "pid_t"), and the fd heuristic additionally
-// requires the field name be "fd", which neither "pid" nor "pgid" is.
-func TestClassifySetpgidNullEnter(t *testing.T) {
- r := ClassifyFormat(&Format{
- Name: "sys_enter_setpgid",
- ExternalFields: []Field{
- {Type: "int", Name: "__syscall_nr"},
- {Type: "pid_t", Name: "pid"},
- {Type: "pid_t", Name: "pgid"},
- },
- })
- if r.Kind != KindNull {
- t.Fatalf("enter_setpgid: got kind %d, want KindNull", r.Kind)
- }
- // Neither pid argument must be captured as a file descriptor or path.
- if r.PathnameField != "" {
- t.Errorf("enter_setpgid: unexpected PathnameField %q, want empty", r.PathnameField)
- }
-}
-
-// TestClassifyExitSetpgidUnclassifiedRet locks in that the setpgid exit
-// tracepoint is classified as KindRet and Unclassified. setpgid(2) returns int
-// (0 on success, -1 on error) — a status code, NOT a transferred byte count —
-// so its exit format carries a single "ret" field and must map to a plain
-// ret_event (KindRet) whose ret_type stays UNCLASSIFIED. This matches its
-// sibling setsid/getsid (asserted in retclassify_test.go); misclassifying it as
-// a READ/WRITE/TRANSFER byte count would be a real bug.
-func TestClassifyExitSetpgidUnclassifiedRet(t *testing.T) {
- r := ClassifyFormat(&Format{
- Name: "sys_exit_setpgid",
- ExternalFields: []Field{
- {Type: "int", Name: "__syscall_nr"},
- {Type: "long", Name: "ret"},
- },
- })
- if r.Kind != KindRet {
- t.Fatalf("exit_setpgid: got kind %d, want KindRet", r.Kind)
- }
- if got := ClassifyRet("sys_exit_setpgid"); got != Unclassified {
- t.Errorf("ClassifyRet(sys_exit_setpgid) = %q, want UNCLASSIFIED", got)
- }
-}
-
-// TestClassifyGetgidNullEnter locks in the getgid(2) enter classification using
-// the syscall's REAL tracepoint fields. getgid(2) is "gid_t getgid(void)" — it
-// takes NO arguments at all, so its enter format carries only the synthetic
-// __syscall_nr field and must classify as KindNull (null_event capturing
-// nothing). This matches the no-arg id-returning reader cluster
-// getuid/geteuid/getegid/getpid/getppid/gettid and the explicit name-only
-// mapping in classify.go. With no real argument fields there is nothing the fd
-// or path heuristics could latch onto, so PathnameField must stay empty.
-func TestClassifyGetgidNullEnter(t *testing.T) {
- r := ClassifyFormat(&Format{
- Name: "sys_enter_getgid",
- ExternalFields: []Field{
- {Type: "int", Name: "__syscall_nr"},
- },
- })
- if r.Kind != KindNull {
- t.Fatalf("enter_getgid: got kind %d, want KindNull", r.Kind)
- }
- // getgid has no arguments, so nothing must be captured as a path/fd.
- if r.PathnameField != "" {
- t.Errorf("enter_getgid: unexpected PathnameField %q, want empty", r.PathnameField)
- }
-}
-
-// TestClassifyExitGetgidUnclassifiedRet locks in that the getgid exit
-// tracepoint is classified as KindRet and Unclassified. getgid(2) returns the
-// real group ID (gid_t) of the caller and ALWAYS succeeds — its return is a
-// numeric credential identifier, NOT a transferred byte count and never an
-// error status. Its exit format carries a single "ret" field and must map to a
-// plain ret_event (KindRet) whose ret_type stays UNCLASSIFIED. Misclassifying
-// the gid as a READ/WRITE/TRANSFER byte count would be a real bug. This matches
-// its no-arg reader siblings getuid/getpid (no byte semantics on their return).
-func TestClassifyExitGetgidUnclassifiedRet(t *testing.T) {
- r := ClassifyFormat(&Format{
- Name: "sys_exit_getgid",
- ExternalFields: []Field{
- {Type: "int", Name: "__syscall_nr"},
- {Type: "long", Name: "ret"},
- },
- })
- if r.Kind != KindRet {
- t.Fatalf("exit_getgid: got kind %d, want KindRet", r.Kind)
- }
- if got := ClassifyRet("sys_exit_getgid"); got != Unclassified {
- t.Errorf("ClassifyRet(sys_exit_getgid) = %q, want UNCLASSIFIED", got)
- }
-}
-
-// TestClassifyGettidNullEnter locks in the gettid(2) enter classification using
-// the syscall's REAL tracepoint fields. gettid(2) is "pid_t gettid(void)" — it
-// takes NO arguments at all, so its enter format carries only the synthetic
-// __syscall_nr field and must classify as KindNull (null_event capturing
-// nothing). This matches the no-arg id-returning reader cluster
-// getuid/geteuid/getegid/getpid/getppid/getgid and the explicit name-only
-// mapping in classify.go. With no real argument fields there is nothing the fd
-// or path heuristics could latch onto, so PathnameField must stay empty.
-func TestClassifyGettidNullEnter(t *testing.T) {
- r := ClassifyFormat(&Format{
- Name: "sys_enter_gettid",
- ExternalFields: []Field{
- {Type: "int", Name: "__syscall_nr"},
- },
- })
- if r.Kind != KindNull {
- t.Fatalf("enter_gettid: got kind %d, want KindNull", r.Kind)
- }
- // gettid has no arguments, so nothing must be captured as a path/fd.
- if r.PathnameField != "" {
- t.Errorf("enter_gettid: unexpected PathnameField %q, want empty", r.PathnameField)
- }
-}
-
-// TestClassifyExitGettidUnclassifiedRet locks in that the gettid exit
-// tracepoint is classified as KindRet and Unclassified. gettid(2) returns the
-// caller's thread ID (pid_t) and ALWAYS succeeds — its return is a numeric
-// thread identifier, NOT a transferred byte count and never an error status.
-// Its exit format carries a single "ret" field and must map to a plain
-// ret_event (KindRet) whose ret_type stays UNCLASSIFIED. Misclassifying the tid
-// as a READ/WRITE/TRANSFER byte count would be a real bug. This matches its
-// no-arg reader siblings getuid/getpid/getgid (no byte semantics on their
-// return).
-func TestClassifyExitGettidUnclassifiedRet(t *testing.T) {
- r := ClassifyFormat(&Format{
- Name: "sys_exit_gettid",
- ExternalFields: []Field{
- {Type: "int", Name: "__syscall_nr"},
- {Type: "long", Name: "ret"},
- },
- })
- if r.Kind != KindRet {
- t.Fatalf("exit_gettid: got kind %d, want KindRet", r.Kind)
- }
- if got := ClassifyRet("sys_exit_gettid"); got != Unclassified {
- t.Errorf("ClassifyRet(sys_exit_gettid) = %q, want UNCLASSIFIED", got)
- }
-}
-
-// TestClassifyExitGetpeername locks in that the getpeername exit tracepoint is
-// classified as KindRet. getpeername(2) returns int (0 on success, -1 on
-// error), so its exit format carries a single "ret" field and must map to a
-// plain ret_event, matching the generated sys_exit_getpeername handler.
-func TestClassifyExitGetpeername(t *testing.T) {
- r := ClassifyFormat(&Format{
- Name: "sys_exit_getpeername",
- ExternalFields: []Field{
- {Type: "long", Name: "__syscall_nr"},
- {Type: "long", Name: "ret"},
- },
- })
- if r.Kind != KindRet {
- t.Errorf("exit_getpeername: got kind %d, want KindRet", r.Kind)
- }
-}
-
-// TestClassifyExitGetsockname locks in that the getsockname exit tracepoint is
-// classified as KindRet. getsockname(2) returns int (0 on success, -1 on
-// error), so its exit format carries a single "ret" field and must map to a
-// plain ret_event, matching the generated sys_exit_getsockname handler — just
-// like its sibling getpeername (see TestClassifyExitGetpeername).
-func TestClassifyExitGetsockname(t *testing.T) {
- r := ClassifyFormat(&Format{
- Name: "sys_exit_getsockname",
- ExternalFields: []Field{
- {Type: "long", Name: "__syscall_nr"},
- {Type: "long", Name: "ret"},
- },
- })
- if r.Kind != KindRet {
- t.Errorf("exit_getsockname: got kind %d, want KindRet", r.Kind)
- }
-}
-
-// TestClassifySetsockoptEnterFd locks in that the setsockopt enter tracepoint is
-// classified as KindFd with the socket fd captured at args[0]. The signature is:
-//
-// int setsockopt(int sockfd, int level, int optname,
-// const void *optval, socklen_t optlen)
-//
-// setsockopt(2) sets a socket option on the socket referred to by sockfd
-// (args[0]); the remaining level/optname/optval/optlen args are NOT captured.
-// optval is a userspace pointer (not a transferred byte buffer we account for),
-// so only the leading sockfd matters — exactly like its KindFd network siblings
-// bind/connect/getsockname/getpeername/getsockopt and the explicit name-only
-// mapping in classify.go. The classification is name-only, so this asserts the
-// kind holds even when the enter format carries the real "fd" field. Capturing
-// any later arg as the fd, or failing to capture args[0], would be a real bug.
-func TestClassifySetsockoptEnterFd(t *testing.T) {
- r := ClassifyFormat(&Format{
- Name: "sys_enter_setsockopt",
- ExternalFields: []Field{
- {Type: "long", Name: "__syscall_nr"},
- {Type: "int", Name: "fd"},
- {Type: "int", Name: "level"},
- {Type: "int", Name: "optname"},
- {Type: "char *", Name: "optval"},
- {Type: "int", Name: "optlen"},
- },
- })
- if r.Kind != KindFd {
- t.Fatalf("enter_setsockopt: got kind %d, want KindFd", r.Kind)
- }
- // optval is a userspace pointer, never a pathname we record.
- if r.PathnameField != "" {
- t.Errorf("enter_setsockopt: unexpected PathnameField %q, want empty", r.PathnameField)
- }
-}
-
-// TestClassifyExitSetsockoptUnclassifiedRet locks in that the setsockopt exit
-// tracepoint is classified as KindRet and Unclassified. setsockopt(2) returns
-// int (0 on success, -1 on error) — a status code, NOT a transferred byte count
-// — so its exit format carries a single "ret" field and must map to a plain
-// ret_event (KindRet) whose ret_type stays UNCLASSIFIED, matching the generated
-// sys_exit_setsockopt handler and its sibling getsockopt. Misclassifying it as a
-// READ/WRITE/TRANSFER byte count would be a real bug.
-func TestClassifyExitSetsockoptUnclassifiedRet(t *testing.T) {
- r := ClassifyFormat(&Format{
- Name: "sys_exit_setsockopt",
- ExternalFields: []Field{
- {Type: "long", Name: "__syscall_nr"},
- {Type: "long", Name: "ret"},
- },
- })
- if r.Kind != KindRet {
- t.Fatalf("exit_setsockopt: got kind %d, want KindRet", r.Kind)
- }
- if got := ClassifyRet("sys_exit_setsockopt"); got != Unclassified {
- t.Errorf("ClassifyRet(sys_exit_setsockopt) = %q, want UNCLASSIFIED", got)
- }
-}
-
-// TestClassifyExitGetsockoptUnclassifiedRet mirrors the setsockopt exit lock-in
-// for its read-side sibling getsockopt(2), which likewise returns int (0/-1) and
-// must map to a plain ret_event (KindRet, UNCLASSIFIED) — never a READ byte
-// count, even though it copies option data into a userspace buffer via a
-// userspace pointer rather than returning a transferred byte total.
-func TestClassifyExitGetsockoptUnclassifiedRet(t *testing.T) {
- r := ClassifyFormat(&Format{
- Name: "sys_exit_getsockopt",
- ExternalFields: []Field{
- {Type: "long", Name: "__syscall_nr"},
- {Type: "long", Name: "ret"},
- },
- })
- if r.Kind != KindRet {
- t.Fatalf("exit_getsockopt: got kind %d, want KindRet", r.Kind)
- }
- if got := ClassifyRet("sys_exit_getsockopt"); got != Unclassified {
- t.Errorf("ClassifyRet(sys_exit_getsockopt) = %q, want UNCLASSIFIED", got)
- }
-}
-
-func TestClassifySocket(t *testing.T) {
- r := classifyFromData(t, FormatSocket)
- if r.Kind != KindSocket {
- t.Errorf("socket: got kind %d, want KindSocket", r.Kind)
- }
-}
-
-func TestClassifySocketpair(t *testing.T) {
- r := classifyFromData(t, FormatSocketpair)
- if r.Kind != KindSocketpair {
- t.Errorf("socketpair: got kind %d, want KindSocketpair", r.Kind)
- }
-}
-
-func TestClassifyExitSocketpair(t *testing.T) {
- r := classifyFromData(t, FormatExitSocketpair)
- if r.Kind != KindSocketpair {
- t.Errorf("exit_socketpair: got kind %d, want KindSocketpair", r.Kind)
- }
-}
-
-// TestClassifySocketpairNotFd is a regression lock-in for the socketpair(2)
-// audit (task c00). socketpair(int domain, int type, int protocol, int sv[2])
-// takes the address-family/domain constant as args[0] (named "family" in the
-// tracepoint format), NOT a file descriptor. The created fds are written into
-// the OUTPUT array sv[2] (args[3]) and are only valid after the call returns.
-// socketpair must therefore be KindSocketpair (read sv[2] at exit), never
-// KindFd, which would record the domain integer as a bogus fd. Pin that the
-// name-based override wins so a future field-shape change cannot make it fall
-// through to the generic KindFd path.
-func TestClassifySocketpairNotFd(t *testing.T) {
- r := classifyFromData(t, FormatSocketpair)
- if r.Kind == KindFd {
- t.Fatal("socketpair classified as KindFd: args[0] is the domain constant, not an fd")
- }
- if r.Kind != KindSocketpair {
- t.Errorf("socketpair: got kind %d, want KindSocketpair", r.Kind)
- }
-}
-
-func TestClassifyPipe(t *testing.T) {
- r := classifyFromData(t, FormatPipe)
- if r.Kind != KindPipe {
- t.Errorf("pipe: got kind %d, want KindPipe", r.Kind)
- }
-}
-
-func TestClassifyPipe2(t *testing.T) {
- r := classifyFromData(t, FormatPipe2)
- if r.Kind != KindPipe {
- t.Errorf("pipe2: got kind %d, want KindPipe", r.Kind)
- }
-}
-
-func TestClassifyExitPipe(t *testing.T) {
- r := classifyFromData(t, FormatExitPipe)
- if r.Kind != KindPipe {
- t.Errorf("exit_pipe: got kind %d, want KindPipe", r.Kind)
- }
-}
-
-func TestClassifyExitPipe2(t *testing.T) {
- r := classifyFromData(t, FormatExitPipe2)
- if r.Kind != KindPipe {
- t.Errorf("exit_pipe2: got kind %d, want KindPipe", r.Kind)
- }
-}
-
-// TestClassifyPipeNotFd locks in that pipe(2) is NOT classified as KindFd.
-// pipe's args[0] is an OUTPUT pointer to int[2] (the two created fds are written
-// there by the kernel and are only valid AFTER the syscall returns), NOT an fd
-// argument. Capturing args[0] as an fd would attribute the pipe to a bogus
-// descriptor; pipe must use the pipe-specific KindPipe path that reads the fd
-// pair from the userspace buffer at exit. Same pitfall as socketpair (task c00).
-func TestClassifyPipeNotFd(t *testing.T) {
- for _, name := range []string{"pipe", "pipe2"} {
- r := classifyFromData(t, map[string]string{
- "pipe": FormatPipe,
- "pipe2": FormatPipe2,
- }[name])
- if r.Kind == KindFd {
- t.Fatalf("%s classified as KindFd: args[0] is an output ptr, not an fd", name)
- }
- if r.Kind != KindPipe {
- t.Errorf("%s: got kind %d, want KindPipe", name, r.Kind)
- }
- }
-}
-
-// TestClassifyPipeUnclassifiedRet locks in that the pipe and pipe2 exit
-// tracepoints stay UNCLASSIFIED. pipe(2)/pipe2(2) return int (0 on success,
-// -1 on error) — a status code, NOT a transferred byte count. They must not be
-// in retClassifications and must never map to READ/WRITE/TRANSFER, which would
-// misreport phantom bytes. The created fds are surfaced via fd0/fd1 in the
-// pipe_event, not via the return value.
-func TestClassifyPipeUnclassifiedRet(t *testing.T) {
- for _, name := range []string{"sys_exit_pipe", "sys_exit_pipe2"} {
- if got := ClassifyRet(name); got != Unclassified {
- t.Errorf("ClassifyRet(%s) = %q, want UNCLASSIFIED", name, got)
- }
- }
-}
-
-func TestClassifyEventfd(t *testing.T) {
- r := classifyFromData(t, FormatEventfd)
- if r.Kind != KindEventfd {
- t.Errorf("eventfd: got kind %d, want KindEventfd", r.Kind)
- }
-}
-
-func TestClassifyEventfd2(t *testing.T) {
- r := classifyFromData(t, FormatEventfd2)
- if r.Kind != KindEventfd {
- t.Errorf("eventfd2: got kind %d, want KindEventfd", r.Kind)
- }
-}
-
-func TestClassifyExitEventfd(t *testing.T) {
- r := classifyFromData(t, FormatExitEventfd)
- if r.Kind != KindEventfd {
- t.Errorf("exit_eventfd: got kind %d, want KindEventfd", r.Kind)
- }
-}
-
-func TestClassifyExitEventfd2(t *testing.T) {
- r := classifyFromData(t, FormatExitEventfd2)
- if r.Kind != KindEventfd {
- t.Errorf("exit_eventfd2: got kind %d, want KindEventfd", r.Kind)
- }
-}
-
-func TestClassifyEventfdSpecializedFdFromAirSyscalls(t *testing.T) {
- tests := []string{
- "sys_enter_memfd_create",
- "sys_exit_memfd_create",
- "sys_enter_memfd_secret",
- "sys_exit_memfd_secret",
- "sys_enter_userfaultfd",
- "sys_exit_userfaultfd",
- "sys_enter_signalfd",
- "sys_exit_signalfd",
- "sys_enter_signalfd4",
- "sys_exit_signalfd4",
- "sys_enter_timerfd_create",
- "sys_exit_timerfd_create",
- }
- for _, name := range tests {
- t.Run(name, func(t *testing.T) {
- r, ok := classifyNameOnly(name)
- if !ok {
- t.Fatalf("classifyNameOnly(%q) did not match", name)
- }
- if r.Kind != KindEventfd {
- t.Fatalf("classifyNameOnly(%q) kind = %v, want KindEventfd", name, r.Kind)
- }
- })
- }
-}
-
-func TestClassifyEpollCtl(t *testing.T) {
- r := classifyFromData(t, FormatEpollCtl)
- if r.Kind != KindEpollCtl {
- t.Errorf("epoll_ctl: got kind %d, want KindEpollCtl", r.Kind)
- }
-}
-
-func TestClassifyEpollWait(t *testing.T) {
- r := classifyFromData(t, FormatEpollWait)
- if r.Kind != KindFd {
- t.Errorf("epoll_wait: got kind %d, want KindFd", r.Kind)
- }
-}
-
-func TestClassifyEpollPwait(t *testing.T) {
- r := classifyFromData(t, FormatEpollPwait)
- if r.Kind != KindFd {
- t.Errorf("epoll_pwait: got kind %d, want KindFd", r.Kind)
- }
-}
-
-func TestClassifyEpollPwait2