summaryrefslogtreecommitdiff
path: root/internal/generate
diff options
context:
space:
mode:
authorPaul Buetow <paul@buetow.org>2026-05-29 22:02:06 +0300
committerPaul Buetow <paul@buetow.org>2026-05-29 22:02:06 +0300
commitf12c93dbf6ac839b25c1863aaa37d3d8be6d7a23 (patch)
tree0331a8667055d63165e59ca9fab75e2be1c5aeb2 /internal/generate
parent8e88f743dfcdd2b347b24dcbfab3a04fbe3c43f1 (diff)
ioprio_set/ioprio_get: classify as Process family
Audit of ioprio_set found a family inconsistency. ioprio_set(which, who, ioprio) and ioprio_get(which, who) query/set the I/O scheduling class and priority of a process, process group, or user. They are the direct I/O-priority analogues of getpriority/setpriority (the CPU nice value) and share the identical which/who selector signature, yet were falling through to FamilyMisc while getpriority/setpriority are FamilyProcess. Reclassify both ioprio syscalls to FamilyProcess for consistency with their priority siblings, update docs/syscall-tracing-plan.md, and regenerate the tracepoint/type artifacts (mage generate is idempotent). Argument capture is unchanged and confirmed correct: the args are all ints (which/who/ioprio), none named fd/path, so ClassifyFormat returns KindNone and the generator promotes the enter format to KindNull (null_event). In particular the 'who' argument (a pid/pgid/uid, never an fd) is not misclassified as KindFd. The exit is a ret_event (UNCLASSIFIED, int 0/-1). Add lock-in tests: - TestClassifyIoprioNullKind asserts KindNone/KindNull using the real kernel tracepoint fields, proving 'who' is not captured as an fd. - Family assertions for the ioprio pair alongside getpriority/setpriority so a stray reclassification of any of them trips the test. Co-Authored-By: Claude Opus 4.8 <noreply@anthropic.com>
Diffstat (limited to 'internal/generate')
-rw-r--r--internal/generate/classify_test.go52
-rw-r--r--internal/generate/family.go8
-rw-r--r--internal/generate/family_test.go16
3 files changed, 76 insertions, 0 deletions
diff --git a/internal/generate/classify_test.go b/internal/generate/classify_test.go
index 92b6e58..1d832a4 100644
--- a/internal/generate/classify_test.go
+++ b/internal/generate/classify_test.go
@@ -1259,6 +1259,58 @@ func TestClassifyE7NullNameOnlyKinds(t *testing.T) {
}
}
+// TestClassifyIoprioNullKind locks in the argument-capture classification for
+// ioprio_set/ioprio_get using their real kernel tracepoint fields. Unlike the
+// name-only Misc/null syscalls above, ioprio_* are NOT in nameOnlyKindsTable:
+// they classify by field fallthrough. ioprio_set(which, who, ioprio) and
+// ioprio_get(which, who) carry only int-typed which/who/ioprio fields. None is
+// named "fd"/"pathname"/"path"/"filename", so ClassifyFormat must return
+// KindNone — in particular the "who" argument (a pid/pgid/uid selected by
+// "which", never an fd) must NOT be misclassified as KindFd, and nothing must be
+// captured as a path. classifyEnterForGeneration then promotes the field-bearing
+// enter format to KindNull (the null_event seen in generated_tracepoints.c).
+func TestClassifyIoprioNullKind(t *testing.T) {
+ cases := []struct {
+ name string
+ fields []Field
+ }{
+ {
+ name: "sys_enter_ioprio_set",
+ fields: []Field{
+ {Type: "int", Name: "__syscall_nr"},
+ {Type: "int", Name: "which"},
+ {Type: "int", Name: "who"},
+ {Type: "int", Name: "ioprio"},
+ },
+ },
+ {
+ name: "sys_enter_ioprio_get",
+ fields: []Field{
+ {Type: "int", Name: "__syscall_nr"},
+ {Type: "int", Name: "which"},
+ {Type: "int", Name: "who"},
+ },
+ },
+ }
+
+ for _, tc := range cases {
+ t.Run(tc.name, func(t *testing.T) {
+ f := &Format{Name: tc.name, ExternalFields: tc.fields}
+
+ // No field should match an fd/path/name pattern: raw classification
+ // is KindNone, proving "who" is not captured as an fd.
+ if r := ClassifyFormat(f); r.Kind != KindNone {
+ t.Fatalf("%s: ClassifyFormat kind = %d, want KindNone", tc.name, r.Kind)
+ }
+
+ // The generator promotes the field-bearing enter format to KindNull.
+ if r := classifyEnterForGeneration(f); r.Kind != KindNull {
+ t.Fatalf("%s: classifyEnterForGeneration kind = %d, want KindNull", tc.name, r.Kind)
+ }
+ })
+ }
+}
+
func TestClassifyB7NameOnlyKinds(t *testing.T) {
tests := []struct {
name string
diff --git a/internal/generate/family.go b/internal/generate/family.go
index fb36cbd..a3c242f 100644
--- a/internal/generate/family.go
+++ b/internal/generate/family.go
@@ -72,6 +72,14 @@ var syscallFamilies = map[string]SyscallFamily{
"getppid": FamilyProcess, "getpriority": FamilyProcess, "getresgid": FamilyProcess,
"getresuid": FamilyProcess, "getrlimit": FamilyProcess, "getrusage": FamilyProcess,
"getsid": FamilyProcess, "gettid": FamilyProcess, "getuid": FamilyProcess,
+ // ioprio_get/ioprio_set query/set the I/O scheduling class and priority of a
+ // process, process group, or user (ioprio_set(which, who, ioprio)). They are
+ // the I/O-priority analogues of getpriority/setpriority (the CPU nice value
+ // for a process/group/user) and share the identical which/who selector
+ // signature, so they classify as Process alongside them rather than falling
+ // through to Misc. The who argument is a pid/pgid/uid (selected by which),
+ // never an fd or path, so argument capture is KindNull (null_event).
+ "ioprio_get": FamilyProcess, "ioprio_set": FamilyProcess,
"kcmp": FamilyProcess, "personality": FamilyProcess, "pivot_root": FamilyProcess,
"prctl": FamilyProcess, "prlimit64": FamilyProcess, "reboot": FamilyProcess,
"restart_syscall": FamilyProcess, "set_tid_address": FamilyProcess,
diff --git a/internal/generate/family_test.go b/internal/generate/family_test.go
index af40e7d..47b7685 100644
--- a/internal/generate/family_test.go
+++ b/internal/generate/family_test.go
@@ -160,6 +160,22 @@ func TestClassifySyscallFamily(t *testing.T) {
{"sys_enter_rt_sigqueueinfo", FamilySignals},
{"sys_enter_rt_tgsigqueueinfo", FamilySignals},
{"sys_enter_sigaltstack", FamilySignals},
+ // ioprio_get/ioprio_set query/set the I/O scheduling class and priority of
+ // a process, process group, or user. They are the I/O-priority analogues of
+ // getpriority/setpriority (the CPU nice value) and share the identical
+ // which/who selector signature, so they classify as Process alongside them
+ // rather than falling through to Misc. Assert the ioprio pair together with
+ // their getpriority/setpriority siblings so a stray reclassification of any
+ // one of them trips this test. Note: the x86 I/O-port syscalls ioperm/iopl
+ // (asserted above as Misc) only share an "io" name prefix; they set
+ // port-access state, not process I/O priority, and stay in Misc. Keep in
+ // sync with the Process list in docs/syscall-tracing-plan.md.
+ {"sys_enter_ioprio_get", FamilyProcess},
+ {"sys_exit_ioprio_get", FamilyProcess},
+ {"sys_enter_ioprio_set", FamilyProcess},
+ {"sys_exit_ioprio_set", FamilyProcess},
+ {"sys_enter_getpriority", FamilyProcess},
+ {"sys_enter_setpriority", FamilyProcess},
{"sys_enter_unlisted_future_syscall", FamilyMisc},
}