summaryrefslogtreecommitdiff
diff options
context:
space:
mode:
authorPaul Buetow <paul@buetow.org>2026-02-21 21:08:23 +0200
committerPaul Buetow <paul@buetow.org>2026-02-21 21:08:23 +0200
commit70ccf3c69fec44833b15d93be754f1fadd25614c (patch)
tree3341c28498600a2d18cfdea1ab8abfddb0be52fd
parent44a5deb87bef82fe88a40bbeac634d841709b290 (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.go69
-rw-r--r--integrationtests/dup_test.go32
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,
+ },
+ })
+}