blob: 929d49da053401f6308fe98bbd9cb883676699ef [file] [log] [blame]
robert.swiecki3bb518c2010-10-14 00:48:24 +00001/*
robert.swiecki@gmail.com3b630b42015-02-16 10:53:53 +00002 *
robert.swiecki@gmail.com772b33d2015-02-14 20:35:00 +00003 * honggfuzz - architecture dependent code (MAC OS X)
4 * -----------------------------------------
robert.swiecki@gmail.com3b630b42015-02-16 10:53:53 +00005 *
robert.swiecki@gmail.com772b33d2015-02-14 20:35:00 +00006 * Author: Robert Swiecki <swiecki@google.com> Felix Gröbert
7 * <groebert@google.com>
robert.swiecki@gmail.com3b630b42015-02-16 10:53:53 +00008 *
robert.swiecki@gmail.com772b33d2015-02-14 20:35:00 +00009 * Copyright 2010-2015 by Google Inc. All Rights Reserved.
robert.swiecki@gmail.com3b630b42015-02-16 10:53:53 +000010 *
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
robert.swiecki@gmail.com772b33d2015-02-14 20:35:00 +000013 * a copy of the License at
robert.swiecki@gmail.com3b630b42015-02-16 10:53:53 +000014 *
robert.swiecki@gmail.com772b33d2015-02-14 20:35:00 +000015 * http://www.apache.org/licenses/LICENSE-2.0
robert.swiecki@gmail.com3b630b42015-02-16 10:53:53 +000016 *
robert.swiecki@gmail.com772b33d2015-02-14 20:35:00 +000017 * 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.
robert.swiecki@gmail.com3b630b42015-02-16 10:53:53 +000022 *
robert.swiecki@gmail.com772b33d2015-02-14 20:35:00 +000023 */
robert.swiecki3bb518c2010-10-14 00:48:24 +000024
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
robert.swiecki@gmail.com772b33d2015-02-14 20:35:00 +000062/*
robert.swiecki@gmail.com3b630b42015-02-16 10:53:53 +000063 * Interface to third_party/CrashReport_*.o
robert.swiecki@gmail.com772b33d2015-02-14 20:35:00 +000064 */
groebert@google.com43216c82015-02-13 11:11:38 +000065/* *INDENT-OFF* */
robert.swiecki@gmail.com4be26672015-03-05 03:36:50 +000066@interface CrashReport : NSObject - (id) initWithTask:(task_t)
67 task exceptionType:(exception_type_t)
68 anExceptionType exceptionCode:(mach_exception_data_t)
69 anExceptionCode exceptionCodeCount:(mach_msg_type_number_t)
70 anExceptionCodeCount thread:(thread_t)
71 thread threadStateFlavor:(thread_state_flavor_t)
72 aThreadStateFlavor threadState:(thread_state_data_t)
73 aThreadState threadStateCount:(mach_msg_type_number_t) aThreadStateCount;
groebert@google.com1bd4c212013-06-19 11:13:56 +000074@end
groebert@google.com43216c82015-02-13 11:11:38 +000075/* *INDENT-ON* */
76
robert.swiecki@gmail.com772b33d2015-02-14 20:35:00 +000077/*
robert.swiecki@gmail.com3b630b42015-02-16 10:53:53 +000078 * Global to have exception port available in the collection thread
robert.swiecki@gmail.com772b33d2015-02-14 20:35:00 +000079 */
groebert@google.com1bd4c212013-06-19 11:13:56 +000080static mach_port_t g_exception_port = MACH_PORT_NULL;
81
robert.swiecki@gmail.com772b33d2015-02-14 20:35:00 +000082/*
robert.swiecki@gmail.com3b630b42015-02-16 10:53:53 +000083 * From xnu/bsd/sys/proc_internal.h
robert.swiecki@gmail.com772b33d2015-02-14 20:35:00 +000084 */
groebert@google.comc659c0e2015-02-13 11:17:00 +000085#define PID_MAX 99999
groebert@google.com43216c82015-02-13 11:11:38 +000086
robert.swiecki@gmail.com772b33d2015-02-14 20:35:00 +000087/*
robert.swiecki@gmail.com3b630b42015-02-16 10:53:53 +000088 * Global to store crash info in exception handler thread
robert.swiecki@gmail.com772b33d2015-02-14 20:35:00 +000089 */
groebert@google.comc659c0e2015-02-13 11:17:00 +000090fuzzer_t g_fuzzer_crash_information[PID_MAX + 1];
groebert@google.com43216c82015-02-13 11:11:38 +000091
robert.swiecki@gmail.com772b33d2015-02-14 20:35:00 +000092/*
robert.swiecki@gmail.com3b630b42015-02-16 10:53:53 +000093 * Global to have a unique service name for each honggfuzz process
robert.swiecki@gmail.com772b33d2015-02-14 20:35:00 +000094 */
groebert@google.com76afff92013-06-20 14:43:41 +000095char g_service_name[256];
groebert@google.com16610ee2013-06-19 13:06:12 +000096
robert.swiecki3bb518c2010-10-14 00:48:24 +000097struct {
98 bool important;
99 const char *descr;
100} arch_sigs[NSIG];
101
robert.swiecki@gmail.comc6d28752014-01-08 12:02:37 +0000102__attribute__ ((constructor))
103void arch_initSigs(void)
robert.swiecki3bb518c2010-10-14 00:48:24 +0000104{
105 for (int x = 0; x < NSIG; x++)
106 arch_sigs[x].important = false;
107
108 arch_sigs[SIGILL].important = true;
109 arch_sigs[SIGILL].descr = "SIGILL";
110 arch_sigs[SIGFPE].important = true;
111 arch_sigs[SIGFPE].descr = "SIGFPE";
112 arch_sigs[SIGSEGV].important = true;
113 arch_sigs[SIGSEGV].descr = "SIGSEGV";
114 arch_sigs[SIGBUS].important = true;
115 arch_sigs[SIGBUS].descr = "SIGBUS";
116 arch_sigs[SIGABRT].important = true;
117 arch_sigs[SIGABRT].descr = "SIGABRT";
118}
119
groebert@google.com1bd4c212013-06-19 11:13:56 +0000120const char *exception_to_string(int exception)
groebert@google.comb857deb2010-10-21 19:09:29 +0000121{
groebert@google.com1bd4c212013-06-19 11:13:56 +0000122 switch (exception) {
123 case EXC_BAD_ACCESS:
124 return "EXC_BAD_ACCESS";
125 case EXC_BAD_INSTRUCTION:
126 return "EXC_BAD_INSTRUCTION";
127 case EXC_ARITHMETIC:
128 return "EXC_ARITHMETIC";
129 case EXC_EMULATION:
130 return "EXC_EMULATION";
131 case EXC_SOFTWARE:
132 return "EXC_SOFTWARE";
133 case EXC_BREAKPOINT:
134 return "EXC_BREAKPOINT";
135 case EXC_SYSCALL:
136 return "EXC_SYSCALL";
137 case EXC_MACH_SYSCALL:
138 return "EXC_MACH_SYSCALL";
139 case EXC_RPC_ALERT:
140 return "EXC_RPC_ALERT";
141 case EXC_CRASH:
142 return "EXC_CRASH";
groebert@google.comb857deb2010-10-21 19:09:29 +0000143 }
groebert@google.com1bd4c212013-06-19 11:13:56 +0000144 return "UNKNOWN";
groebert@google.comb857deb2010-10-21 19:09:29 +0000145}
146
147/*
robert.swiecki3bb518c2010-10-14 00:48:24 +0000148 * Returns true if a process exited (so, presumably, we can delete an input
149 * file)
150 */
robert.swiecki@gmail.com751f6862015-02-11 15:03:26 +0000151static bool arch_analyzeSignal(honggfuzz_t * hfuzz, int status, fuzzer_t * fuzzer)
robert.swiecki3bb518c2010-10-14 00:48:24 +0000152{
153 /*
154 * Resumed by delivery of SIGCONT
155 */
156 if (WIFCONTINUED(status)) {
157 return false;
158 }
159
160 /*
161 * Boring, the process just exited
162 */
163 if (WIFEXITED(status)) {
robert.swiecki@gmail.combb5d2642015-02-25 20:00:00 +0000164 LOGMSG(l_DEBUG, "Process (pid %d) exited normally with status %d", fuzzer->pid,
165 WEXITSTATUS(status));
robert.swiecki3bb518c2010-10-14 00:48:24 +0000166 return true;
167 }
168
169 /*
170 * Shouldn't really happen, but, well..
171 */
172 if (!WIFSIGNALED(status)) {
173 LOGMSG(l_ERROR,
174 "Process (pid %d) exited with the following status %d, please report that as a bug",
robert.swiecki@gmail.com751f6862015-02-11 15:03:26 +0000175 fuzzer->pid, status);
robert.swiecki3bb518c2010-10-14 00:48:24 +0000176 return true;
177 }
178
179 int termsig = WTERMSIG(status);
robert.swiecki@gmail.combb5d2642015-02-25 20:00:00 +0000180 LOGMSG(l_DEBUG, "Process (pid %d) killed by signal %d '%s'", fuzzer->pid, termsig,
181 strsignal(termsig));
robert.swiecki3bb518c2010-10-14 00:48:24 +0000182 if (!arch_sigs[termsig].important) {
183 LOGMSG(l_DEBUG, "It's not that important signal, skipping");
184 return true;
185 }
186
groebert@google.com1bd4c212013-06-19 11:13:56 +0000187 /*
188 * Signal is interesting
189 */
groebert@google.com1bd4c212013-06-19 11:13:56 +0000190 char newname[PATH_MAX];
groebert@google.comb857deb2010-10-21 19:09:29 +0000191
robert.swiecki@gmail.com772b33d2015-02-14 20:35:00 +0000192 /*
robert.swiecki@gmail.com3b630b42015-02-16 10:53:53 +0000193 * Get data from exception handler
robert.swiecki@gmail.com772b33d2015-02-14 20:35:00 +0000194 */
groebert@google.com43216c82015-02-13 11:11:38 +0000195 fuzzer->pc = g_fuzzer_crash_information[fuzzer->pid].pc;
196 fuzzer->exception = g_fuzzer_crash_information[fuzzer->pid].exception;
197 fuzzer->access = g_fuzzer_crash_information[fuzzer->pid].access;
198 fuzzer->backtrace = g_fuzzer_crash_information[fuzzer->pid].backtrace;
199
groebert@google.com1bd4c212013-06-19 11:13:56 +0000200 if (hfuzz->saveUnique) {
201 snprintf(newname, sizeof(newname),
202 "%s.%s.PC.%.16llx.STACK.%.16llx.ADDR.%.16llx.%s.%s",
robert.swiecki@gmail.com772b33d2015-02-14 20:35:00 +0000203 arch_sigs[termsig].descr,
204 exception_to_string(fuzzer->exception), fuzzer->pc,
205 fuzzer->backtrace, fuzzer->access, fuzzer->origFileName, hfuzz->fileExtn);
groebert@google.comb857deb2010-10-21 19:09:29 +0000206 } else {
groebert@google.comb857deb2010-10-21 19:09:29 +0000207
groebert@google.com1bd4c212013-06-19 11:13:56 +0000208 char localtmstr[PATH_MAX];
209 util_getLocalTime("%F.%H.%M.%S", localtmstr, sizeof(localtmstr));
210
211 snprintf(newname, sizeof(newname),
212 "%s.%s.PC.%.16llx.STACK.%.16llx.ADDR.%.16llx.TIME.%s.PID.%.5d.%s.%s",
robert.swiecki@gmail.com772b33d2015-02-14 20:35:00 +0000213 arch_sigs[termsig].descr,
214 exception_to_string(fuzzer->exception), fuzzer->pc,
robert.swiecki@gmail.combb5d2642015-02-25 20:00:00 +0000215 fuzzer->backtrace, fuzzer->access, localtmstr, fuzzer->pid, fuzzer->origFileName,
216 hfuzz->fileExtn);
groebert@google.com1bd4c212013-06-19 11:13:56 +0000217 }
218
robert.swiecki@gmail.com751f6862015-02-11 15:03:26 +0000219 if (link(fuzzer->fileName, newname) == 0) {
groebert@google.com43216c82015-02-13 11:11:38 +0000220 LOGMSG(l_INFO, "Ok, that's interesting, saved '%s' as '%s'", fuzzer->fileName, newname);
groebert@google.com1bd4c212013-06-19 11:13:56 +0000221 } else {
222 if (errno == EEXIST) {
223 LOGMSG(l_INFO, "It seems that '%s' already exists, skipping", newname);
224 } else {
groebert@google.com43216c82015-02-13 11:11:38 +0000225 LOGMSG_P(l_ERROR, "Couldn't link '%s' to '%s'", fuzzer->fileName, newname);
groebert@google.comb857deb2010-10-21 19:09:29 +0000226 }
robert.swiecki3bb518c2010-10-14 00:48:24 +0000227 }
groebert@google.comb857deb2010-10-21 19:09:29 +0000228
robert.swiecki3bb518c2010-10-14 00:48:24 +0000229 return true;
230}
231
232bool arch_launchChild(honggfuzz_t * hfuzz, char *fileName)
233{
robert.swiecki3bb518c2010-10-14 00:48:24 +0000234#define ARGS_MAX 512
235 char *args[ARGS_MAX + 2];
Anestis Bechtsoudisc1f6faa2015-07-31 05:32:19 +0300236 char argData[PATH_MAX] = { 0 };
robert.swiecki3bb518c2010-10-14 00:48:24 +0000237 int x;
238
239 for (x = 0; x < ARGS_MAX && hfuzz->cmdline[x]; x++) {
robert.swiecki@gmail.coma0d87142015-02-14 13:11:18 +0000240 if (!hfuzz->fuzzStdin && strcmp(hfuzz->cmdline[x], _HF_FILE_PLACEHOLDER) == 0) {
robert.swiecki3bb518c2010-10-14 00:48:24 +0000241 args[x] = fileName;
Anestis Bechtsoudisc1f6faa2015-07-31 05:32:19 +0300242 } else if (!hfuzz->fuzzStdin && strstr(hfuzz->cmdline[x], _HF_FILE_PLACEHOLDER)) {
243 const char *off = strstr(hfuzz->cmdline[x], _HF_FILE_PLACEHOLDER);
244 snprintf(argData, PATH_MAX, "%.*s%s", (int)(off - hfuzz->cmdline[x]), hfuzz->cmdline[x], fileName);
245 args[x] = argData;
robert.swiecki3bb518c2010-10-14 00:48:24 +0000246 } else {
247 args[x] = hfuzz->cmdline[x];
248 }
249 }
250
251 args[x++] = NULL;
252
253 LOGMSG(l_DEBUG, "Launching '%s' on file '%s'", args[0], fileName);
254
robert.swiecki@gmail.com772b33d2015-02-14 20:35:00 +0000255 /*
robert.swiecki@gmail.com3b630b42015-02-16 10:53:53 +0000256 * Get child's bootstrap port.
robert.swiecki@gmail.com772b33d2015-02-14 20:35:00 +0000257 */
groebert@google.com1bd4c212013-06-19 11:13:56 +0000258 mach_port_t child_bootstrap = MACH_PORT_NULL;
259 if (task_get_bootstrap_port(mach_task_self(), &child_bootstrap) != KERN_SUCCESS) {
260 return false;
261 }
262
robert.swiecki@gmail.com772b33d2015-02-14 20:35:00 +0000263 /*
robert.swiecki@gmail.com3b630b42015-02-16 10:53:53 +0000264 * Get exception port.
robert.swiecki@gmail.com772b33d2015-02-14 20:35:00 +0000265 */
groebert@google.com1bd4c212013-06-19 11:13:56 +0000266 mach_port_t exception_port = MACH_PORT_NULL;
267
robert.swiecki@gmail.com772b33d2015-02-14 20:35:00 +0000268 if (bootstrap_look_up(child_bootstrap, g_service_name, &exception_port)
robert.swiecki@gmail.com62e34ae2015-03-05 03:39:32 +0000269 != KERN_SUCCESS) {
groebert@google.com1bd4c212013-06-19 11:13:56 +0000270 return false;
271 }
272
robert.swiecki@gmail.com772b33d2015-02-14 20:35:00 +0000273 /*
robert.swiecki@gmail.com3b630b42015-02-16 10:53:53 +0000274 * Here we register the exception port in the child
robert.swiecki@gmail.com772b33d2015-02-14 20:35:00 +0000275 */
groebert@google.com1bd4c212013-06-19 11:13:56 +0000276 if (task_set_exception_ports(mach_task_self(),
277 EXC_MASK_CRASH,
278 exception_port,
robert.swiecki@gmail.com772b33d2015-02-14 20:35:00 +0000279 EXCEPTION_STATE_IDENTITY |
280 MACH_EXCEPTION_CODES, MACHINE_THREAD_STATE) != KERN_SUCCESS) {
groebert@google.com1bd4c212013-06-19 11:13:56 +0000281 return false;
282 }
283
robert.swiecki3bb518c2010-10-14 00:48:24 +0000284 /*
285 * Set timeout (prof), real timeout (2*prof), and rlimit_cpu (2*prof)
286 */
287 if (hfuzz->tmOut) {
288 struct itimerval it;
289
290 /*
291 * The hfuzz->tmOut is real CPU usage time...
292 */
293 it.it_value.tv_sec = hfuzz->tmOut;
294 it.it_value.tv_usec = 0;
295 it.it_interval.tv_sec = 0;
296 it.it_interval.tv_usec = 0;
297 if (setitimer(ITIMER_PROF, &it, NULL) == -1) {
298 LOGMSG_P(l_ERROR, "Couldn't set the ITIMER_PROF timer");
299 return false;
300 }
301
302 /*
303 * ...so, if a process sleeps, this one should
304 * trigger a signal...
305 */
tlogic@gmail.com854c4a12015-04-17 05:52:17 +0000306 it.it_value.tv_sec = hfuzz->tmOut;
robert.swiecki3bb518c2010-10-14 00:48:24 +0000307 it.it_value.tv_usec = 0;
308 it.it_interval.tv_sec = 0;
309 it.it_interval.tv_usec = 0;
310 if (setitimer(ITIMER_REAL, &it, NULL) == -1) {
311 LOGMSG_P(l_ERROR, "Couldn't set the ITIMER_REAL timer");
312 return false;
313 }
314
315 /*
316 * ..if a process sleeps and catches SIGPROF/SIGALRM
tlogic@gmail.com854c4a12015-04-17 05:52:17 +0000317 * rlimits won't help either. However, arch_checkTimeLimit
318 * will send a SIGKILL at tmOut + 2 seconds. That should
319 * do it :)
robert.swiecki3bb518c2010-10-14 00:48:24 +0000320 */
321 struct rlimit rl;
322
tlogic@gmail.com854c4a12015-04-17 05:52:17 +0000323 rl.rlim_cur = hfuzz->tmOut + 1;
324 rl.rlim_max = hfuzz->tmOut + 1;
robert.swiecki3bb518c2010-10-14 00:48:24 +0000325 if (setrlimit(RLIMIT_CPU, &rl) == -1) {
326 LOGMSG_P(l_ERROR, "Couldn't enforce the RLIMIT_CPU resource limit");
327 return false;
328 }
329 }
330
331 /*
332 * The address space limit. If big enough - roughly the size of RAM used
333 */
334 if (hfuzz->asLimit) {
335 struct rlimit rl;
336
337 rl.rlim_cur = hfuzz->asLimit * 1024UL * 1024UL;
338 rl.rlim_max = hfuzz->asLimit * 1024UL * 1024UL;
339 if (setrlimit(RLIMIT_AS, &rl) == -1) {
340 LOGMSG_P(l_DEBUG, "Couldn't encforce the RLIMIT_AS resource limit, ignoring");
341 }
342 }
343
344 if (hfuzz->nullifyStdio) {
345 util_nullifyStdio();
346 }
347
348 if (hfuzz->fuzzStdin) {
robert.swiecki@gmail.com772b33d2015-02-14 20:35:00 +0000349 /*
robert.swiecki@gmail.com3b630b42015-02-16 10:53:53 +0000350 * Uglyyyyyy ;)
robert.swiecki@gmail.com772b33d2015-02-14 20:35:00 +0000351 */
robert.swiecki3bb518c2010-10-14 00:48:24 +0000352 if (!util_redirectStdin(fileName)) {
353 return false;
354 }
355 }
356
robert.swiecki@gmail.com7e3c48c2015-03-04 02:44:43 +0000357 for (size_t i = 0; i < ARRAYSIZE(hfuzz->envs) && hfuzz->envs[i]; i++) {
robert.swiecki@gmail.com15eca6f2015-03-04 03:31:36 +0000358 putenv(hfuzz->envs[i]);
robert.swiecki@gmail.com7e3c48c2015-03-04 02:44:43 +0000359 }
360
robert.swiecki3bb518c2010-10-14 00:48:24 +0000361 execvp(args[0], args);
362
363 util_recoverStdio();
364 LOGMSG(l_FATAL, "Failed to create new '%s' process", args[0]);
365 return false;
366}
367
tlogic@gmail.com854c4a12015-04-17 05:52:17 +0000368static void arch_checkTimeLimit(honggfuzz_t * hfuzz, fuzzer_t * fuzzer)
369{
370 if (!hfuzz->tmOut) {
371 return;
372 }
373 int64_t curMillis = util_timeNowMillis();
374 int64_t diffMillis = curMillis - fuzzer->timeStartedMillis;
375 if (diffMillis > ((hfuzz->tmOut + 2) * 1000)) {
376 LOGMSG(l_WARN, "PID %d took too much time (limit %ld s). Sending SIGKILL",
377 fuzzer->pid, hfuzz->tmOut);
378 kill(fuzzer->pid, SIGKILL);
379 }
380}
381
robert.swiecki@gmail.com751f6862015-02-11 15:03:26 +0000382void arch_reapChild(honggfuzz_t * hfuzz, fuzzer_t * fuzzer)
robert.swiecki3bb518c2010-10-14 00:48:24 +0000383{
groebert@google.com06d93802013-09-16 12:06:40 +0000384 /*
385 * First check manually if we have expired childs
386 */
tlogic@gmail.com854c4a12015-04-17 05:52:17 +0000387 arch_checkTimeLimit(hfuzz, fuzzer);
groebert@google.com06d93802013-09-16 12:06:40 +0000388
389 /*
robert.swiecki@gmail.com751f6862015-02-11 15:03:26 +0000390 * Now check for signals using wait4
groebert@google.com06d93802013-09-16 12:06:40 +0000391 */
tlogic@gmail.com854c4a12015-04-17 05:52:17 +0000392 int options = WUNTRACED;
393 if (hfuzz->tmOut) {
robert.swiecki@gmail.com394d95e2015-04-20 22:15:13 +0000394 options |= WNOHANG;
tlogic@gmail.com854c4a12015-04-17 05:52:17 +0000395 }
robert.swiecki@gmail.com751f6862015-02-11 15:03:26 +0000396
robert.swiecki@gmail.com65cfa1c2015-02-11 15:59:28 +0000397 for (;;) {
398 int status = 0;
tlogic@gmail.com854c4a12015-04-17 05:52:17 +0000399 while (wait4(fuzzer->pid, &status, options, NULL) != fuzzer->pid) {
400 if (hfuzz->tmOut) {
robert.swiecki@gmail.com394d95e2015-04-20 22:15:13 +0000401 arch_checkTimeLimit(hfuzz, fuzzer);
402 usleep(0.20 * 1000000);
tlogic@gmail.com854c4a12015-04-17 05:52:17 +0000403 }
404 }
robert.swiecki@gmail.com65cfa1c2015-02-11 15:59:28 +0000405 LOGMSG(l_DEBUG, "Process (pid %d) came back with status %d", fuzzer->pid, status);
robert.swiecki3bb518c2010-10-14 00:48:24 +0000406
groebert@google.com43216c82015-02-13 11:11:38 +0000407 if (arch_analyzeSignal(hfuzz, status, fuzzer)) {
robert.swiecki@gmail.com65cfa1c2015-02-11 15:59:28 +0000408 return;
409 }
robert.swiecki3bb518c2010-10-14 00:48:24 +0000410 }
robert.swiecki@gmail.com751f6862015-02-11 15:03:26 +0000411}
robert.swiecki28cba5c2011-06-22 01:38:55 +0000412
groebert@google.com1bd4c212013-06-19 11:13:56 +0000413void *wait_for_exception()
414{
415 while (1) {
416 mach_msg_server_once(mach_exc_server, 4096, g_exception_port, MACH_MSG_OPTION_NONE);
417 }
418}
419
420/*
421 * Called once before fuzzing starts. Prepare mach ports for attaching crash reporter.
422 */
robert.swiecki@gmail.com6d6f7562015-02-17 22:18:51 +0000423bool arch_archInit(honggfuzz_t * hfuzz)
robert.swiecki28cba5c2011-06-22 01:38:55 +0000424{
groebert@google.com1bd4c212013-06-19 11:13:56 +0000425 char plist[PATH_MAX];
robert.swiecki@gmail.combb5d2642015-02-25 20:00:00 +0000426 snprintf(plist, sizeof(plist), "/Users/%s/Library/Preferences/com.apple.DebugSymbols.plist",
427 getlogin());
groebert@google.com1bd4c212013-06-19 11:13:56 +0000428
429 if (files_exists(plist)) {
430 LOGMSG(l_WARN,
431 "honggfuzz won't work if DBGShellCommands are set in ~/Library/Preferences/com.apple.DebugSymbols.plist");
432 }
433
robert.swiecki@gmail.com772b33d2015-02-14 20:35:00 +0000434 /*
robert.swiecki@gmail.com3b630b42015-02-16 10:53:53 +0000435 * Allocate exception port.
robert.swiecki@gmail.com772b33d2015-02-14 20:35:00 +0000436 */
robert.swiecki@gmail.combb5d2642015-02-25 20:00:00 +0000437 if (mach_port_allocate(mach_task_self(), MACH_PORT_RIGHT_RECEIVE, &g_exception_port) !=
robert.swiecki@gmail.com62e34ae2015-03-05 03:39:32 +0000438 KERN_SUCCESS) {
groebert@google.com1bd4c212013-06-19 11:13:56 +0000439 return false;
440 }
441
robert.swiecki@gmail.com772b33d2015-02-14 20:35:00 +0000442 /*
robert.swiecki@gmail.com3b630b42015-02-16 10:53:53 +0000443 * Insert exception receive port.
robert.swiecki@gmail.com772b33d2015-02-14 20:35:00 +0000444 */
445 if (mach_port_insert_right
robert.swiecki@gmail.com62e34ae2015-03-05 03:39:32 +0000446 (mach_task_self(), g_exception_port, g_exception_port,
447 MACH_MSG_TYPE_MAKE_SEND) != KERN_SUCCESS) {
groebert@google.com1bd4c212013-06-19 11:13:56 +0000448 return false;
449 }
450
robert.swiecki@gmail.com772b33d2015-02-14 20:35:00 +0000451 /*
robert.swiecki@gmail.com3b630b42015-02-16 10:53:53 +0000452 * Get bootstrap port.
robert.swiecki@gmail.com772b33d2015-02-14 20:35:00 +0000453 */
groebert@google.com1bd4c212013-06-19 11:13:56 +0000454 mach_port_t bootstrap = MACH_PORT_NULL;
455 if (task_get_bootstrap_port(mach_task_self(), &bootstrap) != KERN_SUCCESS) {
456 return false;
457 }
458
robert.swiecki@gmail.com772b33d2015-02-14 20:35:00 +0000459 /*
robert.swiecki@gmail.com3b630b42015-02-16 10:53:53 +0000460 * Generate and register exception port service.
robert.swiecki@gmail.com772b33d2015-02-14 20:35:00 +0000461 */
robert.swiecki@gmail.combb5d2642015-02-25 20:00:00 +0000462 snprintf(g_service_name, sizeof(g_service_name), "com.google.code.honggfuzz.%d",
tlogic@gmail.com479f0f92015-04-15 18:03:16 +0000463 (int)util_rndGet(0, 999999));
groebert@google.com76afff92013-06-20 14:43:41 +0000464 if (bootstrap_check_in(bootstrap, g_service_name, &g_exception_port) != KERN_SUCCESS) {
groebert@google.com1bd4c212013-06-19 11:13:56 +0000465 return false;
466 }
467
robert.swiecki@gmail.com772b33d2015-02-14 20:35:00 +0000468 /*
469 * Create a collection thread to catch the exceptions from the
robert.swiecki@gmail.com3b630b42015-02-16 10:53:53 +0000470 * children
robert.swiecki@gmail.com772b33d2015-02-14 20:35:00 +0000471 */
groebert@google.com1bd4c212013-06-19 11:13:56 +0000472 pthread_t exception_thread;
473
474 if (pthread_create(&exception_thread, NULL, wait_for_exception, 0)) {
475 LOGMSG(l_FATAL, "Parent: could not create thread to wait for child's exception");
476 return false;
477 }
478
479 if (pthread_detach(exception_thread)) {
480 LOGMSG(l_FATAL, "Parent: could not detach thread to wait for child's exception");
481 return false;
482 }
483
robert.swiecki28cba5c2011-06-22 01:38:55 +0000484 return true;
485}
groebert@google.com1bd4c212013-06-19 11:13:56 +0000486
robert.swiecki@gmail.com772b33d2015-02-14 20:35:00 +0000487/*
robert.swiecki@gmail.com3b630b42015-02-16 10:53:53 +0000488 * Write the crash report to DEBUG
robert.swiecki@gmail.com772b33d2015-02-14 20:35:00 +0000489 */
490void
491write_crash_report(thread_port_t thread,
492 task_port_t task,
493 exception_type_t exception,
494 mach_exception_data_t code,
495 mach_msg_type_number_t code_count,
496 int *flavor, thread_state_t in_state, mach_msg_type_number_t in_state_count)
groebert@google.com1bd4c212013-06-19 11:13:56 +0000497{
498
499 NSAutoreleasePool *pool =[[NSAutoreleasePool alloc] init];
500 CrashReport *_crashReport = nil;
501
robert.swiecki@gmail.com4be26672015-03-05 03:36:50 +0000502 /* *INDENT-OFF* */
groebert@google.com43216c82015-02-13 11:11:38 +0000503 _crashReport = [[CrashReport alloc] initWithTask:task
robert.swiecki@gmail.com4be26672015-03-05 03:36:50 +0000504 exceptionType:exception
505 exceptionCode:code
506 exceptionCodeCount:code_count
507 thread:thread
508 threadStateFlavor:*flavor
509 threadState:(thread_state_t)in_state
510 threadStateCount:in_state_count];
511 /* *INDENT-OFF* */
groebert@google.com1bd4c212013-06-19 11:13:56 +0000512
513 NSString *crashDescription =[_crashReport description];
514 char *description = (char *)[crashDescription UTF8String];
515
516 LOGMSG(l_DEBUG, "CrashReport: %s", description);
517
518 [_crashReport release];
519 [pool drain];
520}
521
522/* Hash the callstack in an unique way */
523uint64_t hash_callstack(thread_port_t thread,
524 task_port_t task,
525 exception_type_t exception,
526 mach_exception_data_t code,
527 mach_msg_type_number_t code_count,
groebert@google.com43216c82015-02-13 11:11:38 +0000528 int *flavor,
529 thread_state_t in_state,
530 mach_msg_type_number_t in_state_count) {
groebert@google.com1bd4c212013-06-19 11:13:56 +0000531
532 NSAutoreleasePool *pool =[[NSAutoreleasePool alloc] init];
533 CrashReport *_crashReport = nil;
534
robert.swiecki@gmail.com4be26672015-03-05 03:36:50 +0000535 /* *INDENT-OFF* */
groebert@google.com43216c82015-02-13 11:11:38 +0000536 _crashReport = [[CrashReport alloc] initWithTask:task
robert.swiecki@gmail.com4be26672015-03-05 03:36:50 +0000537 exceptionType:exception
538 exceptionCode:code
539 exceptionCodeCount:code_count
540 thread:thread
541 threadStateFlavor:*flavor
542 threadState:(thread_state_t)in_state
543 threadStateCount:in_state_count];
544 /* *INDENT-ON* */
groebert@google.com1bd4c212013-06-19 11:13:56 +0000545
546 NSString *crashDescription =[_crashReport description];
547 char *description = (char *)[crashDescription UTF8String];
548
robert.swiecki@gmail.com772b33d2015-02-14 20:35:00 +0000549 /*
robert.swiecki@gmail.com3b630b42015-02-16 10:53:53 +0000550 * The callstack begins with the following word
robert.swiecki@gmail.com772b33d2015-02-14 20:35:00 +0000551 */
groebert@google.com06d93802013-09-16 12:06:40 +0000552 char *callstack = strstr(description, "Crashed:");
groebert@google.com1bd4c212013-06-19 11:13:56 +0000553
554 if (callstack == NULL) {
555 LOGMSG(l_FATAL, "Could not find callstack in crash report %s", description);
556 }
557
robert.swiecki@gmail.com772b33d2015-02-14 20:35:00 +0000558 /*
robert.swiecki@gmail.com3b630b42015-02-16 10:53:53 +0000559 * Scroll forward to the next newline
robert.swiecki@gmail.com772b33d2015-02-14 20:35:00 +0000560 */
groebert@google.com1bd4c212013-06-19 11:13:56 +0000561 char *callstack_start = strstr(callstack, "\n");
562
563 if (callstack_start == NULL) {
564 LOGMSG(l_FATAL, "Could not find callstack start in crash report %s", description);
565 }
566
robert.swiecki@gmail.com772b33d2015-02-14 20:35:00 +0000567 /*
robert.swiecki@gmail.com3b630b42015-02-16 10:53:53 +0000568 * Skip the newline
robert.swiecki@gmail.com772b33d2015-02-14 20:35:00 +0000569 */
groebert@google.com1bd4c212013-06-19 11:13:56 +0000570 callstack_start++;
571
robert.swiecki@gmail.com772b33d2015-02-14 20:35:00 +0000572 /*
robert.swiecki@gmail.com3b630b42015-02-16 10:53:53 +0000573 * Determine the end of the callstack
robert.swiecki@gmail.com772b33d2015-02-14 20:35:00 +0000574 */
groebert@google.com1bd4c212013-06-19 11:13:56 +0000575 char *callstack_end = strstr(callstack, "\n\nThread");
576
577 if (callstack_end == NULL) {
578 LOGMSG(l_FATAL, "Could not find callstack end in crash report %s", description);
579 }
580
robert.swiecki@gmail.com772b33d2015-02-14 20:35:00 +0000581 /*
robert.swiecki@gmail.com3b630b42015-02-16 10:53:53 +0000582 * Make sure it's NULL-terminated
robert.swiecki@gmail.com772b33d2015-02-14 20:35:00 +0000583 */
groebert@google.com1bd4c212013-06-19 11:13:56 +0000584 *callstack_end = '\0';
585
586 /*
robert.swiecki@gmail.com3b630b42015-02-16 10:53:53 +0000587 *
robert.swiecki@gmail.com772b33d2015-02-14 20:35:00 +0000588 * For each line, we only take the last three nibbles from the
589 * address.
robert.swiecki@gmail.com3b630b42015-02-16 10:53:53 +0000590 *
robert.swiecki@gmail.com772b33d2015-02-14 20:35:00 +0000591 * Sample output:
robert.swiecki@gmail.com3b630b42015-02-16 10:53:53 +0000592 *
robert.swiecki@gmail.com772b33d2015-02-14 20:35:00 +0000593 * 0 libsystem_kernel.dylib 0x00007fff80514d46 __kill + 10 1
594 * libsystem_c.dylib 0x00007fff85731ec0 __abort + 193 2
595 * libsystem_c.dylib 0x00007fff85732d17 __stack_chk_fail + 195 3
596 * stack_buffer_overflow64-stripped 0x000000010339def5 0x10339d000 +
597 * 3829 4 ??? 0x4141414141414141 0 + 4702111234474983745
robert.swiecki@gmail.com3b630b42015-02-16 10:53:53 +0000598 *
robert.swiecki@gmail.com772b33d2015-02-14 20:35:00 +0000599 * 0 libsystem_kernel.dylib 0x00007fff80514d46 __kill + 10 1
600 * libsystem_c.dylib 0x00007fff85731ec0 __abort + 193 2
601 * libsystem_c.dylib 0x00007fff85732d17 __stack_chk_fail + 195 3
602 * stack_buffer_overflow64 0x0000000108f41ef5 main + 133 4 ???
603 * 0x4141414141414141 0 + 4702111234474983745
robert.swiecki@gmail.com3b630b42015-02-16 10:53:53 +0000604 *
robert.swiecki@gmail.com772b33d2015-02-14 20:35:00 +0000605 * 0 libsystem_kernel.dylib 0x940023ba __kill + 10 1
606 * libsystem_kernel.dylib 0x940014bc kill$UNIX2003 + 32 2
607 * libsystem_c.dylib 0x926f362e __abort + 246 3 libsystem_c.dylib
608 * 0x926c2b60 __chk_fail + 49 4 libsystem_c.dylib 0x926c2bf9
609 * __memset_chk + 53 5 stack_buffer_overflow32-stripped 0x00093ee5
610 * 0x93000 + 3813 6 libdyld.dylib 0x978c6725 start + 1
robert.swiecki@gmail.com3b630b42015-02-16 10:53:53 +0000611 *
robert.swiecki@gmail.com772b33d2015-02-14 20:35:00 +0000612 * 0 libsystem_kernel.dylib 0x940023ba __kill + 10 1
613 * libsystem_kernel.dylib 0x940014bc kill$UNIX2003 + 32 2
614 * libsystem_c.dylib 0x926f362e __abort + 246 3 libsystem_c.dylib
615 * 0x926c2b60 __chk_fail + 49 4 libsystem_c.dylib 0x926c2bf9
robert.swiecki@gmail.com3b630b42015-02-16 10:53:53 +0000616 * __memset_chk + 53 5 stack_buffer_overflow32 0x0003cee5 main + 117 6
robert.swiecki@gmail.com772b33d2015-02-14 20:35:00 +0000617 * libdyld.dylib 0x978c6725 start + 1
robert.swiecki@gmail.com3b630b42015-02-16 10:53:53 +0000618 *
groebert@google.com1bd4c212013-06-19 11:13:56 +0000619 */
620
621 uint64_t hash = 0;
622 char *pos = callstack_start;
623
robert.swiecki@gmail.com772b33d2015-02-14 20:35:00 +0000624 /*
robert.swiecki@gmail.com3b630b42015-02-16 10:53:53 +0000625 * Go through each line until we run out of lines
robert.swiecki@gmail.com772b33d2015-02-14 20:35:00 +0000626 */
groebert@google.com1bd4c212013-06-19 11:13:56 +0000627 while (strstr(pos, "\t") != NULL) {
628 /*
629 * Format: dylib spaces tab address space symbol space plus space offset
630 * Scroll pos forward to the last three nibbles of the address.
631 */
632 if ((pos = strstr(pos, "\t")) == NULL)
633 break;
634 if ((pos = strstr(pos, " ")) == NULL)
635 break;
636 pos = pos - 3;
robert.swiecki@gmail.com772b33d2015-02-14 20:35:00 +0000637 /*
robert.swiecki@gmail.com3b630b42015-02-16 10:53:53 +0000638 * Hash the last three nibbles
robert.swiecki@gmail.com772b33d2015-02-14 20:35:00 +0000639 */
groebert@google.com1bd4c212013-06-19 11:13:56 +0000640 hash ^= util_hash(pos, 3);
robert.swiecki@gmail.com772b33d2015-02-14 20:35:00 +0000641 /*
robert.swiecki@gmail.com3b630b42015-02-16 10:53:53 +0000642 * Scroll pos one forward to skip the current tab
robert.swiecki@gmail.com772b33d2015-02-14 20:35:00 +0000643 */
groebert@google.com1bd4c212013-06-19 11:13:56 +0000644 pos++;
645 }
646
groebert@google.com20e368f2015-02-13 14:19:25 +0000647 LOGMSG(l_DEBUG, "Callstack hash %u", hash);
groebert@google.com1bd4c212013-06-19 11:13:56 +0000648
649 [_crashReport release];
650 [pool drain];
651
652 return hash;
653}
654
robert.swiecki@gmail.com772b33d2015-02-14 20:35:00 +0000655kern_return_t
656catch_mach_exception_raise(mach_port_t exception_port,
657 mach_port_t thread,
658 mach_port_t task,
robert.swiecki@gmail.combb5d2642015-02-25 20:00:00 +0000659 exception_type_t exception, mach_exception_data_t code,
660 mach_msg_type_number_t codeCnt)
groebert@google.comc659c0e2015-02-13 11:17:00 +0000661{
groebert@google.com1bd4c212013-06-19 11:13:56 +0000662 LOGMSG(l_FATAL, "This function should never get called");
663 return KERN_SUCCESS;
664}
665
robert.swiecki@gmail.com772b33d2015-02-14 20:35:00 +0000666kern_return_t
667catch_mach_exception_raise_state(mach_port_t exception_port,
668 exception_type_t exception,
669 const mach_exception_data_t code,
670 mach_msg_type_number_t codeCnt,
671 int *flavor,
672 const thread_state_t old_state,
673 mach_msg_type_number_t old_stateCnt,
674 thread_state_t new_state, mach_msg_type_number_t * new_stateCnt)
groebert@google.comc659c0e2015-02-13 11:17:00 +0000675{
groebert@google.com1bd4c212013-06-19 11:13:56 +0000676 LOGMSG(l_FATAL, "This function should never get called");
677 return KERN_SUCCESS;
678}
679
robert.swiecki@gmail.com772b33d2015-02-14 20:35:00 +0000680kern_return_t catch_mach_exception_raise_state_identity( __attribute__ ((unused))
robert.swiecki@gmail.com62e34ae2015-03-05 03:39:32 +0000681 exception_port_t exception_port,
682 thread_port_t thread,
683 task_port_t task,
684 exception_type_t exception,
685 mach_exception_data_t code,
686 mach_msg_type_number_t
687 code_count, int *flavor,
688 thread_state_t in_state,
689 mach_msg_type_number_t
690 in_state_count,
691 thread_state_t out_state,
692 mach_msg_type_number_t * out_state_count)
groebert@google.comc659c0e2015-02-13 11:17:00 +0000693{
groebert@google.com1bd4c212013-06-19 11:13:56 +0000694 if (exception != EXC_CRASH) {
695 LOGMSG(l_FATAL, "Got non EXC_CRASH! This should not happen.");
696 }
697
robert.swiecki@gmail.com772b33d2015-02-14 20:35:00 +0000698 /*
robert.swiecki@gmail.com3b630b42015-02-16 10:53:53 +0000699 * We will save our results to the honggfuzz_t global
robert.swiecki@gmail.com772b33d2015-02-14 20:35:00 +0000700 */
groebert@google.com1bd4c212013-06-19 11:13:56 +0000701 pid_t pid;
702 pid_for_task(task, &pid);
703 LOGMSG(l_DEBUG, "Crash of pid %d", pid);
704
groebert@google.com20e368f2015-02-13 14:19:25 +0000705 fuzzer_t *fuzzer = &g_fuzzer_crash_information[pid];
robert.swiecki@gmail.comc6d28752014-01-08 12:02:37 +0000706
groebert@google.com1bd4c212013-06-19 11:13:56 +0000707 /*
708 * Get program counter.
709 * Cast to void* in order to silence the alignment warnings
710 */
711
712 x86_thread_state_t *platform_in_state = ((x86_thread_state_t *) (void *)in_state);
713
714 if (x86_THREAD_STATE32 == platform_in_state->tsh.flavor) {
groebert@google.com20e368f2015-02-13 14:19:25 +0000715 fuzzer->pc = platform_in_state->uts.ts32.__eip;
groebert@google.com1bd4c212013-06-19 11:13:56 +0000716 } else {
groebert@google.com20e368f2015-02-13 14:19:25 +0000717 fuzzer->pc = platform_in_state->uts.ts64.__rip;
groebert@google.com1bd4c212013-06-19 11:13:56 +0000718 }
719
robert.swiecki@gmail.com772b33d2015-02-14 20:35:00 +0000720 /*
robert.swiecki@gmail.com3b630b42015-02-16 10:53:53 +0000721 * Get the exception type
robert.swiecki@gmail.com772b33d2015-02-14 20:35:00 +0000722 */
groebert@google.com1bd4c212013-06-19 11:13:56 +0000723
724 exception_type_t exception_type = ((code[0] >> 20) & 0x0F);
725
726 if (exception_type == 0) {
727 exception_type = EXC_CRASH;
728 }
729
groebert@google.com20e368f2015-02-13 14:19:25 +0000730 fuzzer->exception = exception_type;
groebert@google.com1bd4c212013-06-19 11:13:56 +0000731
robert.swiecki@gmail.com772b33d2015-02-14 20:35:00 +0000732 /*
robert.swiecki@gmail.com3b630b42015-02-16 10:53:53 +0000733 * Get the access address.
robert.swiecki@gmail.com772b33d2015-02-14 20:35:00 +0000734 */
groebert@google.com1bd4c212013-06-19 11:13:56 +0000735
736 mach_exception_data_type_t exception_data[2];
737 memcpy(exception_data, code, sizeof(exception_data));
738 exception_data[0] = (code[0] & ~(0x00000000FFF00000));
739 exception_data[1] = code[1];
740
741 mach_exception_data_type_t access_address = exception_data[1];
groebert@google.com20e368f2015-02-13 14:19:25 +0000742 fuzzer->access = (uint64_t) access_address;
groebert@google.com1bd4c212013-06-19 11:13:56 +0000743
robert.swiecki@gmail.com772b33d2015-02-14 20:35:00 +0000744 /*
robert.swiecki@gmail.com3b630b42015-02-16 10:53:53 +0000745 * Get a hash of the callstack
robert.swiecki@gmail.com772b33d2015-02-14 20:35:00 +0000746 */
groebert@google.com1bd4c212013-06-19 11:13:56 +0000747
robert.swiecki@gmail.com772b33d2015-02-14 20:35:00 +0000748 uint64_t hash = hash_callstack(thread, task, exception, code, code_count, flavor,
749 in_state, in_state_count);
groebert@google.com1bd4c212013-06-19 11:13:56 +0000750
groebert@google.com20e368f2015-02-13 14:19:25 +0000751 fuzzer->backtrace = hash;
groebert@google.com1bd4c212013-06-19 11:13:56 +0000752
robert.swiecki@gmail.com772b33d2015-02-14 20:35:00 +0000753 /*
robert.swiecki@gmail.com3b630b42015-02-16 10:53:53 +0000754 * Cleanup
robert.swiecki@gmail.com772b33d2015-02-14 20:35:00 +0000755 */
groebert@google.com1bd4c212013-06-19 11:13:56 +0000756
757 if (mach_port_deallocate(mach_task_self(), task) != KERN_SUCCESS) {
758 LOGMSG(l_WARN, "Exception Handler: Could not deallocate task");
759 }
760
761 if (mach_port_deallocate(mach_task_self(), thread) != KERN_SUCCESS) {
762 LOGMSG(l_WARN, "Exception Handler: Could not deallocate thread");
763 }
764
robert.swiecki@gmail.com3b630b42015-02-16 10:53:53 +0000765 return KERN_SUCCESS; // KERN_SUCCESS indicates that this should
robert.swiecki@gmail.com772b33d2015-02-14 20:35:00 +0000766 // not be forwarded to other crash
767 // handlers
groebert@google.com1bd4c212013-06-19 11:13:56 +0000768}