aboutsummaryrefslogtreecommitdiffstats
path: root/executor
diff options
context:
space:
mode:
authorDmitry Vyukov <dvyukov@google.com>2017-10-14 20:08:11 +0200
committerDmitry Vyukov <dvyukov@google.com>2017-10-16 14:21:54 +0200
commitd158fb9d3b6e03882c60a51854f149a8d2a637a0 (patch)
tree6f805de40ae6544e68758d5dffd2c14a6e0511cb /executor
parent2647772874bf02bb2fb0cb237ba9aaae1e2a14ec (diff)
executor: add akaros support
Does not work yet, also needs ipc changes.
Diffstat (limited to 'executor')
-rw-r--r--executor/common_akaros.h234
-rw-r--r--executor/executor_akaros.cc125
2 files changed, 359 insertions, 0 deletions
diff --git a/executor/common_akaros.h b/executor/common_akaros.h
new file mode 100644
index 000000000..3a12bac0c
--- /dev/null
+++ b/executor/common_akaros.h
@@ -0,0 +1,234 @@
+// 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.
+
+// This file is shared between executor and csource package.
+
+#include <unistd.h>
+#if defined(SYZ_EXECUTOR) || defined(SYZ_THREADED) || defined(SYZ_COLLIDE)
+#include <pthread.h>
+#include <stdlib.h>
+#endif
+#if defined(SYZ_EXECUTOR) || (defined(SYZ_REPEAT) && defined(SYZ_WAIT_REPEAT))
+#include <errno.h>
+#include <signal.h>
+#include <stdarg.h>
+#include <stdio.h>
+#include <sys/time.h>
+#include <sys/wait.h>
+#include <time.h>
+#endif
+#if defined(SYZ_EXECUTOR) || defined(SYZ_HANDLE_SEGV)
+#include <setjmp.h>
+#include <signal.h>
+#endif
+#if defined(SYZ_EXECUTOR) || defined(SYZ_USE_TMP_DIR)
+#include <stdlib.h>
+#include <sys/stat.h>
+#endif
+#if defined(SYZ_EXECUTOR) || (defined(SYZ_REPEAT) && defined(SYZ_WAIT_REPEAT) && defined(SYZ_USE_TMP_DIR))
+#include <dirent.h>
+#endif
+
+#define doexit exit
+#define NORETURN __attribute__((noreturn))
+
+#include "common.h"
+
+#if defined(SYZ_EXECUTOR) || defined(SYZ_HANDLE_SEGV)
+static __thread int skip_segv;
+static __thread jmp_buf segv_env;
+
+static void segv_handler(int sig, siginfo_t* info, void* ctx)
+{
+ // Generated programs can contain bad (unmapped/protected) addresses,
+ // which cause SIGSEGVs during copyin/copyout.
+ // This handler ignores such crashes to allow the program to proceed.
+ // We additionally opportunistically check that the faulty address
+ // is not within executable data region, because such accesses can corrupt
+ // output region and then fuzzer will fail on corrupted data.
+ uintptr_t addr = (uintptr_t)info->si_addr;
+ const uintptr_t prog_start = 1 << 20;
+ const uintptr_t prog_end = 100 << 20;
+ if (__atomic_load_n(&skip_segv, __ATOMIC_RELAXED) && (addr < prog_start || addr > prog_end)) {
+ // Note: this does not work fully. This skips only over the first SIGSEGV in a thread.
+ // See: https://groups.google.com/forum/#!msg/akaros/d8_uwjAfPic/KAF0WwisBAAJ
+ debug("SIGSEGV on %p, skipping\n", addr);
+ siglongjmp(segv_env, 1);
+ }
+ debug("SIGSEGV on %p, exiting\n", addr);
+ doexit(sig);
+ for (;;) {
+ }
+}
+
+static void install_segv_handler()
+{
+ struct sigaction sa;
+
+ memset(&sa, 0, sizeof(sa));
+ sa.sa_sigaction = segv_handler;
+ sa.sa_flags = SA_NODEFER | SA_SIGINFO;
+ sigemptyset(&sa.sa_mask);
+ sigaction(SIGSEGV, &sa, NULL);
+ sigaction(SIGBUS, &sa, NULL);
+}
+
+#define NONFAILING(...) \
+ { \
+ __atomic_fetch_add(&skip_segv, 1, __ATOMIC_SEQ_CST); \
+ if (_setjmp(segv_env) == 0) { \
+ __VA_ARGS__; \
+ } \
+ __atomic_fetch_sub(&skip_segv, 1, __ATOMIC_SEQ_CST); \
+ }
+#endif
+
+#if defined(SYZ_EXECUTOR) || (defined(SYZ_REPEAT) && defined(SYZ_WAIT_REPEAT))
+static uint64_t current_time_ms()
+{
+ struct timespec ts;
+
+ if (clock_gettime(CLOCK_MONOTONIC, &ts))
+ fail("clock_gettime failed");
+ return (uint64_t)ts.tv_sec * 1000 + (uint64_t)ts.tv_nsec / 1000000;
+}
+#endif
+
+#if defined(SYZ_EXECUTOR) || defined(SYZ_USE_TMP_DIR)
+static void use_temporary_dir()
+{
+ char tmpdir_template[] = "./syzkaller.XXXXXX";
+ char* tmpdir = mkdtemp(tmpdir_template);
+ if (!tmpdir)
+ fail("failed to mkdtemp");
+ if (chmod(tmpdir, 0777))
+ fail("failed to chmod");
+ if (chdir(tmpdir))
+ fail("failed to chdir");
+}
+#endif
+
+#if defined(SYZ_EXECUTOR)
+static void sleep_ms(uint64_t ms)
+{
+ usleep(ms * 1000);
+}
+#endif
+
+#if defined(SYZ_EXECUTOR) || defined(SYZ_FAULT_INJECTION)
+static int inject_fault(int nth)
+{
+ return 0;
+}
+
+static int fault_injected(int fail_fd)
+{
+ return 0;
+}
+#endif
+
+#if defined(SYZ_EXECUTOR) || (defined(SYZ_REPEAT) && defined(SYZ_WAIT_REPEAT) && defined(SYZ_USE_TMP_DIR))
+static void remove_dir(const char* dir)
+{
+ DIR* dp;
+ struct dirent* ep;
+ int iter = 0;
+retry:
+ dp = opendir(dir);
+ if (dp == NULL)
+ return;
+ while ((ep = readdir(dp))) {
+ if (strcmp(ep->d_name, ".") == 0 || strcmp(ep->d_name, "..") == 0)
+ continue;
+ char filename[FILENAME_MAX];
+ snprintf(filename, sizeof(filename), "%s/%s", dir, ep->d_name);
+ struct stat st;
+ if (lstat(filename, &st))
+ return;
+ if (S_ISDIR(st.st_mode)) {
+ remove_dir(filename);
+ continue;
+ }
+ int i;
+ for (i = 0;; i++) {
+ if (unlink(filename) == 0)
+ break;
+ if (errno == EROFS)
+ break;
+ if (errno != EBUSY || i > 100)
+ return;
+ }
+ }
+ closedir(dp);
+ int i;
+ for (i = 0;; i++) {
+ if (rmdir(dir) == 0)
+ break;
+ if (i < 100) {
+ if (errno == EROFS)
+ break;
+ if (errno == ENOTEMPTY) {
+ if (iter < 100) {
+ iter++;
+ goto retry;
+ }
+ }
+ }
+ return;
+ }
+}
+#endif
+
+#if defined(SYZ_REPEAT)
+static void test();
+
+#if defined(SYZ_WAIT_REPEAT)
+void loop()
+{
+ int iter;
+ for (iter = 0;; iter++) {
+#ifdef SYZ_USE_TMP_DIR
+ char cwdbuf[256];
+ sprintf(cwdbuf, "./%d", iter);
+ if (mkdir(cwdbuf, 0777))
+ fail("failed to mkdir");
+#endif
+ int pid = fork();
+ if (pid < 0)
+ fail("clone failed");
+ if (pid == 0) {
+#ifdef SYZ_USE_TMP_DIR
+ if (chdir(cwdbuf))
+ fail("failed to chdir");
+#endif
+ test();
+ doexit(0);
+ }
+ int status = 0;
+ uint64_t start = current_time_ms();
+ for (;;) {
+ int res = waitpid(-1, &status, WNOHANG);
+ if (res == pid)
+ break;
+ usleep(1000);
+ if (current_time_ms() - start > 5 * 1000) {
+ kill(pid, SIGKILL);
+ while (waitpid(-1, &status, 0) != pid) {
+ }
+ break;
+ }
+ }
+#ifdef SYZ_USE_TMP_DIR
+ remove_dir(cwdbuf);
+#endif
+ }
+}
+#else
+void loop()
+{
+ while (1) {
+ test();
+ }
+}
+#endif
+#endif
diff --git a/executor/executor_akaros.cc b/executor/executor_akaros.cc
new file mode 100644
index 000000000..15eafba71
--- /dev/null
+++ b/executor/executor_akaros.cc
@@ -0,0 +1,125 @@
+// 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
+
+#define SYZ_EXECUTOR
+#include "common_akaros.h"
+
+#include "executor_posix.h"
+
+#include "executor.h"
+
+#include "syscalls_akaros.h"
+
+char input_buffer[kMaxInput];
+uint32_t output;
+
+struct in_header {
+ uint64_t magic;
+ uint64_t flags;
+ uint64_t pid;
+ uint64_t progSize;
+ uint64_t execFlags;
+ uint64_t prog[0];
+};
+
+struct out_header {
+ uint64_t magic;
+ uint64_t status;
+};
+
+const uint64_t kInMagic = 0xbadc0ffee;
+const uint64_t kOutMagic = 0xbadf00d;
+
+int main(int argc, char** argv)
+{
+ if (argc == 2 && strcmp(argv[1], "version") == 0) {
+ puts("akaros " GOARCH " " SYZ_REVISION " " GIT_REVISION);
+ return 0;
+ }
+
+ use_temporary_dir();
+ install_segv_handler();
+ for (;;) {
+ size_t pos = 0;
+ in_header* hdr = (in_header*)input_buffer;
+ for (;;) {
+ int rv = read(0, input_buffer + pos, sizeof(input_buffer) - pos);
+ if (rv < 0)
+ fail("read failed");
+ if (rv == 0)
+ fail("stdin closed, read %d", (int)pos);
+ pos += rv;
+ if (pos > sizeof(in_header)) {
+ if (hdr->magic != kInMagic)
+ fail("bad header magic 0x%llx", hdr->magic);
+ if (pos > sizeof(in_header) + hdr->progSize)
+ fail("excessive input data");
+ if (pos == sizeof(in_header) + hdr->progSize)
+ break;
+ }
+ }
+ flag_debug = hdr->flags & (1 << 0);
+ flag_threaded = hdr->flags & (1 << 2);
+ flag_collide = hdr->flags & (1 << 3);
+ if (!flag_threaded)
+ flag_collide = false;
+ debug("input %d, threaded=%d collide=%d pid=%llu\n",
+ pos, flag_threaded, flag_collide, hdr->pid);
+ char cwdbuf[128] = "/syz-tmpXXXXXX";
+ mkdtemp(cwdbuf);
+ int pid = fork();
+ if (pid < 0)
+ fail("fork failed");
+ if (pid == 0) {
+ close(0);
+ dup2(2, 1);
+ if (chdir(cwdbuf))
+ fail("chdir failed");
+ execute_one(hdr->prog);
+ doexit(0);
+ }
+ int status = 0;
+ while (waitpid(pid, &status, 0) != pid) {
+ }
+ remove_dir(cwdbuf);
+ out_header out;
+ out.magic = kOutMagic;
+ out.status = 0;
+ if (write(1, &out, sizeof(out)) != sizeof(out))
+ fail("stdout write failed");
+ }
+ return 0;
+}
+
+long execute_syscall(call_t* c, long a0, long a1, long a2, long a3, long a4, long a5, long a6, long a7, long a8)
+{
+ return syscall(c->sys_nr, a0, a1, a2, a3, a4, a5, a6, a7, a8);
+}
+
+void cover_open()
+{
+}
+
+void cover_enable(thread_t* th)
+{
+}
+
+void cover_reset(thread_t* th)
+{
+}
+
+uint64_t read_cover_size(thread_t* th)
+{
+ return 0;
+}
+
+uint32_t* write_output(uint32_t v)
+{
+ return &output;
+}
+
+void write_completed(uint32_t completed)
+{
+}