1 /* 2 * 3 * honggfuzz - architecture dependent code (MAC OS X) 4 * ----------------------------------------- 5 * 6 * Author: Robert Swiecki <swiecki (at) google.com> Felix Grbert 7 * <groebert (at) google.com> 8 * 9 * Copyright 2010-2015 by Google Inc. All Rights Reserved. 10 * 11 * Licensed under the Apache License, Version 2.0 (the "License"); you may 12 * not use this file except in compliance with the License. You may obtain 13 * a copy of the License at 14 * 15 * http://www.apache.org/licenses/LICENSE-2.0 16 * 17 * Unless required by applicable law or agreed to in writing, software 18 * distributed under the License is distributed on an "AS IS" BASIS, 19 * WITHOUT WARRANTIES OR CONDITIONS OF ANY KIND, either express or 20 * implied. See the License for the specific language governing 21 * permissions and limitations under the License. 22 * 23 */ 24 25 #include "arch.h" 26 27 #include <ctype.h> 28 #include <dirent.h> 29 #include <errno.h> 30 #include <fcntl.h> 31 #include <signal.h> 32 #include <stdio.h> 33 #include <stdlib.h> 34 #include <string.h> 35 #include <sys/cdefs.h> 36 #include <sys/mman.h> 37 #include <sys/resource.h> 38 #include <sys/stat.h> 39 #include <sys/time.h> 40 #include <sys/types.h> 41 #include <sys/wait.h> 42 #include <time.h> 43 #include <unistd.h> 44 45 #include "honggfuzz.h" 46 #include "libcommon/common.h" 47 #include "libcommon/files.h" 48 #include "libcommon/log.h" 49 #include "libcommon/util.h" 50 #include "sancov.h" 51 #include "subproc.h" 52 53 #include <mach/i386/thread_status.h> 54 #include <mach/mach.h> 55 #include <mach/mach_types.h> 56 #include <mach/mach_vm.h> 57 #include <mach/task_info.h> 58 #include <pthread.h> 59 #include <servers/bootstrap.h> 60 61 #include "mach_exc.h" 62 #include "mach_excServer.h" 63 64 #import <Foundation/Foundation.h> 65 66 /* 67 * Interface to third_party/CrashReport_*.o 68 */ 69 @interface CrashReport : NSObject 70 - (id)initWithTask:(task_t)task 71 exceptionType:(exception_type_t)anExceptionType 72 exceptionCode:(mach_exception_data_t)anExceptionCode 73 exceptionCodeCount:(mach_msg_type_number_t)anExceptionCodeCount 74 thread:(thread_t)thread 75 threadStateFlavor:(thread_state_flavor_t)aThreadStateFlavor 76 threadState:(thread_state_data_t)aThreadState 77 threadStateCount:(mach_msg_type_number_t)aThreadStateCount; 78 @end 79 80 /* 81 * Global to have exception port available in the collection thread 82 */ 83 static mach_port_t g_exception_port = MACH_PORT_NULL; 84 85 /* 86 * From xnu/bsd/sys/proc_internal.h 87 */ 88 #define PID_MAX 99999 89 90 /* 91 * Global to store crash info in exception handler thread 92 */ 93 run_t g_fuzzer_crash_information[PID_MAX + 1]; 94 95 /* 96 * Global to store the CrashWrangler generated callstack from 97 * the exception handler thread 98 */ 99 static char* g_fuzzer_crash_callstack[PID_MAX + 1]; 100 101 /* 102 * Global to have a unique service name for each honggfuzz process 103 */ 104 char g_service_name[256]; 105 106 struct { 107 bool important; 108 const char* descr; 109 } arch_sigs[NSIG]; 110 111 __attribute__((constructor)) void arch_initSigs(void) { 112 for (int x = 0; x < NSIG; x++) arch_sigs[x].important = false; 113 114 arch_sigs[SIGILL].important = true; 115 arch_sigs[SIGILL].descr = "SIGILL"; 116 arch_sigs[SIGFPE].important = true; 117 arch_sigs[SIGFPE].descr = "SIGFPE"; 118 arch_sigs[SIGSEGV].important = true; 119 arch_sigs[SIGSEGV].descr = "SIGSEGV"; 120 arch_sigs[SIGBUS].important = true; 121 arch_sigs[SIGBUS].descr = "SIGBUS"; 122 123 /* Is affected from monitorSIGABRT flag */ 124 arch_sigs[SIGABRT].important = true; 125 arch_sigs[SIGABRT].descr = "SIGABRT"; 126 127 /* Is affected from tmoutVTALRM flag */ 128 arch_sigs[SIGVTALRM].important = false; 129 arch_sigs[SIGVTALRM].descr = "SIGVTALRM"; 130 } 131 132 const char* exception_to_string(int exception) { 133 switch (exception) { 134 case EXC_BAD_ACCESS: 135 return "EXC_BAD_ACCESS"; 136 case EXC_BAD_INSTRUCTION: 137 return "EXC_BAD_INSTRUCTION"; 138 case EXC_ARITHMETIC: 139 return "EXC_ARITHMETIC"; 140 case EXC_EMULATION: 141 return "EXC_EMULATION"; 142 case EXC_SOFTWARE: 143 return "EXC_SOFTWARE"; 144 case EXC_BREAKPOINT: 145 return "EXC_BREAKPOINT"; 146 case EXC_SYSCALL: 147 return "EXC_SYSCALL"; 148 case EXC_MACH_SYSCALL: 149 return "EXC_MACH_SYSCALL"; 150 case EXC_RPC_ALERT: 151 return "EXC_RPC_ALERT"; 152 case EXC_CRASH: 153 return "EXC_CRASH"; 154 } 155 return "UNKNOWN"; 156 } 157 158 static void arch_generateReport(run_t* run, int termsig) { 159 run->report[0] = '\0'; 160 util_ssnprintf(run->report, sizeof(run->report), "ORIG_FNAME: %s\n", run->origFileName); 161 util_ssnprintf(run->report, sizeof(run->report), "FUZZ_FNAME: %s\n", run->crashFileName); 162 util_ssnprintf(run->report, sizeof(run->report), "PID: %d\n", run->pid); 163 util_ssnprintf( 164 run->report, sizeof(run->report), "SIGNAL: %s (%d)\n", arch_sigs[termsig].descr, termsig); 165 util_ssnprintf( 166 run->report, sizeof(run->report), "EXCEPTION: %s\n", exception_to_string(run->exception)); 167 util_ssnprintf(run->report, sizeof(run->report), "FAULT ADDRESS: %p\n", run->access); 168 util_ssnprintf(run->report, sizeof(run->report), "CRASH FRAME PC: %p\n", run->pc); 169 util_ssnprintf(run->report, sizeof(run->report), "STACK HASH: %016llx\n", run->backtrace); 170 if (g_fuzzer_crash_callstack[run->pid]) { 171 util_ssnprintf( 172 run->report, sizeof(run->report), "STACK: \n%s\n", g_fuzzer_crash_callstack[run->pid]); 173 } else { 174 util_ssnprintf(run->report, sizeof(run->report), "STACK: \n Callstack not available.\n"); 175 } 176 177 return; 178 } 179 180 /* 181 * Returns true if a process exited (so, presumably, we can delete an input 182 * file) 183 */ 184 static bool arch_analyzeSignal(run_t* run, int status) { 185 /* 186 * Resumed by delivery of SIGCONT 187 */ 188 if (WIFCONTINUED(status)) { 189 return false; 190 } 191 192 if (WIFEXITED(status) || WIFSIGNALED(status)) { 193 sancov_Analyze(run); 194 } 195 196 /* 197 * Boring, the process just exited 198 */ 199 if (WIFEXITED(status)) { 200 LOG_D("Process (pid %d) exited normally with status %d", run->pid, WEXITSTATUS(status)); 201 return true; 202 } 203 204 /* 205 * Shouldn't really happen, but, well.. 206 */ 207 if (!WIFSIGNALED(status)) { 208 LOG_E("Process (pid %d) exited with the following status %d, please report that as a bug", 209 run->pid, status); 210 return true; 211 } 212 213 int termsig = WTERMSIG(status); 214 LOG_D("Process (pid %d) killed by signal %d '%s'", run->pid, termsig, strsignal(termsig)); 215 if (!arch_sigs[termsig].important) { 216 LOG_D("It's not that important signal, skipping"); 217 return true; 218 } 219 220 /* 221 * Signal is interesting 222 */ 223 /* 224 * Increase crashes counter presented by ASCII display 225 */ 226 ATOMIC_POST_INC(run->global->cnts.crashesCnt); 227 228 /* 229 * Get data from exception handler 230 */ 231 run->pc = g_fuzzer_crash_information[run->pid].pc; 232 run->exception = g_fuzzer_crash_information[run->pid].exception; 233 run->access = g_fuzzer_crash_information[run->pid].access; 234 run->backtrace = g_fuzzer_crash_information[run->pid].backtrace; 235 236 defer { 237 if (g_fuzzer_crash_callstack[run->pid]) { 238 free(g_fuzzer_crash_callstack[run->pid]); 239 g_fuzzer_crash_callstack[run->pid] = NULL; 240 } 241 }; 242 243 /* 244 * Check if stackhash is blacklisted 245 */ 246 if (run->global->blacklist && (fastArray64Search(run->global->blacklist, 247 run->global->blacklistCnt, run->backtrace) != -1)) { 248 LOG_I("Blacklisted stack hash '%" PRIx64 "', skipping", run->backtrace); 249 ATOMIC_POST_INC(run->global->cnts.blCrashesCnt); 250 return true; 251 } 252 253 /* If dry run mode, copy file with same name into workspace */ 254 if (run->global->mutationsPerRun == 0U && run->global->useVerifier) { 255 snprintf(run->crashFileName, sizeof(run->crashFileName), "%s/%s", run->global->io.crashDir, 256 run->origFileName); 257 } else if (run->global->io.saveUnique) { 258 snprintf(run->crashFileName, sizeof(run->crashFileName), 259 "%s/%s.%s.PC.%.16llx.STACK.%.16llx.ADDR.%.16llx.%s", run->global->io.crashDir, 260 arch_sigs[termsig].descr, exception_to_string(run->exception), run->pc, run->backtrace, 261 run->access, run->global->io.fileExtn); 262 } else { 263 char localtmstr[PATH_MAX]; 264 util_getLocalTime("%F.%H.%M.%S", localtmstr, sizeof(localtmstr), time(NULL)); 265 266 snprintf(run->crashFileName, sizeof(run->crashFileName), 267 "%s/%s.%s.PC.%.16llx.STACK.%.16llx.ADDR.%.16llx.TIME.%s.PID.%.5d.%s", 268 run->global->io.crashDir, arch_sigs[termsig].descr, exception_to_string(run->exception), 269 run->pc, run->backtrace, run->access, localtmstr, run->pid, run->global->io.fileExtn); 270 } 271 272 if (files_exists(run->crashFileName)) { 273 LOG_I("It seems that '%s' already exists, skipping", run->crashFileName); 274 // Clear filename so that verifier can understand we hit a duplicate 275 memset(run->crashFileName, 0, sizeof(run->crashFileName)); 276 return true; 277 } 278 279 if (files_writeBufToFile(run->crashFileName, run->dynamicFile, run->dynamicFileSz, 280 O_CREAT | O_EXCL | O_WRONLY) == false) { 281 LOG_E("Couldn't copy '%s' to '%s'", run->fileName, run->crashFileName); 282 return true; 283 } 284 285 LOG_I("Ok, that's interesting, saved '%s' as '%s'", run->fileName, run->crashFileName); 286 287 ATOMIC_POST_INC(run->global->cnts.uniqueCrashesCnt); 288 /* If unique crash found, reset dynFile counter */ 289 ATOMIC_CLEAR(run->global->dynFileIterExpire); 290 291 arch_generateReport(run, termsig); 292 293 return true; 294 } 295 296 pid_t arch_fork(run_t* run UNUSED) { return fork(); } 297 298 bool arch_launchChild(run_t* run) { 299 #define ARGS_MAX 512 300 const char* args[ARGS_MAX + 2]; 301 char argData[PATH_MAX] = {0}; 302 int x; 303 304 for (x = 0; x < ARGS_MAX && run->global->exe.cmdline[x]; x++) { 305 if (!run->global->exe.fuzzStdin && 306 strcmp(run->global->exe.cmdline[x], _HF_FILE_PLACEHOLDER) == 0) { 307 args[x] = run->fileName; 308 } else if (!run->global->exe.fuzzStdin && 309 strstr(run->global->exe.cmdline[x], _HF_FILE_PLACEHOLDER)) { 310 const char* off = strstr(run->global->exe.cmdline[x], _HF_FILE_PLACEHOLDER); 311 snprintf(argData, PATH_MAX, "%.*s%s", (int)(off - run->global->exe.cmdline[x]), 312 run->global->exe.cmdline[x], run->fileName); 313 args[x] = argData; 314 } else { 315 args[x] = run->global->exe.cmdline[x]; 316 } 317 } 318 319 args[x++] = NULL; 320 321 LOG_D("Launching '%s' on file '%s'", args[0], run->fileName); 322 323 /* 324 * Get child's bootstrap port. 325 */ 326 mach_port_t child_bootstrap = MACH_PORT_NULL; 327 if (task_get_bootstrap_port(mach_task_self(), &child_bootstrap) != KERN_SUCCESS) { 328 return false; 329 } 330 331 /* 332 * Get exception port. 333 */ 334 mach_port_t exception_port = MACH_PORT_NULL; 335 336 if (bootstrap_look_up(child_bootstrap, g_service_name, &exception_port) != KERN_SUCCESS) { 337 return false; 338 } 339 340 /* 341 * Here we register the exception port in the child 342 */ 343 if (task_set_exception_ports(mach_task_self(), EXC_MASK_CRASH, exception_port, 344 EXCEPTION_STATE_IDENTITY | MACH_EXCEPTION_CODES, 345 MACHINE_THREAD_STATE) != KERN_SUCCESS) { 346 return false; 347 } 348 349 /* alarm persists across forks, so disable it here */ 350 alarm(0); 351 execvp(args[0], (char* const*)args); 352 alarm(1); 353 354 return false; 355 } 356 357 void arch_prepareParent(run_t* run UNUSED) {} 358 359 void arch_prepareParentAfterFork(run_t* run UNUSED) {} 360 361 void arch_reapChild(run_t* run) { 362 /* 363 * First check manually if we have expired children 364 */ 365 subproc_checkTimeLimit(run); 366 367 /* 368 * Now check for signals using wait4 369 */ 370 int options = WUNTRACED; 371 if (run->global->timing.tmOut) { 372 options |= WNOHANG; 373 } 374 375 for (;;) { 376 int status = 0; 377 while (wait4(run->pid, &status, options, NULL) != run->pid) { 378 if (run->global->timin.tmOut) { 379 subproc_checkTimeLimit(run); 380 usleep(0.20 * 1000000); 381 } 382 } 383 384 char strStatus[4096]; 385 LOG_D("Process (pid %d) came back with status: %s", run->pid, 386 subproc_StatusToStr(status, strStatus, sizeof(strStatus))); 387 388 if (arch_analyzeSignal(run, status)) { 389 return; 390 } 391 } 392 } 393 394 void* wait_for_exception() { 395 while (1) { 396 mach_msg_server_once(mach_exc_server, 4096, g_exception_port, MACH_MSG_OPTION_NONE); 397 } 398 } 399 400 /* 401 * Called once before fuzzing starts. Prepare mach ports for attaching crash reporter. 402 */ 403 bool arch_archInit(honggfuzz_t* hfuzz) { 404 char plist[PATH_MAX]; 405 snprintf(plist, sizeof(plist), "/Users/%s/Library/Preferences/com.apple.DebugSymbols.plist", 406 getlogin()); 407 408 if (files_exists(plist)) { 409 LOG_W( 410 "honggfuzz won't work if DBGShellCommands are set in " 411 "~/Library/Preferences/com.apple.DebugSymbols.plist"); 412 } 413 414 /* 415 * Allocate exception port. 416 */ 417 if (mach_port_allocate(mach_task_self(), MACH_PORT_RIGHT_RECEIVE, &g_exception_port) != 418 KERN_SUCCESS) { 419 return false; 420 } 421 422 /* 423 * Insert exception receive port. 424 */ 425 if (mach_port_insert_right(mach_task_self(), g_exception_port, g_exception_port, 426 MACH_MSG_TYPE_MAKE_SEND) != KERN_SUCCESS) { 427 return false; 428 } 429 430 /* 431 * Get bootstrap port. 432 */ 433 mach_port_t bootstrap = MACH_PORT_NULL; 434 if (task_get_bootstrap_port(mach_task_self(), &bootstrap) != KERN_SUCCESS) { 435 return false; 436 } 437 438 /* 439 * Generate and register exception port service. 440 */ 441 snprintf(g_service_name, sizeof(g_service_name), "com.google.code.honggfuzz.%d", 442 (int)util_rndGet(0, 999999)); 443 if (bootstrap_check_in(bootstrap, g_service_name, &g_exception_port) != KERN_SUCCESS) { 444 return false; 445 } 446 447 /* 448 * Create a collection thread to catch the exceptions from the 449 * children 450 */ 451 pthread_t exception_thread; 452 453 if (pthread_create(&exception_thread, NULL, wait_for_exception, 0)) { 454 LOG_F("Parent: could not create thread to wait for child's exception"); 455 return false; 456 } 457 458 if (pthread_detach(exception_thread)) { 459 LOG_F("Parent: could not detach thread to wait for child's exception"); 460 return false; 461 } 462 463 /* Default is true for all platforms except Android */ 464 arch_sigs[SIGABRT].important = hfuzz->monitorSIGABRT; 465 466 /* Default is false */ 467 arch_sigs[SIGVTALRM].important = hfuzz->tmoutVTALRM; 468 469 return true; 470 } 471 472 #ifdef DEBUG 473 /* 474 * Write the crash report to DEBUG 475 */ 476 static void write_crash_report(thread_port_t thread, task_port_t task, exception_type_t exception, 477 mach_exception_data_t code, mach_msg_type_number_t code_count, int* flavor, 478 thread_state_t in_state, mach_msg_type_number_t in_state_count) { 479 NSAutoreleasePool* pool = [[NSAutoreleasePool alloc] init]; 480 CrashReport* _crashReport = nil; 481 482 _crashReport = [[CrashReport alloc] initWithTask:task 483 exceptionType:exception 484 exceptionCode:code 485 exceptionCodeCount:code_count 486 thread:thread 487 threadStateFlavor:*flavor 488 threadState:(thread_state_t)in_state 489 threadStateCount:in_state_count]; 490 491 NSString* crashDescription = [_crashReport description]; 492 char* description = (char*)[crashDescription UTF8String]; 493 494 LOG_D("CrashReport: %s", description); 495 496 [_crashReport release]; 497 [pool drain]; 498 } 499 #endif 500 501 /* Hash the callstack in an unique way */ 502 static uint64_t hash_callstack(thread_port_t thread, task_port_t task, exception_type_t exception, 503 mach_exception_data_t code, mach_msg_type_number_t code_count, int* flavor, 504 thread_state_t in_state, mach_msg_type_number_t in_state_count) { 505 NSAutoreleasePool* pool = [[NSAutoreleasePool alloc] init]; 506 CrashReport* _crashReport = nil; 507 508 _crashReport = [[CrashReport alloc] initWithTask:task 509 exceptionType:exception 510 exceptionCode:code 511 exceptionCodeCount:code_count 512 thread:thread 513 threadStateFlavor:*flavor 514 threadState:(thread_state_t)in_state 515 threadStateCount:in_state_count]; 516 517 NSString* crashDescription = [_crashReport description]; 518 char* description = (char*)[crashDescription UTF8String]; 519 520 /* 521 * The callstack begins with the following word 522 */ 523 char* callstack = strstr(description, "Crashed:"); 524 525 if (callstack == NULL) { 526 LOG_F("Could not find callstack in crash report %s", description); 527 } 528 529 /* 530 * Scroll forward to the next newline 531 */ 532 char* callstack_start = strstr(callstack, "\n"); 533 534 if (callstack_start == NULL) { 535 LOG_F("Could not find callstack start in crash report %s", description); 536 } 537 538 /* 539 * Skip the newline 540 */ 541 callstack_start++; 542 543 /* 544 * Determine the end of the callstack 545 */ 546 char* callstack_end = strstr(callstack_start, "\n\nThread"); 547 548 if (callstack_end == NULL) { 549 LOG_F("Could not find callstack end in crash report %s", description); 550 } 551 552 if (callstack_end <= callstack_start) { 553 LOG_F("Malformed callstack: %s", description); 554 } 555 556 /* 557 * Check for too large callstack. 558 */ 559 #define MAX_CALLSTACK_SIZE 4096 560 const size_t callstack_size = (callstack_end - callstack_start); 561 if (callstack_size > MAX_CALLSTACK_SIZE) { 562 LOG_W("Too large callstack (%zu bytes), truncating to %d bytes", callstack_size, 563 MAX_CALLSTACK_SIZE); 564 callstack_start[MAX_CALLSTACK_SIZE] = '\0'; 565 callstack_end = callstack_start + MAX_CALLSTACK_SIZE; 566 } 567 568 pid_t pid; 569 pid_for_task(task, &pid); 570 571 char** buf = &g_fuzzer_crash_callstack[pid]; 572 /* 573 * Check for memory leaks. This shouldn't happen. 574 */ 575 if (*buf) { 576 LOG_E("Memory leak: arch_analyzeSignal didn't free previous callstack"); 577 free(*buf); 578 *buf = NULL; 579 } 580 581 /* 582 * Copy the CrashWrangler formatted callstack and make sure 583 * it's NULL-terminated. 584 */ 585 *callstack_end = '\0'; 586 *buf = util_StrDup(callstack_start); 587 588 /* 589 * 590 * For each line, we only take the last three nibbles from the 591 * address. 592 * 593 * Sample outputs: 594 * 595 * 0 libsystem_kernel.dylib 0x00007fff80514d46 __kill + 10 596 * 1 libsystem_c.dylib 0x00007fff85731ec0 __abort + 193 597 * 2 libsystem_c.dylib 0x00007fff85732d17 __stack_chk_fail + 195 598 * 3 stack_buffer_overflow64-stripped 0x000000010339def5 0x10339d000 + 3829 599 * 4 ??? 0x4141414141414141 0 + 4702111234474983745 600 * 601 * 0 libsystem_kernel.dylib 0x00007fff80514d46 __kill + 10 602 * 1 libsystem_c.dylib 0x00007fff85731ec0 __abort + 193 603 * 2 libsystem_c.dylib 0x00007fff85732d17 __stack_chk_fail + 195 604 * 3 stack_buffer_overflow64 0x0000000108f41ef5 main + 133 605 * 4 ??? 0x4141414141414141 0 + 4702111234474983745 606 * 607 * 0 libsystem_kernel.dylib 0x940023ba __kill + 10 608 * 1 libsystem_kernel.dylib 0x940014bc kill$UNIX2003 + 32 609 * 2 libsystem_c.dylib 0x926f362e __abort + 246 610 * 3 libsystem_c.dylib 0x926c2b60 __chk_fail + 49 611 * 4 libsystem_c.dylib 0x926c2bf9 __memset_chk + 53 612 * 5 stack_buffer_overflow32-stripped 0x00093ee5 0x93000 + 3813 613 * 6 libdyld.dylib 0x978c6725 start + 1 614 * 615 * 0 libsystem_kernel.dylib 0x940023ba __kill + 10 616 * 1 libsystem_kernel.dylib 0x940014bc kill$UNIX2003 + 32 617 * 2 libsystem_c.dylib 0x926f362e __abort + 246 618 * 3 libsystem_c.dylib 0x926c2b60 __chk_fail + 49 619 * 4 libsystem_c.dylib 0x926c2bf9 __memset_chk + 53 620 * 5 stack_buffer_overflow32 0x0003cee5 main + 117 621 * 6 libdyld.dylib 0x978c6725 start + 1 622 * 623 */ 624 625 uint64_t hash = 0; 626 char* pos = callstack_start; 627 628 /* 629 * Go through each line until we run out of lines 630 */ 631 while (strstr(pos, "\t") != NULL) { 632 /* 633 * Format: dylib spaces tab address space symbol space plus space offset 634 * Scroll pos forward to the last three nibbles of the address. 635 */ 636 if ((pos = strstr(pos, "\t")) == NULL) break; 637 if ((pos = strstr(pos, " ")) == NULL) break; 638 pos = pos - 3; 639 /* 640 * Hash the last three nibbles 641 */ 642 hash ^= util_hash(pos, 3); 643 /* 644 * Scroll pos one forward to skip the current tab 645 */ 646 pos++; 647 } 648 649 LOG_D("Callstack hash %llu", hash); 650 651 [_crashReport release]; 652 [pool drain]; 653 654 return hash; 655 } 656 657 kern_return_t catch_mach_exception_raise(mach_port_t exception_port, mach_port_t thread, 658 mach_port_t task, exception_type_t exception, mach_exception_data_t code, 659 mach_msg_type_number_t codeCnt) { 660 LOG_F("This function should never get called"); 661 return KERN_SUCCESS; 662 } 663 664 kern_return_t catch_mach_exception_raise_state(mach_port_t exception_port, 665 exception_type_t exception, const mach_exception_data_t code, mach_msg_type_number_t codeCnt, 666 int* flavor, const thread_state_t old_state, mach_msg_type_number_t old_stateCnt, 667 thread_state_t new_state, mach_msg_type_number_t* new_stateCnt) { 668 LOG_F("This function should never get called"); 669 return KERN_SUCCESS; 670 } 671 672 kern_return_t catch_mach_exception_raise_state_identity( 673 __attribute__((unused)) exception_port_t exception_port, thread_port_t thread, task_port_t task, 674 exception_type_t exception, mach_exception_data_t code, mach_msg_type_number_t code_count, 675 int* flavor, thread_state_t in_state, mach_msg_type_number_t in_state_count, 676 thread_state_t out_state, mach_msg_type_number_t* out_state_count) { 677 if (exception != EXC_CRASH) { 678 LOG_F("Got non EXC_CRASH! This should not happen."); 679 } 680 681 /* 682 * We will save our results to the honggfuzz_t global 683 */ 684 pid_t pid; 685 pid_for_task(task, &pid); 686 LOG_D("Crash of pid %d", pid); 687 688 run_t* run = &g_fuzzer_crash_information[pid]; 689 690 /* 691 * Get program counter. 692 * Cast to void* in order to silence the alignment warnings 693 */ 694 x86_thread_state_t* platform_in_state = ((x86_thread_state_t*)(void*)in_state); 695 696 if (x86_THREAD_STATE32 == platform_in_state->tsh.flavor) { 697 run->pc = platform_in_state->uts.ts32.__eip; 698 } else { 699 run->pc = platform_in_state->uts.ts64.__rip; 700 } 701 702 /* 703 * Get the exception type 704 */ 705 exception_type_t exception_type = ((code[0] >> 20) & 0x0F); 706 if (exception_type == 0) { 707 exception_type = EXC_CRASH; 708 } 709 run->exception = exception_type; 710 711 /* 712 * Get the access address. 713 */ 714 mach_exception_data_type_t exception_data[2]; 715 memcpy(exception_data, code, sizeof(exception_data)); 716 exception_data[0] = (code[0] & ~(0x00000000FFF00000)); 717 exception_data[1] = code[1]; 718 719 mach_exception_data_type_t access_address = exception_data[1]; 720 run->access = (uint64_t)access_address; 721 722 /* 723 * Get a hash of the callstack 724 */ 725 uint64_t hash = 726 hash_callstack(thread, task, exception, code, code_count, flavor, in_state, in_state_count); 727 run->backtrace = hash; 728 729 #ifdef DEBUG 730 write_crash_report(thread, task, exception, code, code_count, flavor, in_state, in_state_count); 731 #endif 732 733 /* 734 * Cleanup 735 */ 736 if (mach_port_deallocate(mach_task_self(), task) != KERN_SUCCESS) { 737 LOG_W("Exception Handler: Could not deallocate task"); 738 } 739 740 if (mach_port_deallocate(mach_task_self(), thread) != KERN_SUCCESS) { 741 LOG_W("Exception Handler: Could not deallocate thread"); 742 } 743 744 /* 745 * KERN_SUCCESS indicates that this should not be forwarded to other crash 746 * handlers 747 */ 748 return KERN_SUCCESS; 749 } 750 751 bool arch_archThreadInit(run_t* run UNUSED) { return true; } 752