summaryrefslogtreecommitdiff
path: root/internal
diff options
context:
space:
mode:
authorPaul Buetow <paul@buetow.org>2026-05-29 17:14:55 +0300
committerPaul Buetow <paul@buetow.org>2026-05-29 17:14:55 +0300
commit8e524f9ca7f8c105f395bfa111f1b052206bc836 (patch)
tree49d4beec8fefc5ad7fb226a764491f0cc5af73a8 /internal
parentfabe36b214a325fba880024106afa95c5153ddab (diff)
test(rt_sigpending): lock in KindNull + FamilySignals + UNCLASSIFIED ret
Audit of rt_sigpending(2) confirmed the existing classification is correct and added lock-in coverage: - KindNull: int rt_sigpending(sigset_t *set, size_t sigsetsize). args[0] is a userspace output pointer to a sigset_t (a signal mask, not a traced I/O resource) and args[1] is the byte size; neither is an fd or path. The enter handler emits a null_event and must not capture either arg. Added TestGenerateRtSigpendingHandler with a negative assertion guarding against any ctx->args[] capture in the enter handler. - Exit ret_type=UNCLASSIFIED: rt_sigpending returns 0/-1, a status code, not a byte count, so it must never be tagged READ/WRITE/TRANSFER. Added an exit handler assertion plus TestClassifyRetRtSigpendingUnclassified. - FamilySignals: shares the family with the whole rt_sig* group plus kill/pause/sigaltstack/tkill/tgkill. Added lock-in family cases asserting every rt_sig* sibling alongside rt_sigpending in TestClassifySyscallFamily. No classification/codegen/doc changes were required; mage generate produces no diff. Full ./internal/... passes (only the known pre-existing flake TestCleanupLeakedWorkloadTempDirCaughtByAssertion fails, unrelated). Co-Authored-By: Claude Opus 4.8 <noreply@anthropic.com>
Diffstat (limited to 'internal')
-rw-r--r--internal/generate/codegen_test.go55
-rw-r--r--internal/generate/family_test.go16
2 files changed, 71 insertions, 0 deletions
diff --git a/internal/generate/codegen_test.go b/internal/generate/codegen_test.go
index eabb1f8..b7bbb94 100644
--- a/internal/generate/codegen_test.go
+++ b/internal/generate/codegen_test.go
@@ -80,6 +80,61 @@ func TestClassifyRetProcessMadviseUnclassified(t *testing.T) {
}
}
+// TestGenerateRtSigpendingHandler locks in how rt_sigpending(2) is generated.
+// Per the man page:
+//
+// int rt_sigpending(sigset_t *set, size_t sigsetsize)
+//
+// It reports the set of signals pending for delivery into the userspace *set
+// buffer and returns 0 on success or -1 on error. Neither argument is an fd or
+// a path: args[0] is a userspace output pointer to a sigset_t (a signal mask,
+// not an I/O resource) and args[1] is the byte size of that set. ior therefore
+// classifies rt_sigpending as KindNull in FamilySignals, alongside the rest of
+// the rt_sig* group. Consequently:
+// - The enter handler emits a struct null_event and must NOT capture args[0]
+// as an fd/path/addr — the sigset 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 TestGenerateRtSigpendingHandler(t *testing.T) {
+ output := GenerateTracepointsC(mustParseAll(t, syntheticPair("rt_sigpending")))
+
+ enterSec := `SEC("tracepoint/syscalls/sys_enter_rt_sigpending")`
+ exitSec := `SEC("tracepoint/syscalls/sys_exit_rt_sigpending")`
+ 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_RT_SIGPENDING;")
+
+ // The KindNull enter handler must not wire the sigset pointer (args[0]) or the
+ // sigsetsize (args[1]) as an fd/path/addr — they are not traced I/O resources.
+ // 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("rt_sigpending: handlers not found in expected order")
+ }
+ enterBody := output[enterStart:exitStart]
+ if strings.Contains(enterBody, "ctx->args[") {
+ t.Error("rt_sigpending 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;")
+}
+
+// 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
+// would wrongly count it as data movement.
+func TestClassifyRetRtSigpendingUnclassified(t *testing.T) {
+ if got := ClassifyRet("sys_exit_rt_sigpending"); got != Unclassified {
+ t.Errorf("rt_sigpending ret classification = %q, want %q", got, Unclassified)
+ }
+}
+
func TestGenerateLandlockAddRuleHandlerUsesFirstArgumentAsFd(t *testing.T) {
output := GenerateTracepointsC(mustParseAll(t, syntheticPair("landlock_add_rule")))
diff --git a/internal/generate/family_test.go b/internal/generate/family_test.go
index 61718af..4a5ccc5 100644
--- a/internal/generate/family_test.go
+++ b/internal/generate/family_test.go
@@ -74,6 +74,22 @@ func TestClassifySyscallFamily(t *testing.T) {
{"sys_exit_rseq", FamilyMisc},
{"sys_enter_set_robust_list", FamilyMisc},
{"sys_enter_get_robust_list", 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/
+ // sigaltstack/tkill/tgkill. The entire group must stay consistent; assert
+ // every rt_sig* sibling alongside rt_sigpending so a stray reclassification
+ // of any one of them trips this test. Keep in sync with the Signals list in
+ // docs/syscall-tracing-plan.md.
+ {"sys_enter_rt_sigpending", FamilySignals},
+ {"sys_exit_rt_sigpending", FamilySignals},
+ {"sys_enter_rt_sigprocmask", FamilySignals},
+ {"sys_enter_rt_sigsuspend", FamilySignals},
+ {"sys_enter_rt_sigtimedwait", FamilySignals},
+ {"sys_enter_rt_sigreturn", FamilySignals},
+ {"sys_enter_rt_sigqueueinfo", FamilySignals},
+ {"sys_enter_rt_tgsigqueueinfo", FamilySignals},
+ {"sys_enter_sigaltstack", FamilySignals},
{"sys_enter_unlisted_future_syscall", FamilyMisc},
}