Exemplo n.º 1
0
Arquivo: log.c Projeto: RishavT/nsjail
/*
 * Log to stderr by default. Use a dup()d fd, because in the future we'll associate the
 * connection socket with fd (0, 1, 2).
 */
bool logInitLogFile(struct nsjconf_t *nsjconf, const char *logfile, bool is_verbose)
{
	log_verbose = is_verbose;

	if (logfile == NULL && nsjconf->daemonize == true) {
		logfile = _LOG_DEFAULT_FILE;
	}
	if (logfile == NULL) {
		logfile = "/proc/self/fd/2";
	}
	log_fd = open(logfile, O_CREAT | O_RDWR | O_APPEND, 0640);
	if (log_fd == -1) {
		log_fd = STDERR_FILENO;
		PLOG_E("Couldn't open logfile open('%s')", logfile);
		return false;
	}
	log_fd_isatty = (isatty(log_fd) == 1 ? true : false);
	return true;
}
Exemplo n.º 2
0
static procMap_t *arch_parsePidMaps(pid_t pid, size_t * mapsCount)
{
    FILE *f = NULL;
    char fProcMaps[PATH_MAX] = { 0 };
    snprintf(fProcMaps, PATH_MAX, "/proc/%d/maps", pid);

    if ((f = fopen(fProcMaps, "rb")) == NULL) {
        PLOG_E("Couldn't open '%s' - R/O mode", fProcMaps);
        return 0;
    }
    defer {
        fclose(f);
    };

    *mapsCount = 0;
    procMap_t *mapsList = malloc(sizeof(procMap_t));
    if (mapsList == NULL) {
        PLOG_W("malloc(size='%zu')", sizeof(procMap_t));
        return NULL;
    }

    while (!feof(f)) {
        char buf[sizeof(procMap_t) + 1];
        if (fgets(buf, sizeof(buf), f) == 0) {
            break;
        }

        mapsList[*mapsCount].name[0] = '\0';
        sscanf(buf, "%lx-%lx %5s %lx %7s %ld %s", &mapsList[*mapsCount].start,
               &mapsList[*mapsCount].end, mapsList[*mapsCount].perms, &mapsList[*mapsCount].offset,
               mapsList[*mapsCount].dev, &mapsList[*mapsCount].inode, mapsList[*mapsCount].name);

        *mapsCount += 1;
        if ((mapsList = realloc(mapsList, (*mapsCount + 1) * sizeof(procMap_t))) == NULL) {
            PLOG_W("realloc failed (sz=%zu)", (*mapsCount + 1) * sizeof(procMap_t));
            free(mapsList);
            return NULL;
        }
    }

    return mapsList;
}
Exemplo n.º 3
0
void util_recoverStdio(void)
{
    int fd = open("/dev/tty", O_RDWR);

    if (fd == -1) {
        PLOG_E("Couldn't open '/dev/tty'");
        return;
    }

    dup2(fd, 0);
    dup2(fd, 1);
    dup2(fd, 2);

    if (tcsetpgrp(fd, getpid()) == -1) {
        PLOG_W("tcsetpgrp(%d) failed", getpid());
    }

    if (fd > 2) {
        close(fd);
    }
}
Exemplo n.º 4
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;
}
Exemplo n.º 5
0
void arch_perfAnalyze(honggfuzz_t * hfuzz, fuzzer_t * fuzzer, perfFd_t * perfFds)
{
    if (hfuzz->dynFileMethod == _HF_DYNFILE_NONE) {
        return;
    }

    uint64_t instrCount = 0;
    if (hfuzz->dynFileMethod & _HF_DYNFILE_INSTR_COUNT) {
        ioctl(perfFds->cpuInstrFd, PERF_EVENT_IOC_DISABLE, 0);
        if (read(perfFds->cpuInstrFd, &instrCount, sizeof(instrCount)) != sizeof(instrCount)) {
            PLOG_E("read(perfFd='%d') failed", perfFds->cpuInstrFd);
        }
        close(perfFds->cpuInstrFd);
    }

    uint64_t branchCount = 0;
    if (hfuzz->dynFileMethod & _HF_DYNFILE_BRANCH_COUNT) {
        ioctl(perfFds->cpuBranchFd, PERF_EVENT_IOC_DISABLE, 0);
        if (read(perfFds->cpuBranchFd, &branchCount, sizeof(branchCount)) != sizeof(branchCount)) {
            PLOG_E("read(perfFd='%d') failed", perfFds->cpuBranchFd);
        }
        close(perfFds->cpuBranchFd);
    }

    uint64_t pathCount = 0;
    if (hfuzz->dynFileMethod & _HF_DYNFILE_UNIQUE_BLOCK_COUNT) {
        ioctl(perfFds->uniquePcFd, PERF_EVENT_IOC_DISABLE, 0);
        close(perfFds->uniquePcFd);
        arch_perfMmapParse();
        pathCount = arch_perfCountBranches();

        if (perfRecordsLost > 0UL) {
            LOG_W("%" PRIu64
                  " PERF_RECORD_LOST events received, possibly too many concurrent fuzzing threads in progress",
                  perfRecordsLost);
        }
    }

    uint64_t edgeCount = 0;
    if (hfuzz->dynFileMethod & _HF_DYNFILE_UNIQUE_EDGE_COUNT) {
        ioctl(perfFds->uniqueEdgeFd, PERF_EVENT_IOC_DISABLE, 0);
        close(perfFds->uniqueEdgeFd);
        arch_perfMmapParse();
        edgeCount = arch_perfCountBranches();

        if (perfRecordsLost > 0UL) {
            LOG_W("%" PRIu64
                  " PERF_RECORD_LOST events received, possibly too many concurrent fuzzing threads in progress",
                  perfRecordsLost);
        }
    }

    if (perfMmapBuf != NULL) {
        munmap(perfMmapBuf, perfMmapSz + getpagesize());
    }
    if (perfBloom != NULL) {
        munmap(perfBloom, _HF_PERF_BLOOM_SZ);
    }

    fuzzer->hwCnts.cpuInstrCnt = instrCount;
    fuzzer->hwCnts.cpuBranchCnt = branchCount;
    fuzzer->hwCnts.pcCnt = pathCount;
    fuzzer->hwCnts.pathCnt = edgeCount;
}
Exemplo n.º 6
0
static bool arch_perfOpen(pid_t pid, dynFileMethod_t method, int *perfFd)
{
    LOG_D("Enabling PERF for PID=%d (mmapBufSz=%zu), method=%x", pid, perfMmapSz, method);

    perfDynamicMethod = method;
    perfBranchesCnt = 0;
    perfRecordsLost = 0;

    struct perf_event_attr pe;
    memset(&pe, 0, sizeof(struct perf_event_attr));
    pe.size = sizeof(struct perf_event_attr);
    pe.disabled = 0;
    pe.exclude_kernel = 1;
    pe.exclude_hv = 1;
    pe.exclude_callchain_kernel = 1;
    pe.enable_on_exec = 1;
    pe.type = PERF_TYPE_HARDWARE;

    switch (method) {
    case _HF_DYNFILE_INSTR_COUNT:
        LOG_D("Using: PERF_COUNT_HW_INSTRUCTIONS for PID: %d", pid);
        pe.config = PERF_COUNT_HW_INSTRUCTIONS;
        pe.inherit = 1;
        break;
    case _HF_DYNFILE_BRANCH_COUNT:
        LOG_D("Using: PERF_COUNT_HW_BRANCH_INSTRUCTIONS for PID: %d", pid);
        pe.config = PERF_COUNT_HW_BRANCH_INSTRUCTIONS;
        pe.inherit = 1;
        break;
    case _HF_DYNFILE_UNIQUE_BLOCK_COUNT:
        LOG_D("Using: PERF_SAMPLE_BRANCH_STACK/PERF_SAMPLE_IP for PID: %d", pid);
        pe.config = PERF_COUNT_HW_BRANCH_INSTRUCTIONS;
        pe.sample_type = PERF_SAMPLE_IP;
        pe.sample_period = 1;   /* It's BTS based, so must be equal to 1 */
        pe.watermark = 1;
        pe.wakeup_events = perfMmapSz / 2;
        break;
    case _HF_DYNFILE_UNIQUE_EDGE_COUNT:
        LOG_D("Using: PERF_SAMPLE_BRANCH_STACK/PERF_SAMPLE_IP|PERF_SAMPLE_ADDR for PID: %d", pid);
        pe.config = PERF_COUNT_HW_BRANCH_INSTRUCTIONS;
        pe.sample_type = PERF_SAMPLE_IP | PERF_SAMPLE_ADDR;
        pe.sample_period = 1;   /* It's BTS based, so must be equal to 1 */
        pe.watermark = 1;
        pe.wakeup_events = perfMmapSz / 2;
        break;
    default:
        LOG_E("Unknown perf mode: '%d' for PID: %d", method, pid);
        return false;
        break;
    }

    *perfFd = perf_event_open(&pe, pid, -1, -1, 0);
    if (*perfFd == -1) {
        if (method == _HF_DYNFILE_UNIQUE_BLOCK_COUNT || method == _HF_DYNFILE_UNIQUE_EDGE_COUNT) {
            LOG_E
                ("'-LDp'/'-LDe' mode (sample IP/jump) requires LBR/BTS, which present in Intel Haswell "
                 "and newer CPUs (i.e. not in AMD CPUs)");
        }
        PLOG_F("perf_event_open() failed");
        return false;
    }

    if (method != _HF_DYNFILE_UNIQUE_BLOCK_COUNT && method != _HF_DYNFILE_UNIQUE_EDGE_COUNT) {
        return true;
    }

    perfBloom = mmap(NULL, _HF_PERF_BLOOM_SZ, PROT_READ | PROT_WRITE,
                     MAP_PRIVATE | MAP_ANONYMOUS | MAP_NORESERVE, -1, 0);
    if (perfBloom == MAP_FAILED) {
        perfBloom = NULL;
        PLOG_E("mmap(size=%zu) failed", (size_t) _HF_PERF_BLOOM_SZ);
    }

    perfMmapBuf =
        mmap(NULL, perfMmapSz + getpagesize(), PROT_READ | PROT_WRITE, MAP_SHARED, *perfFd, 0);
    if (perfMmapBuf == MAP_FAILED) {
        perfMmapBuf = NULL;
        PLOG_E("mmap() failed");
        close(*perfFd);
        return false;
    }

    struct sigaction sa = {
        .sa_handler = arch_perfSigHandler,
        .sa_flags = SA_RESTART,
    };
    sigemptyset(&sa.sa_mask);
    if (sigaction(_HF_RT_SIG, &sa, NULL) == -1) {
        PLOG_E("sigaction() failed");
        return false;
    }
    if (fcntl(*perfFd, F_SETFL, O_RDWR | O_NONBLOCK | O_ASYNC) == -1) {
        PLOG_E("fnctl(F_SETFL)");
        close(*perfFd);
        return false;
    }
    if (fcntl(*perfFd, F_SETSIG, _HF_RT_SIG) == -1) {
        PLOG_E("fnctl(F_SETSIG)");
        close(*perfFd);
        return false;
    }
    struct f_owner_ex foe = {
        .type = F_OWNER_TID,
        .pid = syscall(__NR_gettid)
    };
    if (fcntl(*perfFd, F_SETOWN_EX, &foe) == -1) {
        PLOG_E("fnctl(F_SETOWN_EX)");
        close(*perfFd);
        return false;
    }
    return true;
}
Exemplo 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;
}
Exemplo n.º 8
0
static bool arch_sanCovParseRaw(honggfuzz_t * hfuzz, fuzzer_t * fuzzer)
{
    int dataFd = -1;
    uint8_t *dataBuf = NULL;
    off_t dataFileSz = 0, pos = 0;
    bool is32bit = true, ret = false, isSeedFirstRun = false;
    char covFile[PATH_MAX] = { 0 };

    /* Fuzzer local runtime data structs - need free() before exit */
    uint64_t *startMapsIndex = NULL;
    memMap_t *mapsBuf = NULL;

    /* Local counters */
    uint64_t nBBs = 0;          /* Total BB hits found in raw file */
    uint64_t nZeroBBs = 0;      /* Number of non-hit instrumented BBs */
    uint64_t mapsNum = 0;       /* Total number of entries in map file */
    uint64_t noCovMapsNum = 0;  /* Loaded DSOs not compiled with coverage */

    /* File line-by-line read help buffers */
    char *pLine = NULL;
    size_t lineSz = 0;

    /* Coverage data analysis starts by parsing map file listing */
    snprintf(covFile, sizeof(covFile), "%s/%s/%d.sancov.map", hfuzz->workDir, _HF_SANCOV_DIR,
             fuzzer->pid);
    if (!files_exists(covFile)) {
        LOG_D("sancov map file not found");
        return false;
    }
    FILE *fCovMap = fopen(covFile, "rb");
    if (fCovMap == NULL) {
        PLOG_E("Couldn't open '%s' - R/O mode", covFile);
        goto bail;
    }

    /* First line contains PC length (32/64-bit) */
    if (getline(&pLine, &lineSz, fCovMap) == -1) {
        LOG_E("Invalid map file '%s'", covFile);
        fclose(fCovMap);
        goto bail;
    }
    int pcLen = atoi(pLine);
    if (pcLen == 32) {
        is32bit = true;
    } else if (pcLen == 64) {
        is32bit = false;
    } else {
        LOG_E("Invalid PC length (%d) in map file '%s'", pcLen, covFile);
    }

    /* Interaction with global Trie should mutex wrap to avoid threads races */
    MX_LOCK(&hfuzz->sanCov_mutex);
    {
        /* If runtime data destroy flag, new seed has been picked so destroy old & create new Trie */
        if (hfuzz->clearCovMetadata == true) {
            /* Since this path is invoked on first run too, destroy old Trie only if exists */
            if (hfuzz->covMetadata != NULL) {
                arch_trieDestroy(hfuzz->covMetadata);
            }
            arch_trieCreate(&hfuzz->covMetadata);
            hfuzz->clearCovMetadata = false;
            isSeedFirstRun = true;
        }
    }
    MX_UNLOCK(&hfuzz->sanCov_mutex);

    /* See if #maps is available from previous run to avoid realloc inside loop */
    uint64_t prevMapsNum = __sync_fetch_and_add(&hfuzz->sanCovCnts.dsoCnt, 0UL);
    if (prevMapsNum > 0) {
        if ((mapsBuf = malloc(prevMapsNum * sizeof(memMap_t))) == NULL) {
            PLOG_E("malloc failed (sz=%" PRIu64 ")", prevMapsNum * sizeof(memMap_t));
            /* This will be picked-up later from realloc branch */
            prevMapsNum = 0;
        }
    }

    /* Iterate map entries */
    for (;;) {
        if (getline(&pLine, &lineSz, fCovMap) == -1) {
            break;
        }

        /* Trim trailing whitespaces, not sure if needed copied from upstream sancov.py */
        char *lineEnd = pLine + strlen(pLine) - 1;
        while (lineEnd > pLine && isspace(*lineEnd)) {
            lineEnd--;
        }
        *(lineEnd + 1) = 0;

        /*
         * Each line has following format:
         * Start    End      Base     bin/DSO name
         * b5843000 b584e6ac b5843000 liblog.so
         */
        memMap_t mapData = {.start = 0 };
        char *savePtr = NULL;
        mapData.start = strtoull(strtok_r(pLine, " ", &savePtr), NULL, 16);
        mapData.end = strtoull(strtok_r(NULL, " ", &savePtr), NULL, 16);
        mapData.base = strtoull(strtok_r(NULL, " ", &savePtr), NULL, 16);
        char *mapName = strtok_r(NULL, " ", &savePtr);
        memcpy(mapData.mapName, mapName, strlen(mapName));

        /* Interaction with global Trie should mutex wrap to avoid threads races */
        MX_LOCK(&hfuzz->sanCov_mutex);
        {
            /* Add entry to Trie with zero data if not already */
            if (!arch_trieSearch(hfuzz->covMetadata->children, mapData.mapName)) {
                arch_trieAdd(&hfuzz->covMetadata, mapData.mapName);
            }
        }
        MX_UNLOCK(&hfuzz->sanCov_mutex);

        /* If not DSO number history (first run) or new DSO loaded, realloc local maps metadata buf */
        if (prevMapsNum == 0 || prevMapsNum < mapsNum) {
            if ((mapsBuf = realloc(mapsBuf, (size_t) (mapsNum + 1) * sizeof(memMap_t))) == NULL) {
                PLOG_E("realloc failed (sz=%" PRIu64 ")", (mapsNum + 1) * sizeof(memMap_t));
                goto bail;
            }
        }

        /* Add entry to local maps metadata array */
        memcpy(&mapsBuf[mapsNum], &mapData, sizeof(memMap_t));

        /* Increase loaded maps counter (includes non-instrumented DSOs too) */
        mapsNum++;
    }

    /* Delete .sancov.map file */
    fclose(fCovMap);
    unlink(covFile);

    /* Create a quick index array with maps start addresses */
    startMapsIndex = malloc(mapsNum * sizeof(uint64_t));
    if (startMapsIndex == NULL) {
        PLOG_E("malloc failed (sz=%" PRIu64 ")", mapsNum * sizeof(uint64_t));
        goto bail;
    }

    /* Sort quick maps index */
    qsort(mapsBuf, mapsNum, sizeof(memMap_t), arch_qsortCmp);
    for (size_t i = 0; i < mapsNum; i++) {
        startMapsIndex[i] = mapsBuf[i].start;
    }

    /* mmap() .sancov.raw file */
    snprintf(covFile, sizeof(covFile), "%s/%s/%d.sancov.raw", hfuzz->workDir, _HF_SANCOV_DIR,
             fuzzer->pid);
    dataBuf = files_mapFile(covFile, &dataFileSz, &dataFd, false);
    if (dataBuf == NULL) {
        LOG_E("Couldn't open and map '%s' in R/O mode", covFile);
        goto bail;
    }

    /*
     * Avoid cost of size checks inside raw data read loop by defining the read function
     * & pivot size based on PC length.
     */
    uint64_t(*pReadRawBBAddrFunc) (const uint8_t *) = NULL;
    uint8_t pivot = 0;
    if (is32bit) {
        pReadRawBBAddrFunc = &util_getUINT32;
        pivot = 4;
    } else {
        pReadRawBBAddrFunc = &util_getUINT64;
        pivot = 8;
    }

    /*
     * Take advantage of data locality (next processed addr is very likely to belong
     * to same map) to avoid Trie node search for each read entry.
     */
    node_t *curMap = NULL;
    uint64_t prevIndex = 0;

    /* Iterate over data buffer containing list of hit BB addresses */
    while (pos < dataFileSz) {
        uint64_t bbAddr = pReadRawBBAddrFunc(dataBuf + pos);
        pos += pivot;
        /* Don't bother for zero BB addr (inserted checks without hit) */
        if (bbAddr == 0x0) {
            nZeroBBs++;
            continue;
        } else {
            /* Find best hit based on start addr & verify range for errors */
            uint64_t bestFit = arch_interpSearch(startMapsIndex, mapsNum, bbAddr);
            if (bbAddr >= mapsBuf[bestFit].start && bbAddr < mapsBuf[bestFit].end) {
                /* Increase exe/DSO total BB counter */
                mapsBuf[bestFit].bbCnt++;

                /* Update current Trie node if map changed */
                if (curMap == NULL || (prevIndex != bestFit)) {
                    prevIndex = bestFit;

                    /* Interaction with global Trie should mutex wrap to avoid threads races */
                    MX_LOCK(&hfuzz->sanCov_mutex);
                    {
                        curMap =
                            arch_trieSearch(hfuzz->covMetadata->children, mapsBuf[bestFit].mapName);
                        if (curMap == NULL) {
                            LOG_E("Corrupted Trie - '%s' not found", mapsBuf[bestFit].mapName);
                            MX_UNLOCK(&hfuzz->sanCov_mutex);
                            continue;
                        }

                        /* Maintain bitmaps only for exec/DSOs with coverage enabled - allocate on first use */
                        if (curMap->data.pBM == NULL) {
                            LOG_D("Allocating bitmap for map '%s'", mapsBuf[bestFit].mapName);
                            curMap->data.pBM = arch_newBitmap(_HF_BITMAP_SIZE);

                            /*
                             * If bitmap allocation failed, unset cached Trie node ptr
                             * to execute this selection branch again.
                             */
                            if (curMap->data.pBM == NULL) {
                                curMap = NULL;
                                MX_UNLOCK(&hfuzz->sanCov_mutex);
                                continue;
                            }
                        }
                    }
                    MX_UNLOCK(&hfuzz->sanCov_mutex);
                }

                /* If new relative BB addr update DSO's bitmap */
                uint32_t relAddr = (uint32_t) (bbAddr - mapsBuf[bestFit].base);
                if (!arch_queryBitmap(curMap->data.pBM, relAddr)) {

                    /* Interaction with global Trie should mutex wrap to avoid threads races */
                    MX_LOCK(&hfuzz->sanCov_mutex);
                    {
                        arch_setBitmap(curMap->data.pBM, relAddr);
                    }
                    MX_UNLOCK(&hfuzz->sanCov_mutex);

                    /* Also increase new BBs counter at worker's thread runtime data */
                    mapsBuf[bestFit].newBBCnt++;
                }
            } else {
                /*
                 * Normally this should never get executed. If hit, sanitizer
                 * coverage data collection come across some kind of bug.
                 */
                LOG_E("Invalid BB addr (%" PRIx64 ") at offset %ld", bbAddr, pos);
            }
        }
        nBBs++;
    }

    /* Finally iterate over all instrumented maps to sum-up the number of newly met BB addresses */
    for (uint64_t i = 0; i < mapsNum; i++) {
        if (mapsBuf[i].bbCnt > 0 && !isSeedFirstRun) {
            fuzzer->sanCovCnts.newBBCnt += mapsBuf[i].newBBCnt;
        } else {
            noCovMapsNum++;
        }
    }

    /* Successful parsing - update fuzzer worker's counters */
    fuzzer->sanCovCnts.hitBBCnt = nBBs;
    fuzzer->sanCovCnts.totalBBCnt = nBBs + nZeroBBs;
    fuzzer->sanCovCnts.dsoCnt = mapsNum;
    fuzzer->sanCovCnts.iDsoCnt = mapsNum - noCovMapsNum;        /* Instrumented DSOs */
    ret = true;

 bail:
    unlink(covFile);
    if (dataBuf) {
        munmap(dataBuf, dataFileSz);
    }
    if (dataFd != -1) {
        close(dataFd);
    }
    if (mapsBuf) {
        free(mapsBuf);
    }
    if (startMapsIndex) {
        free(startMapsIndex);
    }
    if (pLine) {
        free(pLine);
    }
    return ret;
}

