diff options
| author | Patrick Meyer <meyerpatrick@google.com> | 2021-05-06 23:08:51 +0200 |
|---|---|---|
| committer | Dmitry Vyukov <dvyukov@google.com> | 2021-05-20 13:29:54 +0200 |
| commit | e5d3015f47ca8f88dfedb85243e8e77957bd51cd (patch) | |
| tree | 6c21d24e729d897ef74ab4583f1ab5d0537c8ec0 /executor/executor_darwin.h | |
| parent | a39775d33f7a69adcf1f5b39474a7dba73c0d049 (diff) | |
executor: initial darwin support
Diffstat (limited to 'executor/executor_darwin.h')
| -rw-r--r-- | executor/executor_darwin.h | 123 |
1 files changed, 123 insertions, 0 deletions
diff --git a/executor/executor_darwin.h b/executor/executor_darwin.h new file mode 100644 index 000000000..c16691663 --- /dev/null +++ b/executor/executor_darwin.h @@ -0,0 +1,123 @@ +// Copyright 2021 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 <math.h> +#include <sys/ioctl.h> +#include <sys/mman.h> + +// FIXME(HerrSpace): As executor is written in C++, we need to make this patch: +// -struct ksancov_trace *trace = (void *)mc.ptr; into +// +struct ksancov_trace *trace = (ksancov_trace *)mc.ptr; +// twice to make this header compile. This used to be C++ friendly in Catalina, +// but was broken in xnu source drop 7195.50.7.100.1. +#include <ksancov.h> + +static void os_init(int argc, char** argv, void* data, size_t data_size) +{ + // Note: We use is_kernel_64_bit in executor.cc to decide which PC pointer + // size to expect. However in KSANCOV we always get back 32bit pointers, + // which then get reconstructed to 64bit pointers by adding a fixed offset. + is_kernel_64_bit = false; + + int prot = PROT_READ | PROT_WRITE | PROT_EXEC; + int flags = MAP_ANON | MAP_PRIVATE | MAP_FIXED; + + if (mmap(data, data_size, prot, flags, -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]); +} + +static void cover_open(cover_t* cov, bool extra) +{ + int fd = ksancov_open(); + if (fd == -1) + fail("open of /dev/ksancov failed"); + if (dup2(fd, cov->fd) < 0) + failmsg("failed to dup cover fd", "from=%d, to=%d", fd, cov->fd); + close(fd); + + // Note: In the other KCOV implementations we pass the shared memory size + // to the initial ioctl, before mmaping. KSANCOV reversed this logic. + // Here we instead pass the maximum number of traced PCs to the initial + // KSANCOV_IOC_TRACE ioctl. We then pass a size_t pointer to the second + // KSANCOV_IOC_MAP ioctl, hence the kernel is instead telling us the final + // size. We have a sanity check in executor.cc checking that cov.size isn't + // larger or equal to kCoverSize. To make sure that assumption holds, we're + // calculating the max_entries accordingly. + size_t max_entries = floor( + (kCoverSize - sizeof(struct ksancov_trace)) / sizeof(uint32_t)); + + // Note: XNUs KSANCOV API forces us to choose the mode after opening the + // device and before mmaping the coverage buffer. As the function we are + // in, cover_open(), expects us to mmap here, we are forced to commit to a + // mode here as well. For other OSes we commit to a mode in cover_enable(), + // based on collect_comps. This is not really a problem though, as TRACE_PC + // is the only relevant mode for us for now. XNU doesn't support TRACE_CMP + // and we don't care about the counters/nedges modes in XNU. + if (ksancov_mode_trace(cov->fd, max_entries)) + fail("ioctl init trace write failed"); + + uintptr_t mmap_ptr = 0; + size_t mmap_alloc_size = 0; + if (ksancov_map(cov->fd, &mmap_ptr, &mmap_alloc_size)) + fail("cover mmap failed"); + + // Sanity check to make sure our assumptions in the max_entries calculation + // hold up. + if (mmap_alloc_size > kCoverSize) + fail("mmap allocation size larger than anticipated"); + + cov->data = (char*)mmap_ptr; + cov->data_end = cov->data + mmap_alloc_size; +} + +static void cover_protect(cover_t* cov) +{ +} + +static void cover_unprotect(cover_t* cov) +{ +} + +static void cover_enable(cover_t* cov, bool collect_comps, bool extra) +{ + if (collect_comps) + fail("TRACE_CMP not implemented on darwin"); + if (extra) + fail("Extra coverage collection not implemented on darwin"); + // Note: we are already comitted to TRACE_PC here, hence we don't make use + // of collect_comps. For more details see the comment in cover_open(). + if (ksancov_thread_self(cov->fd)) + exitf("cover enable write trace failed"); +} + +static void cover_reset(cover_t* cov) +{ + ksancov_reset((struct ksancov_header*)cov->data); + ksancov_start((struct ksancov_header*)cov->data); +} + +static void cover_collect(cover_t* cov) +{ + struct ksancov_trace* trace = (struct ksancov_trace*)cov->data; + cov->size = ksancov_trace_head(trace); + cov->data_offset = ((int64_t) & (trace->pcs)) - ((int64_t)(cov->data)); + cov->pc_offset = trace->offset; +} + +static bool use_cover_edges(uint64 pc) +{ + return true; +} |
