blob: 4fcf2d45e3911f298aaed5c5886b037f613f4e02 [file] [log] [blame]
robert.swiecki3bb518c2010-10-14 00:48:24 +00001/*
2
robert.swiecki40499ff2010-12-13 19:47:08 +00003 honggfuzz - architecture dependent code (MAC OS X)
robert.swiecki3bb518c2010-10-14 00:48:24 +00004 -----------------------------------------
5
6 Author: Robert Swiecki <swiecki@google.com>
groebert@google.comb857deb2010-10-21 19:09:29 +00007 Felix Gröbert <groebert@google.com>
robert.swiecki3bb518c2010-10-14 00:48:24 +00008
robert.swiecki@gmail.comba85c3e2015-02-02 14:55:16 +00009 Copyright 2010-2015 by Google Inc. All Rights Reserved.
robert.swiecki3bb518c2010-10-14 00:48:24 +000010
11 Licensed under the Apache License, Version 2.0 (the "License");
12 you may not use this file except in compliance with the License.
13 You may obtain 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 implied.
20 See the License for the specific language governing permissions and
21 limitations under the License.
22
23*/
24
robert.swiecki@gmail.comba85c3e2015-02-02 14:55:16 +000025#include "common.h"
26#include "arch.h"
27
robert.swiecki3bb518c2010-10-14 00:48:24 +000028#include <ctype.h>
groebert@google.comb857deb2010-10-21 19:09:29 +000029#include <dirent.h>
robert.swiecki@gmail.comba85c3e2015-02-02 14:55:16 +000030#include <errno.h>
groebert@google.comb857deb2010-10-21 19:09:29 +000031#include <fcntl.h>
robert.swiecki@gmail.comba85c3e2015-02-02 14:55:16 +000032#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/time.h>
39#include <sys/types.h>
40#include <sys/wait.h>
41#include <sys/resource.h>
42#include <time.h>
43#include <unistd.h>
robert.swiecki3bb518c2010-10-14 00:48:24 +000044
robert.swiecki3bb518c2010-10-14 00:48:24 +000045#include "log.h"
robert.swiecki3bb518c2010-10-14 00:48:24 +000046#include "util.h"
groebert@google.comb857deb2010-10-21 19:09:29 +000047#include "files.h"
robert.swiecki3bb518c2010-10-14 00:48:24 +000048
groebert@google.com1bd4c212013-06-19 11:13:56 +000049#include <servers/bootstrap.h>
50#include <mach/mach.h>
51#include <mach/mach_vm.h>
52#include <mach/mach_types.h>
53#include <mach/i386/thread_status.h>
54#include <mach/task_info.h>
55#include <pthread.h>
56
57#include "mach_exc.h"
58#include "mach_excServer.h"
59
60#import <Foundation/Foundation.h>
61
groebert@google.com43216c82015-02-13 11:11:38 +000062/* Interface to third_party/CrashReport_*.o */
63/* *INDENT-OFF* */
64@interface CrashReport: NSObject - (id) initWithTask:(task_t)
groebert@google.com1bd4c212013-06-19 11:13:56 +000065task exceptionType:(exception_type_t)
66anExceptionType exceptionCode:(mach_exception_data_t)
67anExceptionCode exceptionCodeCount:(mach_msg_type_number_t)
68anExceptionCodeCount thread:(thread_t)
69thread threadStateFlavor:(thread_state_flavor_t)
70aThreadStateFlavor threadState:(thread_state_data_t)
71aThreadState threadStateCount:(mach_msg_type_number_t) aThreadStateCount;
72@end
groebert@google.com43216c82015-02-13 11:11:38 +000073/* *INDENT-ON* */
74
groebert@google.com1bd4c212013-06-19 11:13:56 +000075/* Global to have exception port available in the collection thread */
76static mach_port_t g_exception_port = MACH_PORT_NULL;
77
groebert@google.com43216c82015-02-13 11:11:38 +000078/* From xnu/bsd/sys/proc_internal.h */
groebert@google.comc659c0e2015-02-13 11:17:00 +000079#define PID_MAX 99999
groebert@google.com43216c82015-02-13 11:11:38 +000080
groebert@google.com20e368f2015-02-13 14:19:25 +000081/* Global to store crash info in exception handler thread */
groebert@google.comc659c0e2015-02-13 11:17:00 +000082fuzzer_t g_fuzzer_crash_information[PID_MAX + 1];
groebert@google.com43216c82015-02-13 11:11:38 +000083
groebert@google.com76afff92013-06-20 14:43:41 +000084/* Global to have a unique service name for each honggfuzz process */
85char g_service_name[256];
groebert@google.com16610ee2013-06-19 13:06:12 +000086
robert.swiecki3bb518c2010-10-14 00:48:24 +000087struct {
88 bool important;
89 const char *descr;
90} arch_sigs[NSIG];
91
robert.swiecki@gmail.comc6d28752014-01-08 12:02:37 +000092__attribute__ ((constructor))
93void arch_initSigs(void)
robert.swiecki3bb518c2010-10-14 00:48:24 +000094{
95 for (int x = 0; x < NSIG; x++)
96 arch_sigs[x].important = false;
97
98 arch_sigs[SIGILL].important = true;
99 arch_sigs[SIGILL].descr = "SIGILL";
100 arch_sigs[SIGFPE].important = true;
101 arch_sigs[SIGFPE].descr = "SIGFPE";
102 arch_sigs[SIGSEGV].important = true;
103 arch_sigs[SIGSEGV].descr = "SIGSEGV";
104 arch_sigs[SIGBUS].important = true;
105 arch_sigs[SIGBUS].descr = "SIGBUS";
106 arch_sigs[SIGABRT].important = true;
107 arch_sigs[SIGABRT].descr = "SIGABRT";
108}
109
groebert@google.com1bd4c212013-06-19 11:13:56 +0000110const char *exception_to_string(int exception)
groebert@google.comb857deb2010-10-21 19:09:29 +0000111{
groebert@google.com1bd4c212013-06-19 11:13:56 +0000112 switch (exception) {
113 case EXC_BAD_ACCESS:
114 return "EXC_BAD_ACCESS";
115 case EXC_BAD_INSTRUCTION:
116 return "EXC_BAD_INSTRUCTION";
117 case EXC_ARITHMETIC:
118 return "EXC_ARITHMETIC";
119 case EXC_EMULATION:
120 return "EXC_EMULATION";
121 case EXC_SOFTWARE:
122 return "EXC_SOFTWARE";
123 case EXC_BREAKPOINT:
124 return "EXC_BREAKPOINT";
125 case EXC_SYSCALL:
126 return "EXC_SYSCALL";
127 case EXC_MACH_SYSCALL:
128 return "EXC_MACH_SYSCALL";
129 case EXC_RPC_ALERT:
130 return "EXC_RPC_ALERT";
131 case EXC_CRASH:
132 return "EXC_CRASH";
groebert@google.comb857deb2010-10-21 19:09:29 +0000133 }
groebert@google.com1bd4c212013-06-19 11:13:56 +0000134 return "UNKNOWN";
groebert@google.comb857deb2010-10-21 19:09:29 +0000135}
136
137/*
robert.swiecki3bb518c2010-10-14 00:48:24 +0000138 * Returns true if a process exited (so, presumably, we can delete an input
139 * file)
140 */
robert.swiecki@gmail.com751f6862015-02-11 15:03:26 +0000141static bool arch_analyzeSignal(honggfuzz_t * hfuzz, int status, fuzzer_t * fuzzer)
robert.swiecki3bb518c2010-10-14 00:48:24 +0000142{
143 /*
144 * Resumed by delivery of SIGCONT
145 */
146 if (WIFCONTINUED(status)) {
147 return false;
148 }
149
150 /*
151 * Boring, the process just exited
152 */
153 if (WIFEXITED(status)) {
robert.swiecki@gmail.com751f6862015-02-11 15:03:26 +0000154 LOGMSG(l_DEBUG, "Process (pid %d) exited normally with status %d", fuzzer->pid,
groebert@google.comb857deb2010-10-21 19:09:29 +0000155 WEXITSTATUS(status));
robert.swiecki3bb518c2010-10-14 00:48:24 +0000156 return true;
157 }
158
159 /*
160 * Shouldn't really happen, but, well..
161 */
162 if (!WIFSIGNALED(status)) {
163 LOGMSG(l_ERROR,
164 "Process (pid %d) exited with the following status %d, please report that as a bug",
robert.swiecki@gmail.com751f6862015-02-11 15:03:26 +0000165 fuzzer->pid, status);
robert.swiecki3bb518c2010-10-14 00:48:24 +0000166 return true;
167 }
168
169 int termsig = WTERMSIG(status);
robert.swiecki@gmail.com751f6862015-02-11 15:03:26 +0000170 LOGMSG(l_DEBUG, "Process (pid %d) killed by signal %d '%s'", fuzzer->pid, termsig,
171 strsignal(termsig));
robert.swiecki3bb518c2010-10-14 00:48:24 +0000172 if (!arch_sigs[termsig].important) {
173 LOGMSG(l_DEBUG, "It's not that important signal, skipping");
174 return true;
175 }
176
groebert@google.com1bd4c212013-06-19 11:13:56 +0000177 /*
178 * Signal is interesting
179 */
groebert@google.com1bd4c212013-06-19 11:13:56 +0000180 char newname[PATH_MAX];
groebert@google.comb857deb2010-10-21 19:09:29 +0000181
groebert@google.com43216c82015-02-13 11:11:38 +0000182 /* Get data from exception handler */
183 fuzzer->pc = g_fuzzer_crash_information[fuzzer->pid].pc;
184 fuzzer->exception = g_fuzzer_crash_information[fuzzer->pid].exception;
185 fuzzer->access = g_fuzzer_crash_information[fuzzer->pid].access;
186 fuzzer->backtrace = g_fuzzer_crash_information[fuzzer->pid].backtrace;
187
groebert@google.com1bd4c212013-06-19 11:13:56 +0000188 if (hfuzz->saveUnique) {
189 snprintf(newname, sizeof(newname),
190 "%s.%s.PC.%.16llx.STACK.%.16llx.ADDR.%.16llx.%s.%s",
groebert@google.com43216c82015-02-13 11:11:38 +0000191 arch_sigs[termsig].descr, exception_to_string(fuzzer->exception),
robert.swiecki@gmail.com751f6862015-02-11 15:03:26 +0000192 fuzzer->pc, fuzzer->backtrace, fuzzer->access,
193 fuzzer->origFileName, hfuzz->fileExtn);
groebert@google.comb857deb2010-10-21 19:09:29 +0000194 } else {
groebert@google.comb857deb2010-10-21 19:09:29 +0000195
groebert@google.com1bd4c212013-06-19 11:13:56 +0000196 char localtmstr[PATH_MAX];
197 util_getLocalTime("%F.%H.%M.%S", localtmstr, sizeof(localtmstr));
198
199 snprintf(newname, sizeof(newname),
200 "%s.%s.PC.%.16llx.STACK.%.16llx.ADDR.%.16llx.TIME.%s.PID.%.5d.%s.%s",
robert.swiecki@gmail.com751f6862015-02-11 15:03:26 +0000201 arch_sigs[termsig].descr, exception_to_string(fuzzer->exception),
groebert@google.com43216c82015-02-13 11:11:38 +0000202 fuzzer->pc, fuzzer->backtrace, fuzzer->access,
robert.swiecki@gmail.com751f6862015-02-11 15:03:26 +0000203 localtmstr, fuzzer->pid, fuzzer->origFileName, hfuzz->fileExtn);
groebert@google.com1bd4c212013-06-19 11:13:56 +0000204 }
205
robert.swiecki@gmail.com751f6862015-02-11 15:03:26 +0000206 if (link(fuzzer->fileName, newname) == 0) {
groebert@google.com43216c82015-02-13 11:11:38 +0000207 LOGMSG(l_INFO, "Ok, that's interesting, saved '%s' as '%s'", fuzzer->fileName, newname);
groebert@google.com1bd4c212013-06-19 11:13:56 +0000208 } else {
209 if (errno == EEXIST) {
210 LOGMSG(l_INFO, "It seems that '%s' already exists, skipping", newname);
211 } else {
groebert@google.com43216c82015-02-13 11:11:38 +0000212 LOGMSG_P(l_ERROR, "Couldn't link '%s' to '%s'", fuzzer->fileName, newname);
groebert@google.comb857deb2010-10-21 19:09:29 +0000213 }
robert.swiecki3bb518c2010-10-14 00:48:24 +0000214 }
groebert@google.comb857deb2010-10-21 19:09:29 +0000215
robert.swiecki3bb518c2010-10-14 00:48:24 +0000216 return true;
217}
218
219bool arch_launchChild(honggfuzz_t * hfuzz, char *fileName)
220{
robert.swiecki3bb518c2010-10-14 00:48:24 +0000221#define ARGS_MAX 512
222 char *args[ARGS_MAX + 2];
223
224 int x;
225
226 for (x = 0; x < ARGS_MAX && hfuzz->cmdline[x]; x++) {
robert.swiecki@gmail.coma0d87142015-02-14 13:11:18 +0000227 if (!hfuzz->fuzzStdin && strcmp(hfuzz->cmdline[x], _HF_FILE_PLACEHOLDER) == 0) {
robert.swiecki3bb518c2010-10-14 00:48:24 +0000228 args[x] = fileName;
229 } else {
230 args[x] = hfuzz->cmdline[x];
231 }
232 }
233
234 args[x++] = NULL;
235
236 LOGMSG(l_DEBUG, "Launching '%s' on file '%s'", args[0], fileName);
237
groebert@google.com1bd4c212013-06-19 11:13:56 +0000238 /* Get child's bootstrap port. */
239 mach_port_t child_bootstrap = MACH_PORT_NULL;
240 if (task_get_bootstrap_port(mach_task_self(), &child_bootstrap) != KERN_SUCCESS) {
241 return false;
242 }
243
244 /* Get exception port. */
245 mach_port_t exception_port = MACH_PORT_NULL;
246
groebert@google.com76afff92013-06-20 14:43:41 +0000247 if (bootstrap_look_up(child_bootstrap, g_service_name, &exception_port) != KERN_SUCCESS) {
groebert@google.com1bd4c212013-06-19 11:13:56 +0000248 return false;
249 }
250
251 /* Here we register the exception port in the child */
252 if (task_set_exception_ports(mach_task_self(),
253 EXC_MASK_CRASH,
254 exception_port,
255 EXCEPTION_STATE_IDENTITY | MACH_EXCEPTION_CODES,
256 MACHINE_THREAD_STATE) != KERN_SUCCESS) {
257 return false;
258 }
259
robert.swiecki3bb518c2010-10-14 00:48:24 +0000260 /*
261 * Set timeout (prof), real timeout (2*prof), and rlimit_cpu (2*prof)
262 */
263 if (hfuzz->tmOut) {
264 struct itimerval it;
265
266 /*
267 * The hfuzz->tmOut is real CPU usage time...
268 */
269 it.it_value.tv_sec = hfuzz->tmOut;
270 it.it_value.tv_usec = 0;
271 it.it_interval.tv_sec = 0;
272 it.it_interval.tv_usec = 0;
273 if (setitimer(ITIMER_PROF, &it, NULL) == -1) {
274 LOGMSG_P(l_ERROR, "Couldn't set the ITIMER_PROF timer");
275 return false;
276 }
277
278 /*
279 * ...so, if a process sleeps, this one should
280 * trigger a signal...
281 */
282 it.it_value.tv_sec = hfuzz->tmOut * 2UL;
283 it.it_value.tv_usec = 0;
284 it.it_interval.tv_sec = 0;
285 it.it_interval.tv_usec = 0;
286 if (setitimer(ITIMER_REAL, &it, NULL) == -1) {
287 LOGMSG_P(l_ERROR, "Couldn't set the ITIMER_REAL timer");
288 return false;
289 }
290
291 /*
292 * ..if a process sleeps and catches SIGPROF/SIGALRM
293 * rlimits won't help either
294 */
295 struct rlimit rl;
296
297 rl.rlim_cur = hfuzz->tmOut * 2;
298 rl.rlim_max = hfuzz->tmOut * 2;
299 if (setrlimit(RLIMIT_CPU, &rl) == -1) {
300 LOGMSG_P(l_ERROR, "Couldn't enforce the RLIMIT_CPU resource limit");
301 return false;
302 }
303 }
304
305 /*
306 * The address space limit. If big enough - roughly the size of RAM used
307 */
308 if (hfuzz->asLimit) {
309 struct rlimit rl;
310
311 rl.rlim_cur = hfuzz->asLimit * 1024UL * 1024UL;
312 rl.rlim_max = hfuzz->asLimit * 1024UL * 1024UL;
313 if (setrlimit(RLIMIT_AS, &rl) == -1) {
314 LOGMSG_P(l_DEBUG, "Couldn't encforce the RLIMIT_AS resource limit, ignoring");
315 }
316 }
317
318 if (hfuzz->nullifyStdio) {
319 util_nullifyStdio();
320 }
321
322 if (hfuzz->fuzzStdin) {
323 /* Uglyyyyyy ;) */
324 if (!util_redirectStdin(fileName)) {
325 return false;
326 }
327 }
328
329 execvp(args[0], args);
330
331 util_recoverStdio();
332 LOGMSG(l_FATAL, "Failed to create new '%s' process", args[0]);
333 return false;
334}
335
robert.swiecki@gmail.com751f6862015-02-11 15:03:26 +0000336void arch_reapChild(honggfuzz_t * hfuzz, fuzzer_t * fuzzer)
robert.swiecki3bb518c2010-10-14 00:48:24 +0000337{
groebert@google.com06d93802013-09-16 12:06:40 +0000338 /*
339 * First check manually if we have expired childs
340 */
341
groebert@google.com43216c82015-02-13 11:11:38 +0000342 double diff = difftime(time(NULL), fuzzer->timeStarted);
robert.swiecki@gmail.com751f6862015-02-11 15:03:26 +0000343 if (diff > (double)hfuzz->tmOut) {
344 LOGMSG(l_WARN,
345 "Process pid %d is overdue (%f seconds, max %f seconds %f), sending a SIGKILL",
groebert@google.com43216c82015-02-13 11:11:38 +0000346 fuzzer->pid, diff, (double)hfuzz->tmOut);
347 kill(fuzzer->pid, SIGKILL);
groebert@google.com06d93802013-09-16 12:06:40 +0000348 }
349
350 /*
robert.swiecki@gmail.com751f6862015-02-11 15:03:26 +0000351 * Now check for signals using wait4
groebert@google.com06d93802013-09-16 12:06:40 +0000352 */
robert.swiecki@gmail.com751f6862015-02-11 15:03:26 +0000353
robert.swiecki@gmail.com65cfa1c2015-02-11 15:59:28 +0000354 for (;;) {
355 int status = 0;
groebert@google.com43216c82015-02-13 11:11:38 +0000356 while (wait4(fuzzer->pid, &status, WUNTRACED, NULL) != fuzzer->pid) ;
robert.swiecki@gmail.com65cfa1c2015-02-11 15:59:28 +0000357 LOGMSG(l_DEBUG, "Process (pid %d) came back with status %d", fuzzer->pid, status);
robert.swiecki3bb518c2010-10-14 00:48:24 +0000358
groebert@google.com43216c82015-02-13 11:11:38 +0000359 if (arch_analyzeSignal(hfuzz, status, fuzzer)) {
robert.swiecki@gmail.com65cfa1c2015-02-11 15:59:28 +0000360 return;
361 }
robert.swiecki3bb518c2010-10-14 00:48:24 +0000362 }
robert.swiecki@gmail.com751f6862015-02-11 15:03:26 +0000363}
robert.swiecki28cba5c2011-06-22 01:38:55 +0000364
groebert@google.com1bd4c212013-06-19 11:13:56 +0000365void *wait_for_exception()
366{
367 while (1) {
368 mach_msg_server_once(mach_exc_server, 4096, g_exception_port, MACH_MSG_OPTION_NONE);
369 }
370}
371
372/*
373 * Called once before fuzzing starts. Prepare mach ports for attaching crash reporter.
374 */
robert.swiecki28cba5c2011-06-22 01:38:55 +0000375bool arch_prepareParent(honggfuzz_t * hfuzz)
376{
groebert@google.com1bd4c212013-06-19 11:13:56 +0000377 char plist[PATH_MAX];
378 snprintf(plist, sizeof(plist), "/Users/%s/Library/Preferences/com.apple.DebugSymbols.plist",
379 getlogin());
380
381 if (files_exists(plist)) {
382 LOGMSG(l_WARN,
383 "honggfuzz won't work if DBGShellCommands are set in ~/Library/Preferences/com.apple.DebugSymbols.plist");
384 }
385
386 /* Allocate exception port. */
387 if (mach_port_allocate(mach_task_self(), MACH_PORT_RIGHT_RECEIVE, &g_exception_port) !=
388 KERN_SUCCESS) {
389 return false;
390 }
391
392 /* Insert exception receive port. */
393 if (mach_port_insert_right(mach_task_self(), g_exception_port, g_exception_port,
394 MACH_MSG_TYPE_MAKE_SEND) != KERN_SUCCESS) {
395 return false;
396 }
397
398 /* Get bootstrap port. */
399 mach_port_t bootstrap = MACH_PORT_NULL;
400 if (task_get_bootstrap_port(mach_task_self(), &bootstrap) != KERN_SUCCESS) {
401 return false;
402 }
403
groebert@google.com76afff92013-06-20 14:43:41 +0000404 /* Generate and register exception port service. */
robert.swiecki@gmail.comc6d28752014-01-08 12:02:37 +0000405 snprintf(g_service_name, sizeof(g_service_name), "com.google.code.honggfuzz.%d",
406 util_rndGet(0, 999999));
groebert@google.com76afff92013-06-20 14:43:41 +0000407 if (bootstrap_check_in(bootstrap, g_service_name, &g_exception_port) != KERN_SUCCESS) {
groebert@google.com1bd4c212013-06-19 11:13:56 +0000408 return false;
409 }
410
411 /* Create a collection thread to catch the exceptions from the children */
412 pthread_t exception_thread;
413
414 if (pthread_create(&exception_thread, NULL, wait_for_exception, 0)) {
415 LOGMSG(l_FATAL, "Parent: could not create thread to wait for child's exception");
416 return false;
417 }
418
419 if (pthread_detach(exception_thread)) {
420 LOGMSG(l_FATAL, "Parent: could not detach thread to wait for child's exception");
421 return false;
422 }
423
robert.swiecki28cba5c2011-06-22 01:38:55 +0000424 return true;
425}
groebert@google.com1bd4c212013-06-19 11:13:56 +0000426
427/* Write the crash report to DEBUG */
428void write_crash_report(thread_port_t thread,
429 task_port_t task,
430 exception_type_t exception,
431 mach_exception_data_t code,
432 mach_msg_type_number_t code_count,
groebert@google.comc659c0e2015-02-13 11:17:00 +0000433 int *flavor, thread_state_t in_state, mach_msg_type_number_t in_state_count)
groebert@google.com1bd4c212013-06-19 11:13:56 +0000434{
435
436 NSAutoreleasePool *pool =[[NSAutoreleasePool alloc] init];
437 CrashReport *_crashReport = nil;
438
groebert@google.com43216c82015-02-13 11:11:38 +0000439/* *INDENT-OFF* */
440 _crashReport = [[CrashReport alloc] initWithTask:task
441 exceptionType:exception
442 exceptionCode:code
443 exceptionCodeCount:code_count
444 thread:thread
445 threadStateFlavor:*flavor
446 threadState:(thread_state_t)in_state
447 threadStateCount:in_state_count];
448/* *INDENT-OFF* */
groebert@google.com1bd4c212013-06-19 11:13:56 +0000449
450 NSString *crashDescription =[_crashReport description];
451 char *description = (char *)[crashDescription UTF8String];
452
453 LOGMSG(l_DEBUG, "CrashReport: %s", description);
454
455 [_crashReport release];
456 [pool drain];
457}
458
459/* Hash the callstack in an unique way */
460uint64_t hash_callstack(thread_port_t thread,
461 task_port_t task,
462 exception_type_t exception,
463 mach_exception_data_t code,
464 mach_msg_type_number_t code_count,
groebert@google.com43216c82015-02-13 11:11:38 +0000465 int *flavor,
466 thread_state_t in_state,
467 mach_msg_type_number_t in_state_count) {
groebert@google.com1bd4c212013-06-19 11:13:56 +0000468
469 NSAutoreleasePool *pool =[[NSAutoreleasePool alloc] init];
470 CrashReport *_crashReport = nil;
471
groebert@google.com43216c82015-02-13 11:11:38 +0000472/* *INDENT-OFF* */
473 _crashReport = [[CrashReport alloc] initWithTask:task
474 exceptionType:exception
475 exceptionCode:code
476 exceptionCodeCount:code_count
477 thread:thread
478 threadStateFlavor:*flavor
479 threadState:(thread_state_t)in_state
480 threadStateCount:in_state_count];
481/* *INDENT-ON* */
groebert@google.com1bd4c212013-06-19 11:13:56 +0000482
483 NSString *crashDescription =[_crashReport description];
484 char *description = (char *)[crashDescription UTF8String];
485
486 /* The callstack begins with the following word */
groebert@google.com06d93802013-09-16 12:06:40 +0000487 char *callstack = strstr(description, "Crashed:");
groebert@google.com1bd4c212013-06-19 11:13:56 +0000488
489 if (callstack == NULL) {
490 LOGMSG(l_FATAL, "Could not find callstack in crash report %s", description);
491 }
492
493 /* Scroll forward to the next newline */
494 char *callstack_start = strstr(callstack, "\n");
495
496 if (callstack_start == NULL) {
497 LOGMSG(l_FATAL, "Could not find callstack start in crash report %s", description);
498 }
499
500 /* Skip the newline */
501 callstack_start++;
502
503 /* Determine the end of the callstack */
504 char *callstack_end = strstr(callstack, "\n\nThread");
505
506 if (callstack_end == NULL) {
507 LOGMSG(l_FATAL, "Could not find callstack end in crash report %s", description);
508 }
509
510 /* Make sure it's NULL-terminated */
511 *callstack_end = '\0';
512
513 /*
514
515 For each line, we only take the last three nibbles from the address.
516
517 Sample output:
518
519 0 libsystem_kernel.dylib 0x00007fff80514d46 __kill + 10
520 1 libsystem_c.dylib 0x00007fff85731ec0 __abort + 193
521 2 libsystem_c.dylib 0x00007fff85732d17 __stack_chk_fail + 195
522 3 stack_buffer_overflow64-stripped 0x000000010339def5 0x10339d000 + 3829
523 4 ??? 0x4141414141414141 0 + 4702111234474983745
524
525 0 libsystem_kernel.dylib 0x00007fff80514d46 __kill + 10
526 1 libsystem_c.dylib 0x00007fff85731ec0 __abort + 193
527 2 libsystem_c.dylib 0x00007fff85732d17 __stack_chk_fail + 195
528 3 stack_buffer_overflow64 0x0000000108f41ef5 main + 133
529 4 ??? 0x4141414141414141 0 + 4702111234474983745
530
531 0 libsystem_kernel.dylib 0x940023ba __kill + 10
532 1 libsystem_kernel.dylib 0x940014bc kill$UNIX2003 + 32
533 2 libsystem_c.dylib 0x926f362e __abort + 246
534 3 libsystem_c.dylib 0x926c2b60 __chk_fail + 49
535 4 libsystem_c.dylib 0x926c2bf9 __memset_chk + 53
536 5 stack_buffer_overflow32-stripped 0x00093ee5 0x93000 + 3813
537 6 libdyld.dylib 0x978c6725 start + 1
538
539 0 libsystem_kernel.dylib 0x940023ba __kill + 10
540 1 libsystem_kernel.dylib 0x940014bc kill$UNIX2003 + 32
541 2 libsystem_c.dylib 0x926f362e __abort + 246
542 3 libsystem_c.dylib 0x926c2b60 __chk_fail + 49
543 4 libsystem_c.dylib 0x926c2bf9 __memset_chk + 53
544 5 stack_buffer_overflow32 0x0003cee5 main + 117
545 6 libdyld.dylib 0x978c6725 start + 1
546
547 */
548
549 uint64_t hash = 0;
550 char *pos = callstack_start;
551
552 /* Go through each line until we run out of lines */
553 while (strstr(pos, "\t") != NULL) {
554 /*
555 * Format: dylib spaces tab address space symbol space plus space offset
556 * Scroll pos forward to the last three nibbles of the address.
557 */
558 if ((pos = strstr(pos, "\t")) == NULL)
559 break;
560 if ((pos = strstr(pos, " ")) == NULL)
561 break;
562 pos = pos - 3;
563 /* Hash the last three nibbles */
564 hash ^= util_hash(pos, 3);
565 /* Scroll pos one forward to skip the current tab */
566 pos++;
567 }
568
groebert@google.com20e368f2015-02-13 14:19:25 +0000569 LOGMSG(l_DEBUG, "Callstack hash %u", hash);
groebert@google.com1bd4c212013-06-19 11:13:56 +0000570
571 [_crashReport release];
572 [pool drain];
573
574 return hash;
575}
576
groebert@google.com43216c82015-02-13 11:11:38 +0000577kern_return_t catch_mach_exception_raise(mach_port_t exception_port,
578 mach_port_t thread,
579 mach_port_t task,
580 exception_type_t exception,
groebert@google.comc659c0e2015-02-13 11:17:00 +0000581 mach_exception_data_t code, mach_msg_type_number_t codeCnt)
582{
groebert@google.com1bd4c212013-06-19 11:13:56 +0000583 LOGMSG(l_FATAL, "This function should never get called");
584 return KERN_SUCCESS;
585}
586
groebert@google.com43216c82015-02-13 11:11:38 +0000587kern_return_t catch_mach_exception_raise_state(mach_port_t exception_port,
588 exception_type_t exception,
589 const mach_exception_data_t code,
590 mach_msg_type_number_t codeCnt,
591 int *flavor,
592 const thread_state_t old_state,
593 mach_msg_type_number_t old_stateCnt,
594 thread_state_t new_state,
groebert@google.comc659c0e2015-02-13 11:17:00 +0000595 mach_msg_type_number_t * new_stateCnt)
596{
groebert@google.com1bd4c212013-06-19 11:13:56 +0000597 LOGMSG(l_FATAL, "This function should never get called");
598 return KERN_SUCCESS;
599}
600
601kern_return_t catch_mach_exception_raise_state_identity( __attribute__ ((unused)) exception_port_t
602 exception_port, thread_port_t thread,
603 task_port_t task,
604 exception_type_t exception,
605 mach_exception_data_t code,
606 mach_msg_type_number_t code_count,
607 int *flavor, thread_state_t in_state,
608 mach_msg_type_number_t in_state_count,
609 thread_state_t out_state,
groebert@google.comc659c0e2015-02-13 11:17:00 +0000610 mach_msg_type_number_t * out_state_count)
611{
groebert@google.com1bd4c212013-06-19 11:13:56 +0000612 if (exception != EXC_CRASH) {
613 LOGMSG(l_FATAL, "Got non EXC_CRASH! This should not happen.");
614 }
615
616 /* We will save our results to the honggfuzz_t global */
617 pid_t pid;
618 pid_for_task(task, &pid);
619 LOGMSG(l_DEBUG, "Crash of pid %d", pid);
620
groebert@google.com20e368f2015-02-13 14:19:25 +0000621 fuzzer_t *fuzzer = &g_fuzzer_crash_information[pid];
robert.swiecki@gmail.comc6d28752014-01-08 12:02:37 +0000622
groebert@google.com1bd4c212013-06-19 11:13:56 +0000623 /*
624 * Get program counter.
625 * Cast to void* in order to silence the alignment warnings
626 */
627
628 x86_thread_state_t *platform_in_state = ((x86_thread_state_t *) (void *)in_state);
629
630 if (x86_THREAD_STATE32 == platform_in_state->tsh.flavor) {
groebert@google.com20e368f2015-02-13 14:19:25 +0000631 fuzzer->pc = platform_in_state->uts.ts32.__eip;
groebert@google.com1bd4c212013-06-19 11:13:56 +0000632 } else {
groebert@google.com20e368f2015-02-13 14:19:25 +0000633 fuzzer->pc = platform_in_state->uts.ts64.__rip;
groebert@google.com1bd4c212013-06-19 11:13:56 +0000634 }
635
636 /* Get the exception type */
637
638 exception_type_t exception_type = ((code[0] >> 20) & 0x0F);
639
640 if (exception_type == 0) {
641 exception_type = EXC_CRASH;
642 }
643
groebert@google.com20e368f2015-02-13 14:19:25 +0000644 fuzzer->exception = exception_type;
groebert@google.com1bd4c212013-06-19 11:13:56 +0000645
groebert@google.com20e368f2015-02-13 14:19:25 +0000646 /* Get the access address. */
groebert@google.com1bd4c212013-06-19 11:13:56 +0000647
648 mach_exception_data_type_t exception_data[2];
649 memcpy(exception_data, code, sizeof(exception_data));
650 exception_data[0] = (code[0] & ~(0x00000000FFF00000));
651 exception_data[1] = code[1];
652
653 mach_exception_data_type_t access_address = exception_data[1];
groebert@google.com20e368f2015-02-13 14:19:25 +0000654 fuzzer->access = (uint64_t) access_address;
groebert@google.com1bd4c212013-06-19 11:13:56 +0000655
656 /* Get a hash of the callstack */
657
658 uint64_t hash =
659 hash_callstack(thread, task, exception, code, code_count, flavor, in_state, in_state_count);
660
groebert@google.com20e368f2015-02-13 14:19:25 +0000661 fuzzer->backtrace = hash;
groebert@google.com1bd4c212013-06-19 11:13:56 +0000662
663 /* Cleanup */
664
665 if (mach_port_deallocate(mach_task_self(), task) != KERN_SUCCESS) {
666 LOGMSG(l_WARN, "Exception Handler: Could not deallocate task");
667 }
668
669 if (mach_port_deallocate(mach_task_self(), thread) != KERN_SUCCESS) {
670 LOGMSG(l_WARN, "Exception Handler: Could not deallocate thread");
671 }
672
groebert@google.com43216c82015-02-13 11:11:38 +0000673 return KERN_SUCCESS; //KERN_SUCCESS indicates that this should not be forwarded to other crash handlers
groebert@google.com1bd4c212013-06-19 11:13:56 +0000674}