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