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