summaryrefslogtreecommitdiff
diff options
context:
space:
mode:
authorPaul Buetow <paul@buetow.org>2026-06-06 10:05:22 +0300
committerPaul Buetow <paul@buetow.org>2026-06-06 10:05:22 +0300
commit92ca9482e44432b85ce09ebdd8a1b4d199b1c77b (patch)
tree353e3bf366b6d3e5f8a5fd7e623bd60a6c2cf7ba
parentd807c1ad9eb8b176e36300c6ea41744431a05bf0 (diff)
test: add end-to-end coverage for getrandom (READ byte count) and flock (KindFd)
Two previously-untested syscalls now have integration coverage: - getrandom (Security family, READ_CLASSIFIED): new security-getrandom scenario fills a 32-byte buffer via unix.Getrandom, looping past any signal-interrupted short reads so the cumulative byte count is strictly positive. TestSecurityGetrandom asserts enter_getrandom MinCount>=1, bytes>=1 (locking in the READ byte-count classification end-to-end), and a positive duration. - flock (FamilyFS, KindFd@args[0], UNCLASSIFIED): new flock-basic scenario opens a temp file, takes LOCK_EX then LOCK_UN via syscall.Flock, and closes it. TestFlockBasic asserts enter_flock with PathContains the temp filename, confirming the fd resolves to the file path via the procfd cache. Both scenarios use raw unix/syscall calls so the exact sys_enter tracepoints fire, and are registered in cmd/ioworkload/scenarios.go. Co-Authored-By: Claude Opus 4.8 <noreply@anthropic.com>
-rw-r--r--cmd/ioworkload/scenario_flock.go39
-rw-r--r--cmd/ioworkload/scenario_security.go34
-rw-r--r--cmd/ioworkload/scenarios.go2
-rw-r--r--integrationtests/flock_test.go22
-rw-r--r--integrationtests/security_test.go23
5 files changed, 120 insertions, 0 deletions
diff --git a/cmd/ioworkload/scenario_flock.go b/cmd/ioworkload/scenario_flock.go
new file mode 100644
index 0000000..a54f5d0
--- /dev/null
+++ b/cmd/ioworkload/scenario_flock.go
@@ -0,0 +1,39 @@
+package main
+
+import (
+ "fmt"
+ "path/filepath"
+ "syscall"
+)
+
+// flockBasic exercises the flock syscall end-to-end. It opens a temp file,
+// acquires an exclusive advisory lock (LOCK_EX), releases it (LOCK_UN), then
+// closes the file and removes the temp dir.
+//
+// flock is unprivileged (FamilyFS) and captured as KindFd at args[0]; ior
+// resolves that fd to the underlying file path via the procfd cache, so the
+// enter_flock record carries the temp filename. Its return value is
+// UNCLASSIFIED (0 on success), so this scenario only locks in the enter path.
+func flockBasic() error {
+ dir, cleanup, err := makeTempDir("flock-basic")
+ if err != nil {
+ return err
+ }
+ defer cleanup()
+
+ path := filepath.Join(dir, "flockfile.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)
+
+ // Use raw syscall.Flock so the exact sys_enter_flock tracepoint fires.
+ if err := syscall.Flock(fd, syscall.LOCK_EX); err != nil {
+ return fmt.Errorf("flock LOCK_EX: %w", err)
+ }
+ if err := syscall.Flock(fd, syscall.LOCK_UN); err != nil {
+ return fmt.Errorf("flock LOCK_UN: %w", err)
+ }
+ return nil
+}
diff --git a/cmd/ioworkload/scenario_security.go b/cmd/ioworkload/scenario_security.go
index f3cf9ba..6e14f0c 100644
--- a/cmd/ioworkload/scenario_security.go
+++ b/cmd/ioworkload/scenario_security.go
@@ -11,6 +11,40 @@ import (
var keySpecProcessKeyringArg = ^uintptr(1)
+// getrandomBufLen is the requested length of the getrandom buffer. getrandom
+// reports the number of random bytes written into buf as its return value,
+// which ior READ-classifies as a byte count.
+const getrandomBufLen = 32
+
+// securityGetrandom exercises the getrandom syscall end-to-end. getrandom
+// (FamilyTime/Security, READ_CLASSIFIED) fills buf with random bytes and
+// returns the count placed there, so ior records that count as the exit byte
+// total.
+//
+// getrandom may return fewer bytes than requested only when interrupted by a
+// signal; to keep the byte count deterministic we loop until the full buffer
+// is filled, accumulating any short reads. The enter tracepoint is null-kind
+// (no fd/path), so this scenario only locks in the READ byte-count classifi-
+// cation, not a path/fd dimension.
+func securityGetrandom() error {
+ buf := make([]byte, getrandomBufLen)
+ for off := 0; off < len(buf); {
+ // Use unix.Getrandom so the exact sys_enter_getrandom tracepoint fires.
+ n, err := unix.Getrandom(buf[off:], 0)
+ if err != nil {
+ if err == unix.EINTR {
+ continue
+ }
+ return fmt.Errorf("getrandom: %w", err)
+ }
+ if n <= 0 {
+ return fmt.Errorf("getrandom returned non-positive count %d", n)
+ }
+ off += n
+ }
+ return nil
+}
+
func securityKeysPtracePerf() error {
nr, err := securitySyscallNumbers(runtime.GOARCH)
if err != nil {
diff --git a/cmd/ioworkload/scenarios.go b/cmd/ioworkload/scenarios.go
index 50f84e1..869c9ec 100644
--- a/cmd/ioworkload/scenarios.go
+++ b/cmd/ioworkload/scenarios.go
@@ -148,6 +148,8 @@ var scenarios = map[string]func() error{
"pidfd-getfd-failure": pidfdGetfdFailure,
"security-keys-ptrace-perf": securityKeysPtracePerf,
"security-landlock": securityLandlockCreateRuleset,
+ "security-getrandom": securityGetrandom,
+ "flock-basic": flockBasic,
"iouring-setup": iouringSetup,
"iouring-enter": iouringEnter,
"iouring-register": iouringRegister,
diff --git a/integrationtests/flock_test.go b/integrationtests/flock_test.go
new file mode 100644
index 0000000..667f0fb
--- /dev/null
+++ b/integrationtests/flock_test.go
@@ -0,0 +1,22 @@
+package integrationtests
+
+import "testing"
+
+// TestFlockBasic asserts end-to-end tracing of the FamilyFS flock syscall. The
+// flock-basic scenario opens a temp file, takes an exclusive advisory lock
+// (LOCK_EX) and releases it (LOCK_UN), then closes the file.
+//
+// flock is captured as KindFd at args[0]; ior resolves that fd to the
+// underlying file path via the procfd cache, so the enter_flock record carries
+// the temp filename. Its return value is UNCLASSIFIED, so we only assert the
+// enter path (path + count), not a byte total.
+func TestFlockBasic(t *testing.T) {
+ runScenario(t, "flock-basic", []ExpectedEvent{
+ {
+ PathContains: "flockfile.txt",
+ Tracepoint: "enter_flock",
+ Comm: "ioworkload",
+ MinCount: 1,
+ },
+ })
+}
diff --git a/integrationtests/security_test.go b/integrationtests/security_test.go
index 8d41691..f4d38ea 100644
--- a/integrationtests/security_test.go
+++ b/integrationtests/security_test.go
@@ -62,6 +62,29 @@ func TestSecurityKeysPtracePerf(t *testing.T) {
}
}
+var getrandomTraceArgs = []string{"-trace-syscalls", "getrandom"}
+
+// TestSecurityGetrandom asserts end-to-end tracing of the getrandom syscall
+// (Security family, READ_CLASSIFIED). The security-getrandom scenario fills a
+// 32-byte buffer via unix.Getrandom, looping until the full buffer is filled.
+//
+// getrandom reports the number of random bytes written into buf as its return
+// value, which ior records as the exit byte count. The scenario loops past any
+// signal-interrupted short reads, so the cumulative byte count is strictly
+// positive; we assert bytes>=1 (the per-call count can be split across reads,
+// so a conservative >=1 minimum is the safe invariant) plus a positive
+// duration. The enter tracepoint is null-kind (no fd/path dimension), so only
+// the READ byte-count classification is locked in here.
+func TestSecurityGetrandom(t *testing.T) {
+ result, _ := runScenarioResultWithIorArgs(t, "security-getrandom", []ExpectedEvent{
+ {Tracepoint: "enter_getrandom", Comm: "ioworkload", MinCount: 1},
+ }, getrandomTraceArgs)
+
+ exp := ExpectedEvent{Tracepoint: "enter_getrandom", Comm: "ioworkload"}
+ assertEventBytesAtLeast(t, result, exp, 1)
+ assertEventDurationPositive(t, result, exp)
+}
+
var landlockTraceArgs = []string{"-trace-syscalls", "landlock_create_ruleset,landlock_add_rule,close"}
// TestSecurityLandlockCreateRuleset asserts end-to-end tracing of the