Ejemplo n.º 1
0
static bool arch_listThreads(int tasks[], size_t thrSz, int pid)
{
    char path[512];
    snprintf(path, sizeof(path), "/proc/%d/task", pid);

    /* An optimization, the number of threads is st.st_nlink - 2 (. and ..) */
    struct stat st;
    if (stat(path, &st) != -1) {
        if (st.st_nlink == 3) {
            tasks[0] = pid;
            tasks[1] = 0;
            return true;
        }
    }

    size_t count = 0;
    DIR *dir = opendir(path);
    if (!dir) {
        PLOG_E("Couldn't open dir '%s'", path);
        return false;
    }
    defer {
        closedir(dir);
    };

    for (;;) {
        errno = 0;
        struct dirent *res = readdir(dir);
        if (res == NULL && errno != 0) {
            PLOG_E("Couldn't read contents of '%s'", path);
            return false;
        }

        if (res == NULL) {
            break;
        }

        pid_t pid = (pid_t) strtol(res->d_name, (char **)NULL, 10);
        if (pid == 0) {
            LOG_D("The following dir entry couldn't be converted to pid_t '%s'", res->d_name);
            continue;
        }

        tasks[count++] = pid;
        LOG_D("Added pid '%d' from '%s/%s'", pid, path, res->d_name);

        if (count >= thrSz) {
            break;
        }
    }
    PLOG_D("Total number of threads in pid '%d': '%zd'", pid, count);
    tasks[count + 1] = 0;
    if (count < 1) {
        return false;
    }
    return true;
}
Ejemplo n.º 2
0
static bool arch_listThreads(int tasks[], size_t thrSz, int pid)
{
    size_t count = 0;
    char path[512];
    snprintf(path, sizeof(path), "/proc/%d/task", pid);
    DIR *dir = opendir(path);
    if (!dir) {
        PLOG_E("Couldn't open dir '%s'", path);
        return false;
    }

    for (;;) {
        struct dirent de, *res;
        if (readdir_r(dir, &de, &res) > 0) {
            PLOG_E("Couldn't read contents of '%s'", path);
            closedir(dir);
            return false;
        }

        if (res == NULL) {
            break;
        }

        pid_t pid = (pid_t) strtol(res->d_name, (char **)NULL, 10);
        if (pid == 0) {
            LOG_D("The following dir entry couldn't be converted to pid_t '%s'", res->d_name);
            continue;
        }

        tasks[count++] = pid;
        LOG_D("Added pid '%d' from '%s/%s'", pid, path, res->d_name);

        if (count >= thrSz) {
            break;
        }
    }
    closedir(dir);
    PLOG_D("Total number of threads in pid '%d': '%zd'", pid, count);
    tasks[count + 1] = 0;
    if (count < 1) {
        return false;
    }
    return true;
}
Ejemplo n.º 3
0
/*
 * dstExists argument can be used by caller for cases where existing destination
 * file requires special handling (e.g. save unique crashes)
 */