static bool arch_sanCovParse(honggfuzz_t * hfuzz, fuzzer_t * fuzzer)
{
    int dataFd = -1;
    uint8_t *dataBuf = NULL;
    off_t dataFileSz = 0, pos = 0;
    bool is32bit = true;
    char covFile[PATH_MAX] = { 0 };
    DIR *pSanCovDir = NULL;
    bool ret = false;

    snprintf(covFile, sizeof(covFile), "%s/%s/%s.%d.sancov", hfuzz->workDir, _HF_SANCOV_DIR,
             files_basename(hfuzz->cmdline[0]), fuzzer->pid);
    if (!files_exists(covFile)) {
        LOG_D("Target sancov file not found");
        return false;
    }

    /* Local cache file suffix to use for file search of worker pid data */
    char pidFSuffix[13] = { 0 };
    snprintf(pidFSuffix, sizeof(pidFSuffix), "%d.sancov", fuzzer->pid);

    /* Total BBs counter summarizes all DSOs */
    uint64_t nBBs = 0;

    /* Iterate sancov dir for files generated against fuzzer pid */
    snprintf(covFile, sizeof(covFile), "%s/%s", hfuzz->workDir, _HF_SANCOV_DIR);
    pSanCovDir = opendir(covFile);
    struct dirent *pDir = NULL;
    while ((pDir = readdir(pSanCovDir)) != NULL) {
        /* Parse files with worker's PID */
        if (strstr(pDir->d_name, pidFSuffix)) {
            snprintf(covFile, sizeof(covFile), "%s/%s/%s", hfuzz->workDir, _HF_SANCOV_DIR,
                     pDir->d_name);
            dataBuf = files_mapFile(covFile, &dataFileSz, &dataFd, false);
            if (dataBuf == NULL) {
                LOG_E("Couldn't open and map '%s' in R/O mode", covFile);
                goto bail;
            }

            if (dataFileSz < 8) {
                LOG_E("Coverage data file too short");
                goto bail;
            }

            /* Check magic values & derive PC length */
            uint64_t magic = util_getUINT64(dataBuf);
            if (magic == kMagic32) {
                is32bit = true;
            } else if (magic == kMagic64) {
                is32bit = false;
            } else {
                LOG_E("Invalid coverage data file");
                goto bail;
            }
            pos += 8;

            /*
             * Avoid cost of size checks inside raw data read loop by defining the read function
             * & pivot size based on PC length.
             */
            uint64_t(*pReadRawBBAddrFunc) (const uint8_t *) = NULL;
            uint8_t pivot = 0;
            if (is32bit) {
                pReadRawBBAddrFunc = &util_getUINT32;
                pivot = 4;
            } else {
                pReadRawBBAddrFunc = &util_getUINT64;
                pivot = 8;
            }

            while (pos < dataFileSz) {
                uint32_t bbAddr = pReadRawBBAddrFunc(dataBuf + pos);
                pos += pivot;
                if (bbAddr == 0x0) {
                    continue;
                }
                nBBs++;
            }
        }
    }

    /* Successful parsing - update fuzzer worker counters */
    fuzzer->sanCovCnts.hitBBCnt = nBBs;
    ret = true;

 bail:
    unlink(covFile);
    if (dataBuf) {
        munmap(dataBuf, dataFileSz);
    }
    if (dataFd != -1) {
        close(dataFd);
    }
    if (pSanCovDir) {
        closedir(pSanCovDir);
    }
    return ret;
}
Exemplo n.º 9
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;
    }

    /*
     * 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);

    /*
     * Wait for the ptrace to attach
     */
    syscall(__NR_tkill, syscall(__NR_gettid), (uintptr_t) SIGSTOP);

