• Home
  • History
  • Annotate
  • Line#
  • Scopes#
  • Navigate#
  • Raw
  • Download
1 /*
2  *
3  * honggfuzz - architecture dependent code (NETBSD/PTRACE)
4  * -----------------------------------------
5  *
6  * Author: Kamil Rytarowski <n54@gmx.com>
7  *
8  * Copyright 2010-2018 by Google Inc. All Rights Reserved.
9  *
10  * Licensed under the Apache License, Version 2.0 (the "License"); you may
11  * not use this file except in compliance with the License. You may obtain
12  * a copy of the License at
13  *
14  * http://www.apache.org/licenses/LICENSE-2.0
15  *
16  * Unless required by applicable law or agreed to in writing, software
17  * distributed under the License is distributed on an "AS IS" BASIS,
18  * WITHOUT WARRANTIES OR CONDITIONS OF ANY KIND, either express or
19  * implied. See the License for the specific language governing
20  * permissions and limitations under the License.
21  *
22  */
23 
24 #include "netbsd/trace.h"
25 
26 // clang-format off
27 #include <sys/param.h>
28 #include <sys/types.h>
29 // clang-format on
30 
31 #include <sys/ptrace.h>
32 #include <sys/resource.h>
33 #include <sys/stat.h>
34 #include <sys/syscall.h>
35 #include <sys/time.h>
36 #include <sys/types.h>
37 #include <sys/uio.h>
38 #include <sys/wait.h>
39 
40 #include <ctype.h>
41 #include <dirent.h>
42 #include <elf.h>
43 #include <endian.h>
44 #include <errno.h>
45 #include <fcntl.h>
46 #include <inttypes.h>
47 #include <signal.h>
48 #include <stdio.h>
49 #include <stdlib.h>
50 #include <string.h>
51 #include <time.h>
52 #include <unistd.h>
53 
54 #include "libhfcommon/common.h"
55 #include "libhfcommon/files.h"
56 #include "libhfcommon/log.h"
57 #include "libhfcommon/util.h"
58 #include "netbsd/unwind.h"
59 #include "socketfuzzer.h"
60 #include "subproc.h"
61 
62 #include <capstone/capstone.h>
63 
64 /*
65  * Size in characters required to store a string representation of a
66  * register value (0xdeadbeef style))
67  */
68 #define REGSIZEINCHAR (2 * sizeof(register_t) + 3)
69 
70 #define _HF_INSTR_SZ 64
71 
72 #if defined(__i386__) || defined(__x86_64__)
73 #define MAX_INSTR_SZ 16
74 #elif defined(__arm__) || defined(__powerpc__) || defined(__powerpc64__)
75 #define MAX_INSTR_SZ 4
76 #elif defined(__aarch64__)
77 #define MAX_INSTR_SZ 8
78 #elif defined(__mips__) || defined(__mips64__)
79 #define MAX_INSTR_SZ 8
80 #endif
81 
82 static struct {
83     const char* descr;
84     bool important;
85 } arch_sigs[_NSIG + 1] = {
86     [0 ...(_NSIG)].important = false,
87     [0 ...(_NSIG)].descr = "UNKNOWN",
88 
89     [SIGTRAP].important = false,
90     [SIGTRAP].descr = "SIGTRAP",
91 
92     [SIGILL].important = true,
93     [SIGILL].descr = "SIGILL",
94 
95     [SIGFPE].important = true,
96     [SIGFPE].descr = "SIGFPE",
97 
98     [SIGSEGV].important = true,
99     [SIGSEGV].descr = "SIGSEGV",
100 
101     [SIGBUS].important = true,
102     [SIGBUS].descr = "SIGBUS",
103 
104     /* Is affected from monitorSIGABRT flag */
105     [SIGABRT].important = false,
106     [SIGABRT].descr = "SIGABRT",
107 
108     /* Is affected from tmoutVTALRM flag */
109     [SIGVTALRM].important = false,
110     [SIGVTALRM].descr = "SIGVTALRM-TMOUT",
111 
112     /* seccomp-bpf kill */
113     [SIGSYS].important = true,
114     [SIGSYS].descr = "SIGSYS",
115 };
116 
117 #ifndef SI_FROMUSER
118 #define SI_FROMUSER(siptr) ((siptr)->si_code == SI_USER)
119 #endif /* SI_FROMUSER */
120 
121 static __thread char arch_signame[32];
arch_sigName(int signo)122 static const char* arch_sigName(int signo) {
123     snprintf(arch_signame, sizeof(arch_signame), "SIG%s", signalname(signo));
124     return arch_signame;
125 }
126 
arch_getProcMem(pid_t pid,uint8_t * buf,size_t len,register_t pc)127 static size_t arch_getProcMem(pid_t pid, uint8_t* buf, size_t len, register_t pc) {
128     struct ptrace_io_desc io;
129     size_t bytes_read;
130 
131     bytes_read = 0;
132     io.piod_op = PIOD_READ_D;
133     io.piod_len = len;
134 
135     do {
136         io.piod_offs = (void*)(pc + bytes_read);
137         io.piod_addr = buf + bytes_read;
138 
139         if (ptrace(PT_IO, pid, &io, 0) == -1) {
140             PLOG_W("Couldn't read process memory on pid %d, "
141                    "piod_op: %d offs: %p addr: %p piod_len: %zu",
142                 pid, io.piod_op, io.piod_offs, io.piod_addr, io.piod_len);
143             break;
144         }
145 
146         bytes_read = io.piod_len;
147         io.piod_len = len - bytes_read;
148     } while (bytes_read < len);
149 
150     return bytes_read;
151 }
152 
arch_getPC(pid_t pid,lwpid_t lwp,register_t * pc,register_t * status_reg HF_ATTR_UNUSED)153 static size_t arch_getPC(
154     pid_t pid, lwpid_t lwp, register_t* pc, register_t* status_reg HF_ATTR_UNUSED) {
155     struct reg r;
156 
157     if (ptrace(PT_GETREGS, pid, &r, lwp) != 0) {
158         PLOG_D("ptrace(PT_GETREGS) failed");
159         return 0;
160     }
161     *pc = PTRACE_REG_PC(&r);
162 #if defined(__i386__)
163     *status_reg = r.regs[_REG_EFLAGS];
164 #elif defined(__x86_64__)
165     *status_reg = r.regs[_REG_RFLAGS];
166 #else
167 #error unsupported CPU architecture
168 #endif
169 
170     return sizeof(r);
171 }
172 
arch_getInstrStr(pid_t pid,lwpid_t lwp,register_t * pc,char * instr)173 static void arch_getInstrStr(pid_t pid, lwpid_t lwp, register_t* pc, char* instr) {
174     /*
175      * We need a value aligned to 8
176      * which is sizeof(long) on 64bit CPU archs (on most of them, I hope;)
177      */
178     uint8_t buf[MAX_INSTR_SZ];
179     size_t memsz;
180     register_t status_reg = 0;
181 
182     snprintf(instr, _HF_INSTR_SZ, "%s", "[UNKNOWN]");
183 
184     size_t pcRegSz = arch_getPC(pid, lwp, pc, &status_reg);
185     if (!pcRegSz) {
186         LOG_W("Current architecture not supported for disassembly");
187         return;
188     }
189 
190     if ((memsz = arch_getProcMem(pid, buf, sizeof(buf), *pc)) == 0) {
191         snprintf(instr, _HF_INSTR_SZ, "%s", "[NOT_MMAPED]");
192         return;
193     }
194 
195     cs_arch arch;
196     cs_mode mode;
197 
198 #if defined(__i386__)
199     arch = CS_ARCH_X86;
200     mode = CS_MODE_32;
201 #elif defined(__x86_64__)
202     arch = CS_ARCH_X86;
203     mode = CS_MODE_64;
204 #else
205 #error Unsupported CPU architecture
206 #endif
207 
208     csh handle;
209     cs_err err = cs_open(arch, mode, &handle);
210     if (err != CS_ERR_OK) {
211         LOG_W("Capstone initialization failed: '%s'", cs_strerror(err));
212         return;
213     }
214 
215     cs_insn* insn;
216     size_t count = cs_disasm(handle, buf, sizeof(buf), *pc, 0, &insn);
217 
218     if (count < 1) {
219         LOG_W("Couldn't disassemble the assembler instructions' stream: '%s'",
220             cs_strerror(cs_errno(handle)));
221         cs_close(&handle);
222         return;
223     }
224 
225     snprintf(instr, _HF_INSTR_SZ, "%s %s", insn[0].mnemonic, insn[0].op_str);
226     cs_free(insn, count);
227     cs_close(&handle);
228 
229     for (int x = 0; instr[x] && x < _HF_INSTR_SZ; x++) {
230         if (instr[x] == '/' || instr[x] == '\\' || isspace((unsigned char)instr[x]) ||
231             !isprint((unsigned char)instr[x])) {
232             instr[x] = '_';
233         }
234     }
235 
236     return;
237 }
238 
arch_hashCallstack(run_t * run,funcs_t * funcs HF_ATTR_UNUSED,size_t funcCnt,bool enableMasking)239 static void arch_hashCallstack(
240     run_t* run, funcs_t* funcs HF_ATTR_UNUSED, size_t funcCnt, bool enableMasking) {
241     uint64_t hash = 0;
242     for (size_t i = 0; i < funcCnt && i < run->global->netbsd.numMajorFrames; i++) {
243         /*
244          * Convert PC to char array to be compatible with hash function
245          */
246         char pcStr[REGSIZEINCHAR] = {0};
247         snprintf(pcStr, REGSIZEINCHAR, "%" PRIxREGISTER, (register_t)(long)funcs[i].pc);
248 
249         /*
250          * Hash the last three nibbles
251          */
252         hash ^= util_hash(&pcStr[strlen(pcStr) - 3], 3);
253     }
254 
255     /*
256      * If only one frame, hash is not safe to be used for uniqueness. We mask it
257      * here with a constant prefix, so analyzers can pick it up and create filenames
258      * accordingly. 'enableMasking' is controlling masking for cases where it should
259      * not be enabled (e.g. fuzzer worker is from verifier).
260      */
261     if (enableMasking && funcCnt == 1) {
262         hash |= _HF_SINGLE_FRAME_MASK;
263     }
264     run->backtrace = hash;
265 }
266 
arch_traceGenerateReport(pid_t pid,run_t * run,funcs_t * funcs,size_t funcCnt,siginfo_t * si,const char * instr)267 static void arch_traceGenerateReport(
268     pid_t pid, run_t* run, funcs_t* funcs, size_t funcCnt, siginfo_t* si, const char* instr) {
269     run->report[0] = '\0';
270     util_ssnprintf(run->report, sizeof(run->report), "ORIG_FNAME: %s\n", run->origFileName);
271     util_ssnprintf(run->report, sizeof(run->report), "FUZZ_FNAME: %s\n", run->crashFileName);
272     util_ssnprintf(run->report, sizeof(run->report), "PID: %d\n", pid);
273     util_ssnprintf(run->report, sizeof(run->report), "SIGNAL: %s (%d)\n",
274         arch_sigName(si->si_signo), si->si_signo);
275     util_ssnprintf(run->report, sizeof(run->report), "FAULT ADDRESS: %p\n",
276         SI_FROMUSER(si) ? NULL : si->si_addr);
277     util_ssnprintf(run->report, sizeof(run->report), "INSTRUCTION: %s\n", instr);
278     util_ssnprintf(
279         run->report, sizeof(run->report), "STACK HASH: %016" PRIx64 "\n", run->backtrace);
280     util_ssnprintf(run->report, sizeof(run->report), "STACK:\n");
281     for (size_t i = 0; i < funcCnt; i++) {
282         util_ssnprintf(run->report, sizeof(run->report), " <%" PRIxREGISTER "> [%s():%zu at %s]\n",
283             (register_t)(long)funcs[i].pc, funcs[i].func, funcs[i].line, funcs[i].mapName);
284     }
285 
286     return;
287 }
288 
arch_traceAnalyzeData(run_t * run,pid_t pid)289 static void arch_traceAnalyzeData(run_t* run, pid_t pid) {
290     ptrace_siginfo_t info;
291     register_t pc = 0, status_reg = 0;
292 
293     if (ptrace(PT_GET_SIGINFO, pid, &info, sizeof(info)) == -1) {
294         PLOG_W("Couldn't get siginfo for pid %d", pid);
295     }
296 
297     size_t pcRegSz = arch_getPC(pid, info.psi_lwpid, &pc, &status_reg);
298     if (!pcRegSz) {
299         LOG_W("ptrace arch_getPC failed");
300         return;
301     }
302 
303     /*
304      * Unwind and resolve symbols
305      */
306     funcs_t* funcs = util_Malloc(_HF_MAX_FUNCS * sizeof(funcs_t));
307     defer {
308         free(funcs);
309     };
310     memset(funcs, 0, _HF_MAX_FUNCS * sizeof(funcs_t));
311 
312     size_t funcCnt = 0;
313 
314     /*
315      * Use PC from ptrace GETREGS if not zero.
316      * If PC reg zero return and callers should handle zero hash case.
317      */
318     if (pc) {
319         /* Manually update major frame PC & frames counter */
320         funcs[0].pc = (void*)(uintptr_t)pc;
321         funcCnt = 1;
322     } else {
323         return;
324     }
325 
326     /*
327      * Calculate backtrace callstack hash signature
328      */
329     arch_hashCallstack(run, funcs, funcCnt, false);
330 }
331 
arch_traceSaveData(run_t * run,pid_t pid)332 static void arch_traceSaveData(run_t* run, pid_t pid) {
333     register_t pc = 0;
334 
335     /* Local copy since flag is overridden for some crashes */
336     bool saveUnique = run->global->io.saveUnique;
337 
338     char instr[_HF_INSTR_SZ] = "\x00";
339     struct ptrace_siginfo info;
340     memset(&info, 0, sizeof(info));
341 
342     if (ptrace(PT_GET_SIGINFO, pid, &info, sizeof(info)) == -1) {
343         PLOG_W("Couldn't get siginfo for pid %d", pid);
344     }
345 
346     arch_getInstrStr(pid, info.psi_lwpid, &pc, instr);
347 
348     LOG_D("Pid: %d, signo: %d, errno: %d, code: %d, addr: %p, pc: %" PRIxREGISTER ", instr: '%s'",
349         pid, info.psi_siginfo.si_signo, info.psi_siginfo.si_errno, info.psi_siginfo.si_code,
350         info.psi_siginfo.si_addr, pc, instr);
351 
352     if (!SI_FROMUSER(&info.psi_siginfo) && pc &&
353         info.psi_siginfo.si_addr < run->global->netbsd.ignoreAddr) {
354         LOG_I("Input is interesting (%s), but the si.si_addr is %p (below %p), skipping",
355             arch_sigName(info.psi_siginfo.si_signo), info.psi_siginfo.si_addr,
356             run->global->netbsd.ignoreAddr);
357         return;
358     }
359 
360     /*
361      * Unwind and resolve symbols
362      */
363     funcs_t* funcs = util_Malloc(_HF_MAX_FUNCS * sizeof(funcs_t));
364     defer {
365         free(funcs);
366     };
367     memset(funcs, 0, _HF_MAX_FUNCS * sizeof(funcs_t));
368 
369     size_t funcCnt = 0;
370 
371     /*
372      * Use PC from ptrace GETREGS if not zero.
373      * If PC reg zero, temporarily disable uniqueness flag since callstack
374      * hash will be also zero, thus not safe for unique decisions.
375      */
376     if (pc) {
377         /* Manually update major frame PC & frames counter */
378         funcs[0].pc = (void*)(uintptr_t)pc;
379         funcCnt = 1;
380     } else {
381         saveUnique = false;
382     }
383 
384     /*
385      * Temp local copy of previous backtrace value in case worker hit crashes into multiple
386      * tids for same target master thread. Will be 0 for first crash against target.
387      */
388     uint64_t oldBacktrace = run->backtrace;
389 
390     /*
391      * Calculate backtrace callstack hash signature
392      */
393     arch_hashCallstack(run, funcs, funcCnt, saveUnique);
394 
395     /*
396      * If unique flag is set and single frame crash, disable uniqueness for this crash
397      * to always save (timestamp will be added to the filename)
398      */
399     if (saveUnique && (funcCnt == 1)) {
400         saveUnique = false;
401     }
402 
403     /*
404      * If worker crashFileName member is set, it means that a tid has already crashed
405      * from target master thread.
406      */
407     if (run->crashFileName[0] != '\0') {
408         LOG_D("Multiple crashes detected from worker against attached tids group");
409 
410         /*
411          * If stackhashes match, don't re-analyze. This will avoid duplicates
412          * and prevent verifier from running multiple passes. Depth of check is
413          * always 1 (last backtrace saved only per target iteration).
414          */
415         if (oldBacktrace == run->backtrace) {
416             return;
417         }
418     }
419 
420     /* Increase global crashes counter */
421     ATOMIC_POST_INC(run->global->cnts.crashesCnt);
422 
423     /*
424      * Check if backtrace contains whitelisted symbol. Whitelist overrides
425      * both stackhash and symbol blacklist. Crash is always kept regardless
426      * of the status of uniqueness flag.
427      */
428     if (run->global->netbsd.symsWl) {
429         char* wlSymbol = arch_btContainsSymbol(
430             run->global->netbsd.symsWlCnt, run->global->netbsd.symsWl, funcCnt, funcs);
431         if (wlSymbol != NULL) {
432             saveUnique = false;
433             LOG_D("Whitelisted symbol '%s' found, skipping blacklist checks", wlSymbol);
434         }
435     } else {
436         /*
437          * Check if stackhash is blacklisted
438          */
439         if (run->global->feedback.blacklist &&
440             (fastArray64Search(run->global->feedback.blacklist, run->global->feedback.blacklistCnt,
441                  run->backtrace) != -1)) {
442             LOG_I("Blacklisted stack hash '%" PRIx64 "', skipping", run->backtrace);
443             ATOMIC_POST_INC(run->global->cnts.blCrashesCnt);
444             return;
445         }
446 
447         /*
448          * Check if backtrace contains blacklisted symbol
449          */
450         char* blSymbol = arch_btContainsSymbol(
451             run->global->netbsd.symsBlCnt, run->global->netbsd.symsBl, funcCnt, funcs);
452         if (blSymbol != NULL) {
453             LOG_I("Blacklisted symbol '%s' found, skipping", blSymbol);
454             ATOMIC_POST_INC(run->global->cnts.blCrashesCnt);
455             return;
456         }
457     }
458 
459     /* If non-blacklisted crash detected, zero set two MSB */
460     ATOMIC_POST_ADD(run->global->cfg.dynFileIterExpire, _HF_DYNFILE_SUB_MASK);
461 
462     void* sig_addr = info.psi_siginfo.si_addr;
463     pc = 0UL;
464     sig_addr = NULL;
465 
466     /* User-induced signals don't set si.si_addr */
467     if (SI_FROMUSER(&info.psi_siginfo)) {
468         sig_addr = NULL;
469     }
470 
471     /* If dry run mode, copy file with same name into workspace */
472     if (run->global->mutate.mutationsPerRun == 0U && run->global->cfg.useVerifier) {
473         snprintf(run->crashFileName, sizeof(run->crashFileName), "%s/%s", run->global->io.crashDir,
474             run->origFileName);
475     } else if (saveUnique) {
476         snprintf(run->crashFileName, sizeof(run->crashFileName),
477             "%s/%s.PC.%" PRIxREGISTER ".STACK.%" PRIx64 ".CODE.%d.ADDR.%p.INSTR.%s.%s",
478             run->global->io.crashDir, arch_sigName(info.psi_siginfo.si_signo), pc, run->backtrace,
479             info.psi_siginfo.si_code, sig_addr, instr, run->global->io.fileExtn);
480     } else {
481         char localtmstr[PATH_MAX];
482         util_getLocalTime("%F.%H:%M:%S", localtmstr, sizeof(localtmstr), time(NULL));
483         snprintf(run->crashFileName, sizeof(run->crashFileName),
484             "%s/%s.PC.%" PRIxREGISTER ".STACK.%" PRIx64 ".CODE.%d.ADDR.%p.INSTR.%s.%s.%d.%s",
485             run->global->io.crashDir, arch_sigName(info.psi_siginfo.si_signo), pc, run->backtrace,
486             info.psi_siginfo.si_code, sig_addr, instr, localtmstr, pid, run->global->io.fileExtn);
487     }
488 
489     /* Target crashed (no duplicate detection yet) */
490     if (run->global->socketFuzzer.enabled) {
491         LOG_D("SocketFuzzer: trace: Crash Identified");
492     }
493 
494     if (files_exists(run->crashFileName)) {
495         LOG_I("Crash (dup): '%s' already exists, skipping", run->crashFileName);
496         // Clear filename so that verifier can understand we hit a duplicate
497         memset(run->crashFileName, 0, sizeof(run->crashFileName));
498         return;
499     }
500 
501     if (!files_writeBufToFile(run->crashFileName, run->dynamicFile, run->dynamicFileSz,
502             O_CREAT | O_EXCL | O_WRONLY | O_CLOEXEC)) {
503         LOG_E("Couldn't write to '%s'", run->crashFileName);
504         return;
505     }
506 
507     /* Unique new crash, notify fuzzer */
508     if (run->global->socketFuzzer.enabled) {
509         LOG_D("SocketFuzzer: trace: New Uniqu Crash");
510         fuzz_notifySocketFuzzerCrash(run);
511     }
512     LOG_I("Crash: saved as '%s'", run->crashFileName);
513 
514     ATOMIC_POST_INC(run->global->cnts.uniqueCrashesCnt);
515     /* If unique crash found, reset dynFile counter */
516     ATOMIC_CLEAR(run->global->cfg.dynFileIterExpire);
517 
518     arch_traceGenerateReport(pid, run, funcs, funcCnt, &info.psi_siginfo, instr);
519 }
520 
arch_traceEvent(run_t * run HF_ATTR_UNUSED,pid_t pid)521 static void arch_traceEvent(run_t* run HF_ATTR_UNUSED, pid_t pid) {
522     ptrace_state_t state;
523     ptrace_siginfo_t info;
524     int sig = 0;
525 
526     if (ptrace(PT_GET_SIGINFO, pid, &info, sizeof(info)) == -1) {
527         PLOG_E("ptrace(PT_GET_SIGINFO, pid=%d)", (int)pid);
528     } else {
529         switch (info.psi_siginfo.si_code) {
530             case TRAP_BRKPT:
531                 /* Software breakpoint trap, pass it over to tracee */
532                 sig = SIGTRAP;
533                 LOG_D("PID: %d breakpoint software trap (TRAP_BRKPT)", pid);
534                 break;
535             case TRAP_TRACE:
536                 /* Single step unused */
537                 LOG_E("PID: %d unexpected single step trace trap (TRAP_TRACE)", pid);
538                 break;
539             case TRAP_EXEC:
540                 /* exec(3) trap, ignore */
541                 LOG_D("PID: %d breakpoint software trap (TRAP_EXEC)", pid);
542                 break;
543             case TRAP_CHLD:
544             case TRAP_LWP:
545                 /* Child/LWP trap, ignore */
546                 if (ptrace(PT_GET_PROCESS_STATE, pid, &state, sizeof(state)) != -1) {
547                     switch (state.pe_report_event) {
548                         case PTRACE_FORK:
549                             LOG_D("PID: %d child trap (TRAP_CHLD) : fork", (int)pid);
550                             break;
551                         case PTRACE_VFORK:
552                             LOG_D("PID: %d child trap (TRAP_CHLD) : vfork", (int)pid);
553                             break;
554                         case PTRACE_VFORK_DONE:
555                             LOG_D("PID: %d child trap (TRAP_CHLD) : vfork (PTRACE_VFORK_DONE)",
556                                 (int)pid);
557                             break;
558                         case PTRACE_LWP_CREATE:
559                             LOG_E("PID: %d unexpected lwp trap (TRAP_LWP) : create "
560                                   "(PTRACE_LWP_CREATE)",
561                                 (int)pid);
562                             break;
563                         case PTRACE_LWP_EXIT:
564                             LOG_E("PID: %d unexpected lwp trap (TRAP_LWP) : exit (PTRACE_LWP_EXIT)",
565                                 (int)pid);
566                             break;
567                         default:
568                             LOG_D("PID: %d unknown child/lwp trap (TRAP_LWP/TRAP_CHLD) : unknown "
569                                   "pe_report_event=%d",
570                                 (int)pid, state.pe_report_event);
571                             break;
572                     }
573                 }
574                 break;
575             case TRAP_DBREG:
576                 /* Debug Register trap unused */
577                 LOG_E("PID: %d unexpected debug register trap (TRAP_DBREG)", pid);
578                 break;
579             case TRAP_SCE:
580                 /* Syscall Enter trap unused */
581                 LOG_E("PID: %d unexpected syscall enter trap (TRAP_SCE)", pid);
582                 break;
583             case TRAP_SCX:
584                 /* Syscall Exit trap unused */
585                 LOG_E("PID: %d unexpected syscall exit trap (TRAP_SCX)", pid);
586                 break;
587             default:
588                 /* Other trap, pass it over to tracee */
589                 sig = SIGTRAP;
590                 LOG_D("PID: %d other trap si_code=%d", pid, info.psi_siginfo.si_code);
591                 break;
592         }
593     }
594 
595     ptrace(PT_CONTINUE, pid, (void*)1, sig);
596 }
597 
arch_traceAnalyze(run_t * run,int status,pid_t pid)598 void arch_traceAnalyze(run_t* run, int status, pid_t pid) {
599     /*
600      * It's a ptrace event, deal with it elsewhere
601      */
602     if (WIFSTOPPED(status) && WSTOPSIG(status) == SIGTRAP) {
603         return arch_traceEvent(run, pid);
604     }
605 
606     if (WIFSTOPPED(status)) {
607         /*
608          * If it's an interesting signal, save the testcase
609          */
610         if (arch_sigs[WSTOPSIG(status)].important) {
611             /*
612              * If fuzzer worker is from core fuzzing process run full
613              * analysis. Otherwise just unwind and get stack hash signature.
614              */
615             if (run->mainWorker) {
616                 arch_traceSaveData(run, pid);
617             } else {
618                 arch_traceAnalyzeData(run, pid);
619             }
620         }
621         /* Do not deliver SIGSTOP */
622         int sig = (WSTOPSIG(status) != SIGSTOP) ? WSTOPSIG(status) : 0;
623         ptrace(PT_CONTINUE, pid, (void*)1, sig);
624         return;
625     }
626 
627     /*
628      * Resumed by delivery of SIGCONT
629      */
630     if (WIFCONTINUED(status)) {
631         return;
632     }
633 
634     /*
635      * Process exited
636      */
637     if (WIFEXITED(status)) {
638         return;
639     }
640 
641     if (WIFSIGNALED(status)) {
642         return;
643     }
644 
645     abort(); /* NOTREACHED */
646 }
647 
arch_traceWaitForPidStop(pid_t pid)648 bool arch_traceWaitForPidStop(pid_t pid) {
649     LOG_D("Waiting for pid=%d to stop", (int)pid);
650 
651     for (;;) {
652         int status;
653         pid_t ret = wait4(pid, &status, __WALL | WUNTRACED | WTRAPPED, NULL);
654         if (ret == -1 && errno == EINTR) {
655             continue;
656         }
657         if (ret == -1) {
658             PLOG_W("wait4(pid=%d) failed", pid);
659             return false;
660         }
661         if (!WIFSTOPPED(status)) {
662             LOG_W("PID %d not in a stopped state - status:%d", pid, status);
663             return false;
664         }
665 
666         LOG_D("pid=%d stopped", (int)pid);
667         return true;
668     }
669 }
670 
arch_traceAttach(run_t * run)671 bool arch_traceAttach(run_t* run) {
672     if (!arch_traceWaitForPidStop(run->pid)) {
673         return false;
674     }
675     if (ptrace(PT_ATTACH, run->pid, NULL, 0) == -1) {
676         PLOG_W("Couldn't ptrace(PT_ATTACH) to pid: %d", (int)run->pid);
677         return false;
678     }
679     if (!arch_traceWaitForPidStop(run->pid)) {
680         return false;
681     }
682 
683     ptrace_event_t event = {
684         /*
685          * NetBSD 8.0 seems to support PTRACE_FORK only:
686          *          .pe_set_event = PTRACE_FORK | PTRACE_VFORK | PTRACE_VFORK_DONE,
687          */
688         .pe_set_event = PTRACE_FORK,
689     };
690     if (ptrace(PT_SET_EVENT_MASK, run->pid, &event, sizeof(event)) == -1) {
691         PLOG_W("Couldn't ptrace(PT_SET_EVENT_MASK) to pid: %d", (int)run->pid);
692         return false;
693     }
694 
695     LOG_D("Attached to PID: %d", run->pid);
696 
697     if (ptrace(PT_CONTINUE, run->pid, (void*)1, 0) == -1) {
698         PLOG_W("Couldn't ptrace(PT_CONTINUE) to pid: (int)%d", run->pid);
699         return false;
700     }
701 
702     return true;
703 }
704 
arch_traceDetach(pid_t pid)705 void arch_traceDetach(pid_t pid) {
706     if (ptrace(PT_DETACH, pid, NULL, 0) == -1) {
707         PLOG_E("PID: %d ptrace(PT_DETACH) failed", pid);
708     }
709 }
710 
arch_traceSignalsInit(honggfuzz_t * hfuzz)711 void arch_traceSignalsInit(honggfuzz_t* hfuzz) {
712     /* Default is true for all platforms except Android */
713     arch_sigs[SIGABRT].important = hfuzz->cfg.monitorSIGABRT;
714 
715     /* Default is false */
716     arch_sigs[SIGVTALRM].important = hfuzz->timing.tmoutVTALRM;
717 }
718