diff options
| author | Paul Buetow <paul@buetow.org> | 2026-02-21 21:35:09 +0200 |
|---|---|---|
| committer | Paul Buetow <paul@buetow.org> | 2026-02-21 21:35:09 +0200 |
| commit | 065389bac04c71a5dd5f44b2cfe5fe801bafabab (patch) | |
| tree | 2be4be6ab3970ffc329d31ad9be44c25a4e15deb /integrationtests | |
| parent | c36cb85503ce96a589a2dd38c1279b31c87164f2 (diff) | |
Add negative integration tests for stat syscalls (task 348)
Add three negative test scenarios for stat-family syscalls:
- stat-enoent: SYS_STAT on nonexistent file (ENOENT)
- stat-access-enoent: SYS_ACCESS on nonexistent file (ENOENT, since
tests run as root and EACCES is bypassed)
- stat-fstat-ebadf: SYS_FSTAT on invalid fd 99999 (EBADF)
Each scenario verifies that ior captures the tracepoint on syscall
entry even when the syscall fails.
Amp-Thread-ID: https://ampcode.com/threads/T-019c81af-d01d-75db-8a92-37951fb1503c
Co-authored-by: Amp <amp@ampcode.com>
Diffstat (limited to 'integrationtests')
| -rw-r--r-- | integrationtests/cmd/ioworkload/scenarios.go | 66 | ||||
| -rw-r--r-- | integrationtests/stat_test.go | 32 |
2 files changed, 98 insertions, 0 deletions
diff --git a/integrationtests/cmd/ioworkload/scenarios.go b/integrationtests/cmd/ioworkload/scenarios.go index 5eb8acb..3f5ee4c 100644 --- a/integrationtests/cmd/ioworkload/scenarios.go +++ b/integrationtests/cmd/ioworkload/scenarios.go @@ -76,6 +76,9 @@ var scenarios = map[string]func() error{ "stat-statx": statStatx, "stat-access": statAccess, "stat-faccessat": statFaccessat, + "stat-enoent": statEnoent, + "stat-access-enoent": statAccessEnoent, + "stat-fstat-ebadf": statFstatEbadf, "sync-basic": syncBasic, "sync-fdatasync": syncFdatasync, "sync-sync": syncSync, @@ -2009,6 +2012,69 @@ func statFaccessat() error { return nil } +// statEnoent attempts to stat a nonexistent file via raw SYS_STAT. +// The syscall fails with ENOENT, but ior captures the enter_newstat +// tracepoint because the filename is read on entry. +func statEnoent() error { + dir, cleanup, err := makeTempDir("stat-enoent") + if err != nil { + return err + } + defer cleanup() + + path := filepath.Join(dir, "stat-enoent-missing.txt") + pathBytes, err := syscall.BytePtrFromString(path) + if err != nil { + return fmt.Errorf("path bytes: %w", err) + } + var stat syscall.Stat_t + _, _, errno := syscall.Syscall(syscall.SYS_STAT, uintptr(unsafe.Pointer(pathBytes)), uintptr(unsafe.Pointer(&stat)), 0) + runtime.KeepAlive(pathBytes) + runtime.KeepAlive(&stat) + if errno == 0 { + return fmt.Errorf("expected ENOENT, but stat succeeded") + } + return nil +} + +// statAccessEnoent attempts to check access on a nonexistent file via raw +// SYS_ACCESS. The syscall fails with ENOENT, but ior captures the +// enter_access tracepoint because the path is read on entry. +// We use ENOENT instead of EACCES because integration tests run as root, +// which bypasses DAC permission checks. +func statAccessEnoent() error { + dir, cleanup, err := makeTempDir("stat-access-enoent") + if err != nil { + return err + } + defer cleanup() + + path := filepath.Join(dir, "access-enoent-missing.txt") + pathBytes, err := syscall.BytePtrFromString(path) + if err != nil { + return fmt.Errorf("path bytes: %w", err) + } + _, _, errno := syscall.Syscall(syscall.SYS_ACCESS, uintptr(unsafe.Pointer(pathBytes)), rOK, 0) + runtime.KeepAlive(pathBytes) + if errno == 0 { + return fmt.Errorf("expected ENOENT, but access succeeded") + } + return nil +} + +// statFstatEbadf calls raw SYS_FSTAT on an invalid fd (99999). +// The syscall fails with EBADF, but ior captures the enter_newfstat +// tracepoint because it is recorded on syscall entry. +func statFstatEbadf() error { + var stat syscall.Stat_t + _, _, errno := syscall.Syscall(syscall.SYS_FSTAT, 99999, uintptr(unsafe.Pointer(&stat)), 0) + runtime.KeepAlive(&stat) + if errno == 0 { + return fmt.Errorf("expected EBADF, but fstat succeeded") + } + return nil +} + // syncBasic opens a file, writes data, and fsyncs it. func syncBasic() error { dir, cleanup, err := makeTempDir("sync-basic") diff --git a/integrationtests/stat_test.go b/integrationtests/stat_test.go index e38171a..400e61a 100644 --- a/integrationtests/stat_test.go +++ b/integrationtests/stat_test.go @@ -78,3 +78,35 @@ func TestStatFaccessat(t *testing.T) { }, }) } + +func TestStatEnoent(t *testing.T) { + runScenario(t, "stat-enoent", []ExpectedEvent{ + { + PathContains: "stat-enoent-missing.txt", + Tracepoint: "enter_newstat", + Comm: "ioworkload", + MinCount: 1, + }, + }) +} + +func TestStatAccessEnoent(t *testing.T) { + runScenario(t, "stat-access-enoent", []ExpectedEvent{ + { + PathContains: "access-enoent-missing.txt", + Tracepoint: "enter_access", + Comm: "ioworkload", + MinCount: 1, + }, + }) +} + +func TestStatFstatEbadf(t *testing.T) { + runScenario(t, "stat-fstat-ebadf", []ExpectedEvent{ + { + Tracepoint: "enter_newfstat", + Comm: "ioworkload", + MinCount: 1, + }, + }) +} |
