summaryrefslogtreecommitdiff
diff options
context:
space:
mode:
authorPaul Buetow <paul@buetow.org>2026-05-29 17:07:01 +0300
committerPaul Buetow <paul@buetow.org>2026-05-29 17:07:01 +0300
commit0961f9df1f51e8f6d7f31cef9234728bcccd188d (patch)
tree8d899df27871be6121b4b5fe2d61cc6944e942fc
parentfd8632e70dd2ec7dc6b0b03f469e3281114d5048 (diff)
test(classify): lock in sched_getattr pid-not-fd classification
Audit of sched_getattr confirmed it is correctly classified as FamilySched + KindNull, consistent with its siblings (sched_setattr, sched_getparam, sched_getscheduler). The syscall's first argument is a pid_t (a process/thread id), not a file descriptor, and the kernel tracepoint field is named "pid" rather than "fd", so the fd heuristic never applies; the name-only classification table also short-circuits before any field inspection. Add TestClassifySchedGetattrPidNotFd as a regression guard that pins KindNull and FamilySched using the real kernel field layout, explicitly asserting the pid arg is never treated as an fd, plus sibling consistency. No behavior or generated-artifact changes (mage generate produces no diff). Co-Authored-By: Claude Opus 4.8 <noreply@anthropic.com>
-rw-r--r--internal/generate/classify_test.go66
1 files changed, 66 insertions, 0 deletions
diff --git a/internal/generate/classify_test.go b/internal/generate/classify_test.go
index b925a03..d4a7d4b 100644
--- a/internal/generate/classify_test.go
+++ b/internal/generate/classify_test.go
@@ -1846,6 +1846,72 @@ func TestIsFdTypeNonMatch(t *testing.T) {
}
}
+// TestClassifySchedGetattrPidNotFd is a lock-in regression test for the
+// sched_getattr audit. The syscall signature is:
+//
+// int sched_getattr(pid_t pid, struct sched_attr *attr,
+// unsigned int size, unsigned int flags)
+//
+// args[0] is a PID (a process/thread id), NOT a file descriptor, and attr is
+// a userspace output pointer. The expected classification is KindNull (no fd,
+// pathname, or other resource handle is extracted on enter) and family Sched.
+//
+// The critical invariant guarded here is that the pid argument must never be
+// misclassified as an fd. ClassifyFormat resolves sched_getattr via the
+// name-only table first, which short-circuits before any field heuristic;
+// this test pins that behavior even when the real kernel field layout (whose
+// first arg is named "pid", not "fd") is supplied.
+func TestClassifySchedGetattrPidNotFd(t *testing.T) {
+ // Field layout mirrors the actual kernel tracepoint format for
+ // sys_enter_sched_getattr: pid_t pid, struct sched_attr *uattr,
+ // unsigned int usize, unsigned int flags.
+ r := ClassifyFormat(&Format{
+ Name: "sys_enter_sched_getattr",
+ ExternalFields: []Field{
+ {Type: "long", Name: "__syscall_nr"},
+ {Type: "pid_t", Name: "pid"},
+ {Type: "struct sched_attr *", Name: "uattr"},
+ {Type: "unsigned int", Name: "usize"},
+ {Type: "unsigned int", Name: "flags"},
+ },
+ })
+ if r.Kind != KindNull {
+ t.Fatalf("sched_getattr: got kind %d, want KindNull (pid arg must not be treated as fd)", r.Kind)
+ }
+ if r.Kind == KindFd {
+ t.Fatalf("sched_getattr: pid arg misclassified as fd")
+ }
+
+ // Family must match the Sched siblings (sched_setattr, sched_getparam,
+ // sched_getscheduler, ...).
+ if fam := ClassifySyscallFamily("sys_enter_sched_getattr"); fam != FamilySched {
+ t.Fatalf("sched_getattr: got family %s, want FamilySched", fam)
+ }
+
+ // Sanity-check sibling consistency: the matching setter and the other
+ // getters share the same family and KindNull classification.
+ siblings := []string{
+ "sys_enter_sched_setattr",
+ "sys_enter_sched_getparam",
+ "sys_enter_sched_getscheduler",
+ }
+ for _, name := range siblings {
+ s := ClassifyFormat(&Format{
+ Name: name,
+ ExternalFields: []Field{
+ {Type: "long", Name: "__syscall_nr"},
+ {Type: "long", Name: "arg0"},
+ },
+ })
+ if s.Kind != KindNull {
+ t.Errorf("%s: got kind %d, want KindNull", name, s.Kind)
+ }
+ if fam := ClassifySyscallFamily(name); fam != FamilySched {
+ t.Errorf("%s: got family %s, want FamilySched", name, fam)
+ }
+ }
+}
+
func mustParseAll(t *testing.T, data string) []Format {
t.Helper()
formats, err := ParseFormats(strings.NewReader(data))