summaryrefslogtreecommitdiff
path: root/integrationtests
diff options
context:
space:
mode:
authorPaul Buetow <paul@buetow.org>2026-02-21 21:03:33 +0200
committerPaul Buetow <paul@buetow.org>2026-02-21 21:03:33 +0200
commit44a5deb87bef82fe88a40bbeac634d841709b290 (patch)
treeb0f8b3af65476603c7a2313ca5ae9e2279810b93 /integrationtests
parentbe7f8fc048fd92eb806159de5bdb3a3becea3c16 (diff)
Add negative integration tests for close syscalls
Add three negative scenarios to verify ior captures tracepoints even when close syscalls fail: - close-invalid-fd: close an fd (99999) that was never opened (EBADF) - close-double-close: open a file, close it, close same fd again (EBADF) - close-range-empty: close_range on high fd range with no open fds Task: 348 Amp-Thread-ID: https://ampcode.com/threads/T-019c8190-c9ae-776e-b00d-476207d8c10e Co-authored-by: Amp <amp@ampcode.com>
Diffstat (limited to 'integrationtests')
-rw-r--r--integrationtests/close_test.go36
-rw-r--r--integrationtests/cmd/ioworkload/scenarios.go56
2 files changed, 90 insertions, 2 deletions
diff --git a/integrationtests/close_test.go b/integrationtests/close_test.go
index d91a37d..3689fb8 100644
--- a/integrationtests/close_test.go
+++ b/integrationtests/close_test.go
@@ -23,3 +23,39 @@ func TestCloseRange(t *testing.T) {
},
})
}
+
+func TestCloseInvalidFd(t *testing.T) {
+ runScenario(t, "close-invalid-fd", []ExpectedEvent{
+ {
+ Tracepoint: "enter_close",
+ Comm: "ioworkload",
+ MinCount: 1,
+ },
+ })
+}
+
+func TestCloseDoubleClose(t *testing.T) {
+ runScenario(t, "close-double-close", []ExpectedEvent{
+ {
+ PathContains: "doubleclosefile.txt",
+ Tracepoint: "enter_close",
+ Comm: "ioworkload",
+ MinCount: 1,
+ },
+ {
+ Tracepoint: "enter_close",
+ Comm: "ioworkload",
+ MinCount: 2,
+ },
+ })
+}
+
+func TestCloseRangeEmpty(t *testing.T) {
+ runScenario(t, "close-range-empty", []ExpectedEvent{
+ {
+ Tracepoint: "enter_close_range",
+ Comm: "ioworkload",
+ MinCount: 1,
+ },
+ })
+}
diff --git a/integrationtests/cmd/ioworkload/scenarios.go b/integrationtests/cmd/ioworkload/scenarios.go
index bf9e0be..390424f 100644
--- a/integrationtests/cmd/ioworkload/scenarios.go
+++ b/integrationtests/cmd/ioworkload/scenarios.go
@@ -28,8 +28,11 @@ var scenarios = map[string]func() error{
"readwrite-rdonly-write": readwriteRdonlyWrite,
"readwrite-pread-invalid": readwritePreadInvalid,
"readwrite-pwrite-invalid": readwritePwriteInvalid,
- "close-basic": closeBasic,
- "close-range": closeRange,
+ "close-basic": closeBasic,
+ "close-range": closeRange,
+ "close-invalid-fd": closeInvalidFd,
+ "close-double-close": closeDoubleClose,
+ "close-range-empty": closeRangeEmpty,
"dup-basic": dupBasic,
"dup-dup2": dupDup2,
"dup-dup3": dupDup3,
@@ -504,6 +507,55 @@ func closeRange() error {
const sysCloseRange = 436
+// closeInvalidFd attempts to close a very high fd number that is not open.
+// The close fails with EBADF, but ior should capture the enter_close tracepoint
+// because arguments are read on syscall entry before the kernel returns an error.
+func closeInvalidFd() error {
+ err := syscall.Close(99999)
+ if err == nil {
+ return fmt.Errorf("expected close of invalid fd to fail")
+ }
+ return nil
+}
+
+// closeDoubleClose opens a file, closes it normally, then closes the same fd again.
+// The second close fails with EBADF, but ior should capture both enter_close
+// tracepoints because arguments are read on syscall entry.
+func closeDoubleClose() error {
+ dir, cleanup, err := makeTempDir("close-double-close")
+ if err != nil {
+ return err
+ }
+ defer cleanup()
+
+ path := filepath.Join(dir, "doubleclosefile.txt")
+ fd, err := syscall.Open(path, syscall.O_RDWR|syscall.O_CREAT, 0o644)
+ if err != nil {
+ return fmt.Errorf("open: %w", err)
+ }
+
+ if err := syscall.Close(fd); err != nil {
+ return fmt.Errorf("first close: %w", err)
+ }
+
+ err = syscall.Close(fd)
+ if err == nil {
+ return fmt.Errorf("expected second close of same fd to fail")
+ }
+ return nil
+}
+
+// closeRangeEmpty calls close_range(2) with a range of very high fd numbers
+// (9000–9999) where no fds are open. The syscall succeeds (empty range is valid),
+// and ior should capture the enter_close_range tracepoint.
+func closeRangeEmpty() error {
+ _, _, errno := syscall.Syscall(sysCloseRange, 9000, 9999, 0)
+ if errno != 0 {
+ return fmt.Errorf("close_range: %w", errno)
+ }
+ return nil
+}
+
// dupBasic opens a file, dups the fd, writes via the dup, closes both.
func dupBasic() error {
dir, cleanup, err := makeTempDir("dup-basic")