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