#ifdef __NR_execveat
    syscall(__NR_execveat, hfuzz->exeFd, "", args, environ, AT_EMPTY_PATH);
#endif
    execvp(args[0], args);

    PLOG_E("execvp('%s')", args[0]);

    return false;
}
Exemplo n.º 10
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);
    }
}
Exemplo n.º 11
0
static bool fuzz_prepareFileExternally(honggfuzz_t * hfuzz, fuzzer_t * fuzzer, int rnd_index)
{
    int dstfd = open(fuzzer->fileName, O_CREAT | O_EXCL | O_RDWR, 0644);
    if (dstfd == -1) {
        PLOG_E("Couldn't create a temporary file '%s'", fuzzer->fileName);
        return false;
    }

    LOG_D("Created '%s' as an input file", fuzzer->fileName);

    if (hfuzz->inputFile) {
        size_t fileSz =
            files_readFileToBufMax(hfuzz->files[rnd_index], fuzzer->dynamicFile, hfuzz->maxFileSz);
        if (fileSz == 0UL) {
            LOG_E("Couldn't read '%s'", hfuzz->files[rnd_index]);
            unlink(fuzzer->fileName);
            return false;
        }

        if (files_writeToFd(dstfd, fuzzer->dynamicFile, fileSz) == false) {
            close(dstfd);
            unlink(fuzzer->fileName);
            return false;
        }
    }

    close(dstfd);

    pid_t pid = arch_fork(hfuzz);
    if (pid == -1) {
        PLOG_E("Couldn't fork");
        return false;
    }

    if (!pid) {
        /*
         * child performs the external file modifications
         */
        execl(hfuzz->externalCommand, hfuzz->externalCommand, fuzzer->fileName, NULL);
        PLOG_F("Couldn't execute '%s %s'", hfuzz->externalCommand, fuzzer->fileName);
        return false;
    }

    /*
     * parent waits until child is done fuzzing the input file
     */
    int childStatus;
    int flags = 0;
#if defined(__WNOTHREAD)
    flags |= __WNOTHREAD;
#endif                          /* defined(__WNOTHREAD) */
    while (wait4(pid, &childStatus, flags, NULL) != pid) ;
    if (WIFEXITED(childStatus)) {
        LOG_D("External command exited with status %d", WEXITSTATUS(childStatus));
        return true;
    }
    if (WIFSIGNALED(childStatus)) {
        LOG_E("External command terminated with signal %d", WTERMSIG(childStatus));
        return false;
    }
    LOG_F("External command terminated abnormally, status: %d", childStatus);
    return false;

    abort();                    /* NOTREACHED */
}