void exit_handler(int signo, siginfo_t * info, void *context) { (void) context; if (info) { LEVEL_DEBUG ("Signal=%d, errno %d, code %d, pid=%ld, Threads: this=%lu main=%lu", signo, info->si_errno, info->si_code, (long int) info->si_pid, pthread_self(), main_threadid); } else { LEVEL_DEBUG("Signal=%d, Threads: this=%lu, main=%lu", signo, pthread_self(), main_threadid); } if (StateInfo.shutting_down) { LEVEL_DEBUG("exit_handler: shutdown already in progress. signo=%d, self=%lu, main=%lu", signo, pthread_self(), main_threadid); } else { StateInfo.shutting_down = 1; if (info != NULL) { if (SI_FROMUSER(info)) { LEVEL_DEBUG("Kill signal from user"); } if (SI_FROMKERNEL(info)) { LEVEL_DEBUG("Kill signal from system"); } } if (!IS_MAINTHREAD) { LEVEL_DEBUG("Kill from main thread: %lu this=%lu Signal=%d", main_threadid, pthread_self(), signo); pthread_kill(main_threadid, signo); } else { LEVEL_DEBUG("Ignore kill from this thread. main=%lu this=%lu Signal=%d", main_threadid, pthread_self(), signo); } } }
int _stack_violation(int sig, const siginfo_t *sip, const ucontext_t *ucp) { uintptr_t addr; uintptr_t base; size_t size; if ((sig != SIGSEGV && sig != SIGBUS) || sip == NULL || ucp == NULL || SI_FROMUSER(sip)) return (0); /* * ucp has the correct view of the stack when the signal was raised. */ base = (uintptr_t)ucp->uc_stack.ss_sp; size = ucp->uc_stack.ss_size; #if defined(__sparc) addr = ucp->uc_mcontext.gregs[REG_SP] + STACK_BIAS; #elif defined(__amd64) || defined(__i386) addr = ucp->uc_mcontext.gregs[REG_SP]; /* * If the faulted address is just below the stack pointer we * might be looking at a push instruction that caused the fault * (the largest amount a push instruction can decrement the * stack pointer by is 32). In that case, use the faulted * address in our computation rather than the stack pointer. */ if (addr - (uintptr_t)sip->si_addr < 32) addr = (uintptr_t)sip->si_addr; #else #error "none of __sparc, __amd64, __i386 is defined" #endif return (!(addr >= base && addr < base + size)); }
static void arch_ptraceGenerateReport(pid_t pid, fuzzer_t * fuzzer, funcs_t * funcs, size_t funcCnt, siginfo_t * si, const char *instr) { fuzzer->report[0] = '\0'; util_ssnprintf(fuzzer->report, sizeof(fuzzer->report), "ORIG_FNAME: %s\n", fuzzer->origFileName); util_ssnprintf(fuzzer->report, sizeof(fuzzer->report), "FUZZ_FNAME: %s\n", fuzzer->crashFileName); util_ssnprintf(fuzzer->report, sizeof(fuzzer->report), "PID: %d\n", pid); util_ssnprintf(fuzzer->report, sizeof(fuzzer->report), "SIGNAL: %s (%d)\n", arch_sigs[si->si_signo].descr, si->si_signo); util_ssnprintf(fuzzer->report, sizeof(fuzzer->report), "FAULT ADDRESS: %p\n", SI_FROMUSER(si) ? NULL : si->si_addr); util_ssnprintf(fuzzer->report, sizeof(fuzzer->report), "INSTRUCTION: %s\n", instr); util_ssnprintf(fuzzer->report, sizeof(fuzzer->report), "STACK HASH: %016llx\n", fuzzer->backtrace); util_ssnprintf(fuzzer->report, sizeof(fuzzer->report), "STACK:\n"); for (size_t i = 0; i < funcCnt; i++) { #ifdef __HF_USE_CAPSTONE__ util_ssnprintf(fuzzer->report, sizeof(fuzzer->report), " <" REG_PD REG_PM "> ", (REG_TYPE) (long)funcs[i].pc, funcs[i].func, funcs[i].line); if (funcs[i].func[0] != '\0') util_ssnprintf(fuzzer->report, sizeof(fuzzer->report), "[%s + 0x%x]\n", funcs[i].func, funcs[i].line); else util_ssnprintf(fuzzer->report, sizeof(fuzzer->report), "[]\n"); #else util_ssnprintf(fuzzer->report, sizeof(fuzzer->report), " <" REG_PD REG_PM "> [%s():%u]\n", (REG_TYPE) (long)funcs[i].pc, funcs[i].func, funcs[i].line); #endif } // libunwind is not working for 32bit targets in 64bit systems #if defined(__aarch64__) if (funcCnt == 0) { util_ssnprintf(fuzzer->report, sizeof(fuzzer->report), " !ERROR: If 32bit fuzz target" " in aarch64 system, try ARM 32bit build\n"); } #endif return; }
int copy_siginfo_from_user (siginfo_t *to, siginfo_t *from) { if (!access_ok(VERIFY_READ, from, sizeof(siginfo_t))) return -EFAULT; if (__copy_from_user(to, from, sizeof(siginfo_t)) != 0) return -EFAULT; if (SI_FROMUSER(to)) return 0; to->si_code &= ~__SI_MASK; if (to->si_code != 0) { switch (to->si_signo) { case SIGILL: case SIGFPE: case SIGSEGV: case SIGBUS: case SIGTRAP: to->si_code |= __SI_FAULT; break; case SIGCHLD: to->si_code |= __SI_CHLD; break; case SIGPOLL: to->si_code |= __SI_POLL; break; case SIGPROF: to->si_code |= __SI_PROF; break; default: break; } } return 0; }
int sendsig(int sig, k_siginfo_t *sip, void (*hdlr)()) { volatile int minstacksz; int newstack; label_t ljb; volatile caddr_t sp; caddr_t fp; struct regs *rp; volatile greg_t upc; volatile proc_t *p = ttoproc(curthread); klwp_t *lwp = ttolwp(curthread); ucontext_t *volatile tuc = NULL; ucontext_t *uc; siginfo_t *sip_addr; volatile int watched; rp = lwptoregs(lwp); upc = rp->r_pc; minstacksz = SA(sizeof (struct sigframe)) + SA(sizeof (*uc)); if (sip != NULL) minstacksz += SA(sizeof (siginfo_t)); ASSERT((minstacksz & (STACK_ALIGN - 1ul)) == 0); /* * Figure out whether we will be handling this signal on * an alternate stack specified by the user. Then allocate * and validate the stack requirements for the signal handler * context. on_fault will catch any faults. */ newstack = sigismember(&PTOU(curproc)->u_sigonstack, sig) && !(lwp->lwp_sigaltstack.ss_flags & (SS_ONSTACK|SS_DISABLE)); if (newstack) { fp = (caddr_t)(SA((uintptr_t)lwp->lwp_sigaltstack.ss_sp) + SA(lwp->lwp_sigaltstack.ss_size) - STACK_ALIGN); } else if ((rp->r_ss & 0xffff) != UDS_SEL) { user_desc_t *ldt; /* * If the stack segment selector is -not- pointing at * the UDS_SEL descriptor and we have an LDT entry for * it instead, add the base address to find the effective va. */ if ((ldt = p->p_ldt) != NULL) fp = (caddr_t)rp->r_sp + USEGD_GETBASE(&ldt[SELTOIDX(rp->r_ss)]); else fp = (caddr_t)rp->r_sp; } else fp = (caddr_t)rp->r_sp; /* * Force proper stack pointer alignment, even in the face of a * misaligned stack pointer from user-level before the signal. * Don't use the SA() macro because that rounds up, not down. */ fp = (caddr_t)((uintptr_t)fp & ~(STACK_ALIGN - 1ul)); sp = fp - minstacksz; /* * Make sure lwp hasn't trashed its stack. */ if (sp >= (caddr_t)USERLIMIT || fp >= (caddr_t)USERLIMIT) { #ifdef DEBUG printf("sendsig: bad signal stack cmd=%s, pid=%d, sig=%d\n", PTOU(p)->u_comm, p->p_pid, sig); printf("sigsp = 0x%p, action = 0x%p, upc = 0x%lx\n", (void *)sp, (void *)hdlr, (uintptr_t)upc); printf("sp above USERLIMIT\n"); #endif return (0); } watched = watch_disable_addr((caddr_t)sp, minstacksz, S_WRITE); if (on_fault(&ljb)) goto badstack; if (sip != NULL) { zoneid_t zoneid; fp -= SA(sizeof (siginfo_t)); uzero(fp, sizeof (siginfo_t)); if (SI_FROMUSER(sip) && (zoneid = p->p_zone->zone_id) != GLOBAL_ZONEID && zoneid != sip->si_zoneid) { k_siginfo_t sani_sip = *sip; sani_sip.si_pid = p->p_zone->zone_zsched->p_pid; sani_sip.si_uid = 0; sani_sip.si_ctid = -1; sani_sip.si_zoneid = zoneid; copyout_noerr(&sani_sip, fp, sizeof (sani_sip)); } else copyout_noerr(sip, fp, sizeof (*sip)); sip_addr = (siginfo_t *)fp; if (sig == SIGPROF && curthread->t_rprof != NULL && curthread->t_rprof->rp_anystate) { /* * We stand on our head to deal with * the real time profiling signal. * Fill in the stuff that doesn't fit * in a normal k_siginfo structure. */ int i = sip->si_nsysarg; while (--i >= 0) suword32_noerr(&(sip_addr->si_sysarg[i]), (uint32_t)lwp->lwp_arg[i]); copyout_noerr(curthread->t_rprof->rp_state, sip_addr->si_mstate, sizeof (curthread->t_rprof->rp_state)); } } else sip_addr = NULL; /* save the current context on the user stack */ fp -= SA(sizeof (*tuc)); uc = (ucontext_t *)fp; tuc = kmem_alloc(sizeof (*tuc), KM_SLEEP); savecontext(tuc, &lwp->lwp_sigoldmask); copyout_noerr(tuc, uc, sizeof (*tuc)); kmem_free(tuc, sizeof (*tuc)); tuc = NULL; lwp->lwp_oldcontext = (uintptr_t)uc; if (newstack) { lwp->lwp_sigaltstack.ss_flags |= SS_ONSTACK; if (lwp->lwp_ustack) copyout_noerr(&lwp->lwp_sigaltstack, (stack_t *)lwp->lwp_ustack, sizeof (stack_t)); } /* * Set up signal handler arguments */ { struct sigframe frame; frame.sip = sip_addr; frame.ucp = uc; frame.signo = sig; frame.retaddr = (void (*)())0xffffffff; /* never return! */ copyout_noerr(&frame, sp, sizeof (frame)); } no_fault(); if (watched) watch_enable_addr((caddr_t)sp, minstacksz, S_WRITE); rp->r_sp = (greg_t)sp; rp->r_pc = (greg_t)hdlr; rp->r_ps = PSL_USER | (rp->r_ps & PS_IOPL); if ((rp->r_cs & 0xffff) != UCS_SEL || (rp->r_ss & 0xffff) != UDS_SEL) { rp->r_cs = UCS_SEL; rp->r_ss = UDS_SEL; } /* * Don't set lwp_eosys here. sendsig() is called via psig() after * lwp_eosys is handled, so setting it here would affect the next * system call. */ return (1); badstack: no_fault(); if (watched) watch_enable_addr((caddr_t)sp, minstacksz, S_WRITE); if (tuc) kmem_free(tuc, sizeof (*tuc)); #ifdef DEBUG printf("sendsig: bad signal stack cmd=%s, pid=%d, sig=%d\n", PTOU(p)->u_comm, p->p_pid, sig); printf("on fault, sigsp = 0x%p, action = 0x%p, upc = 0x%lx\n", (void *)sp, (void *)hdlr, (uintptr_t)upc); #endif return (0); }
int sendsig(int sig, k_siginfo_t *sip, void (*hdlr)()) { volatile int minstacksz; int newstack; label_t ljb; volatile caddr_t sp; caddr_t fp; volatile struct regs *rp; volatile greg_t upc; volatile proc_t *p = ttoproc(curthread); struct as *as = p->p_as; klwp_t *lwp = ttolwp(curthread); ucontext_t *volatile tuc = NULL; ucontext_t *uc; siginfo_t *sip_addr; volatile int watched; /* * This routine is utterly dependent upon STACK_ALIGN being * 16 and STACK_ENTRY_ALIGN being 8. Let's just acknowledge * that and require it. */ #if STACK_ALIGN != 16 || STACK_ENTRY_ALIGN != 8 #error "sendsig() amd64 did not find the expected stack alignments" #endif rp = lwptoregs(lwp); upc = rp->r_pc; /* * Since we're setting up to run the signal handler we have to * arrange that the stack at entry to the handler is (only) * STACK_ENTRY_ALIGN (i.e. 8) byte aligned so that when the handler * executes its push of %rbp, the stack realigns to STACK_ALIGN * (i.e. 16) correctly. * * The new sp will point to the sigframe and the ucontext_t. The * above means that sp (and thus sigframe) will be 8-byte aligned, * but not 16-byte aligned. ucontext_t, however, contains %xmm regs * which must be 16-byte aligned. Because of this, for correct * alignment, sigframe must be a multiple of 8-bytes in length, but * not 16-bytes. This will place ucontext_t at a nice 16-byte boundary. */ /* LINTED: logical expression always true: op "||" */ ASSERT((sizeof (struct sigframe) % 16) == 8); minstacksz = sizeof (struct sigframe) + SA(sizeof (*uc)); if (sip != NULL) minstacksz += SA(sizeof (siginfo_t)); ASSERT((minstacksz & (STACK_ENTRY_ALIGN - 1ul)) == 0); /* * Figure out whether we will be handling this signal on * an alternate stack specified by the user. Then allocate * and validate the stack requirements for the signal handler * context. on_fault will catch any faults. */ newstack = sigismember(&PTOU(curproc)->u_sigonstack, sig) && !(lwp->lwp_sigaltstack.ss_flags & (SS_ONSTACK|SS_DISABLE)); if (newstack) { fp = (caddr_t)(SA((uintptr_t)lwp->lwp_sigaltstack.ss_sp) + SA(lwp->lwp_sigaltstack.ss_size) - STACK_ALIGN); } else { /* * Drop below the 128-byte reserved region of the stack frame * we're interrupting. */ fp = (caddr_t)rp->r_sp - STACK_RESERVE; } /* * Force proper stack pointer alignment, even in the face of a * misaligned stack pointer from user-level before the signal. */ fp = (caddr_t)((uintptr_t)fp & ~(STACK_ENTRY_ALIGN - 1ul)); /* * Most of the time during normal execution, the stack pointer * is aligned on a STACK_ALIGN (i.e. 16 byte) boundary. However, * (for example) just after a call instruction (which pushes * the return address), the callers stack misaligns until the * 'push %rbp' happens in the callee prolog. So while we should * expect the stack pointer to be always at least STACK_ENTRY_ALIGN * aligned, we should -not- expect it to always be STACK_ALIGN aligned. * We now adjust to ensure that the new sp is aligned to * STACK_ENTRY_ALIGN but not to STACK_ALIGN. */ sp = fp - minstacksz; if (((uintptr_t)sp & (STACK_ALIGN - 1ul)) == 0) { sp -= STACK_ENTRY_ALIGN; minstacksz = fp - sp; } /* * Now, make sure the resulting signal frame address is sane */ if (sp >= as->a_userlimit || fp >= as->a_userlimit) { #ifdef DEBUG printf("sendsig: bad signal stack cmd=%s, pid=%d, sig=%d\n", PTOU(p)->u_comm, p->p_pid, sig); printf("sigsp = 0x%p, action = 0x%p, upc = 0x%lx\n", (void *)sp, (void *)hdlr, (uintptr_t)upc); printf("sp above USERLIMIT\n"); #endif return (0); } watched = watch_disable_addr((caddr_t)sp, minstacksz, S_WRITE); if (on_fault(&ljb)) goto badstack; if (sip != NULL) { zoneid_t zoneid; fp -= SA(sizeof (siginfo_t)); uzero(fp, sizeof (siginfo_t)); if (SI_FROMUSER(sip) && (zoneid = p->p_zone->zone_id) != GLOBAL_ZONEID && zoneid != sip->si_zoneid) { k_siginfo_t sani_sip = *sip; sani_sip.si_pid = p->p_zone->zone_zsched->p_pid; sani_sip.si_uid = 0; sani_sip.si_ctid = -1; sani_sip.si_zoneid = zoneid; copyout_noerr(&sani_sip, fp, sizeof (sani_sip)); } else copyout_noerr(sip, fp, sizeof (*sip)); sip_addr = (siginfo_t *)fp; if (sig == SIGPROF && curthread->t_rprof != NULL && curthread->t_rprof->rp_anystate) { /* * We stand on our head to deal with * the real time profiling signal. * Fill in the stuff that doesn't fit * in a normal k_siginfo structure. */ int i = sip->si_nsysarg; while (--i >= 0) sulword_noerr( (ulong_t *)&(sip_addr->si_sysarg[i]), (ulong_t)lwp->lwp_arg[i]); copyout_noerr(curthread->t_rprof->rp_state, sip_addr->si_mstate, sizeof (curthread->t_rprof->rp_state)); } } else sip_addr = NULL; /* * save the current context on the user stack directly after the * sigframe. Since sigframe is 8-byte-but-not-16-byte aligned, * and since sizeof (struct sigframe) is 24, this guarantees * 16-byte alignment for ucontext_t and its %xmm registers. */ uc = (ucontext_t *)(sp + sizeof (struct sigframe)); tuc = kmem_alloc(sizeof (*tuc), KM_SLEEP); no_fault(); savecontext(tuc, &lwp->lwp_sigoldmask); if (on_fault(&ljb)) goto badstack; copyout_noerr(tuc, uc, sizeof (*tuc)); kmem_free(tuc, sizeof (*tuc)); tuc = NULL; lwp->lwp_oldcontext = (uintptr_t)uc; if (newstack) { lwp->lwp_sigaltstack.ss_flags |= SS_ONSTACK; if (lwp->lwp_ustack) copyout_noerr(&lwp->lwp_sigaltstack, (stack_t *)lwp->lwp_ustack, sizeof (stack_t)); } /* * Set up signal handler return and stack linkage */ { struct sigframe frame; /* * ensure we never return "normally" */ frame.retaddr = (caddr_t)(uintptr_t)-1L; frame.signo = sig; frame.sip = sip_addr; copyout_noerr(&frame, sp, sizeof (frame)); } no_fault(); if (watched) watch_enable_addr((caddr_t)sp, minstacksz, S_WRITE); /* * Set up user registers for execution of signal handler. */ rp->r_sp = (greg_t)sp; rp->r_pc = (greg_t)hdlr; rp->r_ps = PSL_USER | (rp->r_ps & PS_IOPL); rp->r_rdi = sig; rp->r_rsi = (uintptr_t)sip_addr; rp->r_rdx = (uintptr_t)uc; if ((rp->r_cs & 0xffff) != UCS_SEL || (rp->r_ss & 0xffff) != UDS_SEL) { /* * Try our best to deliver the signal. */ rp->r_cs = UCS_SEL; rp->r_ss = UDS_SEL; } /* * Don't set lwp_eosys here. sendsig() is called via psig() after * lwp_eosys is handled, so setting it here would affect the next * system call. */ return (1); badstack: no_fault(); if (watched) watch_enable_addr((caddr_t)sp, minstacksz, S_WRITE); if (tuc) kmem_free(tuc, sizeof (*tuc)); #ifdef DEBUG printf("sendsig: bad signal stack cmd=%s, pid=%d, sig=%d\n", PTOU(p)->u_comm, p->p_pid, sig); printf("on fault, sigsp = 0x%p, action = 0x%p, upc = 0x%lx\n", (void *)sp, (void *)hdlr, (uintptr_t)upc); #endif return (0); }
void printsiginfo(siginfo_t *sip, int verbose) { const char *code; if (sip->si_signo == 0) { tprints("{}"); return; } tprints("{si_signo="); printsignal(sip->si_signo); code = xlookup(siginfo_codes, sip->si_code); if (!code) { switch (sip->si_signo) { case SIGTRAP: code = xlookup(sigtrap_codes, sip->si_code); break; case SIGCHLD: code = xlookup(sigchld_codes, sip->si_code); break; case SIGPOLL: code = xlookup(sigpoll_codes, sip->si_code); break; case SIGPROF: code = xlookup(sigprof_codes, sip->si_code); break; case SIGILL: code = xlookup(sigill_codes, sip->si_code); break; #ifdef SIGEMT case SIGEMT: code = xlookup(sigemt_codes, sip->si_code); break; #endif case SIGFPE: code = xlookup(sigfpe_codes, sip->si_code); break; case SIGSEGV: code = xlookup(sigsegv_codes, sip->si_code); break; case SIGBUS: code = xlookup(sigbus_codes, sip->si_code); break; } } if (code) tprintf(", si_code=%s", code); else tprintf(", si_code=%#x", sip->si_code); #ifdef SI_NOINFO if (sip->si_code != SI_NOINFO) #endif { if (sip->si_errno) { if (sip->si_errno < 0 || sip->si_errno >= nerrnos) tprintf(", si_errno=%d", sip->si_errno); else tprintf(", si_errno=%s", errnoent[sip->si_errno]); } #ifdef SI_FROMUSER if (SI_FROMUSER(sip)) { tprintf(", si_pid=%lu, si_uid=%lu", (unsigned long) sip->si_pid, (unsigned long) sip->si_uid); switch (sip->si_code) { #ifdef SI_USER case SI_USER: break; #endif #ifdef SI_TKILL case SI_TKILL: break; #endif #ifdef SI_TIMER case SI_TIMER: tprintf(", si_value=%d", sip->si_int); break; #endif default: if (!sip->si_ptr) break; if (!verbose) tprints(", ..."); else tprintf(", si_value={int=%u, ptr=%#lx}", sip->si_int, (unsigned long) sip->si_ptr); break; } } else #endif /* SI_FROMUSER */ { switch (sip->si_signo) { case SIGCHLD: tprintf(", si_pid=%ld, si_status=", (long) sip->si_pid); if (sip->si_code == CLD_EXITED) tprintf("%d", sip->si_status); else printsignal(sip->si_status); if (!verbose) tprints(", ..."); else tprintf(", si_utime=%llu, si_stime=%llu", (unsigned long long) sip->si_utime, (unsigned long long) sip->si_stime); break; case SIGILL: case SIGFPE: case SIGSEGV: case SIGBUS: tprintf(", si_addr=%#lx", (unsigned long) sip->si_addr); break; case SIGPOLL: switch (sip->si_code) { case POLL_IN: case POLL_OUT: case POLL_MSG: tprintf(", si_band=%ld", (long) sip->si_band); break; } break; default: if (sip->si_pid || sip->si_uid) tprintf(", si_pid=%lu, si_uid=%lu", (unsigned long) sip->si_pid, (unsigned long) sip->si_uid); if (!sip->si_ptr) break; if (!verbose) tprints(", ..."); else { tprintf(", si_value={int=%u, ptr=%#lx}", sip->si_int, (unsigned long) sip->si_ptr); } } } } tprints("}"); }
/*ARGSUSED*/ static int d_siginfo(uintptr_t addr, uint_t flags, int argc, const mdb_arg_t *argv) { static const char *const msname[] = { "USER", "SYSTEM", "TRAP", "TFAULT", "DFAULT", "KFAULT", "USER_LOCK", "SLEEP", "WAIT_CPU", "STOPPED" }; char signame[SIG2STR_MAX]; siginfo_t si; int i; if (argc != 0) return (DCMD_USAGE); if (mdb_vread(&si, sizeof (si), addr) != sizeof (si)) { mdb_warn("failed to read siginfo at %p", addr); return (DCMD_ERR); } if (sig2str(si.si_signo, signame) == -1) (void) strcpy(signame, "unknown"); mdb_printf(" signal %5d (%s)\n", si.si_signo, signame); mdb_printf(" code %5d (", si.si_code); switch (si.si_code) { case SI_NOINFO: mdb_printf("no info"); break; case SI_DTRACE: mdb_printf("from DTrace raise() action"); break; case SI_RCTL: mdb_printf("from rctl action"); break; case SI_USER: mdb_printf("user generated via kill"); break; case SI_LWP: mdb_printf("user generated via lwp_kill"); break; case SI_QUEUE: mdb_printf("user generated via sigqueue"); break; case SI_TIMER: mdb_printf("from timer expiration"); break; case SI_ASYNCIO: mdb_printf("from async i/o completion"); break; case SI_MESGQ: mdb_printf("from message arrival"); break; default: if (SI_FROMUSER(&si)) mdb_printf("from user process"); else mdb_printf("from kernel"); } mdb_printf(")\n errno %5d (%s)\n", si.si_errno, strerror(si.si_errno)); if (si.si_code == SI_USER || si.si_code == SI_QUEUE) { mdb_printf(" signal sent from PID %d (uid %d)\n", si.si_pid, si.si_uid); } if (si.si_code == SI_QUEUE) { mdb_printf(" signal value = 0t%d / %p\n", si.si_value.sival_int, si.si_value.sival_ptr); } switch (si.si_signo) { case SIGCLD: mdb_printf(" signal sent from child PID %d (uid %d)\n", si.si_pid, si.si_uid); mdb_printf(" usr time = 0t%ld ticks, sys time = 0t%ld ticks\n", si.si_utime, si.si_stime); mdb_printf(" wait status = 0x%x\n", si.si_status); break; case SIGSEGV: case SIGBUS: case SIGILL: case SIGTRAP: case SIGFPE: mdb_printf(" fault address = 0x%p\n trapno = %d\n", si.si_addr, si.si_trapno); mdb_printf(" instruction address = 0x%p %lA\n", si.si_pc, si.si_pc); break; case SIGPOLL: case SIGXFSZ: mdb_printf(" fd = %d band = 0x%lx\n", si.si_fd, si.si_band); break; case SIGPROF: mdb_printf(" last fault address = 0x%p fault type = %d\n", si.si_faddr, si.si_fault); mdb_printf(" timestamp = 0t%ld sec 0t%ld nsec\n", si.si_tstamp.tv_sec, si.si_tstamp.tv_nsec); if (si.__data.__prof.__syscall != 0) { mdb_printf(" system call %d (", si.si_syscall); if (si.si_nsysarg > 0) { mdb_printf("%lx", si.si_sysarg[0]); for (i = 1; i < si.si_nsysarg; i++) mdb_printf(", %lx", si.si_sysarg[i]); } mdb_printf(" )\n"); } for (i = 0; i < sizeof (msname) / sizeof (msname[0]); i++) { mdb_printf(" mstate[\"%s\"] = %d\n", msname[i], si.si_mstate[i]); } break; } return (DCMD_OK); }
static void print_si_info(const siginfo_t *sip) { if (sip->si_errno) { tprints(", si_errno="); if ((unsigned) sip->si_errno < nerrnos && errnoent[sip->si_errno]) tprints(errnoent[sip->si_errno]); else tprintf("%d", sip->si_errno); } if (SI_FROMUSER(sip)) { switch (sip->si_code) { case SI_USER: printsigsource(sip); break; case SI_TKILL: printsigsource(sip); break; #if defined HAVE_SIGINFO_T_SI_TIMERID && defined HAVE_SIGINFO_T_SI_OVERRUN case SI_TIMER: tprintf(", si_timerid=%#x, si_overrun=%d", sip->si_timerid, sip->si_overrun); printsigval(sip); break; #endif default: printsigsource(sip); if (sip->si_ptr) printsigval(sip); break; } } else { switch (sip->si_signo) { case SIGCHLD: printsigsource(sip); tprints(", si_status="); if (sip->si_code == CLD_EXITED) tprintf("%d", sip->si_status); else printsignal(sip->si_status); tprintf(", si_utime=%llu, si_stime=%llu", zero_extend_signed_to_ull(sip->si_utime), zero_extend_signed_to_ull(sip->si_stime)); break; case SIGILL: case SIGFPE: case SIGSEGV: case SIGBUS: tprints(", si_addr="); printaddr(ptr_to_kulong(sip->si_addr)); break; case SIGPOLL: switch (sip->si_code) { case POLL_IN: case POLL_OUT: case POLL_MSG: tprintf(", si_band=%ld", (long) sip->si_band); break; } break; #ifdef HAVE_SIGINFO_T_SI_SYSCALL case SIGSYS: { const char *scname = syscall_name((unsigned) sip->si_syscall); tprints(", si_call_addr="); printaddr(ptr_to_kulong(sip->si_call_addr)); tprints(", si_syscall="); if (scname) tprintf("__NR_%s", scname); else tprintf("%u", (unsigned) sip->si_syscall); tprints(", si_arch="); printxval(audit_arch, sip->si_arch, "AUDIT_ARCH_???"); break; } #endif default: if (sip->si_pid || sip->si_uid) printsigsource(sip); if (sip->si_ptr) printsigval(sip); } } }
static void arch_ptraceSaveData(honggfuzz_t * hfuzz, pid_t pid, fuzzer_t * fuzzer) { REG_TYPE pc = 0; /* Local copy since flag is overridden for some crashes */ bool saveUnique = hfuzz->saveUnique; char instr[_HF_INSTR_SZ] = "\x00"; siginfo_t si; bzero(&si, sizeof(si)); if (ptrace(PTRACE_GETSIGINFO, pid, 0, &si) == -1) { PLOG_W("Couldn't get siginfo for pid %d", pid); } arch_getInstrStr(pid, &pc, instr); LOG_D("Pid: %d, signo: %d, errno: %d, code: %d, addr: %p, pc: %" REG_PM ", instr: '%s'", pid, si.si_signo, si.si_errno, si.si_code, si.si_addr, pc, instr); if (!SI_FROMUSER(&si) && pc && si.si_addr < hfuzz->linux.ignoreAddr) { LOG_I("'%s' is interesting (%s), but the si.si_addr is %p (below %p), skipping", fuzzer->fileName, arch_sigs[si.si_signo].descr, si.si_addr, hfuzz->linux.ignoreAddr); return; } /* * Unwind and resolve symbols */ /* *INDENT-OFF* */ funcs_t funcs[_HF_MAX_FUNCS] = { [0 ... (_HF_MAX_FUNCS - 1)].pc = NULL, [0 ... (_HF_MAX_FUNCS - 1)].line = 0, [0 ... (_HF_MAX_FUNCS - 1)].func = {'\0'} , }; /* *INDENT-ON* */ #if !defined(__ANDROID__) size_t funcCnt = arch_unwindStack(pid, funcs); arch_bfdResolveSyms(pid, funcs, funcCnt); #else size_t funcCnt = arch_unwindStack(pid, funcs); #endif /* * If unwinder failed (zero frames), use PC from ptrace GETREGS if not zero. * If PC reg zero, temporarily disable uniqueness flag since callstack * hash will be also zero, thus not safe for unique decisions. */ if (funcCnt == 0) { if (pc) { /* Manually update major frame PC & frames counter */ funcs[0].pc = (void *)(uintptr_t) pc; funcCnt = 1; } else { saveUnique = false; } } /* * Temp local copy of previous backtrace value in case worker hit crashes into multiple * tids for same target master thread. Will be 0 for first crash against target. */ uint64_t oldBacktrace = fuzzer->backtrace; /* * Calculate backtrace callstack hash signature */ arch_hashCallstack(hfuzz, fuzzer, funcs, funcCnt, saveUnique); /* * If fuzzing with sanitizer coverage feedback increase crashes counter used * as metric for dynFile evolution */ if (hfuzz->useSanCov) { fuzzer->sanCovCnts.crashesCnt++; } /* * If unique flag is set and single frame crash, disable uniqueness for this crash * to always save (timestamp will be added to the filename) */ if (saveUnique && (funcCnt == 1)) { saveUnique = false; } /* * If worker crashFileName member is set, it means that a tid has already crashed * from target master thread. */ if (fuzzer->crashFileName[0] != '\0') { LOG_D("Multiple crashes detected from worker against attached tids group"); /* * If stackhashes match, don't re-analyze. This will avoid duplicates * and prevent verifier from running multiple passes. Depth of check is * always 1 (last backtrace saved only per target iteration). */ if (oldBacktrace == fuzzer->backtrace) { return; } } /* Increase global crashes counter */ ATOMIC_POST_INC(hfuzz->crashesCnt); /* * Check if stackhash is blacklisted */ if (hfuzz->blacklist && (fastArray64Search(hfuzz->blacklist, hfuzz->blacklistCnt, fuzzer->backtrace) != -1)) { LOG_I("Blacklisted stack hash '%" PRIx64 "', skipping", fuzzer->backtrace); ATOMIC_POST_INC(hfuzz->blCrashesCnt); return; } /* If non-blacklisted crash detected, zero set two MSB */ ATOMIC_POST_ADD(hfuzz->dynFileIterExpire, _HF_DYNFILE_SUB_MASK); void *sig_addr = si.si_addr; if (hfuzz->linux.disableRandomization == false) { pc = 0UL; sig_addr = NULL; } /* User-induced signals don't set si.si_addr */ if (SI_FROMUSER(&si)) { sig_addr = NULL; } /* If dry run mode, copy file with same name into workspace */ if (hfuzz->origFlipRate == 0.0L && hfuzz->useVerifier) { snprintf(fuzzer->crashFileName, sizeof(fuzzer->crashFileName), "%s/%s", hfuzz->workDir, fuzzer->origFileName); } else if (saveUnique) { snprintf(fuzzer->crashFileName, sizeof(fuzzer->crashFileName), "%s/%s.PC.%" REG_PM ".STACK.%" PRIx64 ".CODE.%d.ADDR.%p.INSTR.%s.%s", hfuzz->workDir, arch_sigs[si.si_signo].descr, pc, fuzzer->backtrace, si.si_code, sig_addr, instr, hfuzz->fileExtn); } else { char localtmstr[PATH_MAX]; util_getLocalTime("%F.%H:%M:%S", localtmstr, sizeof(localtmstr), time(NULL)); snprintf(fuzzer->crashFileName, sizeof(fuzzer->crashFileName), "%s/%s.PC.%" REG_PM ".STACK.%" PRIx64 ".CODE.%d.ADDR.%p.INSTR.%s.%s.%d.%s", hfuzz->workDir, arch_sigs[si.si_signo].descr, pc, fuzzer->backtrace, si.si_code, sig_addr, instr, localtmstr, pid, hfuzz->fileExtn); } if (files_exists(fuzzer->crashFileName)) { LOG_I("It seems that '%s' already exists, skipping", fuzzer->crashFileName); // Clear filename so that verifier can understand we hit a duplicate memset(fuzzer->crashFileName, 0, sizeof(fuzzer->crashFileName)); return; } if (files_writeBufToFile (fuzzer->crashFileName, fuzzer->dynamicFile, fuzzer->dynamicFileSz, O_CREAT | O_EXCL | O_WRONLY | O_CLOEXEC) == false) { LOG_E("Couldn't copy '%s' to '%s'", fuzzer->fileName, fuzzer->crashFileName); return; } LOG_I("Ok, that's interesting, saved '%s' as '%s'", fuzzer->fileName, fuzzer->crashFileName); ATOMIC_POST_INC(hfuzz->uniqueCrashesCnt); /* If unique crash found, reset dynFile counter */ ATOMIC_CLEAR(hfuzz->dynFileIterExpire); arch_ptraceGenerateReport(pid, fuzzer, funcs, funcCnt, &si, instr); }
void printsiginfo(const siginfo_t *sip, int verbose) { const char *code; if (sip->si_signo == 0) { tprints("{}"); return; } tprints("{si_signo="); printsignal(sip->si_signo); code = xlookup(siginfo_codes, sip->si_code); if (!code) { switch (sip->si_signo) { case SIGTRAP: code = xlookup(sigtrap_codes, sip->si_code); break; case SIGCHLD: code = xlookup(sigchld_codes, sip->si_code); break; case SIGPOLL: code = xlookup(sigpoll_codes, sip->si_code); break; case SIGPROF: code = xlookup(sigprof_codes, sip->si_code); break; case SIGILL: code = xlookup(sigill_codes, sip->si_code); break; #ifdef SIGEMT case SIGEMT: code = xlookup(sigemt_codes, sip->si_code); break; #endif case SIGFPE: code = xlookup(sigfpe_codes, sip->si_code); break; case SIGSEGV: code = xlookup(sigsegv_codes, sip->si_code); break; case SIGBUS: code = xlookup(sigbus_codes, sip->si_code); break; case SIGSYS: code = xlookup(sigsys_codes, sip->si_code); break; } } if (code) tprintf(", si_code=%s", code); else tprintf(", si_code=%#x", sip->si_code); #ifdef SI_NOINFO if (sip->si_code != SI_NOINFO) #endif { if (sip->si_errno) { tprints(", si_errno="); if ((unsigned) sip->si_errno < nerrnos && errnoent[sip->si_errno]) tprints(errnoent[sip->si_errno]); else tprintf("%d", sip->si_errno); } #ifdef SI_FROMUSER if (SI_FROMUSER(sip)) { switch (sip->si_code) { #ifdef SI_USER case SI_USER: printsigsource(sip); break; #endif #ifdef SI_TKILL case SI_TKILL: printsigsource(sip); break; #endif #if defined SI_TIMER \ && defined HAVE_SIGINFO_T_SI_TIMERID && defined HAVE_SIGINFO_T_SI_OVERRUN case SI_TIMER: tprintf(", si_timerid=%#x, si_overrun=%d", sip->si_timerid, sip->si_overrun); printsigval(sip, verbose); break; #endif default: printsigsource(sip); if (sip->si_ptr) printsigval(sip, verbose); break; } } else #endif /* SI_FROMUSER */ { switch (sip->si_signo) { case SIGCHLD: printsigsource(sip); tprints(", si_status="); if (sip->si_code == CLD_EXITED) tprintf("%d", sip->si_status); else printsignal(sip->si_status); if (!verbose) tprints(", ..."); else tprintf(", si_utime=%llu, si_stime=%llu", (unsigned long long) sip->si_utime, (unsigned long long) sip->si_stime); break; case SIGILL: case SIGFPE: case SIGSEGV: case SIGBUS: tprintf(", si_addr=%#lx", (unsigned long) sip->si_addr); break; case SIGPOLL: switch (sip->si_code) { case POLL_IN: case POLL_OUT: case POLL_MSG: tprintf(", si_band=%ld", (long) sip->si_band); break; } break; #ifdef HAVE_SIGINFO_T_SI_SYSCALL case SIGSYS: tprintf(", si_call_addr=%#lx, si_syscall=%d, si_arch=%u", (unsigned long) sip->si_call_addr, sip->si_syscall, sip->si_arch); break; #endif default: if (sip->si_pid || sip->si_uid) printsigsource(sip); if (sip->si_ptr) printsigval(sip, verbose); } } } tprints("}"); }
static size_t arch_getProcMem(pid_t pid, uint8_t * buf, size_t len, REG_TYPE pc) { /* * Let's try process_vm_readv first */ const struct iovec local_iov = { .iov_base = buf, .iov_len = len, }; const struct iovec remote_iov = { .iov_base = (void *)(uintptr_t) pc, .iov_len = len, }; if (process_vm_readv(pid, &local_iov, 1, &remote_iov, 1, 0) == (ssize_t) len) { return len; } // Debug if failed since it shouldn't happen very often PLOG_D("process_vm_readv() failed"); /* * Ok, let's do it via ptrace() then. * len must be aligned to the sizeof(long) */ int cnt = len / sizeof(long); size_t memsz = 0; for (int x = 0; x < cnt; x++) { uint8_t *addr = (uint8_t *) (uintptr_t) pc + (int)(x * sizeof(long)); long ret = ptrace(PT_READ_D, pid, addr, NULL); if (errno != 0) { PLOG_W("Couldn't PT_READ_D on pid %d, addr: %p", pid, addr); break; } memsz += sizeof(long); memcpy(&buf[x * sizeof(long)], &ret, sizeof(long)); } return memsz; } void arch_ptraceGetCustomPerf(honggfuzz_t * hfuzz, pid_t pid UNUSED, uint64_t * cnt UNUSED) { if ((hfuzz->dynFileMethod & _HF_DYNFILE_CUSTOM) == 0) { return; } #if defined(__i386__) || defined(__x86_64__) HEADERS_STRUCT regs; struct iovec pt_iov = { .iov_base = ®s, .iov_len = sizeof(regs), }; if (ptrace(PTRACE_GETREGSET, pid, NT_PRSTATUS, &pt_iov) == -1L) { PLOG_D("ptrace(PTRACE_GETREGSET) failed"); // If PTRACE_GETREGSET fails, try PTRACE_GETREGS if available #if PTRACE_GETREGS_AVAILABLE if (ptrace(PTRACE_GETREGS, pid, 0, ®s)) { PLOG_D("ptrace(PTRACE_GETREGS) failed"); LOG_W("ptrace PTRACE_GETREGSET & PTRACE_GETREGS failed to extract target registers"); return; } #else return; #endif } /* * 32-bit */ if (pt_iov.iov_len == sizeof(struct user_regs_struct_32)) { struct user_regs_struct_32 *r32 = (struct user_regs_struct_32 *)®s; *cnt = (uint64_t) r32->gs; return; } /* * 64-bit */ if (pt_iov.iov_len == sizeof(struct user_regs_struct_64)) { struct user_regs_struct_64 *r64 = (struct user_regs_struct_64 *)®s; *cnt = (uint64_t) r64->gs_base; return; } LOG_W("Unknown registers structure size: '%zd'", pt_iov.iov_len); #endif /* defined(__i386__) || defined(__x86_64__) */ } static size_t arch_getPC(pid_t pid, REG_TYPE * pc, REG_TYPE * status_reg) { /* * Some old ARM android kernels are failing with PTRACE_GETREGS to extract * the correct register values if struct size is bigger than expected. As such the * 32/64-bit multiplexing trick is not working for them in case PTRACE_GETREGSET * fails or is not implemented. To cover such cases we explicitly define * the struct size to 32bit version for arm CPU. */ #if defined(__arm__) struct user_regs_struct_32 regs; #else HEADERS_STRUCT regs; #endif struct iovec pt_iov = { .iov_base = ®s, .iov_len = sizeof(regs), }; if (ptrace(PTRACE_GETREGSET, pid, NT_PRSTATUS, &pt_iov) == -1L) { PLOG_D("ptrace(PTRACE_GETREGSET) failed"); // If PTRACE_GETREGSET fails, try PTRACE_GETREGS if available #if PTRACE_GETREGS_AVAILABLE if (ptrace(PTRACE_GETREGS, pid, 0, ®s)) { PLOG_D("ptrace(PTRACE_GETREGS) failed"); LOG_W("ptrace PTRACE_GETREGSET & PTRACE_GETREGS failed to extract target registers"); return 0; } #else return 0; #endif } #if defined(__i386__) || defined(__x86_64__) /* * 32-bit */ if (pt_iov.iov_len == sizeof(struct user_regs_struct_32)) { struct user_regs_struct_32 *r32 = (struct user_regs_struct_32 *)®s; *pc = r32->eip; *status_reg = r32->eflags; return pt_iov.iov_len; } /* * 64-bit */ if (pt_iov.iov_len == sizeof(struct user_regs_struct_64)) { struct user_regs_struct_64 *r64 = (struct user_regs_struct_64 *)®s; *pc = r64->ip; *status_reg = r64->flags; return pt_iov.iov_len; } LOG_W("Unknown registers structure size: '%zd'", pt_iov.iov_len); return 0; #endif /* defined(__i386__) || defined(__x86_64__) */ #if defined(__arm__) || defined(__aarch64__) /* * 32-bit */ if (pt_iov.iov_len == sizeof(struct user_regs_struct_32)) { struct user_regs_struct_32 *r32 = (struct user_regs_struct_32 *)®s; #ifdef __ANDROID__ *pc = r32->ARM_pc; *status_reg = r32->ARM_cpsr; #else *pc = r32->uregs[ARM_pc]; *status_reg = r32->uregs[ARM_cpsr]; #endif return pt_iov.iov_len; } /* * 64-bit */ if (pt_iov.iov_len == sizeof(struct user_regs_struct_64)) { struct user_regs_struct_64 *r64 = (struct user_regs_struct_64 *)®s; *pc = r64->pc; *status_reg = r64->pstate; return pt_iov.iov_len; } LOG_W("Unknown registers structure size: '%zd'", pt_iov.iov_len); return 0; #endif /* defined(__arm__) || defined(__aarch64__) */ #if defined(__powerpc64__) || defined(__powerpc__) /* * 32-bit */ if (pt_iov.iov_len == sizeof(struct user_regs_struct_32)) { struct user_regs_struct_32 *r32 = (struct user_regs_struct_32 *)®s; *pc = r32->nip; return pt_iov.iov_len; } /* * 64-bit */ if (pt_iov.iov_len == sizeof(struct user_regs_struct_64)) { struct user_regs_struct_64 *r64 = (struct user_regs_struct_64 *)®s; *pc = r64->nip; return pt_iov.iov_len; } LOG_W("Unknown registers structure size: '%zd'", pt_iov.iov_len); return 0; #endif /* defined(__powerpc64__) || defined(__powerpc__) */ LOG_D("Unknown/unsupported CPU architecture"); return 0; } static void arch_getInstrStr(pid_t pid, REG_TYPE * pc, char *instr) { /* * We need a value aligned to 8 * which is sizeof(long) on 64bit CPU archs (on most of them, I hope;) */ uint8_t buf[MAX_INSTR_SZ]; size_t memsz; REG_TYPE status_reg = 0; snprintf(instr, _HF_INSTR_SZ, "%s", "[UNKNOWN]"); size_t pcRegSz = arch_getPC(pid, pc, &status_reg); if (!pcRegSz) { LOG_W("Current architecture not supported for disassembly"); return; } if ((memsz = arch_getProcMem(pid, buf, sizeof(buf), *pc)) == 0) { snprintf(instr, _HF_INSTR_SZ, "%s", "[NOT_MMAPED]"); return; } #if !defined(__ANDROID__) arch_bfdDisasm(pid, buf, memsz, instr); #else cs_arch arch; cs_mode mode; #if defined(__arm__) || defined(__aarch64__) arch = (pcRegSz == sizeof(struct user_regs_struct_64)) ? CS_ARCH_ARM64 : CS_ARCH_ARM; if (arch == CS_ARCH_ARM) { mode = (status_reg & 0x20) ? CS_MODE_THUMB : CS_MODE_ARM; } else { mode = CS_MODE_ARM; } #elif defined(__i386__) || defined(__x86_64__) arch = CS_ARCH_X86; mode = (pcRegSz == sizeof(struct user_regs_struct_64)) ? CS_MODE_64 : CS_MODE_32; #else LOG_E("Unknown/unsupported Android CPU architecture"); #endif csh handle; cs_err err = cs_open(arch, mode, &handle); if (err != CS_ERR_OK) { LOG_W("Capstone initialization failed: '%s'", cs_strerror(err)); return; } cs_insn *insn; size_t count = cs_disasm(handle, buf, sizeof(buf), *pc, 0, &insn); if (count < 1) { LOG_W("Couldn't disassemble the assembler instructions' stream: '%s'", cs_strerror(cs_errno(handle))); cs_close(&handle); return; } snprintf(instr, _HF_INSTR_SZ, "%s %s", insn[0].mnemonic, insn[0].op_str); cs_free(insn, count); cs_close(&handle); #endif for (int x = 0; instr[x] && x < _HF_INSTR_SZ; x++) { if (instr[x] == '/' || instr[x] == '\\' || isspace(instr[x]) || !isprint(instr[x])) { instr[x] = '_'; } } return; } static void arch_hashCallstack(fuzzer_t * fuzzer, funcs_t * funcs, size_t funcCnt, bool enableMasking) { uint64_t hash = 0; for (size_t i = 0; i < funcCnt && i < NMAJORFRAMES; i++) { /* * Convert PC to char array to be compatible with hash function */ char pcStr[REGSIZEINCHAR] = { 0 }; snprintf(pcStr, REGSIZEINCHAR, REG_PD REG_PM, (REG_TYPE) (long)funcs[i].pc); /* * Hash the last three nibbles */ hash ^= util_hash(&pcStr[strlen(pcStr) - 3], 3); } /* * If only one frame, hash is not safe to be used for uniqueness. We mask it * here with a constant prefix, so analyzers can pick it up and create filenames * accordingly. 'enableMasking' is controlling masking for cases where it should * not be enabled (e.g. fuzzer worker is from verifier). */ if (enableMasking && funcCnt == 1) { hash |= _HF_SINGLE_FRAME_MASK; } fuzzer->backtrace = hash; } static void arch_ptraceGenerateReport(pid_t pid, fuzzer_t * fuzzer, funcs_t * funcs, size_t funcCnt, siginfo_t * si, const char *instr) { fuzzer->report[0] = '\0'; util_ssnprintf(fuzzer->report, sizeof(fuzzer->report), "ORIG_FNAME: %s\n", fuzzer->origFileName); util_ssnprintf(fuzzer->report, sizeof(fuzzer->report), "FUZZ_FNAME: %s\n", fuzzer->crashFileName); util_ssnprintf(fuzzer->report, sizeof(fuzzer->report), "PID: %d\n", pid); util_ssnprintf(fuzzer->report, sizeof(fuzzer->report), "SIGNAL: %s (%d)\n", arch_sigs[si->si_signo].descr, si->si_signo); util_ssnprintf(fuzzer->report, sizeof(fuzzer->report), "FAULT ADDRESS: %p\n", SI_FROMUSER(si) ? NULL : si->si_addr); util_ssnprintf(fuzzer->report, sizeof(fuzzer->report), "INSTRUCTION: %s\n", instr); util_ssnprintf(fuzzer->report, sizeof(fuzzer->report), "STACK HASH: %016llx\n", fuzzer->backtrace); util_ssnprintf(fuzzer->report, sizeof(fuzzer->report), "STACK:\n"); for (size_t i = 0; i < funcCnt; i++) { #ifdef __HF_USE_CAPSTONE__ util_ssnprintf(fuzzer->report, sizeof(fuzzer->report), " <" REG_PD REG_PM "> ", (REG_TYPE) (long)funcs[i].pc, funcs[i].func, funcs[i].line); if (funcs[i].func[0] != '\0') util_ssnprintf(fuzzer->report, sizeof(fuzzer->report), "[%s + 0x%x]\n", funcs[i].func, funcs[i].line); else util_ssnprintf(fuzzer->report, sizeof(fuzzer->report), "[]\n"); #else util_ssnprintf(fuzzer->report, sizeof(fuzzer->report), " <" REG_PD REG_PM "> [%s():%u]\n", (REG_TYPE) (long)funcs[i].pc, funcs[i].func, funcs[i].line); #endif } // libunwind is not working for 32bit targets in 64bit systems #if defined(__aarch64__) if (funcCnt == 0) { util_ssnprintf(fuzzer->report, sizeof(fuzzer->report), " !ERROR: If 32bit fuzz target" " in aarch64 system, try ARM 32bit build\n"); } #endif return; }
/*ARGSUSED*/ static void flt_handler(int sig, siginfo_t *sip, ucontext_t *ucp, void *data) { static const struct rlimit rl = { (rlim_t)RLIM_INFINITY, (rlim_t)RLIM_INFINITY }; const mdb_idcmd_t *idcp = NULL; if (mdb.m_frame != NULL && mdb.m_frame->f_cp != NULL) idcp = mdb.m_frame->f_cp->c_dcmd; if (sip != NULL) bcopy(sip, &_mdb_abort_info, sizeof (_mdb_abort_info)); if (ucp != NULL) bcopy(ucp, &_mdb_abort_ctx, sizeof (_mdb_abort_ctx)); _mdb_abort_info.si_signo = sig; (void) mdb_signal_sethandler(sig, SIG_DFL, NULL); /* * If there is no current dcmd, or the current dcmd comes from a * builtin module, we don't allow resume and always core dump. */ if (idcp == NULL || idcp->idc_modp == NULL || idcp->idc_modp == &mdb.m_rmod || idcp->idc_modp->mod_hdl == NULL) goto dump; if (mdb.m_term != NULL) { struct frame *fr = (struct frame *) (ucp->uc_mcontext.gregs[STACK_REGISTER] + STACK_BIAS); char signame[SIG2STR_MAX]; int i = 1; char c; if (sig2str(sig, signame) == -1) { mdb_iob_printf(mdb.m_err, "\n*** %s: received signal %d at:\n", mdb.m_pname, sig); } else { mdb_iob_printf(mdb.m_err, "\n*** %s: received signal %s at:\n", mdb.m_pname, signame); } if (ucp->uc_mcontext.gregs[REG_PC] != 0) print_frame(ucp->uc_mcontext.gregs[REG_PC], i++); while (fr != NULL && valid_frame(fr) && fr->fr_savpc != 0) { print_frame(fr->fr_savpc, i++); fr = (struct frame *) ((uintptr_t)fr->fr_savfp + STACK_BIAS); } query: mdb_iob_printf(mdb.m_err, "\n%s: (c)ore dump, (q)uit, " "(r)ecover, or (s)top for debugger [cqrs]? ", mdb.m_pname); mdb_iob_flush(mdb.m_err); for (;;) { if (IOP_READ(mdb.m_term, &c, sizeof (c)) != sizeof (c)) goto dump; switch (c) { case 'c': case 'C': (void) setrlimit(RLIMIT_CORE, &rl); mdb_iob_printf(mdb.m_err, "\n%s: attempting " "to dump core ...\n", mdb.m_pname); goto dump; case 'q': case 'Q': mdb_iob_discard(mdb.m_out); mdb_iob_nl(mdb.m_err); (void) mdb_signal_unblockall(); terminate(1); /*NOTREACHED*/ case 'r': case 'R': mdb_iob_printf(mdb.m_err, "\n%s: unloading " "module '%s' ...\n", mdb.m_pname, idcp->idc_modp->mod_name); (void) mdb_module_unload( idcp->idc_modp->mod_name, 0); (void) mdb_signal_sethandler(sig, flt_handler, NULL); _mdb_abort_rcount++; mdb.m_intr = 0; mdb.m_pend = 0; (void) mdb_signal_unblockall(); longjmp(mdb.m_frame->f_pcb, MDB_ERR_ABORT); /*NOTREACHED*/ case 's': case 'S': mdb_iob_printf(mdb.m_err, "\n%s: " "attempting to stop pid %d ...\n", mdb.m_pname, (int)getpid()); /* * Stop ourself; if this fails or we are * subsequently continued, ask again. */ (void) mdb_signal_raise(SIGSTOP); (void) mdb_signal_unblockall(); goto query; } } } dump: if (SI_FROMUSER(sip)) { (void) mdb_signal_block(sig); (void) mdb_signal_raise(sig); } (void) sigfillset(&ucp->uc_sigmask); (void) sigdelset(&ucp->uc_sigmask, sig); if (_mdb_abort_str == NULL) _mdb_abort_str = "fatal signal received"; ucp->uc_flags |= UC_SIGMASK; (void) setcontext(ucp); }