xref: /xnu-12377.1.9/tests/poll.c (revision f6217f891ac0bb64f3d375211650a4c1ff8ca1ea)
1 #ifdef T_NAMESPACE
2 #undef T_NAMESPACE
3 #endif
4 #include <darwintest.h>
5 
6 #include <dispatch/dispatch.h>
7 #include <fcntl.h>
8 #include <mach/mach.h>
9 #include <poll.h>
10 #include <stdint.h>
11 #include <unistd.h>
12 #include <libproc.h>
13 
14 T_GLOBAL_META(T_META_NAMESPACE("xnu.poll"),
15     T_META_RUN_CONCURRENTLY(true));
16 
17 #define SLEEP_TIME_SECS 1
18 #define POLL_TIMEOUT_MS 1800
19 static_assert(POLL_TIMEOUT_MS > (SLEEP_TIME_SECS * 1000),
20     "poll timeout should be longer than sleep time");
21 
22 /*
23  * This matches the behavior of other UNIXes, but is under-specified in POSIX.
24  *
25  * See <rdar://problem/28372390>.
26  */
27 T_DECL(sleep_with_no_fds,
28     "poll() called with no fds provided should act like sleep", T_META_TAG_VM_PREFERRED)
29 {
30 	uint64_t begin_time, sleep_time, poll_time;
31 	struct pollfd pfd = { .fd = 0, .events = 0, .revents = 0 };
32 
33 	begin_time = mach_absolute_time();
34 	sleep(SLEEP_TIME_SECS);
35 	sleep_time = mach_absolute_time() - begin_time;
36 	T_LOG("sleep(%d) ~= %llu mach absolute time units", SLEEP_TIME_SECS, sleep_time);
37 
38 	begin_time = mach_absolute_time();
39 	T_ASSERT_POSIX_SUCCESS(poll(&pfd, 0, POLL_TIMEOUT_MS),
40 	    "poll() with 0 events and timeout %d ms", POLL_TIMEOUT_MS);
41 	poll_time = mach_absolute_time() - begin_time;
42 
43 	T_EXPECT_GT(poll_time, sleep_time,
44 	    "poll(... %d) should wait longer than sleep(1)", POLL_TIMEOUT_MS);
45 }
46 
47 #define LAUNCHD_PATH "/sbin/launchd"
48 #define PIPE_DIR_TIMEOUT_SECS 1
49 
50 /*
51  * See <rdar://problem/28539155>.
52  */
53 T_DECL(directories,
54     "poll() with directories should return an error", T_META_TAG_VM_PREFERRED)
55 {
56 	int file, dir, pipes[2];
57 	struct pollfd pfd[] = {
58 		{ .events = POLLIN },
59 		{ .events = POLLIN },
60 		{ .events = POLLIN },
61 	};
62 
63 	file = open(LAUNCHD_PATH, O_RDONLY | O_NONBLOCK);
64 	T_QUIET; T_ASSERT_POSIX_SUCCESS(file, "open(%s)", LAUNCHD_PATH);
65 	dir = open(".", O_RDONLY | O_NONBLOCK);
66 	T_QUIET; T_ASSERT_POSIX_SUCCESS(dir, "open(\".\")");
67 	T_QUIET; T_ASSERT_POSIX_SUCCESS(pipe(pipes), NULL);
68 
69 	/* just directory */
70 	pfd[0].fd = dir;
71 	T_EXPECT_POSIX_SUCCESS(poll(pfd, 1, -1), "poll() with a directory");
72 	T_QUIET; T_EXPECT_TRUE(pfd[0].revents & POLLNVAL,
73 	    "directory should be an invalid event");
74 
75 	/* file and directory */
76 	pfd[0].fd = file; pfd[0].revents = 0;
77 	pfd[1].fd = dir; pfd[1].revents = 0;
78 	T_EXPECT_POSIX_SUCCESS(poll(pfd, 2, -1),
79 	    "poll() with a file and directory");
80 	T_QUIET; T_EXPECT_TRUE(pfd[0].revents & POLLIN, "file should be readable");
81 	T_QUIET; T_EXPECT_TRUE(pfd[1].revents & POLLNVAL,
82 	    "directory should be an invalid event");
83 
84 	/* directory and file */
85 	pfd[0].fd = dir; pfd[0].revents = 0;
86 	pfd[1].fd = file; pfd[1].revents = 0;
87 	T_EXPECT_POSIX_SUCCESS(poll(pfd, 2, -1),
88 	    "poll() with a directory and a file");
89 	T_QUIET; T_EXPECT_TRUE(pfd[0].revents & POLLNVAL,
90 	    "directory should be an invalid event");
91 	T_QUIET; T_EXPECT_TRUE(pfd[1].revents & POLLIN, "file should be readable");
92 
93 	/* file and pipe */
94 	pfd[0].fd = file; pfd[0].revents = 0;
95 	pfd[1].fd = pipes[0]; pfd[0].revents = 0;
96 	T_EXPECT_POSIX_SUCCESS(poll(pfd, 2, -1),
97 	    "poll() with a file and pipe");
98 	T_QUIET; T_EXPECT_TRUE(pfd[0].revents & POLLIN, "file should be readable");
99 	T_QUIET; T_EXPECT_FALSE(pfd[1].revents & POLLIN,
100 	    "pipe should not be readable");
101 
102 	/* file, directory, and pipe */
103 	pfd[0].fd = file; pfd[0].revents = 0;
104 	pfd[1].fd = dir; pfd[1].revents = 0;
105 	pfd[2].fd = pipes[0]; pfd[2].revents = 0;
106 	T_EXPECT_POSIX_SUCCESS(poll(pfd, 3, -1),
107 	    "poll() with a file, directory, and pipe");
108 	T_QUIET; T_EXPECT_TRUE(pfd[0].revents & POLLIN, "file should be readable");
109 	T_QUIET; T_EXPECT_TRUE(pfd[1].revents & POLLNVAL,
110 	    "directory should be an invalid event");
111 	T_QUIET; T_EXPECT_FALSE(pfd[2].revents & POLLIN, "pipe should not be readable");
112 
113 	/* directory and pipe */
114 	__block bool timed_out = true;
115 	pfd[0].fd = dir; pfd[0].revents = 0;
116 	pfd[1].fd = pipes[0]; pfd[1].revents = 0;
117 	dispatch_after(dispatch_time(DISPATCH_TIME_NOW,
118 	    PIPE_DIR_TIMEOUT_SECS * NSEC_PER_SEC),
119 	    dispatch_get_global_queue(QOS_CLASS_DEFAULT, 0), ^{
120 		T_ASSERT_FALSE(timed_out, "poll timed out after %d seconds",
121 		PIPE_DIR_TIMEOUT_SECS);
122 	});
123 
124 	T_EXPECT_POSIX_SUCCESS(poll(pfd, 3, -1),
125 	    "poll() with a directory and pipe");
126 	timed_out = false;
127 
128 	T_QUIET; T_EXPECT_TRUE(pfd[0].revents & POLLNVAL,
129 	    "directory should be an invalid event");
130 	T_QUIET; T_EXPECT_FALSE(pfd[1].revents & POLLIN, "pipe should not be readable");
131 }
132 
133 #define PRIVATE
134 #include <libproc.h>
135 
136 static void *
leak_thread(void * ptr)137 leak_thread(void *ptr)
138 {
139 	T_LOG("Trying to find kevent kernel pointer...\n");
140 
141 	unsigned char *buffer = (unsigned char*) malloc(16392 * 8);
142 
143 	while (1) {
144 		memset(buffer, 0, 16392 * 8);
145 
146 		// Dump the kevent udatas for self
147 		// PT: Note that this is exposed by libproc but isn't declared in the header,
148 		// hence the forward declaration here. It seems fine to expose this in the header,
149 		// but I'm unsure of the implications.
150 		int proc_list_uptrs(int pid, uint64_t *buf, uint32_t bufsz);
151 		int ret = proc_list_uptrs(getpid(), buffer, 16392 * 8);
152 
153 		if (ret > 0) {
154 			T_LOG("udata pointers returned: %d\n", ret);
155 			uint64_t *ptrs = (uint64_t*) buffer;
156 			for (int i = 0; i < ret; i++) {
157 				T_EXPECT_EQ(ptrs[i] & 0xffffff0000000000, 0, "kptr? -> 0x%llx\n", ptrs[i]);
158 			}
159 			break;
160 		}
161 	}
162 
163 	free(buffer);
164 	return NULL;
165 }
166 
167 T_DECL(poll_dont_leak_kernel_pointers, "poll and proc_pidinfo should not leak kernel pointers", T_META_TAG_VM_PREFERRED)
168 {
169 	pthread_t thr;
170 	pthread_create(&thr, NULL, *leak_thread, (void *) NULL);
171 
172 	struct pollfd fds[2] = {};
173 	fds[0].fd = STDERR_FILENO;
174 	fds[0].events = POLLERR | POLLHUP;
175 	fds[0].revents = POLLERR;
176 	fds[1].fd = STDOUT_FILENO;
177 	fds[1].events = POLLERR | POLLHUP;
178 	fds[1].revents = POLLERR;
179 
180 	//int poll_nocancel(struct pollfd *fds, u_int nfds, int timeout)
181 	poll(fds, 2, 5000);
182 
183 	pthread_join(thr, NULL);
184 }
185