diff options
| author | Paul Buetow <paul@buetow.org> | 2026-02-21 20:57:21 +0200 |
|---|---|---|
| committer | Paul Buetow <paul@buetow.org> | 2026-02-21 20:57:21 +0200 |
| commit | be7f8fc048fd92eb806159de5bdb3a3becea3c16 (patch) | |
| tree | c05866163ec261f92bdae80ab07cb0abd210c32d /integrationtests | |
| parent | cd516e1577ad97d5c8b1603657b13d110557e1b2 (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.go | 122 | ||||
| -rw-r--r-- | integrationtests/readwrite_test.go | 44 |
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, + }, + }) +} |
