diff options
| author | Paul Buetow <paul@buetow.org> | 2026-05-29 16:48:57 +0300 |
|---|---|---|
| committer | Paul Buetow <paul@buetow.org> | 2026-05-29 16:48:57 +0300 |
| commit | 17822912ffd90e2ad187bfa3ff24e2d0525ecfe7 (patch) | |
| tree | abb1b750f697e9af99df2998be70344f1214f3b5 /internal/eventloop_test.go | |
| parent | 0e835414ad424e2061f5c0203e4ba797dd5a9281 (diff) | |
test(eventloop): lock in sync_file_range EBADF failure path
Audit of sync_file_range(2) confirmed the existing tracing is correct:
classified as KindFd (FS family) with fd from args[0] per the kernel
tracepoint format, and an UNCLASSIFIED ret (int 0/-1, no userspace
bytes transferred) - identical to siblings fsync/fdatasync/fadvise64/
readahead. The byte range/offset/flags are intentionally not captured
for fd-kind syscalls.
Add SyncFileRangeFailureTest to lock in the EBADF path: the enter
fd_event still pairs with the exit ret_event carrying ret=-9, and the
eventloop synthesizes a placeholder FdFile (unknown name, O_NONE flags)
for the never-opened bogus fd rather than dropping file metadata.
Co-Authored-By: Claude Opus 4.8 <noreply@anthropic.com>
Diffstat (limited to 'internal/eventloop_test.go')
| -rw-r--r-- | internal/eventloop_test.go | 77 |
1 files changed, 64 insertions, 13 deletions
diff --git a/internal/eventloop_test.go b/internal/eventloop_test.go index b768fcb..79b8677 100644 --- a/internal/eventloop_test.go +++ b/internal/eventloop_test.go @@ -38,19 +38,20 @@ func TestEventloop(t *testing.T) { "OpenEventTest3": makeOpenEventTestData3(t), "OpenEventFailureTest": makeOpenEventFailureTestData(t), // FdEvent tests - "ReadEventTest": makeReadEventTestData(t), - "WriteEventTest": makeWriteEventTestData(t), - "CloseEventTest": makeCloseEventTestData(t), - "CloseRangeEventTest": makeCloseRangeEventTestData(t), - "CloseRangeFailureTest": makeCloseRangeFailureTestData(t), - "FsyncEventTest": makeFsyncEventTestData(t), - "SyncFileRangeEventTest": makeSyncFileRangeEventTestData(t), - "CopyFileRangeEventTest": makeCopyFileRangeEventTestData(t), - "PidfdGetfdEventTest": makePidfdGetfdEventTestData(t), - "PidfdGetfdFailureTest": makePidfdGetfdFailureTestData(t), - "MmapEventTest": makeMmapEventTestData(t), - "MsyncEventTest": makeMsyncEventTestData(t), - "FtruncateEventTest": makeFtruncateEventTestData(t), + "ReadEventTest": makeReadEventTestData(t), + "WriteEventTest": makeWriteEventTestData(t), + "CloseEventTest": makeCloseEventTestData(t), + "CloseRangeEventTest": makeCloseRangeEventTestData(t), + "CloseRangeFailureTest": makeCloseRangeFailureTestData(t), + "FsyncEventTest": makeFsyncEventTestData(t), + "SyncFileRangeEventTest": makeSyncFileRangeEventTestData(t), + "SyncFileRangeFailureTest": makeSyncFileRangeFailureTestData(t), + "CopyFileRangeEventTest": makeCopyFileRangeEventTestData(t), + "PidfdGetfdEventTest": makePidfdGetfdEventTestData(t), + "PidfdGetfdFailureTest": makePidfdGetfdFailureTestData(t), + "MmapEventTest": makeMmapEventTestData(t), + "MsyncEventTest": makeMsyncEventTestData(t), + "FtruncateEventTest": makeFtruncateEventTestData(t), // PathEvent tests "MkdirEventTest": makeMkdirEventTestData(t), "UnlinkEventTest": makeUnlinkEventTestData(t), @@ -883,6 +884,56 @@ func makeSyncFileRangeEventTestData(t *testing.T) (td testData) { return td } +// makeSyncFileRangeFailureTestData locks in the EBADF failure path of +// sync_file_range(2): the enter fd_event must still pair with an exit +// ret_event carrying the negative errno. ior classifies sync_file_range as a +// plain fd_event (fd from args[0], no byte range / flags captured) with an +// UNCLASSIFIED ret. When the fd was never observed via an open-family syscall, +// the eventloop attaches a synthetic placeholder FdFile for the bogus fd +// (unknown name, O_NONE flags) rather than dropping the file metadata, so the +// failed call is still reported as a complete enter/exit pair. +func makeSyncFileRangeFailureTestData(t *testing.T) (td testData) { + fd := int32(99999) // bogus fd, as in the sync-file-range-ebadf scenario + + enterEv, enterEvBytes := makeEnterFdEvent(t, defaulTime, defaultPid, defaultTid, fd, types.SYS_ENTER_SYNC_FILE_RANGE) + td.rawTracepoints = append(td.rawTracepoints, enterEvBytes) + + // EBADF is errno 9; the kernel reports it to the exit tracepoint as -9. + const ebadf = -9 + exitEv, exitEvBytes := makeExitRetEvent(t, defaulTime+100, defaultPid, defaultTid, types.SYS_EXIT_SYNC_FILE_RANGE, ebadf) + td.rawTracepoints = append(td.rawTracepoints, exitEvBytes) + + td.validates = append(td.validates, func(t *testing.T, el *eventLoop, ep *event.Pair) { + if !enterEv.Equals(ep.EnterEv) { + t.Errorf("Expected '%v' but got '%v'", enterEv, ep.EnterEv) + } + if !exitEv.Equals(ep.ExitEv) { + t.Errorf("Expected '%v' but got '%v'", exitEv, ep.ExitEv) + } + // The exit ret_event must carry the negative EBADF errno unchanged. + retEv, ok := ep.ExitEv.(*types.RetEvent) + if !ok { + t.Fatalf("Expected exit *types.RetEvent for failed sync_file_range, got %T", ep.ExitEv) + } + if retEv.Ret != ebadf { + t.Errorf("Expected ret %d (EBADF) but got %d", ebadf, retEv.Ret) + } + // The bogus fd was never opened, so the eventloop synthesizes a + // placeholder FdFile carrying the same fd and O_NONE flags. + if ep.File == nil { + t.Fatalf("Expected synthetic file metadata for failed sync_file_range") + } + if ep.File.FD() != fd { + t.Errorf("Expected synthetic file fd %d but got %d", fd, ep.File.FD()) + } + if flags := ep.File.Flags().String(); flags != "O_NONE" { + t.Errorf("Expected synthetic file flags 'O_NONE' but got '%s'", flags) + } + }) + + return td +} + func makePidfdGetfdEventTestData(t *testing.T) (td testData) { pid := uint32(os.Getpid()) tid := uint32(os.Getpid()) |
