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