summaryrefslogtreecommitdiff
diff options
context:
space:
mode:
-rw-r--r--internal/generate/codegen_test.go39
1 files changed, 39 insertions, 0 deletions
diff --git a/internal/generate/codegen_test.go b/internal/generate/codegen_test.go
index 1c498db..1d6214c 100644
--- a/internal/generate/codegen_test.go
+++ b/internal/generate/codegen_test.go
@@ -942,6 +942,35 @@ func TestGeneratePselect6HandlerCapturesTimeoutPointer(t *testing.T) {
requireContains(t, output, "ev->timeout_ns = ts.tv_sec * 1000000000LL + ts.tv_nsec;")
}
+// TestGeneratePpollHandlerCapturesNfdsAndTimeoutPointer locks in the ppoll
+// argument layout. ppoll(struct pollfd *fds, nfds_t nfds,
+// const struct timespec *tmo_p, const sigset_t *sigmask): args[0] is a
+// userspace pointer to an ARRAY of pollfd structs (NOT a file descriptor),
+// nfds is args[1], and the timeout is a timespec pointer at args[2]. The
+// handler must therefore capture nfds from args[1] and the timeout from the
+// args[2] timespec, and must NEVER read args[0] as an fd (that would be a real
+// bug: args[0] is a pointer, so an fd capture would record a garbage fd).
+func TestGeneratePpollHandlerCapturesNfdsAndTimeoutPointer(t *testing.T) {
+ output := generateFromPair(t, FormatPpoll, FormatExitPpoll)
+
+ // Enter: poll_event with nfds from args[1] and timespec timeout from args[2].
+ requireContains(t, output, "struct poll_event *ev")
+ requireContains(t, output, "ev->event_type = ENTER_POLL_EVENT;")
+ requireContains(t, output, "ev->trace_id = SYS_ENTER_PPOLL;")
+ requireContains(t, output, "ev->nfds = (__s32)ctx->args[1];")
+ requireContains(t, output, "if (ctx->args[2] != 0) {")
+ requireContains(t, output, "ev->timeout_ns = ts.tv_sec * 1000000000LL + ts.tv_nsec;")
+
+ // Negative: args[0] is a pollfd-array pointer and must never be captured
+ // as an fd, and the exit is an UNCLASSIFIED ret_event (ready count, not a
+ // byte transfer), so no bytes/fd fields are emitted.
+ requireNotContains(t, output, "ev->fd = (__s32)ctx->args[0];")
+ requireNotContains(t, output, "ev->bytes")
+ // Exit: plain ret_event recording the ready-count (>=0) or -1.
+ requireContains(t, output, "ev->event_type = EXIT_RET_EVENT;")
+ requireContains(t, output, "ev->ret = ctx->ret;")
+}
+
func TestGenerateSleepHandlerCapturesRequestedTimespec(t *testing.T) {
output := generateFromPair(t, FormatNanosleep, FormatExitNanosleep)
@@ -1423,3 +1452,13 @@ func requireContains(t *testing.T, haystack, needle string) {
t.Errorf("output missing expected string: %q", needle)
}
}
+
+// requireNotContains fails when haystack unexpectedly contains needle. Used for
+// negative assertions, e.g. that a pointer argument is not misclassified as an
+// fd capture.
+func requireNotContains(t *testing.T, haystack, needle string) {
+ t.Helper()
+ if strings.Contains(haystack, needle) {
+ t.Errorf("output unexpectedly contains forbidden string: %q", needle)
+ }
+}