diff options
| author | Paul Buetow <paul@buetow.org> | 2026-05-19 16:15:18 +0300 |
|---|---|---|
| committer | Paul Buetow <paul@buetow.org> | 2026-05-19 16:15:18 +0300 |
| commit | 843def4f5c23db050cccfae57a9acb5899c110f4 (patch) | |
| tree | d90d94c39ff59c7df7278f29f5402c58c391daec /internal/generate | |
| parent | 061fb2b2380752eed06a78d10567da172ea8e27c (diff) | |
x6: add pipe/eventfd fd-from-air syscall support
Diffstat (limited to 'internal/generate')
| -rw-r--r-- | internal/generate/bpfhandler.go | 26 | ||||
| -rw-r--r-- | internal/generate/classify.go | 18 | ||||
| -rw-r--r-- | internal/generate/classify_test.go | 64 | ||||
| -rw-r--r-- | internal/generate/codegen_test.go | 36 | ||||
| -rw-r--r-- | internal/generate/kindregistry.go | 2 | ||||
| -rw-r--r-- | internal/generate/testdata.go | 114 |
6 files changed, 259 insertions, 1 deletions
diff --git a/internal/generate/bpfhandler.go b/internal/generate/bpfhandler.go index bada317..1a6b210 100644 --- a/internal/generate/bpfhandler.go +++ b/internal/generate/bpfhandler.go @@ -79,6 +79,10 @@ func generateExtra(tp GeneratedTracepoint, isEnter bool) string { return generateExtraSocketpair(isEnter) case KindAccept: return generateExtraAccept(isEnter) + case KindPipe: + return generateExtraPipe(f, isEnter) + case KindEventfd: + return generateExtraEventfd(f, isEnter) case KindOpen: return generateExtraOpen(f) case KindPathname: @@ -173,6 +177,28 @@ func generateExtraAccept(isEnter bool) string { return " ev->fd = -1;\n ev->ret = ctx->ret;\n" } +func generateExtraPipe(f *Format, isEnter bool) string { + if isEnter { + flagsExpr := "0" + if f.Name == "sys_enter_pipe2" { + flagsExpr = "(__s32)ctx->args[1]" + } + return " struct pipe_ctx pending;\n pending.upipefd = ctx->args[0];\n pending.flags = " + flagsExpr + ";\n bpf_map_update_elem(&pipe_ctx_map, &tid, &pending, BPF_ANY);\n ev->flags = pending.flags;\n ev->fd0 = -1;\n ev->fd1 = -1;\n ev->ret = 0;\n" + } + return " __s32 flags = 0;\n __s32 fd0 = -1;\n __s32 fd1 = -1;\n struct pipe_ctx *pending = bpf_map_lookup_elem(&pipe_ctx_map, &tid);\n if (pending) {\n flags = pending->flags;\n if (ctx->ret == 0 && pending->upipefd != 0) {\n int pipefd[2];\n if (bpf_probe_read_user(&pipefd, sizeof(pipefd), (void *)pending->upipefd) == 0) {\n fd0 = (__s32)pipefd[0];\n fd1 = (__s32)pipefd[1];\n }\n }\n bpf_map_delete_elem(&pipe_ctx_map, &tid);\n }\n ev->flags = flags;\n ev->fd0 = fd0;\n ev->fd1 = fd1;\n ev->ret = ctx->ret;\n" +} + +func generateExtraEventfd(f *Format, isEnter bool) string { + if isEnter { + flagsExpr := "0" + if f.Name == "sys_enter_eventfd2" { + flagsExpr = "(__s32)ctx->args[1]" + } + return " __s32 flags = " + flagsExpr + ";\n bpf_map_update_elem(&eventfd_flags_map, &tid, &flags, BPF_ANY);\n ev->flags = flags;\n ev->ret = -1;\n" + } + return " __s32 flags = 0;\n __s32 *pending = bpf_map_lookup_elem(&eventfd_flags_map, &tid);\n if (pending) {\n flags = *pending;\n bpf_map_delete_elem(&eventfd_flags_map, &tid);\n }\n ev->flags = flags;\n ev->ret = ctx->ret;\n" +} + // eventStructName returns the C struct name for a TracepointKind. The mapping // is driven by kindRegistry so adding a new kind only requires a registry entry. func eventStructName(kind TracepointKind) string { diff --git a/internal/generate/classify.go b/internal/generate/classify.go index 4f6d14c..0bc00b3 100644 --- a/internal/generate/classify.go +++ b/internal/generate/classify.go @@ -18,6 +18,8 @@ const ( KindSocket KindSocketpair KindAccept + KindPipe + KindEventfd ) type RetClassification string @@ -96,6 +98,22 @@ func classifyNameOnly(name string) (ClassificationResult, bool) { return ClassificationResult{Kind: KindAccept}, true case "sys_exit_accept4": return ClassificationResult{Kind: KindAccept}, true + case "sys_enter_pipe": + return ClassificationResult{Kind: KindPipe}, true + case "sys_exit_pipe": + return ClassificationResult{Kind: KindPipe}, true + case "sys_enter_pipe2": + return ClassificationResult{Kind: KindPipe}, true + case "sys_exit_pipe2": + return ClassificationResult{Kind: KindPipe}, true + case "sys_enter_eventfd": + return ClassificationResult{Kind: KindEventfd}, true + case "sys_exit_eventfd": + return ClassificationResult{Kind: KindEventfd}, true + case "sys_enter_eventfd2": + return ClassificationResult{Kind: KindEventfd}, true + case "sys_exit_eventfd2": + return ClassificationResult{Kind: KindEventfd}, true case "sys_enter_bind": return ClassificationResult{Kind: KindFd}, true case "sys_enter_connect": diff --git a/internal/generate/classify_test.go b/internal/generate/classify_test.go index 5c7111f..09989b8 100644 --- a/internal/generate/classify_test.go +++ b/internal/generate/classify_test.go @@ -313,6 +313,62 @@ func TestClassifyExitSocketpair(t *testing.T) { } } +func TestClassifyPipe(t *testing.T) { + r := classifyFromData(t, FormatPipe) + if r.Kind != KindPipe { + t.Errorf("pipe: got kind %d, want KindPipe", r.Kind) + } +} + +func TestClassifyPipe2(t *testing.T) { + r := classifyFromData(t, FormatPipe2) + if r.Kind != KindPipe { + t.Errorf("pipe2: got kind %d, want KindPipe", r.Kind) + } +} + +func TestClassifyExitPipe(t *testing.T) { + r := classifyFromData(t, FormatExitPipe) + if r.Kind != KindPipe { + t.Errorf("exit_pipe: got kind %d, want KindPipe", r.Kind) + } +} + +func TestClassifyExitPipe2(t *testing.T) { + r := classifyFromData(t, FormatExitPipe2) + if r.Kind != KindPipe { + t.Errorf("exit_pipe2: got kind %d, want KindPipe", r.Kind) + } +} + +func TestClassifyEventfd(t *testing.T) { + r := classifyFromData(t, FormatEventfd) + if r.Kind != KindEventfd { + t.Errorf("eventfd: got kind %d, want KindEventfd", r.Kind) + } +} + +func TestClassifyEventfd2(t *testing.T) { + r := classifyFromData(t, FormatEventfd2) + if r.Kind != KindEventfd { + t.Errorf("eventfd2: got kind %d, want KindEventfd", r.Kind) + } +} + +func TestClassifyExitEventfd(t *testing.T) { + r := classifyFromData(t, FormatExitEventfd) + if r.Kind != KindEventfd { + t.Errorf("exit_eventfd: got kind %d, want KindEventfd", r.Kind) + } +} + +func TestClassifyExitEventfd2(t *testing.T) { + r := classifyFromData(t, FormatExitEventfd2) + if r.Kind != KindEventfd { + t.Errorf("exit_eventfd2: got kind %d, want KindEventfd", r.Kind) + } +} + func TestClassifyKillRequiresGenerationFallback(t *testing.T) { r := classifyFromData(t, FormatKill) if r.Kind != KindNone { @@ -353,6 +409,10 @@ func TestClassifySyscallPairAccepted(t *testing.T) { {"accept4", FormatAccept4, FormatExitAccept4, KindAccept}, {"socket", FormatSocket, FormatExitSocket, KindSocket}, {"socketpair", FormatSocketpair, FormatExitSocketpair, KindSocketpair}, + {"pipe", FormatPipe, FormatExitPipe, KindPipe}, + {"pipe2", FormatPipe2, FormatExitPipe2, KindPipe}, + {"eventfd", FormatEventfd, FormatExitEventfd, KindEventfd}, + {"eventfd2", FormatEventfd2, FormatExitEventfd2, KindEventfd}, {"kill", FormatKill, FormatExitKill, KindNull}, } @@ -380,6 +440,10 @@ func TestClassifySyscallPairEmitsAllFamilies(t *testing.T) { {"accept4", FormatAccept4, FormatExitAccept4, FamilyNetwork}, {"socket", FormatSocket, FormatExitSocket, FamilyNetwork}, {"socketpair", FormatSocketpair, FormatExitSocketpair, FamilyNetwork}, + {"pipe", FormatPipe, FormatExitPipe, FamilyIPC}, + {"pipe2", FormatPipe2, FormatExitPipe2, FamilyIPC}, + {"eventfd", FormatEventfd, FormatExitEventfd, FamilyIPC}, + {"eventfd2", FormatEventfd2, FormatExitEventfd2, FamilyIPC}, {"kill", FormatKill, FormatExitKill, FamilySignals}, } diff --git a/internal/generate/codegen_test.go b/internal/generate/codegen_test.go index d8130c8..1c99314 100644 --- a/internal/generate/codegen_test.go +++ b/internal/generate/codegen_test.go @@ -225,6 +225,36 @@ func TestGenerateAcceptHandler(t *testing.T) { requireContains(t, output, "ev->ret = ctx->ret;") } +func TestGeneratePipeHandler(t *testing.T) { + output := generateFromPair(t, FormatPipe2, FormatExitPipe2) + + requireContains(t, output, "struct pipe_event *ev") + requireContains(t, output, "ev->event_type = ENTER_PIPE_EVENT;") + requireContains(t, output, "struct pipe_ctx pending;") + requireContains(t, output, "pending.upipefd = ctx->args[0];") + requireContains(t, output, "pending.flags = (__s32)ctx->args[1];") + requireContains(t, output, "bpf_map_update_elem(&pipe_ctx_map, &tid, &pending, BPF_ANY);") + requireContains(t, output, "ev->fd0 = -1;") + requireContains(t, output, "ev->fd1 = -1;") + requireContains(t, output, "SEC(\"tracepoint/syscalls/sys_exit_pipe2\")") + requireContains(t, output, "ev->event_type = EXIT_PIPE_EVENT;") + requireContains(t, output, "struct pipe_ctx *pending = bpf_map_lookup_elem(&pipe_ctx_map, &tid);") + requireContains(t, output, "ev->ret = ctx->ret;") +} + +func TestGenerateEventfdHandler(t *testing.T) { + output := generateFromPair(t, FormatEventfd2, FormatExitEventfd2) + + requireContains(t, output, "struct eventfd_event *ev") + requireContains(t, output, "ev->event_type = ENTER_EVENTFD_EVENT;") + requireContains(t, output, "bpf_map_update_elem(&eventfd_flags_map, &tid, &flags, BPF_ANY);") + requireContains(t, output, "ev->flags = flags;") + requireContains(t, output, "ev->ret = -1;") + requireContains(t, output, "SEC(\"tracepoint/syscalls/sys_exit_eventfd2\")") + requireContains(t, output, "ev->event_type = EXIT_EVENTFD_EVENT;") + requireContains(t, output, "ev->ret = ctx->ret;") +} + func TestGenerateNameToHandleAtHandler(t *testing.T) { output := generateFromPair(t, FormatNameToHandleAt, FormatExitNameToHandleAt) @@ -335,6 +365,8 @@ func TestGenerateAllEventTypes(t *testing.T) { {KindSocket, "ENTER_SOCKET_EVENT", "EXIT_SOCKET_EVENT"}, {KindSocketpair, "ENTER_SOCKETPAIR_EVENT", "EXIT_SOCKETPAIR_EVENT"}, {KindAccept, "ENTER_ACCEPT_EVENT", "EXIT_ACCEPT_EVENT"}, + {KindPipe, "ENTER_PIPE_EVENT", "EXIT_PIPE_EVENT"}, + {KindEventfd, "ENTER_EVENTFD_EVENT", "EXIT_EVENTFD_EVENT"}, } for _, tt := range tests { @@ -364,6 +396,8 @@ func TestEventStructNames(t *testing.T) { {KindSocket, "socket_event"}, {KindSocketpair, "socketpair_event"}, {KindAccept, "accept_event"}, + {KindPipe, "pipe_event"}, + {KindEventfd, "eventfd_event"}, } for _, tt := range tests { @@ -382,7 +416,7 @@ func TestEnterReject(t *testing.T) { t.Error("KindNone should be enter-rejected") } - accepted := []TracepointKind{KindFd, KindOpen, KindPathname, KindName, KindFcntl, KindNull, KindDup3, KindOpenByHandleAt, KindSocket, KindSocketpair, KindAccept} + accepted := []TracepointKind{KindFd, KindOpen, KindPathname, KindName, KindFcntl, KindNull, KindDup3, KindOpenByHandleAt, KindSocket, KindSocketpair, KindAccept, KindPipe, KindEventfd} 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 186dbbc..17f7542 100644 --- a/internal/generate/kindregistry.go +++ b/internal/generate/kindregistry.go @@ -28,6 +28,8 @@ var kindRegistry = map[TracepointKind]kindMeta{ KindSocket: {structName: "socket_event", enterAccepted: true}, KindSocketpair: {structName: "socketpair_event", enterAccepted: true}, KindAccept: {structName: "accept_event", enterAccepted: true}, + KindPipe: {structName: "pipe_event", enterAccepted: true}, + KindEventfd: {structName: "eventfd_event", enterAccepted: true}, // KindNone is intentionally absent: it represents "unclassified" and is // never enter-accepted. lookupKind returns the zero kindMeta (enterAccepted=false) // for any unregistered kind, so KindNone is implicitly rejected. diff --git a/internal/generate/testdata.go b/internal/generate/testdata.go index 090619b..b34f98d 100644 --- a/internal/generate/testdata.go +++ b/internal/generate/testdata.go @@ -885,6 +885,120 @@ format: print fmt: "0x%lx", REC->ret ` +const FormatPipe = `name: sys_enter_pipe +ID: 873 +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 * fildes; offset:16; size:8; signed:0; + +print fmt: "fildes: 0x%08lx", ((unsigned long)(REC->fildes)) +` + +const FormatExitPipe = `name: sys_exit_pipe +ID: 872 +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 FormatPipe2 = `name: sys_enter_pipe2 +ID: 875 +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 * fildes; offset:16; size:8; signed:0; + field:int flags; offset:24; size:8; signed:0; + +print fmt: "fildes: 0x%08lx, flags: 0x%08lx", ((unsigned long)(REC->fildes)), ((unsigned long)(REC->flags)) +` + +const FormatExitPipe2 = `name: sys_exit_pipe2 +ID: 874 +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 FormatEventfd = `name: sys_enter_eventfd +ID: 1095 +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:unsigned int count; offset:16; size:8; signed:0; + +print fmt: "count: 0x%08lx", ((unsigned long)(REC->count)) +` + +const FormatExitEventfd = `name: sys_exit_eventfd +ID: 1094 +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 FormatEventfd2 = `name: sys_enter_eventfd2 +ID: 1097 +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:unsigned int count; offset:16; size:8; signed:0; + field:int flags; offset:24; size:8; signed:0; + +print fmt: "count: 0x%08lx, flags: 0x%08lx", ((unsigned long)(REC->count)), ((unsigned long)(REC->flags)) +` + +const FormatExitEventfd2 = `name: sys_exit_eventfd2 +ID: 1096 +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 FormatPread64 = `name: sys_enter_pread64 ID: 840 format: |
