aboutsummaryrefslogtreecommitdiffstats
path: root/executor
diff options
context:
space:
mode:
Diffstat (limited to 'executor')
-rw-r--r--executor/common.h17
-rw-r--r--executor/common_bsd.h26
-rw-r--r--executor/executor.cc23
-rw-r--r--executor/executor_darwin.h123
-rw-r--r--executor/gen.go2
5 files changed, 170 insertions, 21 deletions
diff --git a/executor/common.h b/executor/common.h
index b5a1ba593..c8bfccb9b 100644
--- a/executor/common.h
+++ b/executor/common.h
@@ -17,6 +17,13 @@
#if GOOS_freebsd || GOOS_test && HOSTGOOS_freebsd
#include <sys/endian.h> // for htobe*.
+#elif GOOS_darwin
+#include <libkern/OSByteOrder.h>
+#define htobe16(x) OSSwapHostToBigInt16(x)
+#define htobe32(x) OSSwapHostToBigInt32(x)
+#define htobe64(x) OSSwapHostToBigInt64(x)
+#define le16toh(x) OSSwapLittleToHostInt16(x)
+#define htole16(x) OSSwapHostToLittleInt16(x)
#elif GOOS_windows
#define htobe16 _byteswap_ushort
#define htobe32 _byteswap_ulong
@@ -51,7 +58,7 @@ NORETURN void doexit(int status)
#if SYZ_EXECUTOR || SYZ_MULTI_PROC || SYZ_REPEAT && SYZ_CGROUPS || \
SYZ_NET_DEVICES || __NR_syz_mount_image || __NR_syz_read_part_table || \
__NR_syz_usb_connect || __NR_syz_usb_connect_ath9k || \
- (GOOS_freebsd || GOOS_openbsd || GOOS_netbsd) && SYZ_NET_INJECTION
+ (GOOS_freebsd || GOOS_darwin || GOOS_openbsd || GOOS_netbsd) && SYZ_NET_INJECTION
static unsigned long long procid;
#endif
@@ -208,7 +215,7 @@ static void use_temporary_dir(void)
#endif
#endif
-#if GOOS_akaros || GOOS_netbsd || GOOS_freebsd || GOOS_openbsd || GOOS_test
+#if GOOS_akaros || GOOS_netbsd || GOOS_freebsd || GOOS_darwin || GOOS_openbsd || GOOS_test
#if (SYZ_EXECUTOR || SYZ_REPEAT) && SYZ_EXECUTOR_USES_FORK_SERVER && (SYZ_EXECUTOR || SYZ_USE_TMP_DIR)
#include <dirent.h>
#include <errno.h>
@@ -311,7 +318,7 @@ static void thread_start(void* (*fn)(void*), void* arg)
#endif
#endif
-#if GOOS_freebsd || GOOS_netbsd || GOOS_openbsd || GOOS_akaros || GOOS_test
+#if GOOS_freebsd || GOOS_darwin || GOOS_netbsd || GOOS_openbsd || GOOS_akaros || GOOS_test
#if SYZ_EXECUTOR || SYZ_THREADED
#include <pthread.h>
@@ -428,7 +435,7 @@ static uint16 csum_inet_digest(struct csum_inet* csum)
#if GOOS_akaros
#include "common_akaros.h"
-#elif GOOS_freebsd || GOOS_netbsd || GOOS_openbsd
+#elif GOOS_freebsd || GOOS_darwin || GOOS_netbsd || GOOS_openbsd
#include "common_bsd.h"
#elif GOOS_fuchsia
#include "common_fuchsia.h"
@@ -442,7 +449,7 @@ static uint16 csum_inet_digest(struct csum_inet* csum)
#error "unknown OS"
#endif
-#if SYZ_EXECUTOR || __NR_syz_execute_func
+#if !GOOS_darwin && SYZ_EXECUTOR || __NR_syz_execute_func
// syz_execute_func(text ptr[in, text[taget]])
static long syz_execute_func(volatile long text)
{
diff --git a/executor/common_bsd.h b/executor/common_bsd.h
index 1bed0cf3c..398d402fb 100644
--- a/executor/common_bsd.h
+++ b/executor/common_bsd.h
@@ -89,12 +89,11 @@ static int fault_injected(int fd)
#endif
-#if GOOS_openbsd
-
+#if GOOS_openbsd || GOOS_darwin
#define __syscall syscall
+#endif // GOOS_openbsd || GOOS_darwin
-#if SYZ_EXECUTOR || __NR_syz_open_pts
-
+#if GOOS_openbsd && (SYZ_EXECUTOR || __NR_syz_open_pts)
#include <termios.h>
#include <util.h>
@@ -110,15 +109,18 @@ static uintptr_t syz_open_pts(void)
close(master);
return slave;
}
-
-#endif // SYZ_EXECUTOR || __NR_syz_open_pts
-
-#endif // GOOS_openbsd
+#endif // GOOS_openbsd && (SYZ_EXECUTOR || __NR_syz_open_pts)
#if SYZ_EXECUTOR || SYZ_NET_INJECTION
#include <fcntl.h>
+// FIXME(HerrSpace): XNU has xnu/bsd/netinet/if_tun.h, but it's not shipped in
+// the installed header files (probably internal). It's also likely not very
+// interesting, as XNU only ships a tun driver called utun, not tap which is
+// currently required for SYZ_NET_INJECTION anyhow.
+#if !GOOS_darwin
#include <net/if_tun.h>
+#endif // !GOOS_darwin
#include <sys/types.h>
static int tunfd = -1;
@@ -269,7 +271,7 @@ static void initialize_tun(int tun_id)
#endif // SYZ_EXECUTOR || SYZ_NET_INJECTION
-#if SYZ_EXECUTOR || __NR_syz_emit_ethernet && SYZ_NET_INJECTION
+#if !GOOS_darwin && SYZ_EXECUTOR || __NR_syz_emit_ethernet && SYZ_NET_INJECTION
#include <stdbool.h>
#include <sys/uio.h>
@@ -287,7 +289,7 @@ static long syz_emit_ethernet(volatile long a0, volatile long a1)
}
#endif
-#if SYZ_EXECUTOR || SYZ_NET_INJECTION && (__NR_syz_extract_tcp_res || SYZ_REPEAT)
+#if !GOOS_darwin && SYZ_EXECUTOR || SYZ_NET_INJECTION && (__NR_syz_extract_tcp_res || SYZ_REPEAT)
#include <errno.h>
static int read_tun(char* data, int size)
@@ -305,14 +307,14 @@ static int read_tun(char* data, int size)
}
#endif
-#if SYZ_EXECUTOR || __NR_syz_extract_tcp_res && SYZ_NET_INJECTION
+#if !GOOS_darwin && SYZ_EXECUTOR || __NR_syz_extract_tcp_res && SYZ_NET_INJECTION
struct tcp_resources {
uint32 seq;
uint32 ack;
};
-#if GOOS_freebsd
+#if GOOS_freebsd || GOOS_darwin
#include <net/ethernet.h>
#else
#include <net/ethertypes.h>
diff --git a/executor/executor.cc b/executor/executor.cc
index 161616be4..254a5d74a 100644
--- a/executor/executor.cc
+++ b/executor/executor.cc
@@ -213,6 +213,19 @@ struct cover_t {
uint32 size;
char* data;
char* data_end;
+ // Note: On everything but darwin the first value in data is the count of
+ // recorded PCs, followed by the PCs. We therefore set data_offset to the
+ // size of one PC.
+ // On darwin data points to an instance of the ksancov_trace struct. Here we
+ // set data_offset to the offset between data and the structs 'pcs' member,
+ // which contains the PCs.
+ intptr_t data_offset;
+ // Note: On everything but darwin this is 0, as the PCs contained in data
+ // are already correct. XNUs KSANCOV API, however, chose to always squeeze
+ // PCs into 32 bit. To make the recorded PC fit, KSANCOV substracts a fixed
+ // offset (VM_MIN_KERNEL_ADDRESS for AMD64) and then truncates the result to
+ // uint32_t. We get this from the 'offset' member in ksancov_trace.
+ intptr_t pc_offset;
};
struct thread_t {
@@ -356,6 +369,8 @@ static void setup_features(char** enable, int n);
#include "executor_akaros.h"
#elif GOOS_freebsd || GOOS_netbsd || GOOS_openbsd
#include "executor_bsd.h"
+#elif GOOS_darwin
+#include "executor_darwin.h"
#elif GOOS_windows
#include "executor_windows.h"
#elif GOOS_test
@@ -895,12 +910,12 @@ void write_coverage_signal(cover_t* cov, uint32* signal_count_pos, uint32* cover
{
// Write out feedback signals.
// Currently it is code edges computed as xor of two subsequent basic block PCs.
- cover_data_t* cover_data = ((cover_data_t*)cov->data) + 1;
+ cover_data_t* cover_data = (cover_data_t*)(cov->data + cov->data_offset);
uint32 nsig = 0;
cover_data_t prev_pc = 0;
bool prev_filter = true;
for (uint32 i = 0; i < cov->size; i++) {
- cover_data_t pc = cover_data[i];
+ cover_data_t pc = cover_data[i] + cov->pc_offset;
uint32 sig = pc;
if (use_cover_edges(pc))
sig ^= hash(prev_pc);
@@ -932,7 +947,7 @@ void write_coverage_signal(cover_t* cov, uint32* signal_count_pos, uint32* cover
// Truncate PCs to uint32 assuming that they fit into 32-bits.
// True for x86_64 and arm64 without KASLR.
for (uint32 i = 0; i < cover_size; i++)
- write_output(cover_data[i]);
+ write_output(cover_data[i] + cov->pc_offset);
*cover_count_pos = cover_size;
}
#endif
@@ -1098,6 +1113,8 @@ void thread_create(thread_t* th, int id)
th->created = true;
th->id = id;
th->executing = false;
+ th->cov.data_offset = is_kernel_64_bit ? sizeof(uint64_t) : sizeof(uint32_t);
+ th->cov.pc_offset = 0;
event_init(&th->ready);
event_init(&th->done);
event_set(&th->done);
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;
+}
diff --git a/executor/gen.go b/executor/gen.go
index e00528816..a4932913f 100644
--- a/executor/gen.go
+++ b/executor/gen.go
@@ -1,7 +1,7 @@
// 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.
-// +build amd64,!freebsd,!openbsd,!netbsd
+// +build amd64,!freebsd,!darwin,!openbsd,!netbsd
//go:generate bash -c "gcc kvm_gen.cc kvm.S -o kvm_gen && ./kvm_gen > kvm.S.h && rm ./kvm_gen"