xref: /xnu-11215.41.3/tests/vm/test_vm_no_pager.m (revision 33de042d024d46de5ff4e89f2471de6608e37fa4)
1#include <errno.h>
2#include <fcntl.h>
3#include <kern/kcdata.h>
4#include <mach/kern_return.h>
5#include <signal.h>
6#include <stdbool.h>
7#include <stdarg.h>
8#include <stdint.h>
9#include <stdlib.h>
10#include <string.h>
11#include <stdio.h>
12#include <unistd.h>
13
14#include <sys/fsctl.h>
15#include <sys/stat.h>
16#include <sys/mman.h>
17#include <sys/sysctl.h>
18
19#include <mach/mach.h>
20#include <excserver.h>
21#include <dispatch/dispatch.h>
22#import <Foundation/Foundation.h>
23#import <System/corpses/task_corpse.h>
24#include <kdd.h>
25#include <kern/kern_cdata.h>
26#include <sys/reason.h>
27
28#include <darwintest.h>
29#include <darwintest_utils.h>
30
31T_GLOBAL_META(
32	T_META_OWNER("y_feigelson"),
33	T_META_NAMESPACE("xnu.vm"),
34	T_META_RADAR_COMPONENT_NAME("xnu"),
35	T_META_RADAR_COMPONENT_VERSION("VM"));
36
37static int verbose = 0;
38
39// KDBG_TRIAGE_VM_OBJECT_NO_PAGER_FORCED_UNMOUNT
40#define FORCED_UNMOUNT_ERROR "Object has no pager because the backing vnode was force unmounted"
41// KDBG_TRIAGE_VM_OBJECT_NO_PAGER_UNGRAFT
42#define UNGRAFTED_ERROR "Object has no pager because the backing vnode was ungrafted"
43
44static dispatch_semaphore_t sync_sema;
45static char* current_expected_triage_string;
46
47/* Use darwintests' launch and waitpid */
48static int
49my_system(const char *command, const char *arg)
50{
51	pid_t pid;
52	int ret;
53	const char *argv[] = {
54		command,
55		arg,
56		verbose ? "-v" : "",
57		NULL
58	};
59
60	dt_launch_tool(&pid, (char **)(void *)argv, FALSE, NULL, NULL);
61	// Status and signal will be empty since we took over exception handling
62	dt_waitpid(pid, NULL, NULL, 100);
63
64	return 0;
65}
66
67static int
68system_corpse_limit_reached(void)
69{
70	size_t output_size;
71	int total_corpse_count;
72	int ret;
73
74	output_size = sizeof(total_corpse_count);
75
76	ret = sysctlbyname("kern.total_corpses_count", &total_corpse_count, &output_size, NULL, 0);
77	if (ret != 0) {
78		T_LOG("sysctlbyname kern.total_corpses_count returned error: %d", ret);
79		return TRUE;
80	}
81
82	T_LOG("System corpse count is %d", total_corpse_count);
83
84	/* Abort the test if total_corpse_count is at TOTAL_CORPSES_ALLOWED */
85	if (total_corpse_count >= 4) {
86		return TRUE;
87	}
88
89	return FALSE;
90}
91
92/* Iterate corpse kcdata and verify `current_expected_triage_string` is found */
93void
94verify_corpse_data(mach_port_t task, mach_vm_address_t corpse_addr, size_t corpse_size)
95{
96	void * result                   = NULL;
97	mach_vm_address_t start_address;
98	mach_vm_address_t end_address;;
99	uint8_t * local_start;
100	uint64_t local_len;
101	kern_return_t r;
102
103	uint32_t t                          = 0;
104	uint32_t s                          = 0;
105	uint64_t f                          = 0;
106	uint64_t crashed_thread_id_reported = 0;
107	void * d                            = NULL;
108	int i                               = 0;
109	kern_return_t kret                  = KERN_SUCCESS;
110	task_crashinfo_item_t corpse_data   = NULL;
111
112	T_LOG("Verifiyng corpse data");
113	start_address = trunc_page((size_t)corpse_addr);
114	end_address  = round_page(corpse_addr + corpse_size);
115	r = task_map_corpse_info_64(mach_task_self(), task, (mach_vm_address_t *)&local_start, &local_len);
116	corpse_addr = (mach_vm_address_t)local_start;
117	start_address = (mach_vm_address_t)local_start;
118	corpse_size = local_len;
119	if (r == KERN_SUCCESS) {
120		corpse_data = malloc(corpse_size);
121		if (corpse_data) {
122			void * src = &local_start[(mach_vm_address_t)corpse_addr - start_address];
123			memcpy(corpse_data, src, corpse_size);
124		} else {
125			T_FAIL("Failed to malloc for corpse data");
126			return;
127		}
128		vm_deallocate(mach_task_self(), (uintptr_t)local_start, local_len);
129	}
130
131	kcdata_iter_t iter = kcdata_iter(corpse_data, corpse_size);
132	KCDataType * kcd_type = NULL;
133
134	KCDATA_ITER_FOREACH(iter)
135	{
136		i++;
137		t        = kcdata_iter_type(iter);
138		s        = kcdata_iter_size(iter);
139		f        = kcdata_iter_flags(iter);
140		d        = kcdata_iter_payload(iter);
141		kcd_type = getKCDataTypeForID(t);
142
143		if (t == TASK_CRASHINFO_KERNEL_TRIAGE_INFO_V1) {
144			struct kernel_triage_info_v1 kt = *(struct kernel_triage_info_v1 *) d;
145
146			for (char* str_iter = &kt; str_iter < (char*)&kt + sizeof(struct kernel_triage_info_v1); str_iter += MAX_TRIAGE_STRING_LEN) {
147				if (strlen(str_iter)) {
148					if (strstr(str_iter, current_expected_triage_string)) {
149						free(corpse_data);
150						T_PASS("Found expected crash triage string in corpse kcdata:\n`%s`", kt.triage_string1);
151						return;
152					}
153					else {
154						printf("Observed a triage string: %s\n", str_iter);
155					}
156				}
157			}
158		}
159	}
160
161	if (KCDATA_ITER_FOREACH_FAILED(iter)) {
162		T_FAIL("kcdata iteration failed");
163	}
164	free(corpse_data);
165
166	// rdar://123586379 (Revisit skipping in test_vm_no_pager.m)
167	T_SKIP("Didn't find expected crash string.\nExpected: `%s`", current_expected_triage_string);
168}
169
170/* Mach exception handler routines */
171kern_return_t
172catch_mach_exception_raise(mach_port_t exception_port,
173                           mach_port_t thread,
174                           mach_port_t task,
175                           exception_type_t exception,
176                           mach_exception_data_t code,
177                           mach_msg_type_number_t codeCnt)
178{
179	if (exception == EXC_CORPSE_NOTIFY) {
180		T_LOG("successfully caught EXC_CORPSE_NOTIFY %d code[0] = 0x%016llx at 0x%016llx", exception, code[0], code[1]);
181		verify_corpse_data(task, (mach_vm_address_t)code[0], (size_t)code[1]);
182		dispatch_semaphore_signal(sync_sema);
183		return KERN_SUCCESS;
184	}
185
186	T_LOG("caught %d %s(%d) at 0x%016llx returning KERN_FAILURE", exception, mach_error_string((int)code[0]), (int)code[0],
187	       code[1]);
188	return KERN_FAILURE;
189}
190
191kern_return_t
192catch_mach_exception_raise_state(mach_port_t exception_port,
193                                 exception_type_t exception,
194                                 const mach_exception_data_t code,
195                                 mach_msg_type_number_t codeCnt,
196                                 int * flavor,
197                                 const thread_state_t old_state,
198                                 mach_msg_type_number_t old_stateCnt,
199                                 thread_state_t new_state,
200                                 mach_msg_type_number_t * new_stateCnt)
201{
202	return KERN_NOT_SUPPORTED;
203}
204
205kern_return_t
206catch_mach_exception_raise_state_identity(mach_port_t exception_port,
207                                          mach_port_t thread,
208                                          mach_port_t task,
209                                          exception_type_t exception,
210                                          mach_exception_data_t code,
211                                          mach_msg_type_number_t codeCnt,
212                                          int * flavor,
213                                          thread_state_t old_state,
214                                          mach_msg_type_number_t old_stateCnt,
215                                          thread_state_t new_state,
216                                          mach_msg_type_number_t * new_stateCnt)
217{
218	return KERN_NOT_SUPPORTED;
219}
220
221kern_return_t
222catch_mach_exception_raise_identity_protected(
223	__unused mach_port_t      exception_port,
224	uint64_t                  thread_id,
225	mach_port_t               task_id_token,
226	exception_type_t          exception,
227	mach_exception_data_t     code,
228	mach_msg_type_number_t    codeCnt)
229{
230	return KERN_NOT_SUPPORTED;
231}
232
233
234/*
235 * Setup exception handling port for EXC_CORPSE_NOTIFY.
236 * Runs mach_msg_server once for receiving exception messages from kernel
237 */
238static void *
239setup_mach_server(void * arg __unused)
240{
241	kern_return_t kret;
242	mach_port_t exception_port;
243
244	kret = mach_port_allocate(mach_task_self(), MACH_PORT_RIGHT_RECEIVE, &exception_port);
245	T_EXPECT_MACH_SUCCESS(kret, "mach_port_allocate: %s (%d)", mach_error_string(kret), kret);
246
247	kret = mach_port_insert_right(mach_task_self(), exception_port, exception_port, MACH_MSG_TYPE_MAKE_SEND);
248	T_EXPECT_MACH_SUCCESS(kret, "mach_port_insert_right: %s (%d)", mach_error_string(kret), kret);
249
250	kret = task_set_exception_ports(mach_task_self(), EXC_MASK_CORPSE_NOTIFY, exception_port,
251	                                EXCEPTION_DEFAULT | MACH_EXCEPTION_CODES, 0);
252	T_EXPECT_MACH_SUCCESS(kret, "task_set_exception_ports: %s (%d)", mach_error_string(kret), kret);
253
254	dispatch_semaphore_signal(sync_sema);
255
256	kret = mach_msg_server(mach_exc_server, MACH_MSG_SIZE_RELIABLE, exception_port, 0);
257	T_EXPECT_MACH_SUCCESS(kret, "mach_msg_server: %s (%d)", mach_error_string(kret), kret);
258
259	return NULL;
260}
261
262static void
263parse_args(int argc, char** argv)
264{
265	char c;
266	opterr = 0;
267    optind = 0;
268
269    while ((c = getopt(argc, argv, "v")) != -1) {
270        switch (c) {
271        case 'v':
272            verbose = 1;
273            break;
274        }
275    }
276}
277
278/* Perform necessary setup prior to running crash program */
279static void
280setup_for_crash()
281{
282	T_SETUPBEGIN;
283
284	int ret;
285	pthread_t handle_thread;
286
287	ret = system_corpse_limit_reached();
288	if (ret) {
289		T_SKIP("Too many processes already crashing, can't test corpses. Aborting test.");
290		return;
291	}
292
293	sync_sema = dispatch_semaphore_create(0);
294
295	ret = pthread_create(&handle_thread, NULL, setup_mach_server, NULL);
296	T_QUIET; T_EXPECT_EQ(ret, 0, "pthread_create failed");
297
298	T_SETUPEND;
299}
300
301/* Run the helper with the chosen test number */
302static void
303run_test(const char* test_num, int argc, char** argv)
304{
305	parse_args(argc, argv);
306	setup_for_crash();
307
308	dispatch_semaphore_wait(sync_sema, DISPATCH_TIME_FOREVER); // Wait for exception handler setup
309	my_system("./test_vm_no_pager_helper", test_num);
310	dispatch_semaphore_wait(sync_sema, DISPATCH_TIME_FOREVER); // Wait for corpse kcdata processing
311}
312
313
314/* Test Declarations  */
315T_DECL(vm_no_pager_force_unmount, "test correct detection and propagation of reason for not having a pager (forced unmount)",
316	T_META_IGNORECRASHES(".*test_vm_no_pager.*"),
317	T_META_ENABLED(!TARGET_OS_BRIDGE),
318	T_META_ASROOT(true))
319{
320	current_expected_triage_string = FORCED_UNMOUNT_ERROR;
321	run_test("1", argc, argv);
322}
323
324T_DECL(vm_no_pager_ungraft, "test correct detection and propagation of reason for not having a pager (ungraft)",
325	T_META_IGNORECRASHES(".*test_vm_no_pager.*"),
326	T_META_ENABLED(!TARGET_OS_BRIDGE),
327	T_META_ASROOT(true))
328{
329	current_expected_triage_string = UNGRAFTED_ERROR;
330	run_test("2", argc, argv);
331}
332