Home | History | Annotate | Download | only in mac
      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