diff options
| author | Paul Buetow <paul@buetow.org> | 2026-05-30 22:25:49 +0300 |
|---|---|---|
| committer | Paul Buetow <paul@buetow.org> | 2026-05-30 22:25:49 +0300 |
| commit | 136c4dfb6846595b98cf2b04a93525ce91d86d5e (patch) | |
| tree | 32c4eb7a6203879796c4598d82f51c028a182971 /internal/generate/codegen.go | |
| parent | db7c18b976c6bb87ca3dbbfdf436c1945aab3289 (diff) | |
generate: treat rt_sigreturn as noreturn (suppress dead exit handler)
rt_sigreturn(2) restores the pre-signal execution context off the signal
stack frame and resumes the interrupted instruction; it never returns to
the instruction after the syscall. man sigreturn(2) states plainly that
"sigreturn() never returns", and tracing against /sys/kernel/tracing
confirms it: sys_enter_rt_sigreturn fires once per signal-handler return
while sys_exit_rt_sigreturn never fires.
The generator previously emitted a dead handle_sys_exit_rt_sigreturn (it
can never run) and recorded a per-tid syscall_enter_state_map entry on the
enter path that nothing would ever delete (no exit fires), leaking entries
in the bounded map on every signal-handler return.
Add rt_sigreturn to noreturnSyscalls so codegen suppresses the dead exit
handler and routes the enter handler through ior_on_noreturn_syscall_enter
(sampling decision only, no map write), exactly like exit/exit_group. The
enter null_event is still emitted, and the FamilySignals/KindNull
classification is unchanged. Regenerated the C/Go artifacts and the result
baseline accordingly, and generalized the related comments.
Lock-in tests: TestRtSigreturnIsNoreturn asserts rt_sigreturn is noreturn;
TestRtSigSiblingsAreNotNoreturn guards that the returning rt_sig* siblings
are not; TestGenerateExitNoreturnHandlers now also covers rt_sigreturn.
Co-Authored-By: Claude Opus 4.8 <noreply@anthropic.com>
Diffstat (limited to 'internal/generate/codegen.go')
| -rw-r--r-- | internal/generate/codegen.go | 27 |
1 files changed, 20 insertions, 7 deletions
diff --git a/internal/generate/codegen.go b/internal/generate/codegen.go index 339dc1f..5b9ea7c 100644 --- a/internal/generate/codegen.go +++ b/internal/generate/codegen.go @@ -126,9 +126,10 @@ func classifySyscall(sc Syscall) ([]GeneratedTracepoint, string) { result = append(result, GeneratedTracepoint{Format: sc.Enter, Classification: enterClass}) } // 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. + // Noreturn syscalls (exit, exit_group, rt_sigreturn) never return to the + // syscall site, 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}) } @@ -150,13 +151,25 @@ func isEnterRejected(kind TracepointKind) bool { return !lookupKind(kind).enterAccepted } -// noreturnSyscalls lists syscalls that never return control to userspace. +// noreturnSyscalls lists syscalls that never return to the syscall site. // 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. +// generated BPF program, and the enter handler uses the noreturn enter hook +// that skips the (otherwise un-reclaimable) syscall_enter_state_map write. +// +// - exit / exit_group terminate the thread/process; control never returns. +// - rt_sigreturn restores the pre-signal execution context off the signal +// stack frame and resumes the interrupted instruction. It does NOT return +// to the instruction after the rt_sigreturn syscall, so the kernel never +// fires sys_exit_rt_sigreturn. Verified empirically against +// /sys/kernel/tracing: sys_enter_rt_sigreturn fires once per signal-handler +// return while sys_exit_rt_sigreturn never does. The man page (sigreturn(2)) +// states plainly that "sigreturn() never returns". rt_sigreturn is emitted +// by the signal trampoline, not called directly by applications. var noreturnSyscalls = map[string]bool{ - "exit": true, - "exit_group": true, + "exit": true, + "exit_group": true, + "rt_sigreturn": true, } // isNoreturnSyscall reports whether the named syscall never returns and thus |
