1 /*
2 * Copyright (c) 2024 Apple Inc. All rights reserved.
3 *
4 * @APPLE_OSREFERENCE_LICENSE_HEADER_START@
5 *
6 * This file contains Original Code and/or Modifications of Original Code
7 * as defined in and that are subject to the Apple Public Source License
8 * Version 2.0 (the 'License'). You may not use this file except in
9 * compliance with the License. The rights granted to you under the License
10 * may not be used to create, or enable the creation or redistribution of,
11 * unlawful or unlicensed copies of an Apple operating system, or to
12 * circumvent, violate, or enable the circumvention or violation of, any
13 * terms of an Apple operating system software license agreement.
14 *
15 * Please obtain a copy of the License at
16 * http://www.opensource.apple.com/apsl/ and read it before using this file.
17 *
18 * The Original Code and all software distributed under the License are
19 * distributed on an 'AS IS' basis, WITHOUT WARRANTY OF ANY KIND, EITHER
20 * EXPRESS OR IMPLIED, AND APPLE HEREBY DISCLAIMS ALL SUCH WARRANTIES,
21 * INCLUDING WITHOUT LIMITATION, ANY WARRANTIES OF MERCHANTABILITY,
22 * FITNESS FOR A PARTICULAR PURPOSE, QUIET ENJOYMENT OR NON-INFRINGEMENT.
23 * Please see the License for the specific language governing rights and
24 * limitations under the License.
25 *
26 * @APPLE_OSREFERENCE_LICENSE_HEADER_END@
27 */
28 #include <stdlib.h>
29 #include <unistd.h>
30 #include <signal.h>
31 #include <pthread.h>
32 #include <mach/mach.h>
33 #include <darwintest.h>
34 #include <darwintest_utils.h>
35 #include <darwintest_multiprocess.h>
36 #include <CrashReporterClient.h>
37
38 #include "exc_helpers.h"
39 #include "skywalk_test_driver.h"
40
41 #define SKT_DT_HELPER_TIMEOUT 16
42 #define SKT_DT_NUM_HELPERS 1
43
44 #define test_exit(ecode) \
45 { \
46 T_LOG("%s:%d := Test exiting with error code %d\n", __func__, __LINE__, (ecode)); \
47 T_END; \
48 }
49
50 static mach_port_t exc_port;
51 static mach_exception_data_type_t exception_code;
52 static mach_exception_data_type_t expected_exception_code;
53 static mach_exception_data_type_t expected_exception_code_ignore;
54 static uint64_t skywalk_features;
55 static bool testing_shutdown_sockets;
56 static bool ignore_test_failures;
57 bool skywalk_in_driver;
58
59 boolean_t
60 mach_exc_server(mach_msg_header_t *InHeadP, mach_msg_header_t *OutHeadP);
61
62 kern_return_t
catch_mach_exception_raise(mach_port_t exception_port,mach_port_t thread,mach_port_t task,exception_type_t exception,mach_exception_data_t code,mach_msg_type_number_t codeCnt,int * flavor,thread_state_t old_state,mach_msg_type_number_t old_stateCnt,thread_state_t new_state,mach_msg_type_number_t * new_stateCnt)63 catch_mach_exception_raise(mach_port_t exception_port,
64 mach_port_t thread,
65 mach_port_t task,
66 exception_type_t exception,
67 mach_exception_data_t code,
68 mach_msg_type_number_t codeCnt,
69 int *flavor,
70 thread_state_t old_state,
71 mach_msg_type_number_t old_stateCnt,
72 thread_state_t new_state,
73 mach_msg_type_number_t *new_stateCnt)
74 {
75 /* Set global variable to indicate exception received */
76 if (exception != EXC_CRASH && exception != EXC_GUARD) {
77 T_LOG("Unexpected exception received: %d", exception);
78 test_exit(1);
79 return KERN_FAILURE;
80 }
81
82 exception_code = *code;
83
84 if (!ignore_test_failures) {
85 /* If the exception code is unexpected,
86 * return failure so we generate a crash report.
87 */
88 if ((exception_code ^ expected_exception_code)
89 & ~expected_exception_code_ignore) {
90 return KERN_FAILURE;
91 }
92 }
93
94 /* Return KERN_SUCCESS to prevent report crash from being called. */
95 return KERN_SUCCESS;
96 }
97
98 kern_return_t
catch_mach_exception_raise_state(mach_port_t exception_port,exception_type_t exception,const mach_exception_data_t code,mach_msg_type_number_t codeCnt,int * flavor,const thread_state_t old_state,mach_msg_type_number_t old_stateCnt,thread_state_t new_state,mach_msg_type_number_t * new_stateCnt)99 catch_mach_exception_raise_state(mach_port_t exception_port,
100 exception_type_t exception,
101 const mach_exception_data_t code,
102 mach_msg_type_number_t codeCnt,
103 int *flavor,
104 const thread_state_t old_state,
105 mach_msg_type_number_t old_stateCnt,
106 thread_state_t new_state,
107 mach_msg_type_number_t *new_stateCnt)
108 {
109 T_LOG("Unexpected exception handler called: %s", __func__);
110 test_exit(1);
111 return KERN_FAILURE;
112 }
113
114 kern_return_t
catch_mach_exception_raise_state_identity(mach_port_t exception_port,mach_port_t thread,mach_port_t task,exception_type_t exception,mach_exception_data_t code,mach_msg_type_number_t codeCnt)115 catch_mach_exception_raise_state_identity(mach_port_t exception_port,
116 mach_port_t thread,
117 mach_port_t task,
118 exception_type_t exception,
119 mach_exception_data_t code,
120 mach_msg_type_number_t codeCnt)
121 {
122 T_LOG("Unexpected exception handler called: %s", __func__);
123 test_exit(1);
124 return KERN_FAILURE;
125 }
126
127 static void *
server_thread(void * arg)128 server_thread(void *arg)
129 {
130 kern_return_t kr;
131
132 while (1) {
133 /* Handle exceptions on exc_port */
134 if ((kr = mach_msg_server_once(mach_exc_server, 4096, exc_port, 0)) != KERN_SUCCESS) {
135 mach_error("mach_msg_server_once", kr);
136 test_exit(1);
137 }
138 }
139 return NULL;
140 }
141
142
143 static void
skywalk_test_driver_init(bool test_shutdown,bool ignore_failures)144 skywalk_test_driver_init(bool test_shutdown, bool ignore_failures)
145 {
146 kern_return_t kr;
147 pthread_t exception_thread;
148 int error;
149 size_t len;
150
151 assert(!exc_port); // This routine can only be called once
152
153 skywalk_in_driver = true;
154
155 testing_shutdown_sockets = test_shutdown;
156 ignore_test_failures = ignore_failures;
157
158 /* Allocate and initialize new exception port */
159 if ((kr = mach_port_allocate(mach_task_self(), MACH_PORT_RIGHT_RECEIVE, &exc_port)) != KERN_SUCCESS) {
160 mach_error("mach_port_allocate", kr);
161 test_exit(1);
162 }
163
164 if ((kr = mach_port_insert_right(mach_task_self(), exc_port, exc_port, MACH_MSG_TYPE_MAKE_SEND)) != KERN_SUCCESS) {
165 mach_error("mach_port_insert_right", kr);
166 test_exit(1);
167 }
168
169 /* Create exception serving thread */
170 if ((error = pthread_create(&exception_thread, NULL, server_thread, 0)) != 0) {
171 T_LOG("pthread_create server_thread: %s\n", strerror(error));
172 test_exit(1);
173 }
174
175 /* Query the kernel for available skywalk features */
176 len = sizeof(skywalk_features);
177 sysctlbyname("kern.skywalk.features", &skywalk_features, &len, NULL, 0);
178
179 pthread_detach(exception_thread);
180 }
181
182 int
skywalk_test_driver_run(struct skywalk_test * skt,int argc,char ** argv,uint32_t memfail,bool ignorefail,bool doshutdown,int itersecs)183 skywalk_test_driver_run(struct skywalk_test *skt, int argc, char **argv,
184 uint32_t memfail, bool ignorefail, bool doshutdown, int itersecs)
185 {
186 kern_return_t kr;
187 mach_msg_type_number_t maskCount;
188 exception_mask_t masks[2];
189 exception_handler_t handlers[2];
190 exception_behavior_t behaviors[2];
191 thread_state_flavor_t flavors[2];
192 int pid, child_status;
193 size_t len;
194 int error;
195 int itercount = -1;
196 struct timeval start, end;
197 uint32_t memfail_original;
198
199 len = sizeof(memfail_original);
200 if (sysctlbyname("kern.skywalk.mem.region_mtbf", &memfail_original, &len, NULL, 0) != 0) {
201 SKT_LOG("warning got errno %d getting kern.skywalk.mem.region_mtbf: %s\n",
202 errno, strerror(errno));
203 }
204 if (memfail_original) {
205 CRSetCrashLogMessage("kern.skywalk.mem.region_mtbf was found already set");
206 }
207
208 if (memfail) {
209 CRSetCrashLogMessage("parent set kern.skywalk.mem.region_mtbf");
210 }
211 T_LOG("setting kern.skywalk.mem.region_mtbf to %"PRId32, memfail);
212 len = sizeof(memfail);
213 if (sysctlbyname("kern.skywalk.mem.region_mtbf", NULL, NULL, &memfail, len) != 0) {
214 SKT_LOG("warning got errno %d setting kern.skywalk.mem.region_mtbf: %s",
215 errno, strerror(errno));
216 }
217
218 skywalk_test_driver_init(doshutdown, ignorefail);
219
220 gettimeofday(&start, NULL);
221
222 do {
223 /* Get Current exception ports */
224 if ((kr = task_get_exception_ports(mach_task_self(), EXC_MASK_GUARD | EXC_MASK_CRASH, masks, &maskCount, handlers, behaviors, flavors))
225 != KERN_SUCCESS) {
226 mach_error("task_get_exception_ports", kr);
227 test_exit(1);
228 }
229 assert(maskCount <= 2);
230 exception_code = 0;
231
232 if (skt->skt_init != NULL) {
233 T_LOG("Running init");
234 skt->skt_init();
235 }
236
237 expected_exception_code = skt->skt_expected_exception_code;
238 expected_exception_code_ignore = skt->skt_expected_exception_code_ignore;
239
240 for (int j = 0; j < maskCount; j++) {
241 // Set Exception Ports for Current Task
242 if ((kr = task_set_exception_ports(mach_task_self(), masks[j], exc_port,
243 EXCEPTION_DEFAULT | MACH_EXCEPTION_CODES, flavors[j])) != KERN_SUCCESS) {
244 mach_error("task_set_exception_ports", kr);
245 test_exit(1);
246 }
247 }
248
249 pid = fork();
250 if (pid == 0) {
251 int ret;
252
253 len = sizeof(memfail_original);
254 if (sysctlbyname("kern.skywalk.mem.region_mtbf", &memfail_original, &len, NULL, 0) != 0) {
255 SKT_LOG("warning got errno %d getting kern.skywalk.mem.region_mtbf: %s\n",
256 errno, strerror(errno));
257 }
258 if (memfail_original) {
259 CRSetCrashLogMessage("kern.skywalk.mem.region_mtbf was found already set");
260 }
261
262 ret = skt->skt_main(argc, argv);
263 /* return ret; results in "Unresolved" test results */
264 exit(ret);
265 }
266 T_QUIET;
267 T_ASSERT_GT(pid, 0, "pid must be > 0");
268
269 /* Restore exception ports for parent */
270 for (int j = 0; j < maskCount; j++) {
271 if ((kr = task_set_exception_ports(mach_task_self(), masks[j], handlers[j],
272 EXCEPTION_DEFAULT | MACH_EXCEPTION_CODES, flavors[j])) != KERN_SUCCESS) {
273 mach_error("task_set_exception_ports reset", kr);
274 test_exit(1);
275 }
276 }
277
278 if (testing_shutdown_sockets) {
279 int shutdown_cnt = 0;
280
281 while (1) {
282 struct timespec st = { .tv_sec = 0, .tv_nsec = 0 };
283 /*
284 * st.tv_nsec = arc4random_uniform(1000000); // 0-1 ms
285 * st.tv_nsec = arc4random_uniform(10000000); // 0-10 ms
286 */
287 st.tv_nsec = arc4random_uniform(100000000); /* 0-100 ms */
288 nanosleep(&st, NULL);
289
290 error = pid_shutdown_sockets(pid, SHUTDOWN_SOCKET_LEVEL_DISCONNECT_ALL);
291 if (error) {
292 break;
293 }
294
295 shutdown_cnt++;
296 }
297
298 if (error == -1 && errno != ESRCH) {
299 SKT_LOG("pid_shutdown_sockets: %s", strerror(errno));
300 test_exit(1);
301 }
302
303 T_LOG("shutdown cnt %d", shutdown_cnt);
304 }
305
306 waitpid(pid, &child_status, 0);
307 T_LOG("child_status: %d", child_status);
308
309 if (skt->skt_fini != NULL) {
310 T_LOG("Running fini");
311 skt->skt_fini();
312 }
313
314 if (WIFEXITED(child_status)) {
315 T_LOG("Child exited with status %d", WEXITSTATUS(child_status));
316 }
317 if (WIFSIGNALED(child_status)) {
318 T_LOG("Child signaled with signal %d coredump %d", WTERMSIG(child_status), WCOREDUMP(child_status));
319 }
320
321 if (exception_code) {
322 T_LOG("Got exception code: Yes (Code 0x%llx)", exception_code);
323 } else {
324 T_LOG("Got exception code: No");
325 }
326 if (skt->skt_expected_exception_code & ~skt->skt_expected_exception_code_ignore) {
327 T_LOG("Expected exception code: Yes (Code 0x%llx, ignore 0x%llx)",
328 skt->skt_expected_exception_code, skt->skt_expected_exception_code_ignore);
329 } else {
330 T_LOG("Expected exception code: No");
331 }
332
333 if ((WIFEXITED(child_status) && WEXITSTATUS(child_status)) ||
334 ((exception_code ^ skt->skt_expected_exception_code) &
335 ~skt->skt_expected_exception_code_ignore)) {
336 if (ignorefail) {
337 T_PASS("Returning overall success because"
338 " ignorefail is set for Test %s: %s",
339 skt->skt_testname, skt->skt_testdesc);
340 } else {
341 T_FAIL("Test %s: %s", skt->skt_testname, skt->skt_testdesc);
342 }
343 } else {
344 T_PASS("Test %s: %s", skt->skt_testname, skt->skt_testdesc);
345 }
346
347 gettimeofday(&end, NULL);
348 gettimeofday(&end, NULL);
349 timersub(&end, &start, &end);
350 } while (--itercount > 0 || end.tv_sec < itersecs);
351
352 memfail = 0;
353 T_LOG("setting kern.skywalk.mem.region_mtbf to %"PRId32, memfail);
354 len = sizeof(memfail);
355 if (sysctlbyname("kern.skywalk.mem.region_mtbf", NULL, NULL, &memfail, len) != 0) {
356 SKT_LOG("warning got errno %d setting kern.skywalk.mem.region_mtbf: %s",
357 errno, strerror(errno));
358 }
359 return 0;
360 }
361