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 int testid;
194 size_t len;
195 int error;
196 int itercount = -1;
197 struct timeval start, end;
198 uint32_t memfail_original;
199
200 len = sizeof(memfail_original);
201 if (sysctlbyname("kern.skywalk.mem.region_mtbf", &memfail_original, &len, NULL, 0) != 0) {
202 SKT_LOG("warning got errno %d getting kern.skywalk.mem.region_mtbf: %s\n",
203 errno, strerror(errno));
204 }
205 if (memfail_original) {
206 CRSetCrashLogMessage("kern.skywalk.mem.region_mtbf was found already set");
207 }
208
209 if (memfail) {
210 CRSetCrashLogMessage("parent set kern.skywalk.mem.region_mtbf");
211 }
212 T_LOG("setting kern.skywalk.mem.region_mtbf to %"PRId32, memfail);
213 len = sizeof(memfail);
214 if (sysctlbyname("kern.skywalk.mem.region_mtbf", NULL, NULL, &memfail, len) != 0) {
215 SKT_LOG("warning got errno %d setting kern.skywalk.mem.region_mtbf: %s",
216 errno, strerror(errno));
217 }
218
219 skywalk_test_driver_init(doshutdown, ignorefail);
220
221 gettimeofday(&start, NULL);
222
223 do {
224 /* Get Current exception ports */
225 if ((kr = task_get_exception_ports(mach_task_self(), EXC_MASK_GUARD | EXC_MASK_CRASH, masks, &maskCount, handlers, behaviors, flavors))
226 != KERN_SUCCESS) {
227 mach_error("task_get_exception_ports", kr);
228 test_exit(1);
229 }
230 assert(maskCount <= 2);
231 exception_code = 0;
232
233 if (skt->skt_init != NULL) {
234 T_LOG("Running init");
235 skt->skt_init();
236 }
237
238 expected_exception_code = skt->skt_expected_exception_code;
239 expected_exception_code_ignore = skt->skt_expected_exception_code_ignore;
240
241 for (int j = 0; j < maskCount; j++) {
242 // Set Exception Ports for Current Task
243 if ((kr = task_set_exception_ports(mach_task_self(), masks[j], exc_port,
244 EXCEPTION_DEFAULT | MACH_EXCEPTION_CODES, flavors[j])) != KERN_SUCCESS) {
245 mach_error("task_set_exception_ports", kr);
246 test_exit(1);
247 }
248 }
249
250 pid = fork();
251 if (pid == 0) {
252 int ret;
253
254 len = sizeof(memfail_original);
255 if (sysctlbyname("kern.skywalk.mem.region_mtbf", &memfail_original, &len, NULL, 0) != 0) {
256 SKT_LOG("warning got errno %d getting kern.skywalk.mem.region_mtbf: %s\n",
257 errno, strerror(errno));
258 }
259 if (memfail_original) {
260 CRSetCrashLogMessage("kern.skywalk.mem.region_mtbf was found already set");
261 }
262
263 ret = skt->skt_main(argc, argv);
264 /* return ret; results in "Unresolved" test results */
265 exit(ret);
266 }
267 T_QUIET;
268 T_ASSERT_GT(pid, 0, "pid must be > 0");
269
270 /* Restore exception ports for parent */
271 for (int j = 0; j < maskCount; j++) {
272 if ((kr = task_set_exception_ports(mach_task_self(), masks[j], handlers[j],
273 EXCEPTION_DEFAULT | MACH_EXCEPTION_CODES, flavors[j])) != KERN_SUCCESS) {
274 mach_error("task_set_exception_ports reset", kr);
275 test_exit(1);
276 }
277 }
278
279 if (testing_shutdown_sockets) {
280 int shutdown_cnt = 0;
281
282 while (1) {
283 struct timespec st = { .tv_sec = 0, .tv_nsec = 0 };
284 /*
285 * st.tv_nsec = arc4random_uniform(1000000); // 0-1 ms
286 * st.tv_nsec = arc4random_uniform(10000000); // 0-10 ms
287 */
288 st.tv_nsec = arc4random_uniform(100000000); /* 0-100 ms */
289 nanosleep(&st, NULL);
290
291 error = pid_shutdown_sockets(pid, SHUTDOWN_SOCKET_LEVEL_DISCONNECT_ALL);
292 if (error) {
293 break;
294 }
295
296 shutdown_cnt++;
297 }
298
299 if (error == -1 && errno != ESRCH) {
300 SKT_LOG(stderr, "pid_shutdown_sockets: %s", strerror(errno));
301 test_exit(1);
302 }
303
304 T_LOG("shutdown cnt %d", shutdown_cnt);
305 }
306
307 waitpid(pid, &child_status, 0);
308 T_LOG("child_status: %d", child_status);
309
310 if (skt->skt_fini != NULL) {
311 T_LOG("Running fini");
312 skt->skt_fini();
313 }
314
315 if (WIFEXITED(child_status)) {
316 T_LOG("Child exited with status %d", WEXITSTATUS(child_status));
317 }
318 if (WIFSIGNALED(child_status)) {
319 T_LOG("Child signaled with signal %d coredump %d", WTERMSIG(child_status), WCOREDUMP(child_status));
320 }
321
322 if (exception_code) {
323 T_LOG("Got exception code: Yes (Code 0x%llx)", exception_code);
324 } else {
325 T_LOG("Got exception code: No");
326 }
327 if (skt->skt_expected_exception_code & ~skt->skt_expected_exception_code_ignore) {
328 T_LOG("Expected exception code: Yes (Code 0x%llx, ignore 0x%llx)",
329 skt->skt_expected_exception_code, skt->skt_expected_exception_code_ignore);
330 } else {
331 T_LOG("Expected exception code: No");
332 }
333
334 if ((WIFEXITED(child_status) && WEXITSTATUS(child_status)) ||
335 ((exception_code ^ skt->skt_expected_exception_code) &
336 ~skt->skt_expected_exception_code_ignore)) {
337 if (ignorefail) {
338 T_PASS("Returning overall success because"
339 " ignorefail is set for Test %s: %s",
340 skt->skt_testname, skt->skt_testdesc);
341 } else {
342 T_FAIL("Test %s: %s", skt->skt_testname, skt->skt_testdesc);
343 }
344 } else {
345 T_PASS("Test %s: %s", skt->skt_testname, skt->skt_testdesc);
346 }
347
348 gettimeofday(&end, NULL);
349 gettimeofday(&end, NULL);
350 timersub(&end, &start, &end);
351 } while (--itercount > 0 || end.tv_sec < itersecs);
352
353 memfail = 0;
354 T_LOG("setting kern.skywalk.mem.region_mtbf to %"PRId32, memfail);
355 len = sizeof(memfail);
356 if (sysctlbyname("kern.skywalk.mem.region_mtbf", NULL, NULL, &memfail, len) != 0) {
357 SKT_LOG("warning got errno %d setting kern.skywalk.mem.region_mtbf: %s",
358 errno, strerror(errno));
359 }
360 return 0;
361 }
362