diff options
Diffstat (limited to 'internal')
| -rw-r--r-- | internal/c/generated_tracepoints.c | 7 | ||||
| -rw-r--r-- | internal/c/generated_tracepoints_result.txt | 2 | ||||
| -rw-r--r-- | internal/generate/classify.go | 13 | ||||
| -rw-r--r-- | internal/generate/classify_test.go | 52 | ||||
| -rw-r--r-- | internal/tracepoints/generated_tracepoints.go | 2 |
5 files changed, 69 insertions, 7 deletions
diff --git a/internal/c/generated_tracepoints.c b/internal/c/generated_tracepoints.c index fbf690c..a8437df 100644 --- a/internal/c/generated_tracepoints.c +++ b/internal/c/generated_tracepoints.c @@ -10118,7 +10118,7 @@ int handle_sys_exit_pwritev2(struct syscall_trace_exit *ctx) { return 0; } -/// sys_enter_sendfile64 is a struct null_event (kind=null) +/// sys_enter_sendfile64 is a struct fd_event (kind=fd) SEC("tracepoint/syscalls/sys_enter_sendfile64") int handle_sys_enter_sendfile64(struct syscall_trace_enter *ctx) { __u32 pid, tid; @@ -10128,15 +10128,16 @@ int handle_sys_enter_sendfile64(struct syscall_trace_enter *ctx) { if (!ior_on_syscall_enter(tid, SYS_ENTER_SENDFILE64)) return 0; - struct null_event *ev = bpf_ringbuf_reserve(&event_map, sizeof(struct null_event), 0); + struct fd_event *ev = bpf_ringbuf_reserve(&event_map, sizeof(struct fd_event), 0); if (!ev) return 0; - ev->event_type = ENTER_NULL_EVENT; + ev->event_type = ENTER_FD_EVENT; ev->trace_id = SYS_ENTER_SENDFILE64; ev->pid = pid; ev->tid = tid; ev->time = bpf_ktime_get_boot_ns(); + ev->fd = (__s32)ctx->args[0]; bpf_ringbuf_submit(ev, 0); return 0; diff --git a/internal/c/generated_tracepoints_result.txt b/internal/c/generated_tracepoints_result.txt index f59a820..3587939 100644 --- a/internal/c/generated_tracepoints_result.txt +++ b/internal/c/generated_tracepoints_result.txt @@ -279,7 +279,7 @@ sys_enter_semctl is a struct null_event (kind=sysv-op) sys_enter_semget is a struct null_event (kind=sysv-id) sys_enter_semop is a struct null_event (kind=sysv-op) sys_enter_semtimedop is a struct null_event (kind=sysv-op) -sys_enter_sendfile64 is a struct null_event (kind=null) +sys_enter_sendfile64 is a struct fd_event (kind=fd) sys_enter_sendmmsg is a struct fd_event (kind=fd) sys_enter_sendmsg is a struct fd_event (kind=fd) sys_enter_sendto is a struct fd_event (kind=fd) diff --git a/internal/generate/classify.go b/internal/generate/classify.go index b7e9c0f..f85cb93 100644 --- a/internal/generate/classify.go +++ b/internal/generate/classify.go @@ -246,7 +246,18 @@ var nameOnlyKindsTable = map[string]TracepointKind{ // fd_event. This lets the runtime honour the upper bound and the // CLOSE_RANGE_CLOEXEC flag instead of closing every fd >= first. "sys_enter_close_range": KindTwoFd, - "sys_enter_statmount": KindNull, + // sendfile64(out_fd, in_fd, offset, count) transfers bytes between two file + // descriptors inside the kernel and returns the number of bytes written to + // out_fd (TransferClassified, see retClassifications). Its tracepoint fields + // (out_fd, in_fd, offset, count) carry no field literally named "fd", so + // without an explicit override it would fall through to KindNull and capture + // no descriptor at all — unlike its sibling copy_file_range, which is a + // KindFd event. Capture out_fd (args[0], the destination the bytes are + // written to) so sendfile64 attributes its transfer to a concrete fd, matching + // the single-fd KindFd convention used for copy_file_range and the + // read/write/sendto/recvfrom families. + "sys_enter_sendfile64": KindFd, + "sys_enter_statmount": KindNull, "sys_enter_listmount": KindNull, "sys_enter_listns": KindNull, diff --git a/internal/generate/classify_test.go b/internal/generate/classify_test.go index 25d01b4..f161ef8 100644 --- a/internal/generate/classify_test.go +++ b/internal/generate/classify_test.go @@ -985,6 +985,56 @@ func TestClassifyN7NameOnlyKinds(t *testing.T) { } } +// TestClassifySendfile64CapturesOutFd locks in the sendfile64 audit (task az): +// sendfile64(out_fd, in_fd, offset, count) transfers bytes between two file +// descriptors inside the kernel and returns the count written to out_fd. Its +// real tracepoint fields carry no field literally named "fd", so without the +// explicit nameOnlyKindsTable override it would fall through to KindNull and +// capture no descriptor — inconsistent with its sibling copy_file_range (KindFd) +// and the read/write/sendto/recvfrom families. This test pins that sendfile64 is +// a KindFd event capturing out_fd (args[0], the write destination) and that the +// generated C emits exactly that capture, never a null_event. +func TestClassifySendfile64CapturesOutFd(t *testing.T) { + // Realistic enter layout from /sys/kernel/tracing for sys_enter_sendfile64. + enter := &Format{ + Name: "sys_enter_sendfile64", + ExternalFields: []Field{ + {Type: "long", Name: "__syscall_nr"}, + {Type: "int", Name: "out_fd"}, + {Type: "int", Name: "in_fd"}, + {Type: "off_t *", Name: "offset"}, + {Type: "size_t", Name: "count"}, + }, + } + r := ClassifyFormat(enter) + if r.Kind != KindFd { + t.Fatalf("sendfile64: got kind %d, want KindFd (must not fall back to KindNull)", r.Kind) + } + // Negative guard: out_fd/in_fd must not be mistaken for a two-fd event; the + // audit deliberately keeps sendfile64 single-fd like copy_file_range. + if r.Kind == KindTwoFd || r.Kind == KindNull { + t.Fatalf("sendfile64: kind %d, want single-fd KindFd, not two-fd/null", r.Kind) + } + + // Generated C must capture out_fd at args[0] (the byte-write destination) via + // a struct fd_event, never a struct null_event. + output := GenerateTracepointsC(phaseAFormats("sendfile64", 9500)) + if !strings.Contains(output, "/// sys_enter_sendfile64 is a struct fd_event") { + t.Fatalf("sys_enter_sendfile64 should be a struct fd_event:\n%s", output) + } + if strings.Contains(output, "/// sys_enter_sendfile64 is a struct null_event") { + t.Fatalf("sys_enter_sendfile64 must not be a struct null_event:\n%s", output) + } + if !strings.Contains(output, "ev->fd = (__s32)ctx->args[0];") { + t.Fatalf("sys_enter_sendfile64 should capture out_fd from args[0]:\n%s", output) + } + // Return value stays TransferClassified: sendfile64 moves bytes between two + // fds, consistent with copy_file_range/splice/tee/vmsplice. + if c := ClassifyRet("sys_exit_sendfile64"); c != TransferClassified { + t.Fatalf("sendfile64 ret: got %v, want TransferClassified", c) + } +} + func TestClassifyG7NameOnlyKinds(t *testing.T) { tests := []struct { name string @@ -2000,7 +2050,7 @@ func TestClassifyPhaseAByteSyscallPairsAccepted(t *testing.T) { {"recvmsg", "struct fd_event", "READ_CLASSIFIED"}, {"sendto", "struct fd_event", "WRITE_CLASSIFIED"}, {"sendmsg", "struct fd_event", "WRITE_CLASSIFIED"}, - {"sendfile64", "struct null_event", "TRANSFER_CLASSIFIED"}, + {"sendfile64", "struct fd_event", "TRANSFER_CLASSIFIED"}, {"splice", "struct null_event", "TRANSFER_CLASSIFIED"}, {"tee", "struct null_event", "TRANSFER_CLASSIFIED"}, {"process_vm_readv", "struct null_event", "READ_CLASSIFIED"}, diff --git a/internal/tracepoints/generated_tracepoints.go b/internal/tracepoints/generated_tracepoints.go index b0f9112..38b1f4c 100644 --- a/internal/tracepoints/generated_tracepoints.go +++ b/internal/tracepoints/generated_tracepoints.go @@ -1388,7 +1388,7 @@ var syscallKinds = map[string]string{ "semget": "sysv-id", "semop": "sysv-op", "semtimedop": "sysv-op", - "sendfile64": "null", + "sendfile64": "fd", "sendmmsg": "fd", "sendmsg": "fd", "sendto": "fd", |
