// Copyright 2017 syzkaller project authors. All rights reserved. // Use of this source code is governed by Apache 2 LICENSE that can be found in the LICENSE file. #include #include #include #include #include #include #include #include #if GOOS_openbsd #include #endif static void os_init(int argc, char** argv, void* data, size_t data_size) { #if GOOS_openbsd // W^X not allowed by default on OpenBSD. int prot = PROT_READ | PROT_WRITE; #elif GOOS_netbsd // W^X not allowed by default on NetBSD (PaX MPROTECT). int prot = PROT_READ | PROT_WRITE | PROT_MPROTECT(PROT_EXEC); #else int prot = PROT_READ | PROT_WRITE | PROT_EXEC; #endif if (mmap(data, data_size, prot, MAP_ANON | MAP_PRIVATE | MAP_FIXED, -1, 0) != data) fail("mmap of data segment failed"); // Makes sure the file descriptor limit is sufficient to map control pipes. struct rlimit rlim; rlim.rlim_cur = rlim.rlim_max = kMaxFd; setrlimit(RLIMIT_NOFILE, &rlim); } static intptr_t execute_syscall(const call_t* c, intptr_t a[kMaxArgs]) { if (c->call) return c->call(a[0], a[1], a[2], a[3], a[4], a[5], a[6], a[7], a[8]); return __syscall(c->sys_nr, a[0], a[1], a[2], a[3], a[4], a[5], a[6], a[7], a[8]); } #if GOOS_freebsd || GOOS_openbsd || GOOS_netbsd // KCOV support was added to FreeBSD in https://svnweb.freebsd.org/changeset/base/342962 // KCOV support added to NetBSD in https://github.com/NetBSD/src/commit/080a26f01d79ded8a2154c128950d1d663c6ca88 #include static void cover_open(cover_t* cov, bool extra) { int fd = open("/dev/kcov", O_RDWR); if (fd == -1) fail("open of /dev/kcov failed"); if (dup2(fd, cov->fd) < 0) fail("failed to dup2(%d, %d) cover fd", fd, cov->fd); close(fd); #if GOOS_freebsd if (ioctl(cov->fd, KIOSETBUFSIZE, kCoverSize)) fail("ioctl init trace write failed"); #elif GOOS_openbsd unsigned long cover_size = kCoverSize; if (ioctl(cov->fd, KIOSETBUFSIZE, &cover_size)) fail("ioctl init trace write failed"); #elif GOOS_netbsd uint64_t cover_size = kCoverSize; if (ioctl(cov->fd, KCOV_IOC_SETBUFSIZE, &cover_size)) fail("ioctl init trace write failed"); #endif #if GOOS_freebsd || GOOS_netbsd size_t mmap_alloc_size = kCoverSize * KCOV_ENTRY_SIZE; #else size_t mmap_alloc_size = kCoverSize * (is_kernel_64_bit ? 8 : 4); #endif void* mmap_ptr = mmap(NULL, mmap_alloc_size, PROT_READ | PROT_WRITE, MAP_SHARED, cov->fd, 0); if (mmap_ptr == MAP_FAILED) fail("cover mmap failed"); cov->data = (char*)mmap_ptr; cov->data_end = cov->data + mmap_alloc_size; } static void cover_protect(cover_t* cov) { #if GOOS_freebsd || GOOS_netbsd size_t mmap_alloc_size = kCoverSize * KCOV_ENTRY_SIZE; long page_size = sysconf(_SC_PAGESIZE); if (page_size > 0) mprotect(cov->data + page_size, mmap_alloc_size - page_size, PROT_READ); #elif GOOS_openbsd int mib[2], page_size; size_t len; size_t mmap_alloc_size = kCoverSize * sizeof(uintptr_t); mib[0] = CTL_HW; mib[1] = HW_PAGESIZE; len = sizeof(page_size); if (sysctl(mib, ARRAY_SIZE(mib), &page_size, &len, NULL, 0) != -1) mprotect(cov->data + page_size, mmap_alloc_size - page_size, PROT_READ); #endif } static void cover_unprotect(cover_t* cov) { #if GOOS_freebsd || GOOS_netbsd size_t mmap_alloc_size = kCoverSize * KCOV_ENTRY_SIZE; mprotect(cov->data, mmap_alloc_size, PROT_READ | PROT_WRITE); #elif GOOS_openbsd size_t mmap_alloc_size = kCoverSize * sizeof(uintptr_t); mprotect(cov->data, mmap_alloc_size, PROT_READ | PROT_WRITE); #endif } static void cover_enable(cover_t* cov, bool collect_comps, bool extra) { int kcov_mode = collect_comps ? KCOV_MODE_TRACE_CMP : KCOV_MODE_TRACE_PC; #if GOOS_freebsd // FreeBSD uses an int as the third argument. if (ioctl(cov->fd, KIOENABLE, kcov_mode)) exitf("cover enable write trace failed, mode=%d", kcov_mode); #elif GOOS_openbsd // OpenBSD uses an pointer to an int as the third argument. if (ioctl(cov->fd, KIOENABLE, &kcov_mode)) exitf("cover enable write trace failed, mode=%d", kcov_mode); #elif GOOS_netbsd if (ioctl(cov->fd, KCOV_IOC_ENABLE, &kcov_mode)) exitf("cover enable write trace failed, mode=%d", kcov_mode); #endif } static void cover_reset(cover_t* cov) { *(uint64*)cov->data = 0; } static void cover_collect(cover_t* cov) { cov->size = *(uint64*)cov->data; } static bool cover_check(uint32 pc) { return true; } static bool cover_check(uint64 pc) { return true; } #else #include "nocover.h" #endif