diff options
| -rw-r--r-- | internal/generate/codegen_test.go | 55 | ||||
| -rw-r--r-- | internal/generate/family_test.go | 15 |
2 files changed, 70 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 diff --git a/internal/generate/family_test.go b/internal/generate/family_test.go index f6c9ca4..0e284a3 100644 --- a/internal/generate/family_test.go +++ b/internal/generate/family_test.go @@ -87,6 +87,21 @@ func TestClassifySyscallFamily(t *testing.T) { {"sys_exit_rseq", FamilyMisc}, {"sys_enter_set_robust_list", FamilyMisc}, {"sys_enter_get_robust_list", FamilyMisc}, + // sysinfo(2) returns overall system statistics (memory/swap usage and + // load averages) into a single userspace struct sysinfo *info pointer + // (an output buffer, not an fd/path). It is not in the explicit family + // table and intentionally falls through to Misc, sharing the family with + // its closest system-introspection siblings newuname/sysfs (also Misc). + // NOTE: other "system info" relatives are deliberately classified + // elsewhere — getrusage is Process, times/gettimeofday are Time — so + // sysinfo is grouped with the uname/sysfs cluster rather than any of + // those. ustat(2) is NOT a sibling here: it contains the "stat" name + // marker and is classified FamilyFS by isFSSyscall. Keep this in sync + // with the Misc list in docs/syscall-tracing-plan.md. + {"sys_enter_sysinfo", FamilyMisc}, + {"sys_exit_sysinfo", FamilyMisc}, + {"sys_enter_newuname", FamilyMisc}, + {"sys_enter_sysfs", FamilyMisc}, // rt_sigpending(2) examines the set of signals pending for delivery // (sigset_t *set, size_t sigsetsize). It is a signal-handling syscall and // shares FamilySignals with the whole rt_sig* group as well as kill/pause/ |
