summaryrefslogtreecommitdiff
path: root/internal/generate/codegen_test.go
diff options
context:
space:
mode:
authorPaul Buetow <paul@buetow.org>2026-05-29 17:27:31 +0300
committerPaul Buetow <paul@buetow.org>2026-05-29 17:27:31 +0300
commitb184674de2bc4f3cb27aaa31bcc64c3f3976ecff (patch)
tree86a8e2e870f3ce9bd8425417446019b9e1ccc7db /internal/generate/codegen_test.go
parent4df620818f603564b2e6f4def6052baa72b2fbb1 (diff)
test(sysinfo): lock in KindNull + FamilyMisc + UNCLASSIFIED ret
Audit of sysinfo(2): int sysinfo(struct sysinfo *info) returns overall system statistics into a single userspace output pointer (not an fd or path) and returns 0/-1. The existing classification was already correct (KindNull, FamilyMisc, UNCLASSIFIED return) and docs/generated artifacts are consistent; no behavior change was needed. Add dedicated lock-in regression tests mirroring the rt_sigpending audit: - TestGenerateSysinfoHandler: enter emits a null_event and captures no args; exit emits a ret_event with ctx->ret tagged UNCLASSIFIED. - TestClassifyRetSysinfoUnclassified: the 0/-1 status is never a byte count. - family_test.go: assert sysinfo/newuname/sysfs stay in FamilyMisc, with a note that ustat is FamilyFS (matched by the 'stat' name marker) and is intentionally not grouped here. Co-Authored-By: Claude Opus 4.8 <noreply@anthropic.com>
Diffstat (limited to 'internal/generate/codegen_test.go')
-rw-r--r--internal/generate/codegen_test.go55
1 files changed, 55 insertions, 0 deletions
diff --git a/internal/generate/codegen_test.go b/internal/generate/codegen_test.go
index b7bbb94..746aa07 100644
--- a/internal/generate/codegen_test.go
+++ b/internal/generate/codegen_test.go
@@ -125,6 +125,61 @@ func TestGenerateRtSigpendingHandler(t *testing.T) {
requireContains(t, output, "ev->ret_type = UNCLASSIFIED;")
}
+// TestGenerateSysinfoHandler locks in how sysinfo(2) is generated. Per the man
+// page:
+//
+// int sysinfo(struct sysinfo *info)
+//
+// It returns overall system statistics (memory/swap usage and load averages)
+// into the single userspace *info output buffer and returns 0 on success or -1
+// on error. The lone argument is NOT an fd or a path: it is a userspace output
+// pointer to a struct sysinfo (a statistics buffer, not an I/O resource). ior
+// therefore classifies sysinfo as KindNull in FamilyMisc, alongside its
+// system-introspection siblings newuname/sysfs/ustat. Consequently:
+// - The enter handler emits a struct null_event and must NOT capture args[0]
+// as an fd/path/addr — the info pointer is not a traced I/O resource.
+// - The exit handler reports the raw int status as UNCLASSIFIED; the 0/-1
+// return is not a byte count, so it must never be tagged READ/WRITE/TRANSFER.
+func TestGenerateSysinfoHandler(t *testing.T) {
+ output := GenerateTracepointsC(mustParseAll(t, syntheticPair("sysinfo")))
+
+ enterSec := `SEC("tracepoint/syscalls/sys_enter_sysinfo")`
+ exitSec := `SEC("tracepoint/syscalls/sys_exit_sysinfo")`
+ requireContains(t, output, enterSec)
+ requireContains(t, output, "struct null_event *ev")
+ requireContains(t, output, "ev->event_type = ENTER_NULL_EVENT;")
+ requireContains(t, output, "ev->trace_id = SYS_ENTER_SYSINFO;")
+
+ // The KindNull enter handler must not wire the info pointer (args[0]) as an
+ // fd/path/addr — it is not a traced I/O resource. Scope to the enter handler
+ // body (everything from the enter SEC up to the exit SEC) so we only check
+ // what the enter handler emits.
+ enterStart := strings.Index(output, enterSec)
+ exitStart := strings.Index(output, exitSec)
+ if enterStart < 0 || exitStart < 0 || exitStart <= enterStart {
+ t.Fatalf("sysinfo: handlers not found in expected order")
+ }
+ enterBody := output[enterStart:exitStart]
+ if strings.Contains(enterBody, "ctx->args[") {
+ t.Error("sysinfo must be KindNull: enter handler must not capture any arg")
+ }
+
+ // The exit handler reports the raw 0/-1 status as UNCLASSIFIED, not a byte count.
+ requireContains(t, output, exitSec)
+ requireContains(t, output, "ev->ret = ctx->ret;")
+ requireContains(t, output, "ev->ret_type = UNCLASSIFIED;")
+}
+
+// TestClassifyRetSysinfoUnclassified locks in that sysinfo's return value is
+// UNCLASSIFIED. sysinfo(2) returns 0 on success or -1 on error — a status code,
+// not a number of bytes transferred — so classifying it as READ/WRITE/TRANSFER
+// would wrongly count it as data movement.
+func TestClassifyRetSysinfoUnclassified(t *testing.T) {
+ if got := ClassifyRet("sys_exit_sysinfo"); got != Unclassified {
+ t.Errorf("sysinfo ret classification = %q, want %q", got, Unclassified)
+ }
+}
+
// TestClassifyRetRtSigpendingUnclassified locks in that rt_sigpending's return
// value is UNCLASSIFIED. It returns 0 on success or -1 on error — a status code,
// not a number of bytes transferred — so classifying it as READ/WRITE/TRANSFER