bool files_copyFile(const char *source, const char *destination, bool * dstExists)
{
    if (dstExists)
        *dstExists = false;
    if (link(source, destination) == 0) {
        return true;
    } else {
        if (errno == EEXIST) {
            // Should kick-in before MAC, so avoid the hassle
            if (dstExists)
                *dstExists = true;
            return false;
        } else {
            PLOG_D("Couldn't link '%s' as '%s'", source, destination);
            /*
             * Don't fail yet as we might have a running env which doesn't allow
             * hardlinks (e.g. SELinux)
             */
        }
    }

    // Now try with a verbose POSIX alternative
    int inFD, outFD, dstOpenFlags;
    mode_t dstFilePerms;

    // O_EXCL is important for saving unique crashes
    dstOpenFlags = O_CREAT | O_WRONLY | O_EXCL;
    dstFilePerms = S_IRUSR | S_IWUSR | S_IRGRP | S_IWGRP | S_IROTH | S_IWOTH;

    inFD = open(source, O_RDONLY);
    if (inFD == -1) {
        PLOG_D("Couldn't open '%s' source", source);
        return false;
    }
    defer {
        close(inFD);
    };

    struct stat inSt;
    if (fstat(inFD, &inSt) == -1) {
        PLOG_W("Couldn't fstat(fd='%d' fileName='%s')", inFD, source);
        return false;
    }

    outFD = open(destination, dstOpenFlags, dstFilePerms);
    if (outFD == -1) {
        if (errno == EEXIST) {
            if (dstExists)
                *dstExists = true;
        }
        PLOG_D("Couldn't open '%s' destination", destination);
        return false;
    }
    defer {
        close(outFD);
    };

    uint8_t *inFileBuf = malloc(inSt.st_size);
    if (!inFileBuf) {
        PLOG_W("malloc(%zu) failed", (size_t) inSt.st_size);
        return false;
    }
    defer {
        free(inFileBuf);
    };

    ssize_t readSz = files_readFromFd(inFD, inFileBuf, (size_t) inSt.st_size);
    if (readSz < 0) {
        PLOG_W("Couldn't read '%s' to a buf", source);
        return false;
    }

    if (files_writeToFd(outFD, inFileBuf, readSz) == false) {
        PLOG_W("Couldn't write '%zu' bytes to file '%s' (fd='%d')", (size_t) readSz,
               destination, outFD);
        unlink(destination);
        return false;
    }

    return true;
}
Ejemplo n.º 4
0
static int arch_parseAsanReport(honggfuzz_t * hfuzz, pid_t pid, funcs_t * funcs, void **crashAddr,
                                char **op)
{
    char crashReport[PATH_MAX] = { 0 };
    const char *const crashReportCpy = crashReport;
    snprintf(crashReport, sizeof(crashReport), "%s/%s.%d", hfuzz->workDir, kLOGPREFIX, pid);

    FILE *fReport = fopen(crashReport, "rb");
    if (fReport == NULL) {
        PLOG_D("Couldn't open '%s' - R/O mode", crashReport);
        return -1;
    }
    defer {
        fclose(fReport);
    };
    defer {
        unlink(crashReportCpy);
    };

    char header[35] = { 0 };
    snprintf(header, sizeof(header), "==%d==ERROR: AddressSanitizer:", pid);
    size_t headerSz = strlen(header);
    bool headerFound = false;

    uint8_t frameIdx = 0;
    char framePrefix[5] = { 0 };
    snprintf(framePrefix, sizeof(framePrefix), "#%" PRIu8, frameIdx);

    char *lineptr = NULL, *cAddr = NULL;
    size_t n = 0;
    defer {
        free(lineptr);
    };
    for (;;) {
        if (getline(&lineptr, &n, fReport) == -1) {
            break;
        }

        /* First step is to identify header */
        if (headerFound == false) {
            if ((strlen(lineptr) > headerSz) && (strncmp(header, lineptr, headerSz) == 0)) {
                headerFound = true;

                /* Parse crash address */
                cAddr = strstr(lineptr, "address 0x");
                if (cAddr) {
                    cAddr = cAddr + strlen("address ");
                    char *endOff = strchr(cAddr, ' ');
                    cAddr[endOff - cAddr] = '\0';
                    *crashAddr = (void *)((size_t) strtoull(cAddr, NULL, 16));
                } else {
                    *crashAddr = 0x0;
                }
            }
            continue;
        } else {
            char *pLineLC = lineptr;
            /* Trim leading spaces */
            while (*pLineLC != '\0' && isspace(*pLineLC)) {
                ++pLineLC;
            }

            /* Separator for crash thread stack trace is an empty line (after trmming \n */
            if ((*pLineLC == '\0') && (frameIdx != 0)) {
                break;
            }

            /* Basic length checks */
            if (strlen(pLineLC) < 10) {
                continue;
            }

            /* If available parse the type of error (READ/WRITE) */
            if (cAddr && strstr(pLineLC, cAddr)) {
                if (strncmp(pLineLC, "READ", 4) == 0) {
                    *op = "READ";
                } else if (strncmp(pLineLC, "WRITE", 5) == 0) {
                    *op = "WRITE";
                }
                cAddr = NULL;
            }

            /* Check for crash thread frames */
            if (strncmp(pLineLC, framePrefix, strlen(framePrefix)) == 0) {
                /* Abort if max depth */
                if (frameIdx >= _HF_MAX_FUNCS) {
                    break;
                }

                /*
                 * Frames have following format:
                 #0 0xaa860177  (/system/lib/libc.so+0x196177)
                 */
                char *savePtr = NULL;
                strtok_r(pLineLC, " ", &savePtr);
                funcs[frameIdx].pc =
                    (void *)((size_t) strtoull(strtok_r(NULL, " ", &savePtr), NULL, 16));

                /* DSO & code offset parsing */
                char *targetStr = strtok_r(NULL, " ", &savePtr);
                char *startOff = strchr(targetStr, '(') + 1;
                char *plusOff = strchr(targetStr, '+');
                char *endOff = strrchr(targetStr, ')');
                targetStr[endOff - startOff] = '\0';
                if ((startOff == NULL) || (endOff == NULL) || (plusOff == NULL)) {
                    LOG_D("Invalid ASan report entry (%s)", lineptr);
                } else {
                    size_t dsoSz = MIN(sizeof(funcs[frameIdx].func), (size_t) (plusOff - startOff));
                    memcpy(funcs[frameIdx].func, startOff, dsoSz);
                    char *codeOff = targetStr + (plusOff - startOff) + 1;
                    funcs[frameIdx].line = strtoull(codeOff, NULL, 16);
                }

                frameIdx++;
                snprintf(framePrefix, sizeof(framePrefix), "#%" PRIu8, frameIdx);
            }
        }
    }

    return frameIdx;
}
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(PTRACE_PEEKDATA, 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, uint64_t * cnt UNUSED)
{
    if ((hfuzz->dynFileMethod & _HF_DYNFILE_CUSTOM) == 0) {
        return;
    }

    if (hfuzz->persistent) {
        ptrace(PTRACE_INTERRUPT, pid, 0, 0);
        arch_ptraceWaitForPidStop(pid);
    }

    defer {
        if (hfuzz->persistent) {
            ptrace(PTRACE_CONT, pid, 0, 0);
        }
    };

#if defined(__x86_64__)
    struct user_regs_struct_64 regs;
    if (ptrace(PTRACE_GETREGS, pid, 0, &regs) != -1) {
        *cnt = regs.gs_base;
        return;
    }
#endif                          /*       defined(__x86_64__) */
    *cnt = 0ULL;
}

void arch_ptraceSetCustomPerf(honggfuzz_t * hfuzz, pid_t pid, uint64_t cnt UNUSED)
{
    if ((hfuzz->dynFileMethod & _HF_DYNFILE_CUSTOM) == 0) {
        return;
    }

    if (hfuzz->persistent) {
        ptrace(PTRACE_INTERRUPT, pid, 0, 0);
        arch_ptraceWaitForPidStop(pid);
    }

    defer {
        if (hfuzz->persistent) {
            ptrace(PTRACE_CONT, pid, 0, 0);
        }
    };

#if defined(__x86_64__)
    struct user_regs_struct_64 regs;
    if (ptrace(PTRACE_GETREGS, pid, 0, &regs) == -1) {
        return;
    }
    regs.gs_base = cnt;
    if (ptrace(PTRACE_SETREGS, pid, 0, &regs) == -1) {
        return;
    }
#endif                          /*            defined(__x86_64__) */
}

static size_t arch_getPC(pid_t pid, REG_TYPE * pc, REG_TYPE * status_reg UNUSED)
{
    /*
     * 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                          /* defined(__ANDROID__) */

    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(honggfuzz_t * hfuzz, fuzzer_t * fuzzer, funcs_t * funcs,
                               size_t funcCnt, bool enableMasking)
{
    uint64_t hash = 0;
    for (size_t i = 0; i < funcCnt && i < hfuzz->linux.numMajorFrames; 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;
}
Ejemplo n.º 6
0
bool arch_launchChild(honggfuzz_t * hfuzz, char *fileName)
{
#define ARGS_MAX 512
    char *args[ARGS_MAX + 2];
    char argData[PATH_MAX] = { 0 };
    int x;

    for (x = 0; x < ARGS_MAX && hfuzz->cmdline[x]; x++) {
        if (!hfuzz->fuzzStdin && strcmp(hfuzz->cmdline[x], _HF_FILE_PLACEHOLDER) == 0) {
            args[x] = fileName;
        } else if (!hfuzz->fuzzStdin && strstr(hfuzz->cmdline[x], _HF_FILE_PLACEHOLDER)) {
            const char *off = strstr(hfuzz->cmdline[x], _HF_FILE_PLACEHOLDER);
            snprintf(argData, PATH_MAX, "%.*s%s", (int)(off - hfuzz->cmdline[x]), hfuzz->cmdline[x],
                     fileName);
            args[x] = argData;
        } else {
            args[x] = hfuzz->cmdline[x];
        }
    }

    args[x++] = NULL;

    LOG_D("Launching '%s' on file '%s'", args[0], fileName);

    /*
     * Set timeout (prof), real timeout (2*prof), and rlimit_cpu (2*prof)
     */
    if (hfuzz->tmOut) {
        struct itimerval it;

        /*
         * The hfuzz->tmOut is real CPU usage time...
         */
        it.it_value.tv_sec = hfuzz->tmOut;
        it.it_value.tv_usec = 0;
        it.it_interval.tv_sec = 0;
        it.it_interval.tv_usec = 0;
        if (setitimer(ITIMER_PROF, &it, NULL) == -1) {
            PLOG_E("Couldn't set the ITIMER_PROF timer");
            return false;
        }

        /*
         * ...so, if a process sleeps, this one should
         * trigger a signal...
         */
        it.it_value.tv_sec = hfuzz->tmOut * 2UL;
        it.it_value.tv_usec = 0;
        it.it_interval.tv_sec = 0;
        it.it_interval.tv_usec = 0;
        if (setitimer(ITIMER_REAL, &it, NULL) == -1) {
            PLOG_E("Couldn't set the ITIMER_REAL timer");
            return false;
        }

        /*
         * ..if a process sleeps and catches SIGPROF/SIGALRM
         * rlimits won't help either
         */
        struct rlimit rl;

        rl.rlim_cur = hfuzz->tmOut * 2;
        rl.rlim_max = hfuzz->tmOut * 2;
        if (setrlimit(RLIMIT_CPU, &rl) == -1) {
            PLOG_E("Couldn't enforce the RLIMIT_CPU resource limit");
            return false;
        }
    }

    /*
     * The address space limit. If big enough - roughly the size of RAM used
     */
    if (hfuzz->asLimit) {
        struct rlimit rl;

        rl.rlim_cur = hfuzz->asLimit * 1024UL * 1024UL;
        rl.rlim_max = hfuzz->asLimit * 1024UL * 1024UL;
        if (setrlimit(RLIMIT_AS, &rl) == -1) {
            PLOG_D("Couldn't enforce the RLIMIT_AS resource limit, ignoring");
        }
    }

    if (hfuzz->nullifyStdio) {
        util_nullifyStdio();
    }

    if (hfuzz->fuzzStdin) {
        /*
         * Uglyyyyyy ;)
         */
        if (!util_redirectStdin(fileName)) {
            return false;
        }
    }

    for (size_t i = 0; i < ARRAYSIZE(hfuzz->envs) && hfuzz->envs[i]; i++) {
        putenv(hfuzz->envs[i]);
    }

    execvp(args[0], args);

    util_recoverStdio();
    LOG_F("Failed to create new '%s' process", args[0]);
    return false;
}
Ejemplo n.º 7
0
static bool containDropPrivs(struct nsjconf_t *nsjconf)
{
	/*
	 * Best effort because of /proc/self/setgroups
	 */
	gid_t *group_list = NULL;
	if (setgroups(0, group_list) == -1) {
		PLOG_D("setgroups(NULL) failed");
	}
	if (setresgid(nsjconf->inside_gid, nsjconf->inside_gid, nsjconf->inside_gid) == -1) {
		PLOG_E("setresgid(%u)", nsjconf->inside_gid);
		return false;
	}
	if (setresuid(nsjconf->inside_uid, nsjconf->inside_uid, nsjconf->inside_uid) == -1) {
		PLOG_E("setresuid(%u)", nsjconf->inside_uid);
		return false;
	}
#ifndef PR_SET_NO_NEW_PRIVS
#define PR_SET_NO_NEW_PRIVS 38
#endif
	if (prctl(PR_SET_NO_NEW_PRIVS, 1, 0, 0, 0) == -1) {
		/* Only new kernels support it */
		PLOG_W("prctl(PR_SET_NO_NEW_PRIVS, 1)");
	}

	if (nsjconf->keep_caps == false) {
		for (unsigned long i = 0; i < 128UL; i++) {
			/*
			 * Number of capabilities differs between kernels, so
			 * wait for the first one which returns EINVAL
			 */
			if (prctl(PR_CAPBSET_DROP, i, 0UL, 0UL, 0UL) == -1 && errno == EINVAL) {
				break;
			}
		}
		if (prctl(PR_SET_KEEPCAPS, 0, 0, 0, 0) == -1) {
			PLOG_E("prctl(PR_SET_KEEPCAPS, 0)");
			return false;
		}
		struct __user_cap_header_struct cap_hdr = {
			.version = _LINUX_CAPABILITY_VERSION_3,
			.pid = 0,
		};
		const struct __user_cap_data_struct cap_data[_LINUX_CAPABILITY_U32S_3] = {
			[0 ... (_LINUX_CAPABILITY_U32S_3 - 1)].inheritable = 0U,
			[0 ... (_LINUX_CAPABILITY_U32S_3 - 1)].effective = 0U,
			[0 ... (_LINUX_CAPABILITY_U32S_3 - 1)].permitted = 0U,
		};
		if (syscall(__NR_capset, &cap_hdr, &cap_data) == -1) {
			PLOG_E("capset()");
			return false;
		}
	}
	return true;
}

static bool containPrepareEnv(struct nsjconf_t *nsjconf)
{
	if (prctl(PR_SET_PDEATHSIG, SIGKILL, 0, 0, 0) == -1) {
		PLOG_E("prctl(PR_SET_PDEATHSIG, SIGKILL)");
		return false;
	}
	if (nsjconf->personality && personality(nsjconf->personality) == -1) {
		PLOG_E("personality(%lx)", nsjconf->personality);
		return false;
	}
	errno = 0;
	if (setpriority(PRIO_PROCESS, 0, 19) == -1 && errno != 0) {
		PLOG_W("setpriority(19)");
	}
	if (nsjconf->skip_setsid == false) {
		setsid();
	}
	return true;
}

static bool containInitMountNs(struct nsjconf_t *nsjconf)
{
	return mountInitNs(nsjconf);
}

static bool containSetLimits(struct nsjconf_t *nsjconf)
{
	struct rlimit64 rl;
	rl.rlim_cur = rl.rlim_max = nsjconf->rl_as;
	if (prlimit64(0, RLIMIT_AS, &rl, NULL) == -1) {
		PLOG_E("prlimit64(0, RLIMIT_AS, %" PRIu64 ")", nsjconf->rl_as);
		return false;
	}
	rl.rlim_cur = rl.rlim_max = nsjconf->rl_core;
	if (prlimit64(0, RLIMIT_CORE, &rl, NULL) == -1) {
		PLOG_E("prlimit64(0, RLIMIT_CORE, %" PRIu64 ")", nsjconf->rl_core);
		return false;
	}
	rl.rlim_cur = rl.rlim_max = nsjconf->rl_cpu;
	if (prlimit64(0, RLIMIT_CPU, &rl, NULL) == -1) {
		PLOG_E("prlimit64(0, RLIMIT_CPU, %" PRIu64 ")", nsjconf->rl_cpu);
		return false;
	}
	rl.rlim_cur = rl.rlim_max = nsjconf->rl_fsize;
	if (prlimit64(0, RLIMIT_FSIZE, &rl, NULL) == -1) {
		PLOG_E("prlimit64(0, RLIMIT_FSIZE, %" PRIu64 ")", nsjconf->rl_fsize);
		return false;
	}
	rl.rlim_cur = rl.rlim_max = nsjconf->rl_nofile;
	if (prlimit64(0, RLIMIT_NOFILE, &rl, NULL) == -1) {
		PLOG_E("prlimit64(0, RLIMIT_NOFILE, %" PRIu64 ")", nsjconf->rl_nofile);
		return false;
	}
	rl.rlim_cur = rl.rlim_max = nsjconf->rl_nproc;
	if (prlimit64(0, RLIMIT_NPROC, &rl, NULL) == -1) {
		PLOG_E("prlimit64(0, RLIMIT_NPROC, %" PRIu64 ")", nsjconf->rl_nproc);
		return false;
	}
	rl.rlim_cur = rl.rlim_max = nsjconf->rl_stack;
	if (prlimit64(0, RLIMIT_STACK, &rl, NULL) == -1) {
		PLOG_E("prlimit64(0, RLIMIT_STACK, %" PRIu64 ")", nsjconf->rl_stack);
		return false;
	}
	return true;
}

static bool containMakeFdsCOENaive(void)
{
	// Don't use getrlimit(RLIMIT_NOFILE) here, as it can return an artifically small value
	// (e.g. 32), which could be smaller than a maximum assigned number to file-descriptors
	// in this process. Just use some reasonably sane value (e.g. 1024)
	for (unsigned fd = (STDERR_FILENO + 1); fd < 1024; fd++) {
		int flags = fcntl(fd, F_GETFD, 0);
		if (flags == -1) {
			continue;
		}
		fcntl(fd, F_SETFD, flags | FD_CLOEXEC);
		LOG_D("Set fd '%d' flag to FD_CLOEXEC", fd);
	}
	return true;
}

static bool containMakeFdsCOEProc(void)
{
	/* Make all fds above stderr close-on-exec */
	DIR *dir = opendir("/proc/self/fd");
	if (dir == NULL) {
		PLOG_D("opendir('/proc/self/fd')");
		return false;
	}
	defer {
		closedir(dir);
	};
	for (;;) {
		errno = 0;
		struct dirent *entry = readdir(dir);
		if (entry == NULL && errno != 0) {
			PLOG_D("readdir('/proc/self/fd')");
			return false;
		}
		if (entry == NULL) {
			break;
		}
		if (strcmp(".", entry->d_name) == 0) {
			continue;
		}
		if (strcmp("..", entry->d_name) == 0) {
			continue;
		}
		int fd = strtoul(entry->d_name, NULL, 10);
		if (errno == EINVAL) {
			LOG_W("Cannot convert /proc/self/fd/%s to a number", entry->d_name);
			continue;
		}
		if (fd > STDERR_FILENO) {
			int flags = fcntl(fd, F_GETFD, 0);
			if (flags == -1) {
				PLOG_D("fcntl(fd, F_GETFD, 0)");
				return false;
			}
			fcntl(fd, F_SETFD, flags | FD_CLOEXEC);
			LOG_D("Set fd '%d' flag to FD_CLOEXEC", fd);
		}
	}
	return true;
}
Ejemplo n.º 8
0
bool arch_launchChild(honggfuzz_t * hfuzz, char *fileName)
{
    /*
     * Kill the children when fuzzer dies (e.g. due to Ctrl+C)
     */
    if (prctl(PR_SET_PDEATHSIG, (long)SIGKILL, 0L, 0L, 0L) == -1) {
        PLOG_E("prctl(PR_SET_PDEATHSIG, SIGKILL) failed");
        return false;
    }

    /*
     * Kill a process which corrupts its own heap (with ABRT)
     */
    if (setenv("MALLOC_CHECK_", "3", 1) == -1) {
        PLOG_E("setenv(MALLOC_CHECK_=3) failed");
        return false;
    }

    /*
     * Tell asan to ignore SEGVs
     */
    if (setenv
        ("ASAN_OPTIONS",
         "allow_user_segv_handler=1:handle_segv=0:abort_on_error=1:allocator_may_return_null=1",
         1) == -1) {
        PLOG_E("setenv(ASAN_OPTIONS) failed");
        return false;
    }

    const char *msan_options =
        "exit_code=" HF_MSAN_EXIT_CODE_STR ":report_umrs=0:wrap_signals=0:print_stats=1";
    if (hfuzz->msanReportUMRS == true) {
        msan_options =
            "exit_code=" HF_MSAN_EXIT_CODE_STR ":report_umrs=1:wrap_signals=0:print_stats=1";
    }
    if (setenv("MSAN_OPTIONS", msan_options, 1) == -1) {
        PLOG_E("setenv(MSAN_OPTIONS) failed");
        return false;
    }

    /*
     * Disable ASLR
     */
    if (hfuzz->disableRandomization && personality(ADDR_NO_RANDOMIZE) == -1) {
        PLOG_E("personality(ADDR_NO_RANDOMIZE) failed");
        return false;
    }
#define ARGS_MAX 512
    char *args[ARGS_MAX + 2];
    char argData[PATH_MAX] = { 0 };
    int x;

    for (x = 0; x < ARGS_MAX && hfuzz->cmdline[x]; x++) {
        if (!hfuzz->fuzzStdin && strcmp(hfuzz->cmdline[x], _HF_FILE_PLACEHOLDER) == 0) {
            args[x] = fileName;
        } else if (!hfuzz->fuzzStdin && strstr(hfuzz->cmdline[x], _HF_FILE_PLACEHOLDER)) {
            const char *off = strstr(hfuzz->cmdline[x], _HF_FILE_PLACEHOLDER);
            snprintf(argData, PATH_MAX, "%.*s%s", (int)(off - hfuzz->cmdline[x]), hfuzz->cmdline[x],
                     fileName);
            args[x] = argData;
        } else {
            args[x] = hfuzz->cmdline[x];
        }
    }

    args[x++] = NULL;

    LOG_D("Launching '%s' on file '%s'", args[0], fileName);

    /*
     * Set timeout (prof), real timeout (2*prof), and rlimit_cpu (2*prof)
     */
    if (hfuzz->tmOut) {
        /* 
         * Set the CPU rlimit to twice the value of the time-out
         */
        struct rlimit rl = {
            .rlim_cur = hfuzz->tmOut * 2,
            .rlim_max = hfuzz->tmOut * 2,
        };
        if (setrlimit(RLIMIT_CPU, &rl) == -1) {
            PLOG_E("Couldn't enforce the RLIMIT_CPU resource limit");
            return false;
        }
    }

    /*
     * The address space limit. If big enough - roughly the size of RAM used
     */
    if (hfuzz->asLimit) {
        struct rlimit64 rl = {
            .rlim_cur = hfuzz->asLimit * 1024ULL * 1024ULL,
            .rlim_max = hfuzz->asLimit * 1024ULL * 1024ULL,
        };
        if (prlimit64(0, RLIMIT_AS, &rl, NULL) == -1) {
            PLOG_D("Couldn't enforce the RLIMIT_AS resource limit, ignoring");
        }
    }

    for (size_t i = 0; i < ARRAYSIZE(hfuzz->envs) && hfuzz->envs[i]; i++) {
        putenv(hfuzz->envs[i]);
    }

    if (hfuzz->nullifyStdio) {
        util_nullifyStdio();
    }

    if (hfuzz->fuzzStdin) {
        /*
         * Uglyyyyyy ;)
         */
        if (!util_redirectStdin(fileName)) {
            return false;
        }
    }
    /*
     * Wait for the ptrace to attach
     */
    syscall(__NR_tkill, syscall(__NR_gettid), SIGSTOP);
    execvp(args[0], args);

    util_recoverStdio();
    LOG_F("Failed to create new '%s' process", args[0]);
    return false;
}

static void arch_sigFunc(int signo, siginfo_t * si, void *dummy)
{
    if (signo != SIGALRM) {
        LOG_E("Signal != SIGALRM (%d)", signo);
    }
    return;
    if (si == NULL) {
        return;
    }
    if (dummy == NULL) {
        return;
    }
}

static void arch_removeTimer(timer_t * timerid)
{
    timer_delete(*timerid);
}

static bool arch_setTimer(timer_t * timerid)
{
    struct sigevent sevp = {
        .sigev_value.sival_ptr = timerid,
        .sigev_signo = SIGALRM,
        .sigev_notify = SIGEV_THREAD_ID | SIGEV_SIGNAL,
        ._sigev_un._tid = syscall(__NR_gettid),
    };
    if (timer_create(CLOCK_REALTIME, &sevp, timerid) == -1) {
        PLOG_E("timer_create(CLOCK_REALTIME) failed");
        return false;
    }
    /* 
     * Kick in every 200ms, starting with the next second
     */
    const struct itimerspec ts = {
        .it_value = {.tv_sec = 1,.tv_nsec = 0},
        .it_interval = {.tv_sec = 0,.tv_nsec = 200000000,},
    };
    if (timer_settime(*timerid, 0, &ts, NULL) == -1) {
        PLOG_E("timer_settime() failed");
        timer_delete(*timerid);
        return false;
    }
    sigset_t smask;
    sigemptyset(&smask);
    struct sigaction sa = {
        .sa_handler = NULL,
        .sa_sigaction = arch_sigFunc,
        .sa_mask = smask,
        .sa_flags = SA_SIGINFO,
        .sa_restorer = NULL,
    };
    if (sigaction(SIGALRM, &sa, NULL) == -1) {
        PLOG_E("sigaction(SIGALRM) failed");
        return false;
    }

    return true;
}

static void arch_checkTimeLimit(honggfuzz_t * hfuzz, fuzzer_t * fuzzer)
{
    int64_t curMillis = util_timeNowMillis();
    int64_t diffMillis = curMillis - fuzzer->timeStartedMillis;
    if (diffMillis > (hfuzz->tmOut * 1000)) {
        LOG_W("PID %d took too much time (limit %ld s). Sending SIGKILL",
              fuzzer->pid, hfuzz->tmOut);
        kill(fuzzer->pid, SIGKILL);
        __sync_fetch_and_add(&hfuzz->timeoutedCnt, 1UL);
    }
}