Ejemplo n.º 1
0
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;
}
Ejemplo n.º 2
0
/*
 * Special book keeping for cases where crashes are detected based on exitcode and not
 * a raised signal. Such case is the ASan fuzzing for Android. Crash file name maintains
 * the same format for compatibility with post campaign tools.
 */
static void arch_ptraceExitSaveData(honggfuzz_t * hfuzz, pid_t pid, fuzzer_t * fuzzer, int exitCode)
{
    REG_TYPE pc = 0;
    void *crashAddr = 0;
    char *op = "UNKNOWN";
    pid_t targetPid = (hfuzz->linux.pid > 0) ? hfuzz->linux.pid : fuzzer->pid;

    /* Save only the first hit for each worker */
    if (fuzzer->crashFileName[0] != '\0') {
        return;
    }

    /* Increase global crashes counter */
    ATOMIC_POST_INC(hfuzz->crashesCnt);
    ATOMIC_POST_AND(hfuzz->dynFileIterExpire, _HF_DYNFILE_SUB_MASK);

    /*
     * If fuzzing with sanitizer coverage feedback increase crashes counter used
     * as metric for dynFile evolution
     */
    if (hfuzz->useSanCov) {
        fuzzer->sanCovCnts.crashesCnt++;
    }

    /* Get sanitizer string tag based on exitcode */
    const char *sanStr = arch_sanCodeToStr(exitCode);

    /* If sanitizer produces reports with stack traces (e.g. ASan), they're parsed manually */
    int funcCnt = 0;

    /*  *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 ASan crash, parse report */
    if (exitCode == HF_ASAN_EXIT_CODE) {

        /* ASan is saving reports against parent PID */
        if (targetPid != pid) {
            return;
        }
        funcCnt = arch_parseAsanReport(hfuzz, pid, funcs, &crashAddr, &op);

        /*
         * -1 error indicates a file not found for report. This is expected to happen often since
         * ASan report is generated once for crashing TID. Ptrace arch is not guaranteed to parse
         * that TID first. Not setting the 'crashFileName' variable will ensure that this branch
         * is executed again for all TIDs until the matching report is found
         */
        if (funcCnt == -1) {
            return;
        }

        /* Since crash address is available, apply ignoreAddr filters */
        if (crashAddr < hfuzz->linux.ignoreAddr) {
            LOG_I("'%s' is interesting, but the crash addr is %p (below %p), skipping",
                  fuzzer->fileName, crashAddr, hfuzz->linux.ignoreAddr);
            return;
        }

        /* If frames successfully recovered, calculate stack hash & populate crash PC */
        arch_hashCallstack(hfuzz, fuzzer, funcs, funcCnt, false);
        pc = (uintptr_t) funcs[0].pc;

        /* Since stack hash is available apply blacklist filters */
        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 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 {
        /* Keep the crashes file name format identical */
        if (fuzzer->backtrace != 0ULL && hfuzz->saveUnique) {
            snprintf(fuzzer->crashFileName, sizeof(fuzzer->crashFileName),
                     "%s/%s.PC.%" REG_PM ".STACK.%" PRIx64 ".CODE.%s.ADDR.%p.INSTR.%s.%s",
                     hfuzz->workDir, sanStr, pc, fuzzer->backtrace,
                     op, crashAddr, "[UNKNOWN]", hfuzz->fileExtn);
        } else {
            /* If no stack hash available, all crashes treated as unique */
            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.%s.ADDR.%p.INSTR.%s.%s.%s",
                     hfuzz->workDir, sanStr, pc, fuzzer->backtrace,
                     op, crashAddr, "[UNKNOWN]", localtmstr, hfuzz->fileExtn);
        }
    }

    bool dstFileExists = false;
    if (files_copyFile(fuzzer->fileName, fuzzer->crashFileName, &dstFileExists)) {
        LOG_I("Ok, that's interesting, saved '%s' as '%s'", fuzzer->fileName,
              fuzzer->crashFileName);

        /* Increase unique crashes counters */
        ATOMIC_POST_INC(hfuzz->uniqueCrashesCnt);
        ATOMIC_CLEAR(hfuzz->dynFileIterExpire);
    } else {
        if (dstFileExists) {
            LOG_I("It seems that '%s' already exists, skipping", fuzzer->crashFileName);

            /* Clear stack hash so that verifier can understand we hit a duplicate */
            fuzzer->backtrace = 0ULL;
        } else {
            LOG_E("Couldn't copy '%s' to '%s'", fuzzer->fileName, fuzzer->crashFileName);

            /* In case of write error, clear crashFileName to so that other monitored TIDs can retry */
            memset(fuzzer->crashFileName, 0, sizeof(fuzzer->crashFileName));
        }

        /* Don't bother generating reports for duplicate or non-saved crashes */
        return;
    }

    /* Generate report */
    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), "EXIT CODE: %d (%s)\n", exitCode,
                   sanStr);
    util_ssnprintf(fuzzer->report, sizeof(fuzzer->report), "OPERATION: %s\n", op);
    util_ssnprintf(fuzzer->report, sizeof(fuzzer->report), "FAULT ADDRESS: %p\n", crashAddr);
    if (funcCnt > 0) {
        util_ssnprintf(fuzzer->report, sizeof(fuzzer->report), "STACK HASH: %016llx\n",
                       fuzzer->backtrace);
        util_ssnprintf(fuzzer->report, sizeof(fuzzer->report), "STACK:\n");
        for (int i = 0; i < funcCnt; i++) {
            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");
            }
        }
    }
}
Ejemplo n.º 3
0
void log_msg(log_level_t dl, bool perr, const char *file, const char *func, int line,
             const char *fmt, ...)
{
    char msg[8192] = { "\0" };

    if (dl > log_minLevel) {
        if (dl == l_FATAL) {
            exit(EXIT_FAILURE);
        }
        return;
    }

    char strerr[512];
    if (perr) {
        snprintf(strerr, sizeof(strerr), "%s", strerror(errno));
    }

    struct tm tm;
    struct timeval tv;

    gettimeofday(&tv, NULL);
    localtime_r((const time_t *)&tv.tv_sec, &tm);

    if (log_isStdioTTY == true) {
        util_ssnprintf(msg, sizeof(msg), "%s", logLevels[dl].prefix);
    }
#if defined(_HF_ARCH_LINUX)
#include <unistd.h>
#include <sys/syscall.h>
    pid_t pid = (pid_t) syscall(__NR_gettid);
#else                           /* defined(_HF_ARCH_LINUX) */
    pid_t pid = getpid();
#endif                          /* defined(_HF_ARCH_LINUX) */

    if (log_minLevel != l_INFO || !log_isStdioTTY) {
        util_ssnprintf(msg, sizeof(msg), "%s [%d] %d/%02d/%02d %02d:%02d:%02d (%s:%s %d) ",
                       logLevels[dl].descr, pid, tm.tm_year + 1900,
                       tm.tm_mon + 1, tm.tm_mday, tm.tm_hour, tm.tm_min, tm.tm_sec, file, func,
                       line);
    } else {
        util_ssnprintf(msg, sizeof(msg), "%s ", logLevels[dl].descr);
    }

    va_list args;
    va_start(args, fmt);
    util_vssnprintf(msg, sizeof(msg), fmt, args);
    va_end(args);

    if (perr) {
        util_ssnprintf(msg, sizeof(msg), ": %s", strerr);
    }

    if (log_isStdioTTY == true) {
        util_ssnprintf(msg, sizeof(msg), "\033[0m");
    }

    util_ssnprintf(msg, sizeof(msg), "\n");

    log_mutexLock();
    if (write(STDOUT_FILENO, msg, strlen(msg)) == -1) {
    }
    log_mutexUnLock();

    if (dl == l_FATAL) {
        exit(EXIT_FAILURE);
    }
}
Ejemplo n.º 4
0
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
    LOGMSG_P(l_DEBUG, "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) {
            LOGMSG_P(l_WARN, "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;
}

// Non i386 / x86_64 ISA fail build due to unused pid argument
#pragma GCC diagnostic push
#pragma GCC diagnostic ignored "-Wunused-parameter"
uint64_t arch_ptraceGetCustomPerf(honggfuzz_t * hfuzz, pid_t pid)
{
    if ((hfuzz->dynFileMethod & _HF_DYNFILE_CUSTOM) == 0) {
        return 0ULL;
    }
#if defined(__i386__) || defined(__x86_64__)
    HEADERS_STRUCT regs;
    struct iovec pt_iov = {
        .iov_base = &regs,
        .iov_len = sizeof(regs),
    };

    if (ptrace(PTRACE_GETREGSET, pid, NT_PRSTATUS, &pt_iov) == -1L) {
        LOGMSG_P(l_DEBUG, "ptrace(PTRACE_GETREGSET) failed");

        // If PTRACE_GETREGSET fails, try PTRACE_GETREGS if available
#if PTRACE_GETREGS_AVAILABLE
        if (ptrace(PTRACE_GETREGS, pid, 0, &regs)) {
            LOGMSG_P(l_DEBUG, "ptrace(PTRACE_GETREGS) failed");
            LOGMSG(l_WARN, "ptrace PTRACE_GETREGSET & PTRACE_GETREGS failed to"
                   " extract target registers");
            return 0ULL;
        }
#else
        return 0ULL;
#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 *)&regs;
        return (uint64_t) r32->gs;
    }

    /*
     * 64-bit
     */
    if (pt_iov.iov_len == sizeof(struct user_regs_struct_64)) {
        struct user_regs_struct_64 *r64 = (struct user_regs_struct_64 *)&regs;
        return (uint64_t) r64->gs_base;
    }

    LOGMSG(l_WARN, "Unknown registers structure size: '%d'", pt_iov.iov_len);
#endif                          /* defined(__i386__) || defined(__x86_64__) */

    return 0ULL;
}

#pragma GCC diagnostic pop      /* ignored "-Wunused-parameter" */

static size_t arch_getPC(pid_t pid, REG_TYPE * pc, REG_TYPE * status_reg)
{
    HEADERS_STRUCT regs;
    struct iovec pt_iov = {
        .iov_base = &regs,
        .iov_len = sizeof(regs),
    };

    if (ptrace(PTRACE_GETREGSET, pid, NT_PRSTATUS, &pt_iov) == -1L) {
        LOGMSG_P(l_DEBUG, "ptrace(PTRACE_GETREGSET) failed");

        // If PTRACE_GETREGSET fails, try PTRACE_GETREGS if available
#if PTRACE_GETREGS_AVAILABLE
        if (ptrace(PTRACE_GETREGS, pid, 0, &regs)) {
            LOGMSG_P(l_DEBUG, "ptrace(PTRACE_GETREGS) failed");
            LOGMSG(l_WARN, "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 *)&regs;
        *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 *)&regs;
        *pc = r64->ip;
        *status_reg = r64->flags;
        return pt_iov.iov_len;
    }
    LOGMSG(l_WARN, "Unknown registers structure size: '%d'", 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 *)&regs;
#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 *)&regs;
        *pc = r64->pc;
        *status_reg = r64->pstate;
        return pt_iov.iov_len;
    }
    LOGMSG(l_WARN, "Unknown registers structure size: '%d'", 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 *)&regs;
        *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 *)&regs;
        *pc = r64->nip;
        return pt_iov.iov_len;
    }

    LOGMSG(l_WARN, "Unknown registers structure size: '%d'", pt_iov.iov_len);
    return 0;
#endif                          /* defined(__powerpc64__) || defined(__powerpc__) */

    LOGMSG(l_DEBUG, "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) {
        LOGMSG(l_WARN, "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
    LOGMSG(l_ERROR, "Unknown/unsupported Android CPU architecture");
#endif

    csh handle;
    cs_err err = cs_open(arch, mode, &handle);
    if (err != CS_ERR_OK) {
        LOGMSG(l_WARN, "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) {
        LOGMSG(l_WARN, "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_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->fileName);
    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->si_addr);
    util_ssnprintf(fuzzer->report, sizeof(fuzzer->report), "INSTRUCTION: %s\n", instr);
    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;
}
Ejemplo n.º 5
0
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 = &regs,
        .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, &regs)) {
            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 *)&regs;
        *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 *)&regs;
        *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 = &regs,
        .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, &regs)) {
            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 *)&regs;
        *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 *)&regs;
        *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 *)&regs;
#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 *)&regs;
        *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 *)&regs;
        *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 *)&regs;
        *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;
}