summaryrefslogtreecommitdiff
path: root/internal/generate
diff options
context:
space:
mode:
authorPaul Buetow <paul@buetow.org>2026-05-20 23:42:12 +0300
committerPaul Buetow <paul@buetow.org>2026-05-20 23:42:12 +0300
commitbe6d4e8ffc722bf0d36c5b01ff46f817539a1525 (patch)
tree7bb0aeb51e29cfbc6735af15bb812b888f4b3574 /internal/generate
parent2156d6e51b18e29fe8dfe8e1a519e1a84e0a1fe6 (diff)
task-47: add KindExec for execve paths
Diffstat (limited to 'internal/generate')
-rw-r--r--internal/generate/bpfhandler.go26
-rw-r--r--internal/generate/classify.go9
-rw-r--r--internal/generate/classify_test.go38
-rw-r--r--internal/generate/codegen_test.go15
-rw-r--r--internal/generate/kindregistry.go1
-rw-r--r--internal/generate/testdata.go32
6 files changed, 116 insertions, 5 deletions
diff --git a/internal/generate/bpfhandler.go b/internal/generate/bpfhandler.go
index b166725..5489d88 100644
--- a/internal/generate/bpfhandler.go
+++ b/internal/generate/bpfhandler.go
@@ -111,6 +111,8 @@ func generateExtra(tp GeneratedTracepoint, isEnter bool) string {
return generateExtraOpen(f)
case KindMqOpen:
return generateExtraMqOpen(f)
+ case KindExec:
+ return generateExtraExec(f)
case KindPathname:
return generateExtraPathname(tp, f)
case KindName:
@@ -146,6 +148,30 @@ func generateExtraMqOpen(f *Format) string {
return generateExtraOpenWithFields(f, "u_name", "oflag")
}
+func generateExtraExec(f *Format) string {
+ filenameIdx := f.FieldNumber("filename")
+ dirfdIdx := f.FieldNumber("dfd")
+ flagsIdx := f.FieldNumber("flags")
+ if filenameIdx < 0 {
+ filenameIdx = 0
+ }
+ var b strings.Builder
+ b.WriteString(" __builtin_memset(&(ev->filename), 0, sizeof(ev->filename) + sizeof(ev->comm));\n")
+ fmt.Fprintf(&b, " bpf_probe_read_user_str(ev->filename, sizeof(ev->filename), (void *)ctx->args[%d]);\n", filenameIdx)
+ b.WriteString(" bpf_get_current_comm(&ev->comm, sizeof(ev->comm));\n")
+ if dirfdIdx > -1 {
+ fmt.Fprintf(&b, " ev->dirfd = (__s32)ctx->args[%d];\n", dirfdIdx)
+ } else {
+ b.WriteString(" ev->dirfd = -1;\n")
+ }
+ if flagsIdx > -1 {
+ fmt.Fprintf(&b, " ev->flags = (__s32)ctx->args[%d];\n", flagsIdx)
+ } else {
+ b.WriteString(" ev->flags = 0;\n")
+ }
+ return b.String()
+}
+
func generateExtraOpenWithFields(f *Format, pathnameField, flagsField string) string {
filenameIdx := f.FieldNumber(pathnameField)
flagsIdx := f.FieldNumber(flagsField)
diff --git a/internal/generate/classify.go b/internal/generate/classify.go
index 77bab7e..af7d78d 100644
--- a/internal/generate/classify.go
+++ b/internal/generate/classify.go
@@ -9,6 +9,7 @@ const (
KindFd
KindOpen
KindMqOpen
+ KindExec
KindPathname
KindName
KindRet
@@ -193,6 +194,14 @@ func classifyNameOnly(name string) (ClassificationResult, bool) {
return ClassificationResult{Kind: KindFd}, true
case "sys_enter_mq_getsetattr":
return ClassificationResult{Kind: KindFd}, true
+ case "sys_enter_execve":
+ return ClassificationResult{Kind: KindExec}, true
+ case "sys_enter_execveat":
+ return ClassificationResult{Kind: KindExec}, true
+ case "sys_enter_exit":
+ return ClassificationResult{Kind: KindNull}, true
+ case "sys_enter_exit_group":
+ return ClassificationResult{Kind: KindNull}, true
}
if strings.HasPrefix(name, "sys_enter_io_") {
return ClassificationResult{Kind: KindNull}, true
diff --git a/internal/generate/classify_test.go b/internal/generate/classify_test.go
index 2a12911..5d6424b 100644
--- a/internal/generate/classify_test.go
+++ b/internal/generate/classify_test.go
@@ -231,10 +231,17 @@ func TestClassifyPathnameMknod(t *testing.T) {
}
}
-func TestClassifyPathnameExecve(t *testing.T) {
+func TestClassifyExecExecve(t *testing.T) {
r := classifyFromData(t, FormatExecve)
- if r.Kind != KindPathname {
- t.Errorf("execve: got kind %d, want KindPathname", r.Kind)
+ 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)
}
}
@@ -694,6 +701,24 @@ func TestClassifyKillRequiresGenerationFallback(t *testing.T) {
}
}
+func TestClassifyNullExitByName(t *testing.T) {
+ tests := []string{"sys_enter_exit", "sys_enter_exit_group"}
+ for _, name := range tests {
+ t.Run(name, func(t *testing.T) {
+ r := ClassifyFormat(&Format{
+ Name: name,
+ ExternalFields: []Field{
+ {Type: "long", Name: "__syscall_nr"},
+ {Type: "int", Name: "error_code"},
+ },
+ })
+ if r.Kind != KindNull {
+ t.Errorf("%s: got kind %d, want KindNull", name, r.Kind)
+ }
+ })
+ }
+}
+
// --- End-to-end classification with enter+exit pairs ---
func TestClassifySyscallPairAccepted(t *testing.T) {
@@ -722,7 +747,8 @@ func TestClassifySyscallPairAccepted(t *testing.T) {
{"pread64", FormatPread64, FormatExitPread64, KindFd},
{"symlink", FormatSymlink, FormatExitSymlink, KindName},
{"mknod", FormatMknod, FormatExitMknod, KindPathname},
- {"execve", FormatExecve, FormatExitExecve, KindPathname},
+ {"execve", FormatExecve, FormatExitExecve, KindExec},
+ {"execveat", FormatExecveat, FormatExitExecveat, KindExec},
{"accept", FormatAccept, FormatExitAccept, KindAccept},
{"accept4", FormatAccept4, FormatExitAccept4, KindAccept},
{"socket", FormatSocket, FormatExitSocket, KindSocket},
@@ -760,6 +786,8 @@ func TestClassifySyscallPairAccepted(t *testing.T) {
{"swapon", FormatSwapon, FormatExitSwapon, KindPathname},
{"swapoff", FormatSwapoff, FormatExitSwapoff, KindPathname},
{"kill", FormatKill, FormatExitKill, KindNull},
+ {"exit", syntheticEnter("exit", 9310), syntheticExit("exit", 9309), KindNull},
+ {"exit_group", syntheticEnter("exit_group", 9312), syntheticExit("exit_group", 9311), KindNull},
}
for _, tt := range tests {
@@ -782,6 +810,7 @@ func TestClassifySyscallPairEmitsAllFamilies(t *testing.T) {
}{
{"mknod", FormatMknod, FormatExitMknod, FamilyFS},
{"execve", FormatExecve, FormatExitExecve, FamilyProcess},
+ {"execveat", FormatExecveat, FormatExitExecveat, FamilyProcess},
{"accept", FormatAccept, FormatExitAccept, FamilyNetwork},
{"accept4", FormatAccept4, FormatExitAccept4, FamilyNetwork},
{"socket", FormatSocket, FormatExitSocket, FamilyNetwork},
@@ -816,6 +845,7 @@ func TestClassifySyscallPairEmitsAllFamilies(t *testing.T) {
{"swapon", FormatSwapon, FormatExitSwapon, FamilyFS},
{"swapoff", FormatSwapoff, FormatExitSwapoff, FamilyFS},
{"kill", FormatKill, FormatExitKill, FamilySignals},
+ {"exit_group", syntheticEnter("exit_group", 9316), syntheticExit("exit_group", 9315), FamilyProcess},
}
for _, tt := range tests {
diff --git a/internal/generate/codegen_test.go b/internal/generate/codegen_test.go
index f1c98df..95ced4d 100644
--- a/internal/generate/codegen_test.go
+++ b/internal/generate/codegen_test.go
@@ -64,6 +64,17 @@ func TestGenerateMqOpenHandler(t *testing.T) {
requireContains(t, output, "ev->flags = ctx->args[1];")
}
+func TestGenerateExecHandler(t *testing.T) {
+ output := generateFromPair(t, FormatExecveat, FormatExitExecveat)
+
+ requireContains(t, output, `SEC("tracepoint/syscalls/sys_enter_execveat")`)
+ requireContains(t, output, "struct exec_event *ev")
+ requireContains(t, output, "ev->event_type = ENTER_EXEC_EVENT;")
+ requireContains(t, output, "bpf_probe_read_user_str(ev->filename, sizeof(ev->filename), (void *)ctx->args[1]);")
+ requireContains(t, output, "ev->dirfd = (__s32)ctx->args[0];")
+ requireContains(t, output, "ev->flags = (__s32)ctx->args[4];")
+}
+
func TestGenerateOpenat2Handler(t *testing.T) {
f := mustParseOne(t, FormatOpenat2)
r := ClassifyFormat(&f)
@@ -515,6 +526,7 @@ func TestGenerateAllEventTypes(t *testing.T) {
{KindFd, "ENTER_FD_EVENT", "EXIT_FD_EVENT"},
{KindOpen, "ENTER_OPEN_EVENT", "EXIT_OPEN_EVENT"},
{KindMqOpen, "ENTER_OPEN_EVENT", "EXIT_OPEN_EVENT"},
+ {KindExec, "ENTER_EXEC_EVENT", "EXIT_EXEC_EVENT"},
{KindPathname, "ENTER_PATH_EVENT", "EXIT_PATH_EVENT"},
{KindName, "ENTER_NAME_EVENT", "EXIT_NAME_EVENT"},
{KindRet, "ENTER_RET_EVENT", "EXIT_RET_EVENT"},
@@ -555,6 +567,7 @@ func TestEventStructNames(t *testing.T) {
{KindFd, "fd_event"},
{KindOpen, "open_event"},
{KindMqOpen, "open_event"},
+ {KindExec, "exec_event"},
{KindPathname, "path_event"},
{KindName, "name_event"},
{KindRet, "ret_event"},
@@ -593,7 +606,7 @@ func TestEnterReject(t *testing.T) {
t.Error("KindNone should be enter-rejected")
}
- accepted := []TracepointKind{KindFd, KindOpen, KindMqOpen, KindPathname, KindName, KindFcntl, KindNull, KindDup3, KindOpenByHandleAt, KindSocket, KindSocketpair, KindAccept, KindPipe, KindEventfd, KindEpollCtl, KindTwoFd, KindPoll, KindMem, KindSleep, KindKeyctl, KindPtrace, KindPerfOpen}
+ accepted := []TracepointKind{KindFd, KindOpen, KindMqOpen, KindExec, KindPathname, KindName, KindFcntl, KindNull, KindDup3, KindOpenByHandleAt, KindSocket, KindSocketpair, KindAccept, KindPipe, KindEventfd, KindEpollCtl, KindTwoFd, KindPoll, KindMem, KindSleep, KindKeyctl, KindPtrace, KindPerfOpen}
for _, k := range accepted {
if isEnterRejected(k) {
t.Errorf("kind %d should NOT be enter-rejected", k)
diff --git a/internal/generate/kindregistry.go b/internal/generate/kindregistry.go
index 6afe4c1..a5f5795 100644
--- a/internal/generate/kindregistry.go
+++ b/internal/generate/kindregistry.go
@@ -19,6 +19,7 @@ var kindRegistry = map[TracepointKind]kindMeta{
KindFd: {structName: "fd_event", enterAccepted: true},
KindOpen: {structName: "open_event", enterAccepted: true},
KindMqOpen: {structName: "open_event", enterAccepted: true},
+ KindExec: {structName: "exec_event", enterAccepted: true},
KindPathname: {structName: "path_event", enterAccepted: true},
KindName: {structName: "name_event", enterAccepted: true},
KindRet: {structName: "ret_event", enterAccepted: false},
diff --git a/internal/generate/testdata.go b/internal/generate/testdata.go
index 2bc041e..d94c141 100644
--- a/internal/generate/testdata.go
+++ b/internal/generate/testdata.go
@@ -1098,6 +1098,38 @@ format:
print fmt: "0x%lx", REC->ret
`
+const FormatExecveat = `name: sys_enter_execveat
+ID: 869
+format:
+ field:unsigned short common_type; offset:0; size:2; signed:0;
+ field:unsigned char common_flags; offset:2; size:1; signed:0;
+ field:unsigned char common_preempt_count; offset:3; size:1; signed:0;
+ field:int common_pid; offset:4; size:4; signed:1;
+
+ field:int __syscall_nr; offset:8; size:4; signed:1;
+ field:int dfd; offset:16; size:8; signed:0;
+ field:const char * filename; offset:24; size:8; signed:0;
+ field:const char *const * argv; offset:32; size:8; signed:0;
+ field:const char *const * envp; offset:40; size:8; signed:0;
+ field:int flags; offset:48; size:8; signed:0;
+
+print fmt: "dfd: 0x%08lx, filename: 0x%08lx, argv: 0x%08lx, envp: 0x%08lx, flags: 0x%08lx", ((unsigned long)(REC->dfd)), ((unsigned long)(REC->filename)), ((unsigned long)(REC->argv)), ((unsigned long)(REC->envp)), ((unsigned long)(REC->flags))
+`
+
+const FormatExitExecveat = `name: sys_exit_execveat
+ID: 868
+format:
+ field:unsigned short common_type; offset:0; size:2; signed:0;
+ field:unsigned char common_flags; offset:2; size:1; signed:0;
+ field:unsigned char common_preempt_count; offset:3; size:1; signed:0;
+ field:int common_pid; offset:4; size:4; signed:1;
+
+ field:int __syscall_nr; offset:8; size:4; signed:1;
+ field:long ret; offset:16; size:8; signed:1;
+
+print fmt: "0x%lx", REC->ret
+`
+
const FormatMknod = `name: sys_enter_mknod
ID: 894
format: