diff options
| author | Paul Buetow <paul@buetow.org> | 2026-05-29 17:07:01 +0300 |
|---|---|---|
| committer | Paul Buetow <paul@buetow.org> | 2026-05-29 17:07:01 +0300 |
| commit | 0961f9df1f51e8f6d7f31cef9234728bcccd188d (patch) | |
| tree | 8d899df27871be6121b4b5fe2d61cc6944e942fc /internal/generate/classify_test.go | |
| parent | fd8632e70dd2ec7dc6b0b03f469e3281114d5048 (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>
Diffstat (limited to 'internal/generate/classify_test.go')
| -rw-r--r-- | internal/generate/classify_test.go | 66 |
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)) |
