aboutsummaryrefslogtreecommitdiffstats
path: root/executor/executor_darwin.h
diff options
context:
space:
mode:
authorPatrick Meyer <meyerpatrick@google.com>2021-05-06 23:08:51 +0200
committerDmitry Vyukov <dvyukov@google.com>2021-05-20 13:29:54 +0200
commite5d3015f47ca8f88dfedb85243e8e77957bd51cd (patch)
tree6c21d24e729d897ef74ab4583f1ab5d0537c8ec0 /executor/executor_darwin.h
parenta39775d33f7a69adcf1f5b39474a7dba73c0d049 (diff)
executor: initial darwin support
Diffstat (limited to 'executor/executor_darwin.h')
-rw-r--r--executor/executor_darwin.h123
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;
+}