xref: /xnu-11215/tests/corpse_backtrace.c (revision 8d741a5d)
1 #include <darwintest.h>
2 #include <darwintest_utils.h>
3 
4 #include <TargetConditionals.h>
5 
6 #include <mach/mach.h>
7 #include <mach/mach_types.h>
8 #include <mach/mach_vm.h>
9 #include <mach/mach_error.h>
10 #include <mach/task.h>
11 
12 #include <servers/bootstrap.h>
13 
14 #include <sys/resource.h>
15 
16 #include <kern/kcdata.h>
17 
18 #include <os/reason_private.h>
19 
20 #include <System/uuid/uuid.h>
21 #include "exc_helpers.h"
22 
23 #include <unistd.h>
24 #include <errno.h>
25 
26 T_GLOBAL_META(
27 	T_META_NAMESPACE("xnu.ipc"),
28 	T_META_RUN_CONCURRENTLY(TRUE),
29 	T_META_RADAR_COMPONENT_NAME("xnu"),
30 	T_META_RADAR_COMPONENT_VERSION("IPC"));
31 
32 
33 bool expect_backtrace = TRUE;
34 
35 static kern_return_t
exc_handler_backtrace(mach_port_t kcdata_object,exception_type_t exception,mach_exception_data_t codes)36 exc_handler_backtrace(
37 	mach_port_t kcdata_object,
38 	exception_type_t exception,
39 	mach_exception_data_t codes)
40 {
41 	kern_return_t kr;
42 	mach_vm_address_t btinfo_begin;
43 	mach_vm_size_t btinfo_size;
44 
45 	if (expect_backtrace == FALSE) {
46 		T_FAIL("Does not expect backtrace for this test case");
47 	}
48 
49 	T_LOG("Received backtrace exception.");
50 	T_ASSERT_EQ(exception, EXC_CORPSE_NOTIFY, "Exception should be corpse notify");
51 	T_ASSERT_EQ(codes[0], EXC_GUARD, "Effective exception should be EXC_GUARD");
52 
53 	kr = task_map_kcdata_object_64(mach_task_self(), kcdata_object, &btinfo_begin, &btinfo_size);
54 	T_ASSERT_MACH_SUCCESS(kr, "task_map_kcdata_object_64() should succeed");
55 
56 	kcdata_iter_t btdata = kcdata_iter((void *)btinfo_begin, (unsigned long)btinfo_size);
57 	if (kcdata_iter_valid(btdata) && kcdata_iter_type(btdata) == TASK_BTINFO_BEGIN) {
58 		/* loop through data provided by kernel */
59 		KCDATA_ITER_FOREACH(btdata) {
60 			switch (kcdata_iter_type(btdata)) {
61 			case TASK_BTINFO_GID: {
62 				int gid = *(int *)(kcdata_iter_payload(btdata));
63 				T_LOG("Found TASK_BTINFO_GID: %d", gid);
64 				break;
65 			}
66 			case TASK_BTINFO_CPUTYPE: {
67 				cpu_type_t type = *(cpu_type_t *)(kcdata_iter_payload(btdata));
68 				T_LOG("Found TASK_BTINFO_CPUTYPE: %d", type);
69 				break;
70 			}
71 			case TASK_BTINFO_THREAD_STATE: {
72 				struct btinfo_thread_state_data_t data = *(struct btinfo_thread_state_data_t *)(kcdata_iter_payload(btdata));
73 				T_LOG("Found TASK_BTINFO_THREAD_STATE: Flavor %d, Count %d", data.flavor, data.count);
74 				break;
75 			}
76 			case TASK_BTINFO_THREAD_EXCEPTION_STATE: {
77 				struct btinfo_thread_state_data_t data = *(struct btinfo_thread_state_data_t *)(kcdata_iter_payload(btdata));
78 				T_LOG("Found TASK_BTINFO_THREAD_EXCEPTION_STATE: Flavor %d, Count %d", data.flavor, data.count);
79 				break;
80 			}
81 			case TASK_BTINFO_PROC_NAME: {
82 		    #define MAXCOMLEN 16
83 				char process_name[MAXCOMLEN + 1];
84 				memcpy(process_name, kcdata_iter_payload(btdata), MAXCOMLEN); // limited to 16 chars
85 				process_name[MAXCOMLEN] = '\0'; // ensure string is null terminated
86 				T_LOG("Found TASK_BTINFO_PROC_NAME: %s", process_name);
87 				break;
88 			}
89 			case TASK_BTINFO_PROC_PATH: {
90 				const char *c_str = (const char *)kcdata_iter_payload(btdata);
91 				T_LOG("Found TASK_BTINFO_PROC_PATH: %s", c_str);
92 				break;
93 			}
94 			case TASK_BTINFO_PLATFORM: {
95 				uint32_t platform = *(uint32_t *)(kcdata_iter_payload(btdata));
96 				T_LOG("Found TASK_BTINFO_PLATFORM: %d", platform);
97 				break;
98 			}
99 			case TASK_BTINFO_RUSAGE_INFO: {
100 				struct rusage_info_v0 rui = *(struct rusage_info_v0 *)kcdata_iter_payload(btdata);
101 				uuid_string_t uuid;
102 				uint64_t _proc_start_abstime = rui.ri_proc_start_abstime;
103 				uint64_t _proc_exit_abstime = rui.ri_proc_exit_abstime;
104 				uuid_unparse(rui.ri_uuid, uuid);
105 				T_LOG("Found TASK_BTINFO_RUSAGE_INFO: uuid: %s, start time: %llu, \
106 					exit time: %llu", uuid, _proc_start_abstime, _proc_exit_abstime);
107 				break;
108 			}
109 			case TASK_BTINFO_SC_LOADINFO64: {
110 				struct btinfo_sc_load_info64 info = *(struct btinfo_sc_load_info64 *)kcdata_iter_payload(btdata);
111 				uuid_string_t uuid;
112 				uuid_unparse(info.sharedCacheUUID, uuid);
113 				T_LOG("Found TASK_BTINFO_SC_LOADINFO64: uuid: %s, slide: %llu, base: %llu",
114 				    uuid, info.sharedCacheSlide, info.sharedCacheBaseAddress);
115 				break;
116 			}
117 			case TASK_BTINFO_SC_LOADINFO: {
118 				struct btinfo_sc_load_info info = *(struct btinfo_sc_load_info *)kcdata_iter_payload(btdata);
119 				uuid_string_t uuid;
120 				uuid_unparse(info.sharedCacheUUID, uuid);
121 				T_LOG("Found TASK_BTINFO_SC_LOADINFO: uuid: %s, slide: %d, base: %d",
122 				    uuid, info.sharedCacheSlide, info.sharedCacheBaseAddress);
123 				break;
124 			}
125 			case EXIT_REASON_SNAPSHOT: {
126 				struct exit_reason_snapshot *snapshot = (struct exit_reason_snapshot *)kcdata_iter_payload(btdata);
127 				T_LOG("Found EXIT_REASON_SNAPSHOT with namespace %x code %x", snapshot->ers_namespace, snapshot->ers_code);
128 				break;
129 			}
130 			case KCDATA_TYPE_ARRAY: {
131 				int count = kcdata_iter_array_elem_count(btdata);
132 				uint32_t type = kcdata_iter_array_elem_type(btdata);
133 				uint32_t size = kcdata_iter_array_elem_size(btdata);
134 
135 				if (type == TASK_BTINFO_BACKTRACE64) {
136 					T_LOG("Found TASK_BTINFO_BACKTRACE64, with %d backtrace frames", count);
137 					T_QUIET; T_ASSERT_EQ(size, sizeof(uint64_t), "Address size should be 64 bits");
138 
139 					for (int i = 0; i < count; i++) {
140 						T_LOG("Frame %d: %p", i, ((uint64_t *)kcdata_iter_payload(btdata))[i]);
141 					}
142 				} else if (type == TASK_BTINFO_DYLD_LOADINFO64) {
143 					T_LOG("Found TASK_BTINFO_DYLD_LOADINFO64, with %d image infos", count);
144 					T_QUIET; T_ASSERT_EQ(size, sizeof(struct dyld_uuid_info_64), "Struct size should match");
145 
146 					for (int i = 0; i < count; i++) {
147 						uuid_string_t uuid_str;
148 						uuid_unparse(((struct dyld_uuid_info_64 *)kcdata_iter_payload(btdata))[i].imageUUID, uuid_str);
149 
150 						T_LOG("Image %d: <%s, %p>", i, uuid_str,
151 						    ((struct dyld_uuid_info_64 *)kcdata_iter_payload(btdata))[i].imageLoadAddress);
152 					}
153 				} else if (type == TASK_BTINFO_BACKTRACE) {
154 					T_LOG("Found TASK_BTINFO_BACKTRACE, with %d backtrace frames", count);
155 					T_QUIET; T_ASSERT_EQ(size, sizeof(uint64_t), "Address size on arm64_32 should be 64 bits");
156 
157 					for (int i = 0; i < count; i++) {
158 						T_LOG("Frame %d: %p", i, ((uint32_t *)kcdata_iter_payload(btdata))[i]);
159 					}
160 				} else if (type == TASK_BTINFO_DYLD_LOADINFO) {
161 					T_LOG("Found TASK_BTINFO_DYLD_LOADINFO, with %d image infos", count);
162 					T_QUIET; T_ASSERT_EQ(size, sizeof(struct dyld_uuid_info_32), "Struct size should match");
163 
164 					for (int i = 0; i < count; i++) {
165 						uuid_string_t uuid_str;
166 						uuid_unparse(((struct dyld_uuid_info_32 *)kcdata_iter_payload(btdata))[i].imageUUID, uuid_str);
167 
168 						T_LOG("Image %d: <%s, %p>", i, uuid_str,
169 						    ((struct dyld_uuid_info_32 *)kcdata_iter_payload(btdata))[i].imageLoadAddress);
170 					}
171 				}
172 				break;
173 			}
174 			case TASK_BTINFO_THREAD_ID: {
175 				uint64_t thread_id = *(uint64_t *)(kcdata_iter_payload(btdata));
176 				T_LOG("Found TASK_BTINFO_THREAD_ID: 0x%lx", thread_id);
177 				break;
178 			}
179 			default:
180 				break;
181 			}
182 		}
183 	} else {
184 		T_FAIL("Unexpected kcdata object type");
185 	}
186 
187 	mach_vm_deallocate(mach_task_self(), btinfo_begin, btinfo_size);
188 	mach_port_deallocate(mach_task_self(), kcdata_object);
189 
190 	T_END;
191 }
192 
193 static size_t
exc_handler_identity_protected(task_id_token_t token,__unused uint64_t thread_id,__unused exception_type_t type,__unused exception_data_t codes)194 exc_handler_identity_protected(
195 	task_id_token_t token,
196 	__unused uint64_t thread_id,
197 	__unused exception_type_t type,
198 	__unused exception_data_t codes)
199 {
200 	mach_port_t port1, port2;
201 	kern_return_t kr;
202 
203 	if (expect_backtrace) {
204 		T_FAIL("Expect backtrace for this test case");
205 	}
206 
207 	T_LOG("Got protected exception!");
208 
209 	port1 = mach_task_self();
210 	kr = task_identity_token_get_task_port(token, TASK_FLAVOR_CONTROL, &port2); /* Immovable control port for self */
211 	T_ASSERT_MACH_SUCCESS(kr, "task_identity_token_get_task_port() - CONTROL");
212 	T_EXPECT_EQ(port1, port2, "Control port matches!");
213 
214 	T_END;
215 }
216 
217 /* Lightweight corpse not enabled on macOS yet */
218 #if !TARGET_OS_OSX
219 T_DECL(corpse_backtrace_os_log_lightweight,
220     "Test os_log_fault() fast backtracing with lightweight corpse",
221     T_META_CHECK_LEAKS(false), T_META_TAG_VM_PREFERRED) /* Test may otherwise time out after T_END */
222 {
223 	mach_port_t exc_port = MACH_PORT_NULL;
224 
225 	expect_backtrace = TRUE;
226 
227 	exc_port = create_exception_port_behavior64(EXC_MASK_CORPSE_NOTIFY,
228 	    EXCEPTION_IDENTITY_PROTECTED | MACH_EXCEPTION_BACKTRACE_PREFERRED);
229 
230 	T_ASSERT_NE(exc_port, MACH_PORT_NULL, "Exception port should be valid.");
231 
232 	T_LOG("Exception port: %d\n", exc_port);
233 
234 	run_exception_handler_behavior64(exc_port, exc_handler_backtrace, NULL,
235 	    EXCEPTION_IDENTITY_PROTECTED | MACH_EXCEPTION_BACKTRACE_PREFERRED, true);
236 
237 	/* Generate a non-fatal EXC_GUARD */
238 	uint64_t payload = 0xDEADBEEF;
239 	int ret = os_fault_with_payload(OS_REASON_LIBSYSTEM, OS_REASON_LIBSYSTEM_CODE_FAULT,
240 	    &payload, sizeof(payload), "Generating a user fault", 0);
241 	T_QUIET; T_ASSERT_EQ(ret, 0, "os_fault_with_payload should succeed");
242 
243 	T_LOG("Wait for exception on main thread..");
244 	for (int i = 0; i < 10; i++) {
245 		sleep(2);
246 	}
247 
248 	T_FAIL("Did not receive exception within timeout");
249 }
250 #endif
251 
252 T_DECL(corpse_backtrace_bad_access,
253     "Test os_bad_access fast backtracing with lightweight corpse",
254     T_META_CHECK_LEAKS(false), T_META_TAG_VM_PREFERRED)
255 {
256 	mach_port_t exc_port = MACH_PORT_NULL;
257 
258 	expect_backtrace = FALSE;
259 	/* Prefer backtrace on EXC_BAD_ACCESS, but should be ignored by kernel */
260 	exc_port = create_exception_port_behavior64(EXC_MASK_BAD_ACCESS,
261 	    EXCEPTION_IDENTITY_PROTECTED | MACH_EXCEPTION_BACKTRACE_PREFERRED);
262 
263 	T_ASSERT_NE(exc_port, MACH_PORT_NULL, "Exception port should be valid.");
264 
265 	T_LOG("Exception port 2: %d\n", exc_port);
266 
267 	run_exception_handler_behavior64(exc_port, exc_handler_backtrace, exc_handler_identity_protected,
268 	    EXCEPTION_IDENTITY_PROTECTED | MACH_EXCEPTION_BACKTRACE_PREFERRED, true);
269 
270 	/* Generate an EXC_BAD_ACCESS */
271 	*(void *volatile*)0 = 0;
272 
273 	T_FAIL("Should not reach here");
274 }
275 
276 T_DECL(corpse_backtrace_os_log_lightweight_reportcrash,
277     "Test os_log_fault() fast backtracing with lightweight corpse and report crash",
278     T_META_ENABLED(false), T_META_TAG_VM_PREFERRED)
279 {
280 	mach_port_t rc_port = MACH_PORT_NULL, bootstrap = MACH_PORT_NULL;
281 	kern_return_t kr;
282 
283 	expect_backtrace = TRUE;
284 
285 	kr = task_get_bootstrap_port(mach_task_self(), &bootstrap);
286 
287 	kr = bootstrap_look_up(bootstrap, "com.apple.ReportCrash", &rc_port);
288 
289 	T_ASSERT_NE(rc_port, MACH_PORT_NULL, "Exception port should be valid.");
290 
291 	T_LOG("ReportCrash exception port: %d\n", rc_port);
292 
293 #ifndef ARM_THREAD_STATE
294 #define ARM_THREAD_STATE 1
295 #endif
296 
297 	kr = task_set_exception_ports(mach_task_self(),
298 	    EXC_MASK_CORPSE_NOTIFY,
299 	    rc_port,
300 	    EXCEPTION_IDENTITY_PROTECTED | MACH_EXCEPTION_BACKTRACE_PREFERRED | MACH_EXCEPTION_CODES,
301 	    ARM_THREAD_STATE);
302 
303 	T_QUIET; T_ASSERT_EQ(kr, KERN_SUCCESS, "Registration with ReportCrash should succeed");
304 
305 	/* Generate a non-fatal EXC_GUARD */
306 	uint64_t payload = 0xDEADBEEF;
307 	int ret = os_fault_with_payload(OS_REASON_LIBSYSTEM, OS_REASON_LIBSYSTEM_CODE_FAULT,
308 	    &payload, sizeof(payload), "Generating a user fault", 0);
309 	T_QUIET; T_ASSERT_EQ(ret, 0, "os_fault_with_payload should succeed");
310 
311 	T_LOG("Kernel should have delivered a message to ReportCrash. Exit now.");
312 	T_END;
313 }
314