summaryrefslogtreecommitdiff
path: root/internal/generate
diff options
context:
space:
mode:
authorPaul Buetow <paul@buetow.org>2026-05-30 16:31:26 +0300
committerPaul Buetow <paul@buetow.org>2026-05-30 16:31:26 +0300
commit5210f20b16a224f58117ffb71b2a0691c02a50ed (patch)
treeae271827a28d39e3ae149b73f7ca10fad9521c6d /internal/generate
parent0f470e8e63d9cb458c711ade594e18acd8791504 (diff)
test(signalfd4): lock in args[3] flags index for eventfd handler
Audit of signalfd4(2) confirmed the tracing is correct: classified as KindEventfd in FamilyIPC like its fd-creating siblings (eventfd2, timerfd_create, inotify_init1, signalfd), flags captured from args[3] per signalfd4(ufd, mask, sizemask, flags), and the return value left Unclassified (it is an fd, not a byte count). Add testdata fixtures FormatSignalfd4/FormatExitSignalfd4 (real Linux 7.0 tracepoint data) and a codegen lock-in test asserting the generated handler reads flags from args[3], with negative guards against args[0] (ufd), args[1] (mask pointer) and args[2] (sizemask). Co-Authored-By: Claude Opus 4.8 <noreply@anthropic.com>
Diffstat (limited to 'internal/generate')
-rw-r--r--internal/generate/codegen_test.go33
-rw-r--r--internal/generate/testdata.go37
2 files changed, 70 insertions, 0 deletions
diff --git a/internal/generate/codegen_test.go b/internal/generate/codegen_test.go
index 3753b17..68a372e 100644
--- a/internal/generate/codegen_test.go
+++ b/internal/generate/codegen_test.go
@@ -1330,6 +1330,39 @@ func TestGeneratePidfdOpenHandlerUsesArg1Flags(t *testing.T) {
requireContains(t, output, "ev->ret = ctx->ret;")
}
+// TestGenerateSignalfd4HandlerUsesArg3Flags locks in that signalfd4(2) is
+// emitted as a KindEventfd handler whose flags are read from args[3]. The raw
+// syscall is signalfd4(int ufd, const sigset_t *mask, size_t sizemask, int
+// flags): args[0] (ufd) is an existing signalfd or -1, args[1]/args[2] are the
+// mask pointer and its size — none of those are the flags. A wrong index would
+// silently report bogus SFD_NONBLOCK/SFD_CLOEXEC bits, so guard args[3]
+// explicitly. The exit returns the new (or modified) fd, captured as a plain
+// ret with no read/write byte classification.
+func TestGenerateSignalfd4HandlerUsesArg3Flags(t *testing.T) {
+ output := generateFromPair(t, FormatSignalfd4, FormatExitSignalfd4)
+
+ requireContains(t, output, "struct eventfd_event *ev")
+ requireContains(t, output, "ev->event_type = ENTER_EVENTFD_EVENT;")
+ requireContains(t, output, "ev->trace_id = SYS_ENTER_SIGNALFD4;")
+ // signalfd4(ufd, mask, sizemask, flags): flags is at args[3].
+ requireContains(t, output, "__s32 flags = (__s32)ctx->args[3];")
+ // Must not mistake ufd (args[0]), the mask pointer (args[1]) or sizemask
+ // (args[2]) for the flags argument.
+ for _, wrong := range []string{
+ "flags = (__s32)ctx->args[0]",
+ "flags = (__s32)ctx->args[1]",
+ "flags = (__s32)ctx->args[2]",
+ } {
+ if strings.Contains(output, wrong) {
+ t.Errorf("signalfd4 handler must read flags from args[3], not via %q", wrong)
+ }
+ }
+ requireContains(t, output, "ev->ret = -1;")
+ requireContains(t, output, "SEC(\"tracepoint/syscalls/sys_exit_signalfd4\")")
+ requireContains(t, output, "ev->event_type = EXIT_EVENTFD_EVENT;")
+ requireContains(t, output, "ev->ret = ctx->ret;")
+}
+
func TestGenerateEpollCtlHandler(t *testing.T) {
output := generateFromPair(t, FormatEpollCtl, FormatExitEpollCtl)
diff --git a/internal/generate/testdata.go b/internal/generate/testdata.go
index 5b38cfd..6555fc7 100644
--- a/internal/generate/testdata.go
+++ b/internal/generate/testdata.go
@@ -2053,6 +2053,43 @@ format:
print fmt: "0x%lx", REC->ret
`
+// FormatSignalfd4 is real sysfs data for signalfd4(2) captured from a Linux 7.0
+// kernel. The raw syscall is signalfd4(int ufd, const sigset_t *mask, size_t
+// sizemask, int flags): ufd at args[0], user_mask at args[1], sizemask at
+// args[2], and crucially the flags (SFD_NONBLOCK/SFD_CLOEXEC) at args[3]. ior
+// classifies it as KindEventfd (an fd-creating IPC syscall), so the generator
+// must capture flags from args[3], never any earlier index.
+const FormatSignalfd4 = `name: sys_enter_signalfd4
+ID: 1087
+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 ufd; offset:16; size:8; signed:0;
+ field:sigset_t * user_mask; offset:24; size:8; signed:0;
+ field:size_t sizemask; offset:32; size:8; signed:0;
+ field:int flags; offset:40; size:8; signed:0;
+
+print fmt: "ufd: 0x%08lx, user_mask: 0x%08lx, sizemask: 0x%08lx, flags: 0x%08lx", ((unsigned long)(REC->ufd)), ((unsigned long)(REC->user_mask)), ((unsigned long)(REC->sizemask)), ((unsigned long)(REC->flags))
+`
+
+const FormatExitSignalfd4 = `name: sys_exit_signalfd4
+ID: 1086
+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
+`
+
// FormatMkdirat is real sysfs data for mkdirat(2): the pathname argument sits
// at args[1], AFTER the dirfd at args[0]. Captured from a Linux 7.0 kernel,
// which also exposes the __data_loc __pathname_val trailing field. The