From 768e53d90be2d15242266b898023c9c39dacf47d Mon Sep 17 00:00:00 2001 From: Paul Buetow Date: Sat, 30 May 2026 10:13:17 +0300 Subject: fix(z10): skip enter-state write for noreturn syscalls After p10 suppressed the sys_exit_exit/sys_exit_exit_group handlers, the enter handlers for exit/exit_group still called ior_on_syscall_enter, which writes a per-tid entry into syscall_enter_state_map. With the exit handler gone, nothing ever bpf_map_delete_elem'd that entry, so stale per-tid state accumulated in the bounded (32768) map on hosts churning many distinct tids and could starve legitimate inserts. Add ior_on_noreturn_syscall_enter in internal/c/filter.c: it only makes the sampling decision (ior_should_emit_trace) and deliberately does NOT record enter-state. The code generator now emits this hook for noreturn enter handlers (detected via isNoreturnSyscall(syscallName(name))) so the enter null_event is still emitted while the dead, unreclaimable map write is skipped. Regenerated generated_tracepoints.c accordingly. Extend TestGenerateExitNoreturnHandlers with a negative assertion (no ior_on_syscall_enter for noreturn) and add TestGenerateReturningSyscallEnterRecordsState as a positive contrast. Co-Authored-By: Claude Opus 4.8 --- internal/generate/codegen_test.go | 41 +++++++++++++++++++++++++++++++++++++++ 1 file changed, 41 insertions(+) (limited to 'internal/generate/codegen_test.go') diff --git a/internal/generate/codegen_test.go b/internal/generate/codegen_test.go index 7e0e122..7f9c223 100644 --- a/internal/generate/codegen_test.go +++ b/internal/generate/codegen_test.go @@ -1409,10 +1409,51 @@ func TestGenerateExitNoreturnHandlers(t *testing.T) { if strings.Contains(enterBody, "ctx->args[") { t.Errorf("%s: enter handler unexpectedly captures an arg; the int status must be ignored", syscall) } + + // Regression guard (task z10): the noreturn enter handler must emit + // the enter null_event WITHOUT recording enter-state. Because the + // exit handler is suppressed, nothing would ever look up or delete a + // syscall_enter_state_map entry, so recording one would leak a stale + // per-tid entry in the bounded map. The handler must therefore call + // the dedicated ior_on_noreturn_syscall_enter hook (which only makes + // the sampling decision) and must NOT call the state-recording + // ior_on_syscall_enter that normal returning syscalls use. + requireContains(t, output, "ior_on_noreturn_syscall_enter("+strings.ToUpper("sys_enter_"+syscall)+")") + if strings.Contains(enterBody, "ior_on_syscall_enter(") { + t.Errorf("%s: noreturn enter handler must not record enter-state "+ + "(found ior_on_syscall_enter, which writes syscall_enter_state_map)", syscall) + } }) } } +// TestGenerateReturningSyscallEnterRecordsState is the positive contrast to +// TestGenerateExitNoreturnHandlers: a normal returning syscall's enter handler +// DOES record enter-state via ior_on_syscall_enter (so its later exit handler +// can pair durations and delete the entry), and must NOT use the noreturn hook. +func TestGenerateReturningSyscallEnterRecordsState(t *testing.T) { + syscall := "sched_get_priority_min" // a returning KindNull syscall + output := GenerateTracepointsC(mustParseAll(t, syntheticPair(syscall))) + + enterSec := `SEC("tracepoint/syscalls/sys_enter_` + syscall + `")` + enterStart := strings.Index(output, enterSec) + if enterStart < 0 { + t.Fatalf("%s: enter handler not found", syscall) + } + enterEnd := strings.Index(output[enterStart+len(enterSec):], `SEC("tracepoint/`) + enterBody := output[enterStart:] + if enterEnd >= 0 { + enterBody = output[enterStart : enterStart+len(enterSec)+enterEnd] + } + + if !strings.Contains(enterBody, "ior_on_syscall_enter(tid, "+strings.ToUpper("sys_enter_"+syscall)+")") { + t.Errorf("%s: returning syscall enter handler must record enter-state via ior_on_syscall_enter", syscall) + } + if strings.Contains(enterBody, "ior_on_noreturn_syscall_enter(") { + t.Errorf("%s: returning syscall enter handler must not use the noreturn hook", syscall) + } +} + // TestGenerateSchedGetPriorityMinHandler locks in how sched_get_priority_min // (and its identical sibling sched_get_priority_max) are generated. Per // sched_get_priority_min(2): `int sched_get_priority_min(int policy)` takes a -- cgit v1.2.3