diff options
| author | Paul Buetow <paul@buetow.org> | 2026-06-09 22:12:09 +0300 |
|---|---|---|
| committer | Paul Buetow <paul@buetow.org> | 2026-06-09 22:12:09 +0300 |
| commit | bab929022f4f4bba77439c63d130c833595758b6 (patch) | |
| tree | a36b8a45e32aab2e79ecd5bd6a5704c2f94ce141 /cmd | |
| parent | 3cd5e655ee1768b4118815d1ea887acdd57eb498 (diff) | |
test: add deterministic ioctl coverage and quotactl_fd in mountfs
ioctl (FamilyFS, KindFd@arg0) previously only fired implicitly via the Go
runtime/terminal. Add scenario_ioctl.go issuing a benign FIONREAD ioctl on
an opened temp file (registered as ioctl-basic) and ioctl_test.go asserting
enter_ioctl resolves to the temp file path, mirroring the fcntl suite.
quotactl_fd (FamilyFS, KindFd@arg0) had no coverage while its sibling
quotactl was tested in mountfs. Add a best-effort RawSyscall6 SYS_QUOTACTL_FD
call on an fd opened on the mount point in scenario_mountfs.go, extend
mountfsTraceArgs, and assert enter_quotactl_fd (MinCount>=1). The sys_enter
tracepoint fires on kernel entry regardless of privilege/quota support.
Co-Authored-By: Claude Opus 4.8 <noreply@anthropic.com>
Diffstat (limited to 'cmd')
| -rw-r--r-- | cmd/ioworkload/scenario_ioctl.go | 48 | ||||
| -rw-r--r-- | cmd/ioworkload/scenario_mountfs.go | 12 | ||||
| -rw-r--r-- | cmd/ioworkload/scenarios.go | 1 |
3 files changed, 61 insertions, 0 deletions
diff --git a/cmd/ioworkload/scenario_ioctl.go b/cmd/ioworkload/scenario_ioctl.go new file mode 100644 index 0000000..ad7ed7d --- /dev/null +++ b/cmd/ioworkload/scenario_ioctl.go @@ -0,0 +1,48 @@ +package main + +import ( + "fmt" + "path/filepath" + "syscall" + + "golang.org/x/sys/unix" +) + +// fionread is the FIONREAD ioctl request (number of bytes available to read). +// golang.org/x/sys/unix does not export it as a portable constant, so we define +// it here. The value 0x541B is shared by the architectures this project targets +// (amd64, arm64; see scenario_mountfs.go's listns arch table). +const fionread = 0x541B + +// ioctlBasic issues a benign, deterministic ioctl on a known fd so the +// enter_ioctl tracepoint fires under our control rather than only implicitly +// (via the Go runtime / terminal). ioctl is FamilyFS / KindFd (fd@arg0), so +// the captured event resolves the fd to the temp file path. +// +// We open a regular temp file and call FIONREAD via unix.IoctlGetInt, which +// reports the number of bytes available to read. On a regular file this is a +// harmless query that does not mutate state; we ignore its result. The file is +// cleaned up via the deferred cleanup. +func ioctlBasic() error { + dir, cleanup, err := makeTempDir("ioctl-basic") + if err != nil { + return err + } + defer cleanup() + + path := filepath.Join(dir, "ioctlfile.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) + + // FIONREAD on a regular file is safe and portable. Even if the kernel + // rejects it for this fd type, the enter_ioctl tracepoint fires on syscall + // entry, so coverage holds regardless of the return value. + if _, err := unix.IoctlGetInt(fd, fionread); err != nil { + // Tolerated: the sys_enter_ioctl tracepoint has already fired. + return nil + } + return nil +} diff --git a/cmd/ioworkload/scenario_mountfs.go b/cmd/ioworkload/scenario_mountfs.go index 8736da6..5053dca 100644 --- a/cmd/ioworkload/scenario_mountfs.go +++ b/cmd/ioworkload/scenario_mountfs.go @@ -100,6 +100,18 @@ func mountfsManagement() error { _, _, _ = syscall.RawSyscall(unix.SYS_FSMOUNT, ^uintptr(0), 0, 0) _, _, _ = syscall.RawSyscall(unix.SYS_PIVOT_ROOT, uintptr(unsafe.Pointer(newRoot)), uintptr(unsafe.Pointer(putOld)), 0) _, _, _ = syscall.RawSyscall6(unix.SYS_QUOTACTL, 0, uintptr(unsafe.Pointer(mountPath)), 0, 0, 0, 0) + + // quotactl_fd(fd, cmd, id, addr) is the fd-based variant of quotactl: it is + // a KindFd syscall capturing fd@arg0. We point it at an fd opened on the + // mount point directory with best-effort args (Q_GETQUOTA-style cmd, id 0, + // nil addr). Quota support / privilege is irrelevant: the sys_enter_ + // quotactl_fd tracepoint fires on kernel entry before any check, exactly + // like the quotactl call above, so MinCount>=1 holds regardless of errno. + if quotaFd, err := syscall.Open(mountPoint, syscall.O_RDONLY, 0); err == nil { + _, _, _ = syscall.RawSyscall6(unix.SYS_QUOTACTL_FD, uintptr(quotaFd), 0, 0, 0, 0, 0) + _ = syscall.Close(quotaFd) + } + _, _, _ = syscall.RawSyscall(unix.SYS_SWAPON, uintptr(unsafe.Pointer(swapPath)), 0, 0) _, _, _ = syscall.RawSyscall(unix.SYS_SWAPOFF, uintptr(unsafe.Pointer(swapPath)), 0, 0) diff --git a/cmd/ioworkload/scenarios.go b/cmd/ioworkload/scenarios.go index cc3123f..22f3e3c 100644 --- a/cmd/ioworkload/scenarios.go +++ b/cmd/ioworkload/scenarios.go @@ -74,6 +74,7 @@ var scenarios = map[string]func() error{ "fcntl-dupfd-cloexec": fcntlDupfdCloexec, "fcntl-invalid-fd": fcntlInvalidFd, "fcntl-dupfd-max": fcntlDupfdMax, + "ioctl-basic": ioctlBasic, "rename-basic": renameBasic, "rename-renameat": renameRenameat, "rename-renameat2": renameRenameat2, |
