1 /*
2  *
3  * honggfuzz - architecture dependent code (MAC OS X)
4  * -----------------------------------------
5  *
6  * Authors: Robert Swiecki <swiecki@google.com>
7  *          Felix Gröbert <groebert@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 
arch_initSigs(void)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 
exception_to_string(int exception)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 
arch_generateReport(run_t * run,int termsig)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  */
arch_analyzeSignal(run_t * run,int status)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 
arch_fork(run_t * run HF_ATTR_UNUSED)294 pid_t arch_fork(run_t* run HF_ATTR_UNUSED) {
295     return fork();
296 }
297 
arch_launchChild(run_t * run)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 
arch_prepareParent(run_t * run HF_ATTR_UNUSED)359 void arch_prepareParent(run_t* run HF_ATTR_UNUSED) {
360 }
361 
arch_prepareParentAfterFork(run_t * run HF_ATTR_UNUSED)362 void arch_prepareParentAfterFork(run_t* run HF_ATTR_UNUSED) {
363 }
364 
arch_reapChild(run_t * run)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 
wait_for_exception()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  */
arch_archInit(honggfuzz_t * hfuzz)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  */
write_crash_report(thread_port_t thread,task_port_t task,exception_type_t exception,mach_exception_data_t code,mach_msg_type_number_t code_count,int * flavor,thread_state_t in_state,mach_msg_type_number_t in_state_count)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 */
hash_callstack(thread_port_t thread,task_port_t task,exception_type_t exception,mach_exception_data_t code,mach_msg_type_number_t code_count,int * flavor,thread_state_t in_state,mach_msg_type_number_t in_state_count)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 
catch_mach_exception_raise(mach_port_t exception_port,mach_port_t thread,mach_port_t task,exception_type_t exception,mach_exception_data_t code,mach_msg_type_number_t codeCnt)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 
catch_mach_exception_raise_state(mach_port_t exception_port,exception_type_t exception,const mach_exception_data_t code,mach_msg_type_number_t codeCnt,int * flavor,const thread_state_t old_state,mach_msg_type_number_t old_stateCnt,thread_state_t new_state,mach_msg_type_number_t * new_stateCnt)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 
catch_mach_exception_raise_state_identity(exception_port_t exception_port,thread_port_t thread,task_port_t task,exception_type_t exception,mach_exception_data_t code,mach_msg_type_number_t code_count,int * flavor,thread_state_t in_state,mach_msg_type_number_t in_state_count,thread_state_t out_state,mach_msg_type_number_t * out_state_count)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 
arch_archThreadInit(run_t * run HF_ATTR_UNUSED)787 bool arch_archThreadInit(run_t* run HF_ATTR_UNUSED) {
788     return true;
789 }
790