diff options
| author | Paul Buetow <paul@buetow.org> | 2026-02-21 21:08:23 +0200 |
|---|---|---|
| committer | Paul Buetow <paul@buetow.org> | 2026-02-21 21:08:23 +0200 |
| commit | 70ccf3c69fec44833b15d93be754f1fadd25614c (patch) | |
| tree | 3341c28498600a2d18cfdea1ab8abfddb0be52fd | |
| parent | 44a5deb87bef82fe88a40bbeac634d841709b290 (diff) | |
Add negative integration tests for dup syscalls
Add three negative test scenarios for dup syscalls:
- dup-invalid-fd: dup on invalid fd 99999 (EBADF)
- dup2-same-fd: dup2(fd, fd) no-op case (POSIX documented behavior)
- dup3-invalid-flags: dup3 with 0xBAD flags (EINVAL)
All verify ior captures the tracepoint even when the syscall fails,
since BPF reads arguments on syscall entry before execution.
Task: 348
Amp-Thread-ID: https://ampcode.com/threads/T-019c8196-6186-7054-a4e5-640fce69e493
Co-authored-by: Amp <amp@ampcode.com>
| -rw-r--r-- | integrationtests/cmd/ioworkload/scenarios.go | 69 | ||||
| -rw-r--r-- | integrationtests/dup_test.go | 32 |
2 files changed, 98 insertions, 3 deletions
diff --git a/integrationtests/cmd/ioworkload/scenarios.go b/integrationtests/cmd/ioworkload/scenarios.go index 390424f..dd8d46e 100644 --- a/integrationtests/cmd/ioworkload/scenarios.go +++ b/integrationtests/cmd/ioworkload/scenarios.go @@ -33,9 +33,12 @@ var scenarios = map[string]func() error{ "close-invalid-fd": closeInvalidFd, "close-double-close": closeDoubleClose, "close-range-empty": closeRangeEmpty, - "dup-basic": dupBasic, - "dup-dup2": dupDup2, - "dup-dup3": dupDup3, + "dup-basic": dupBasic, + "dup-dup2": dupDup2, + "dup-dup3": dupDup3, + "dup-invalid-fd": dupInvalidFd, + "dup2-same-fd": dup2SameFd, + "dup3-invalid-flags": dup3InvalidFlags, "fcntl-dupfd": fcntlDupfd, "fcntl-setfl": fcntlSetfl, "fcntl-dupfd-cloexec": fcntlDupfdCloexec, @@ -640,6 +643,66 @@ func dupDup3() error { return nil } +// dupInvalidFd attempts to dup a very high invalid fd number. +// The syscall fails with EBADF, but ior should capture the enter_dup +// tracepoint because arguments are read on syscall entry. +func dupInvalidFd() error { + _, err := syscall.Dup(99999) + if err == nil { + return fmt.Errorf("expected dup of invalid fd to fail") + } + return nil +} + +// dup2SameFd calls dup2 with the same fd for both oldfd and newfd. +// Per POSIX, dup2(fd, fd) is a no-op that returns fd without closing +// and reopening. ior should capture the enter_dup2 tracepoint. +func dup2SameFd() error { + dir, cleanup, err := makeTempDir("dup2-same-fd") + if err != nil { + return err + } + defer cleanup() + + path := filepath.Join(dir, "dup2samefile.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.Dup2(fd, fd); err != nil { + return fmt.Errorf("dup2 same fd: %w", err) + } + return nil +} + +// dup3InvalidFlags calls dup3 with an invalid flags value. +// dup3 only accepts O_CLOEXEC; any other flag causes EINVAL. +// ior should capture the enter_dup3 tracepoint. +func dup3InvalidFlags() error { + dir, cleanup, err := makeTempDir("dup3-invalid-flags") + if err != nil { + return err + } + defer cleanup() + + path := filepath.Join(dir, "dup3flagsfile.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) + + targetFd := 502 + _, _, errno := syscall.Syscall(syscall.SYS_DUP3, uintptr(fd), uintptr(targetFd), 0xBAD) + if errno == 0 { + syscall.Close(targetFd) + return fmt.Errorf("expected dup3 with invalid flags to fail") + } + return nil +} + // fcntlDupfd uses fcntl F_DUPFD to duplicate a file descriptor. func fcntlDupfd() error { dir, cleanup, err := makeTempDir("fcntl-dupfd") diff --git a/integrationtests/dup_test.go b/integrationtests/dup_test.go index 6289465..4a15168 100644 --- a/integrationtests/dup_test.go +++ b/integrationtests/dup_test.go @@ -34,3 +34,35 @@ func TestDupDup3(t *testing.T) { }, }) } + +func TestDupInvalidFd(t *testing.T) { + runScenario(t, "dup-invalid-fd", []ExpectedEvent{ + { + Tracepoint: "enter_dup", + Comm: "ioworkload", + MinCount: 1, + }, + }) +} + +func TestDup2SameFd(t *testing.T) { + runScenario(t, "dup2-same-fd", []ExpectedEvent{ + { + PathContains: "dup2samefile.txt", + Tracepoint: "enter_dup2", + Comm: "ioworkload", + MinCount: 1, + }, + }) +} + +func TestDup3InvalidFlags(t *testing.T) { + runScenario(t, "dup3-invalid-flags", []ExpectedEvent{ + { + PathContains: "dup3flagsfile.txt", + Tracepoint: "enter_dup3", + Comm: "ioworkload", + MinCount: 1, + }, + }) +} |
