diff options
| author | Dmitry Vyukov <dvyukov@google.com> | 2017-10-14 20:08:11 +0200 |
|---|---|---|
| committer | Dmitry Vyukov <dvyukov@google.com> | 2017-10-16 14:21:54 +0200 |
| commit | d158fb9d3b6e03882c60a51854f149a8d2a637a0 (patch) | |
| tree | 6f805de40ae6544e68758d5dffd2c14a6e0511cb /executor | |
| parent | 2647772874bf02bb2fb0cb237ba9aaae1e2a14ec (diff) | |
executor: add akaros support
Does not work yet, also needs ipc changes.
Diffstat (limited to 'executor')
| -rw-r--r-- | executor/common_akaros.h | 234 | ||||
| -rw-r--r-- | executor/executor_akaros.cc | 125 |
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) +{ +} |
