xref: /xnu-11215.81.4/tests/skywalk/skywalk_test_driver.c (revision d4514f0bc1d3f944c22d92e68b646ac3fb40d452)
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