summaryrefslogtreecommitdiff
path: root/integrationtests
diff options
context:
space:
mode:
authorPaul Buetow <paul@buetow.org>2026-02-21 20:57:21 +0200
committerPaul Buetow <paul@buetow.org>2026-02-21 20:57:21 +0200
commitbe7f8fc048fd92eb806159de5bdb3a3becea3c16 (patch)
treec05866163ec261f92bdae80ab07cb0abd210c32d /integrationtests
parentcd516e1577ad97d5c8b1603657b13d110557e1b2 (diff)
Add negative integration tests for readwrite syscalls (task 348)
Add four negative scenarios that verify ior captures tracepoints even when syscalls fail: - readwrite-wronly-read: read from O_WRONLY fd (EBADF) - readwrite-rdonly-write: write to O_RDONLY fd (EBADF) - readwrite-pread-invalid: pread64 with negative offset (EINVAL) - readwrite-pwrite-invalid: pwrite64 with negative offset (EINVAL) Amp-Thread-ID: https://ampcode.com/threads/T-019c818c-ec91-7673-a5ba-e9b2ba53379b Co-authored-by: Amp <amp@ampcode.com>
Diffstat (limited to 'integrationtests')
-rw-r--r--integrationtests/cmd/ioworkload/scenarios.go122
-rw-r--r--integrationtests/readwrite_test.go44
2 files changed, 161 insertions, 5 deletions
diff --git a/integrationtests/cmd/ioworkload/scenarios.go b/integrationtests/cmd/ioworkload/scenarios.go
index 4ced6d5..bf9e0be 100644
--- a/integrationtests/cmd/ioworkload/scenarios.go
+++ b/integrationtests/cmd/ioworkload/scenarios.go
@@ -19,11 +19,15 @@ var scenarios = map[string]func() error{
"open-enoent": openEnoent,
"open-rdonly-write": openRdonlyWrite,
"open-pid-filter": openPidFilter,
- "readwrite-basic": readwriteBasic,
- "readwrite-pread": readwritePread,
- "readwrite-pwrite": readwritePwrite,
- "readwrite-readv": readwriteReadv,
- "readwrite-writev": readwriteWritev,
+ "readwrite-basic": readwriteBasic,
+ "readwrite-pread": readwritePread,
+ "readwrite-pwrite": readwritePwrite,
+ "readwrite-readv": readwriteReadv,
+ "readwrite-writev": readwriteWritev,
+ "readwrite-wronly-read": readwriteWronlyRead,
+ "readwrite-rdonly-write": readwriteRdonlyWrite,
+ "readwrite-pread-invalid": readwritePreadInvalid,
+ "readwrite-pwrite-invalid": readwritePwriteInvalid,
"close-basic": closeBasic,
"close-range": closeRange,
"dup-basic": dupBasic,
@@ -334,6 +338,114 @@ func readwriteWritev() error {
return nil
}
+// readwriteWronlyRead opens a file O_WRONLY, then attempts to read from it.
+// The read fails with EBADF, but ior should capture the enter_read tracepoint
+// because arguments are read on syscall entry before the kernel returns an error.
+func readwriteWronlyRead() error {
+ dir, cleanup, err := makeTempDir("readwrite-wronly-read")
+ if err != nil {
+ return err
+ }
+ defer cleanup()
+
+ path := filepath.Join(dir, "wronlyfile.txt")
+ fd, err := syscall.Open(path, syscall.O_WRONLY|syscall.O_CREAT, 0o644)
+ if err != nil {
+ return fmt.Errorf("open: %w", err)
+ }
+ defer syscall.Close(fd)
+
+ buf := make([]byte, 16)
+ _, err = syscall.Read(fd, buf)
+ if err == nil {
+ return fmt.Errorf("expected read from wronly fd to fail")
+ }
+ return nil
+}
+
+// readwriteRdonlyWrite opens a file O_RDONLY, then attempts to write to it.
+// The write fails with EBADF, but ior should capture the enter_write tracepoint
+// because arguments are read on syscall entry before the kernel returns an error.
+func readwriteRdonlyWrite() error {
+ dir, cleanup, err := makeTempDir("readwrite-rdonly-write")
+ if err != nil {
+ return err
+ }
+ defer cleanup()
+
+ path := filepath.Join(dir, "rdonlywritefile.txt")
+ fd, err := syscall.Open(path, syscall.O_RDWR|syscall.O_CREAT, 0o644)
+ if err != nil {
+ return fmt.Errorf("create file: %w", err)
+ }
+ syscall.Close(fd)
+
+ fd, err = syscall.Open(path, syscall.O_RDONLY, 0)
+ if err != nil {
+ return fmt.Errorf("open rdonly: %w", err)
+ }
+ defer syscall.Close(fd)
+
+ _, err = syscall.Write(fd, []byte("should fail"))
+ if err == nil {
+ return fmt.Errorf("expected write to rdonly fd to fail")
+ }
+ return nil
+}
+
+// readwritePreadInvalid calls pread64 with a negative offset (-1).
+// The syscall fails with EINVAL, but ior should capture the enter_pread64
+// tracepoint because arguments are read on syscall entry.
+func readwritePreadInvalid() error {
+ dir, cleanup, err := makeTempDir("readwrite-pread-invalid")
+ if err != nil {
+ return err
+ }
+ defer cleanup()
+
+ path := filepath.Join(dir, "preadinvalid.txt")
+ fd, err := syscall.Open(path, syscall.O_RDWR|syscall.O_CREAT, 0o644)
+ if err != nil {
+ return fmt.Errorf("open: %w", err)
+ }
+ defer syscall.Close(fd)
+
+ if _, err := syscall.Write(fd, []byte("some data")); err != nil {
+ return fmt.Errorf("write: %w", err)
+ }
+
+ buf := make([]byte, 16)
+ _, err = syscall.Pread(fd, buf, -1)
+ if err == nil {
+ return fmt.Errorf("expected pread with negative offset to fail")
+ }
+ return nil
+}
+
+// readwritePwriteInvalid calls pwrite64 with a negative offset (-1).
+// The syscall fails with EINVAL, but ior should capture the enter_pwrite64
+// tracepoint because arguments are read on syscall entry.
+func readwritePwriteInvalid() error {
+ dir, cleanup, err := makeTempDir("readwrite-pwrite-invalid")
+ if err != nil {
+ return err
+ }
+ defer cleanup()
+
+ path := filepath.Join(dir, "pwriteinvalid.txt")
+ fd, err := syscall.Open(path, syscall.O_RDWR|syscall.O_CREAT, 0o644)
+ if err != nil {
+ return fmt.Errorf("open: %w", err)
+ }
+ defer syscall.Close(fd)
+
+ _, err = syscall.Pwrite(fd, []byte("should fail"), -1)
+ if err == nil {
+ return fmt.Errorf("expected pwrite with negative offset to fail")
+ }
+ return nil
+}
+
// closeBasic opens multiple files and closes them.
func closeBasic() error {
dir, cleanup, err := makeTempDir("close-basic")
diff --git a/integrationtests/readwrite_test.go b/integrationtests/readwrite_test.go
index 3770ae1..e7492ec 100644
--- a/integrationtests/readwrite_test.go
+++ b/integrationtests/readwrite_test.go
@@ -62,3 +62,47 @@ func TestReadwriteWritev(t *testing.T) {
},
})
}
+
+func TestReadwriteWronlyRead(t *testing.T) {
+ runScenario(t, "readwrite-wronly-read", []ExpectedEvent{
+ {
+ PathContains: "wronlyfile.txt",
+ Tracepoint: "enter_read",
+ Comm: "ioworkload",
+ MinCount: 1,
+ },
+ })
+}
+
+func TestReadwriteRdonlyWrite(t *testing.T) {
+ runScenario(t, "readwrite-rdonly-write", []ExpectedEvent{
+ {
+ PathContains: "rdonlywritefile.txt",
+ Tracepoint: "enter_write",
+ Comm: "ioworkload",
+ MinCount: 1,
+ },
+ })
+}
+
+func TestReadwritePreadInvalid(t *testing.T) {
+ runScenario(t, "readwrite-pread-invalid", []ExpectedEvent{
+ {
+ PathContains: "preadinvalid.txt",
+ Tracepoint: "enter_pread64",
+ Comm: "ioworkload",
+ MinCount: 1,
+ },
+ })
+}
+
+func TestReadwritePwriteInvalid(t *testing.T) {
+ runScenario(t, "readwrite-pwrite-invalid", []ExpectedEvent{
+ {
+ PathContains: "pwriteinvalid.txt",
+ Tracepoint: "enter_pwrite64",
+ Comm: "ioworkload",
+ MinCount: 1,
+ },
+ })
+}