summaryrefslogtreecommitdiff
path: root/internal/generate
diff options
context:
space:
mode:
Diffstat (limited to 'internal/generate')
-rw-r--r--internal/generate/classify_test.go12
-rw-r--r--internal/generate/codegen.go21
-rw-r--r--internal/generate/codegen_test.go22
3 files changed, 38 insertions, 17 deletions
diff --git a/internal/generate/classify_test.go b/internal/generate/classify_test.go
index 28b4137..904a7ff 100644
--- a/internal/generate/classify_test.go
+++ b/internal/generate/classify_test.go
@@ -1522,7 +1522,17 @@ func TestClassifySyscallPairEmitsAllFamilies(t *testing.T) {
if !strings.Contains(output, `SEC("tracepoint/syscalls/sys_enter_`+tt.name+`")`) {
t.Errorf("syscall %s missing enter handler", tt.name)
}
- if !strings.Contains(output, `SEC("tracepoint/syscalls/sys_exit_`+tt.name+`")`) {
+ // Noreturn syscalls (exit, exit_group) are deliberately exempt from
+ // the exit-handler requirement: their sys_exit handler can never
+ // fire because the syscall never returns, so codegen suppresses it
+ // via isNoreturnSyscall (see TestGenerateExitNoreturnHandlers). For
+ // every other syscall the exit handler is still required.
+ hasExitHandler := strings.Contains(output, `SEC("tracepoint/syscalls/sys_exit_`+tt.name+`")`)
+ if isNoreturnSyscall(tt.name) {
+ if hasExitHandler {
+ t.Errorf("noreturn syscall %s must not emit an exit handler", tt.name)
+ }
+ } else if !hasExitHandler {
t.Errorf("syscall %s missing exit handler", tt.name)
}
})
diff --git a/internal/generate/codegen.go b/internal/generate/codegen.go
index 3848b93..339dc1f 100644
--- a/internal/generate/codegen.go
+++ b/internal/generate/codegen.go
@@ -125,7 +125,11 @@ func classifySyscall(sc Syscall) ([]GeneratedTracepoint, string) {
if sc.Enter != nil {
result = append(result, GeneratedTracepoint{Format: sc.Enter, Classification: enterClass})
}
- if sc.Exit != nil {
+ // Emit the exit handler only for syscalls that can actually return.
+ // Noreturn syscalls (exit, exit_group) never return to userspace, so their
+ // sys_exit tracepoint never fires; emitting a handler would be dead code in
+ // the generated BPF program. We still emit their enter handler above.
+ if sc.Exit != nil && !isNoreturnSyscall(sc.Name) {
result = append(result, GeneratedTracepoint{Format: sc.Exit, Classification: exitClass})
}
return result, ""
@@ -146,6 +150,21 @@ func isEnterRejected(kind TracepointKind) bool {
return !lookupKind(kind).enterAccepted
}
+// noreturnSyscalls lists syscalls that never return control to userspace.
+// Their sys_exit tracepoint can never fire, so the generator suppresses the
+// matching exit handler (see classifySyscall) to avoid dead code in the
+// generated BPF program.
+var noreturnSyscalls = map[string]bool{
+ "exit": true,
+ "exit_group": true,
+}
+
+// isNoreturnSyscall reports whether the named syscall never returns and thus
+// must not have an exit handler emitted.
+func isNoreturnSyscall(name string) bool {
+ return noreturnSyscalls[name]
+}
+
func syscallFormatNames(sc Syscall) []string {
var names []string
if sc.Enter != nil {
diff --git a/internal/generate/codegen_test.go b/internal/generate/codegen_test.go
index 946aea7..ae2d643 100644
--- a/internal/generate/codegen_test.go
+++ b/internal/generate/codegen_test.go
@@ -594,10 +594,9 @@ func TestGenerateFallbackNullHandler(t *testing.T) {
// (FamilyProcess), so:
// - The enter handler emits a struct null_event and intentionally does NOT
// capture the int status arg (it is not an I/O resource like an fd/path).
-// - The kernel still exposes sys_exit_{exit,exit_group} tracepoints, so the
-// generator emits matching EXIT_RET_EVENT handlers, but those handlers can
-// never fire at runtime because the syscall does not return. This is
-// harmless: the generator pairs by name, not by observed return events.
+// - The kernel still exposes sys_exit_{exit,exit_group} tracepoints, but
+// those handlers can never fire at runtime because the syscall does not
+// return. The generator suppresses the dead exit handlers.
func TestGenerateExitNoreturnHandlers(t *testing.T) {
for _, syscall := range []string{"exit", "exit_group"} {
t.Run(syscall, func(t *testing.T) {
@@ -608,22 +607,15 @@ func TestGenerateExitNoreturnHandlers(t *testing.T) {
requireContains(t, output, enterSec)
requireContains(t, output, "struct null_event *ev")
requireContains(t, output, "ev->event_type = ENTER_NULL_EVENT;")
- // The noreturn exit handler is still emitted (kernel exposes the
- // tracepoint) even though it never fires.
- requireContains(t, output, exitSec)
- requireContains(t, output, "ev->event_type = EXIT_RET_EVENT;")
-
- // The int status arg must NOT be captured: a null_event has no
- // arg fields, so no ctx->args[...] read should be generated for
- // the enter handler.
+ if strings.Contains(output, exitSec) {
+ t.Errorf("%s: noreturn syscall must not emit an exit handler", syscall)
+ }
+
enterStart := strings.Index(output, enterSec)
if enterStart < 0 {
t.Fatalf("%s: enter handler not found", syscall)
}
enterBody := output[enterStart:]
- if exitStart := strings.Index(enterBody, exitSec); exitStart > 0 {
- enterBody = enterBody[:exitStart]
- }
if strings.Contains(enterBody, "ctx->args[") {
t.Errorf("%s: enter handler unexpectedly captures an arg; the int status must be ignored", syscall)
}