diff options
| -rw-r--r-- | executor/common.h | 16 | ||||
| -rw-r--r-- | executor/common_fuchsia.h | 284 | ||||
| -rw-r--r-- | executor/common_linux.h | 29 | ||||
| -rw-r--r-- | executor/executor_fuchsia.cc | 19 | ||||
| -rw-r--r-- | executor/executor_fuchsia.h | 62 | ||||
| -rw-r--r-- | executor/executor_linux.cc | 15 | ||||
| -rw-r--r-- | pkg/csource/akaros_common.go | 16 | ||||
| -rw-r--r-- | pkg/csource/common.go | 9 | ||||
| -rw-r--r-- | pkg/csource/csource.go | 83 | ||||
| -rw-r--r-- | pkg/csource/csource_test.go | 61 | ||||
| -rw-r--r-- | pkg/csource/freebsd_common.go | 16 | ||||
| -rw-r--r-- | pkg/csource/fuchsia_common.go | 657 | ||||
| -rw-r--r-- | pkg/csource/gen.go | 21 | ||||
| -rw-r--r-- | pkg/csource/linux_common.go | 45 | ||||
| -rw-r--r-- | pkg/csource/netbsd_common.go | 16 | ||||
| -rw-r--r-- | pkg/csource/options.go | 54 | ||||
| -rw-r--r-- | pkg/csource/options_test.go | 14 | ||||
| -rw-r--r-- | pkg/repro/repro.go | 25 | ||||
| -rw-r--r-- | pkg/repro/repro_test.go | 2 |
19 files changed, 1253 insertions, 191 deletions
diff --git a/executor/common.h b/executor/common.h index fe1290f73..880c479bf 100644 --- a/executor/common.h +++ b/executor/common.h @@ -8,7 +8,8 @@ #if defined(SYZ_EXECUTOR) || (defined(SYZ_REPEAT) && defined(SYZ_WAIT_REPEAT)) || \ defined(SYZ_USE_TMP_DIR) || defined(SYZ_TUN_ENABLE) || defined(SYZ_SANDBOX_NAMESPACE) || \ defined(SYZ_SANDBOX_NONE) || defined(SYZ_SANDBOX_SETUID) || defined(SYZ_FAULT_INJECTION) || \ - defined(__NR_syz_kvm_setup_cpu) || defined(__NR_syz_init_net_socket) + defined(__NR_syz_kvm_setup_cpu) || defined(__NR_syz_init_net_socket) || \ + defined(__NR_syz_mmap) #include <errno.h> #include <stdarg.h> #include <stdio.h> @@ -74,7 +75,7 @@ struct call_t { #if defined(SYZ_EXECUTOR) || (defined(SYZ_REPEAT) && defined(SYZ_WAIT_REPEAT)) || \ defined(SYZ_USE_TMP_DIR) || defined(SYZ_TUN_ENABLE) || defined(SYZ_SANDBOX_NAMESPACE) || \ defined(SYZ_SANDBOX_NONE) || defined(SYZ_SANDBOX_SETUID) || defined(SYZ_FAULT_INJECTION) || \ - defined(__NR_syz_kvm_setup_cpu) + defined(__NR_syz_kvm_setup_cpu) || defined(__NR_syz_mmap) const int kFailStatus = 67; const int kRetryStatus = 69; #endif @@ -83,11 +84,12 @@ const int kRetryStatus = 69; const int kErrorStatus = 68; #endif -#if defined(SYZ_EXECUTOR) || (defined(SYZ_REPEAT) && defined(SYZ_WAIT_REPEAT)) || \ - defined(SYZ_USE_TMP_DIR) || defined(SYZ_TUN_ENABLE) || defined(SYZ_SANDBOX_NAMESPACE) || \ - defined(SYZ_SANDBOX_NONE) || defined(SYZ_SANDBOX_SETUID) || defined(__NR_syz_kvm_setup_cpu) || \ - defined(__NR_syz_init_net_socket) && \ - (defined(SYZ_SANDBOX_NONE) || defined(SYZ_SANDBOX_SETUID) || defined(SYZ_SANDBOX_NAMESPACE)) +#if defined(SYZ_EXECUTOR) || (defined(SYZ_REPEAT) && defined(SYZ_WAIT_REPEAT)) || \ + defined(SYZ_USE_TMP_DIR) || defined(SYZ_TUN_ENABLE) || defined(SYZ_SANDBOX_NAMESPACE) || \ + defined(SYZ_SANDBOX_NONE) || defined(SYZ_SANDBOX_SETUID) || defined(__NR_syz_kvm_setup_cpu) || \ + defined(__NR_syz_init_net_socket) && \ + (defined(SYZ_SANDBOX_NONE) || defined(SYZ_SANDBOX_SETUID) || defined(SYZ_SANDBOX_NAMESPACE)) || \ + defined(__NR_syz_mmap) // logical error (e.g. invalid input program), use as an assert() alernative NORETURN PRINTF static void fail(const char* msg, ...) { diff --git a/executor/common_fuchsia.h b/executor/common_fuchsia.h index 8b5b9f6a0..7db03e05e 100644 --- a/executor/common_fuchsia.h +++ b/executor/common_fuchsia.h @@ -3,28 +3,36 @@ // This file is shared between executor and csource package. +#ifndef _GNU_SOURCE +#define _GNU_SOURCE +#endif + #include <fcntl.h> #include <poll.h> +#include <signal.h> #include <sys/file.h> #include <sys/stat.h> +#include <sys/time.h> #include <sys/types.h> #include <sys/uio.h> +#include <time.h> #include <unistd.h> #include <utime.h> #include <zircon/process.h> #include <zircon/syscalls.h> -#if defined(SYZ_EXECUTOR) || defined(SYZ_THREADED) || defined(SYZ_COLLIDE) +#if defined(SYZ_EXECUTOR) || defined(SYZ_THREADED) || defined(SYZ_COLLIDE) || defined(SYZ_HANDLE_SEGV) #include <pthread.h> #include <stdlib.h> #endif +#if defined(SYZ_EXECUTOR) || (defined(SYZ_REPEAT) && defined(SYZ_WAIT_REPEAT) && defined(SYZ_USE_TMP_DIR)) +#include <dirent.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 <zircon/syscalls/debug.h> @@ -36,7 +44,8 @@ #if defined(SYZ_EXECUTOR) || (defined(SYZ_REPEAT) && defined(SYZ_WAIT_REPEAT)) || \ defined(SYZ_USE_TMP_DIR) || defined(SYZ_HANDLE_SEGV) || defined(SYZ_TUN_ENABLE) || \ defined(SYZ_SANDBOX_NAMESPACE) || defined(SYZ_SANDBOX_SETUID) || \ - defined(SYZ_SANDBOX_NONE) || defined(SYZ_FAULT_INJECTION) || defined(__NR_syz_kvm_setup_cpu) + defined(SYZ_SANDBOX_NONE) || defined(SYZ_FAULT_INJECTION) || \ + defined(__NR_syz_mmap) __attribute__((noreturn)) static void doexit(int status) { _exit(status); @@ -72,7 +81,7 @@ static void* ex_handler(void* arg) continue; } debug("got exception packet: type=%d status=%d tid=%llu\n", - packet.type, packet.status, static_cast<unsigned long long>(packet.exception.tid)); + packet.type, packet.status, (unsigned long long)(packet.exception.tid)); zx_handle_t thread; status = zx_object_get_child(zx_process_self(), packet.exception.tid, ZX_RIGHT_SAME_RIGHTS, &thread); @@ -95,12 +104,14 @@ static void* ex_handler(void* arg) #error "unsupported arch" #endif status = zx_thread_write_state(thread, ZX_THREAD_STATE_GENERAL_REGS, ®s, sizeof(regs)); - if (status != ZX_OK) + if (status != ZX_OK) { debug("zx_thread_write_state failed: %d\n", status); + } } status = zx_task_resume(thread, ZX_RESUME_EXCEPTION); - if (status != ZX_OK) + if (status != ZX_OK) { debug("zx_task_resume failed: %d\n", status); + } zx_handle_close(thread); } doexit(1); @@ -167,7 +178,7 @@ long syz_mmap(size_t addr, size_t size) zx_info_vmar_t info; zx_status_t status = zx_object_get_info(root, ZX_INFO_VMAR, &info, sizeof(info), 0, 0); if (status != ZX_OK) - error("zx_object_get_info(ZX_INFO_VMAR) failed: %d", status); + fail("zx_object_get_info(ZX_INFO_VMAR) failed: %d", status); zx_handle_t vmo; status = zx_vmo_create(size, 0, &vmo); if (status != ZX_OK) @@ -225,3 +236,260 @@ long syz_future_time(long when) return now + delta_ms * 1000 * 1000; } #endif + +#if defined(SYZ_SANDBOX_NONE) +static void loop(); +static int do_sandbox_none(void) +{ + loop(); + return 0; +} +#endif + +#if 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_REPEAT) && defined(SYZ_WAIT_REPEAT) && defined(SYZ_USE_TMP_DIR)) +static void remove_dir(const char* dir) +{ + struct dirent* ep; + DIR* dp = opendir(dir); + if (dp == NULL) + exitf("opendir(%s) failed", dir); + 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)) + exitf("lstat(%s) failed", filename); + if (S_ISDIR(st.st_mode)) { + remove_dir(filename); + continue; + } + if (unlink(filename)) + exitf("unlink(%s) failed", filename); + } + closedir(dp); + if (rmdir(dir)) + exitf("rmdir(%s) failed", dir); +} +#endif + +#if defined(SYZ_EXECUTOR) || defined(SYZ_REPEAT) +static void execute_one(); +extern unsigned long long procid; + +#if defined(SYZ_EXECUTOR) +void reply_handshake(); +void receive_execute(); +void reply_execute(int status); +extern uint32* output_data; +extern uint32* output_pos; +#endif + +#if defined(SYZ_WAIT_REPEAT) +static void loop() +{ +#if defined(SYZ_EXECUTOR) + // Tell parent that we are ready to serve. + reply_handshake(); +#endif + int iter; + for (iter = 0;; iter++) { +#if defined(SYZ_EXECUTOR) || defined(SYZ_USE_TMP_DIR) + // Create a new private work dir for this test (removed at the end of the loop). + char cwdbuf[32]; + sprintf(cwdbuf, "./%d", iter); + if (mkdir(cwdbuf, 0777)) + fail("failed to mkdir"); +#endif +#if defined(SYZ_EXECUTOR) + // TODO: consider moving the read into the child. + // Potentially it can speed up things a bit -- when the read finishes + // we already have a forked worker process. + receive_execute(); +#endif + int pid = fork(); + if (pid < 0) + fail("clone failed"); + if (pid == 0) { +#if defined(SYZ_EXECUTOR) || defined(SYZ_USE_TMP_DIR) + if (chdir(cwdbuf)) + fail("failed to chdir"); +#endif +#if defined(SYZ_EXECUTOR) + close(kInPipeFd); + close(kOutPipeFd); +#endif +#if defined(SYZ_EXECUTOR) + output_pos = output_data; +#endif + execute_one(); + debug("worker exiting\n"); + doexit(0); + } + debug("spawned worker pid %d\n", pid); + + // We used to use sigtimedwait(SIGCHLD) to wait for the subprocess. + // But SIGCHLD is also delivered when a process stops/continues, + // so it would require a loop with status analysis and timeout recalculation. + // SIGCHLD should also unblock the usleep below, so the spin loop + // should be as efficient as sigtimedwait. + int status = 0; + uint64 start = current_time_ms(); +#if defined(SYZ_EXECUTOR) + uint64 last_executed = start; + uint32 executed_calls = __atomic_load_n(output_data, __ATOMIC_RELAXED); +#endif + for (;;) { + int res = waitpid(-1, &status, WNOHANG); + if (res == pid) { + debug("waitpid(%d)=%d\n", pid, res); + break; + } + usleep(1000); +#if defined(SYZ_EXECUTOR) + // Even though the test process executes exit at the end + // and execution time of each syscall is bounded by 20ms, + // this backup watchdog is necessary and its performance is important. + // The problem is that exit in the test processes can fail (sic). + // One observed scenario is that the test processes prohibits + // exit_group syscall using seccomp. Another observed scenario + // is that the test processes setups a userfaultfd for itself, + // then the main thread hangs when it wants to page in a page. + // Below we check if the test process still executes syscalls + // and kill it after 500ms of inactivity. + uint64 now = current_time_ms(); + uint32 now_executed = __atomic_load_n(output_data, __ATOMIC_RELAXED); + if (executed_calls != now_executed) { + executed_calls = now_executed; + last_executed = now; + } + if ((now - start < 3 * 1000) && (now - start < 1000 || now - last_executed < 500)) + continue; +#else + if (current_time_ms() - start < 3 * 1000) + continue; +#endif + debug("waitpid(%d)=%d\n", pid, res); + debug("killing\n"); + kill(-pid, SIGKILL); + kill(pid, SIGKILL); + while (waitpid(-1, &status, 0) != pid) { + } + break; + } +#if defined(SYZ_EXECUTOR) + status = WEXITSTATUS(status); + if (status == kFailStatus) + fail("child failed"); + if (status == kErrorStatus) + error("child errored"); + reply_execute(0); +#endif +#if defined(SYZ_EXECUTOR) || defined(SYZ_USE_TMP_DIR) + remove_dir(cwdbuf); +#endif + } +} +#else +void loop() +{ + while (1) { + execute_one(); + } +} +#endif +#endif + +#if defined(SYZ_THREADED) +struct thread_t { + int created, running, call; + pthread_t th; + pthread_mutex_t mu; + pthread_cond_t cv; +}; + +static struct thread_t threads[16]; +static void execute_call(int call); +static int running; +#if defined(SYZ_COLLIDE) +static int collide; +#endif + +static void* thr(void* arg) +{ + struct thread_t* th = (struct thread_t*)arg; + for (;;) { + pthread_mutex_lock(&th->mu); + while (!th->running) + pthread_cond_wait(&th->cv, &th->mu); + pthread_mutex_unlock(&th->mu); + execute_call(th->call); + __atomic_fetch_sub(&running, 1, __ATOMIC_RELAXED); + pthread_mutex_lock(&th->mu); + __atomic_store_n(&th->running, 0, __ATOMIC_RELEASE); + pthread_mutex_unlock(&th->mu); + pthread_cond_signal(&th->cv); + } + return 0; +} + +static void execute(int num_calls) +{ + int call, thread; + running = 0; + for (call = 0; call < num_calls; call++) { + for (thread = 0; thread < sizeof(threads) / sizeof(threads[0]); thread++) { + struct thread_t* th = &threads[thread]; + if (!th->created) { + th->created = 1; + pthread_attr_t attr; + pthread_attr_init(&attr); + pthread_attr_setstacksize(&attr, 128 << 10); + pthread_create(&th->th, &attr, thr, th); + pthread_mutex_init(&th->mu, 0); + pthread_cond_init(&th->cv, 0); + } + if (!__atomic_load_n(&th->running, __ATOMIC_ACQUIRE)) { + th->call = call; + __atomic_fetch_add(&running, 1, __ATOMIC_RELAXED); + + pthread_mutex_lock(&th->mu); + th->running = 1; + pthread_mutex_unlock(&th->mu); + pthread_cond_signal(&th->cv); +#if defined(SYZ_COLLIDE) + if (collide && call % 2) + break; +#endif + struct timespec ts; + ts.tv_sec = 0; + ts.tv_nsec = 20 * 1000 * 1000; + pthread_mutex_lock(&th->mu); + while (th->running && ts.tv_nsec >= 5 * 1000 * 1000) { + pthread_cond_timedwait(&th->cv, &th->mu, &ts); + ts.tv_nsec /= 2; + } + pthread_mutex_unlock(&th->mu); + if (__atomic_load_n(&running, __ATOMIC_RELAXED)) + usleep((call == num_calls - 1) ? 10000 : 1000); + break; + } + } + } +} +#endif diff --git a/executor/common_linux.h b/executor/common_linux.h index 661f58c94..18acaaa7e 100644 --- a/executor/common_linux.h +++ b/executor/common_linux.h @@ -1192,6 +1192,17 @@ static void sandbox_common() debug("unshare(CLONE_SYSVSEM): %d\n", errno); } } + +int wait_for_loop(int pid) +{ + if (pid < 0) + fail("sandbox fork failed"); + debug("spawned loop pid %d\n", pid); + int status = 0; + while (waitpid(-1, &status, __WALL) != pid) { + } + return WEXITSTATUS(status); +} #endif #if defined(SYZ_EXECUTOR) || defined(SYZ_SANDBOX_NONE) @@ -1207,10 +1218,8 @@ static int do_sandbox_none(void) debug("unshare(CLONE_NEWPID): %d\n", errno); } int pid = fork(); - if (pid < 0) - fail("sandbox fork failed"); - if (pid) - return pid; + if (pid <= 0) + return wait_for_loop(pid); #if defined(SYZ_EXECUTOR) || defined(SYZ_ENABLE_CGROUPS) setup_cgroups(); @@ -1237,10 +1246,8 @@ static int do_sandbox_setuid(void) if (unshare(CLONE_NEWPID)) fail("unshare(CLONE_NEWPID)"); int pid = fork(); - if (pid < 0) - fail("sandbox fork failed"); - if (pid) - return pid; + if (pid <= 0) + return wait_for_loop(pid); #if defined(SYZ_EXECUTOR) || defined(SYZ_ENABLE_CGROUPS) setup_cgroups(); @@ -1404,9 +1411,7 @@ static int do_sandbox_namespace(void) mprotect(sandbox_stack, 4096, PROT_NONE); // to catch stack underflows pid = clone(namespace_sandbox_proc, &sandbox_stack[sizeof(sandbox_stack) - 64], CLONE_NEWUSER | CLONE_NEWPID, 0); - if (pid < 0) - fail("sandbox clone failed"); - return pid; + return wait_for_loop(pid); } #endif @@ -2205,7 +2210,7 @@ static void execute(int num_calls) ts.tv_sec = 0; ts.tv_nsec = 20 * 1000 * 1000; syscall(SYS_futex, &th->running, FUTEX_WAIT, 1, &ts); - if (running) + if (__atomic_load_n(&running, __ATOMIC_RELAXED)) usleep((call == num_calls - 1) ? 10000 : 1000); break; } diff --git a/executor/executor_fuchsia.cc b/executor/executor_fuchsia.cc index c21198449..ab6d627a3 100644 --- a/executor/executor_fuchsia.cc +++ b/executor/executor_fuchsia.cc @@ -6,7 +6,7 @@ #define SYZ_EXECUTOR #include "common_fuchsia.h" -#include "executor_posix.h" +#include "executor_fuchsia.h" #include "syscalls_fuchsia.h" @@ -27,6 +27,7 @@ int main(int argc, char** argv) install_segv_handler(); main_init(); execute_one(); + (void)error; // prevent unused function warning return 0; } @@ -34,10 +35,18 @@ long execute_syscall(const call_t* c, long a0, long a1, long a2, long a3, long a { long res = ZX_ERR_INVALID_ARGS; NONFAILING(res = c->call(a0, a1, a2, a3, a4, a5, a6, a7, a8)); - if (res == ZX_OK) - return 0; - errno = res; - return -1; + if (strncmp(c->name, "zx_", 3) == 0) { + // Convert zircon error convention to the libc convention that executor expects. + if (res == ZX_OK) + return 0; + errno = res; + return -1; + } + // We cast libc functions to signature returning long, + // as the result int -1 is returned as 0x00000000ffffffff rather than full -1. + if (res == 0xffffffff) + res = (long)-1; + return res; } void cover_open() diff --git a/executor/executor_fuchsia.h b/executor/executor_fuchsia.h new file mode 100644 index 000000000..e49f75b67 --- /dev/null +++ b/executor/executor_fuchsia.h @@ -0,0 +1,62 @@ +// Copyright 2018 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. + +// Fuchsia's pthread_cond_timedwait just returns immidiately, so we use simple spin wait. + +#include <pthread.h> + +typedef pthread_t osthread_t; + +void thread_start(osthread_t* t, void* (*fn)(void*), void* arg) +{ + pthread_attr_t attr; + pthread_attr_init(&attr); + pthread_attr_setstacksize(&attr, 128 << 10); + if (pthread_create(t, &attr, fn, arg)) + exitf("pthread_create failed"); + pthread_attr_destroy(&attr); +} + +struct event_t { + int state; +}; + +void event_init(event_t* ev) +{ + ev->state = 0; +} + +void event_reset(event_t* ev) +{ + ev->state = 0; +} + +void event_set(event_t* ev) +{ + if (ev->state) + fail("event already set"); + __atomic_store_n(&ev->state, 1, __ATOMIC_RELEASE); +} + +void event_wait(event_t* ev) +{ + while (!__atomic_load_n(&ev->state, __ATOMIC_ACQUIRE)) + usleep(200); +} + +bool event_isset(event_t* ev) +{ + return __atomic_load_n(&ev->state, __ATOMIC_ACQUIRE); +} + +bool event_timedwait(event_t* ev, uint64 timeout_ms) +{ + uint64 start = current_time_ms(); + for (;;) { + if (__atomic_load_n(&ev->state, __ATOMIC_RELAXED)) + return true; + if (current_time_ms() - start > timeout_ms) + return false; + usleep(200); + } +} diff --git a/executor/executor_linux.cc b/executor/executor_linux.cc index 33474b75f..f2a21343a 100644 --- a/executor/executor_linux.cc +++ b/executor/executor_linux.cc @@ -76,27 +76,20 @@ int main(int argc, char** argv) install_segv_handler(); use_temporary_dir(); - int pid = -1; + int status = 0; switch (flag_sandbox) { case sandbox_none: - pid = do_sandbox_none(); + status = do_sandbox_none(); break; case sandbox_setuid: - pid = do_sandbox_setuid(); + status = do_sandbox_setuid(); break; case sandbox_namespace: - pid = do_sandbox_namespace(); + status = do_sandbox_namespace(); break; default: fail("unknown sandbox type"); } - if (pid < 0) - fail("clone failed"); - debug("spawned loop pid %d\n", pid); - int status = 0; - while (waitpid(-1, &status, __WALL) != pid) { - } - status = WEXITSTATUS(status); // Other statuses happen when fuzzer processes manages to kill loop. if (status != kFailStatus && status != kErrorStatus) status = kRetryStatus; diff --git a/pkg/csource/akaros_common.go b/pkg/csource/akaros_common.go index f8a657096..1de8ed1b0 100644 --- a/pkg/csource/akaros_common.go +++ b/pkg/csource/akaros_common.go @@ -51,7 +51,8 @@ __attribute__((noreturn)) static void doexit(int status) #if defined(SYZ_EXECUTOR) || (defined(SYZ_REPEAT) && defined(SYZ_WAIT_REPEAT)) || \ defined(SYZ_USE_TMP_DIR) || defined(SYZ_TUN_ENABLE) || defined(SYZ_SANDBOX_NAMESPACE) || \ defined(SYZ_SANDBOX_NONE) || defined(SYZ_SANDBOX_SETUID) || defined(SYZ_FAULT_INJECTION) || \ - defined(__NR_syz_kvm_setup_cpu) || defined(__NR_syz_init_net_socket) + defined(__NR_syz_kvm_setup_cpu) || defined(__NR_syz_init_net_socket) || \ + defined(__NR_syz_mmap) #include <errno.h> #include <stdarg.h> #include <stdio.h> @@ -109,7 +110,7 @@ struct call_t { #if defined(SYZ_EXECUTOR) || (defined(SYZ_REPEAT) && defined(SYZ_WAIT_REPEAT)) || \ defined(SYZ_USE_TMP_DIR) || defined(SYZ_TUN_ENABLE) || defined(SYZ_SANDBOX_NAMESPACE) || \ defined(SYZ_SANDBOX_NONE) || defined(SYZ_SANDBOX_SETUID) || defined(SYZ_FAULT_INJECTION) || \ - defined(__NR_syz_kvm_setup_cpu) + defined(__NR_syz_kvm_setup_cpu) || defined(__NR_syz_mmap) const int kFailStatus = 67; const int kRetryStatus = 69; #endif @@ -118,11 +119,12 @@ const int kRetryStatus = 69; const int kErrorStatus = 68; #endif -#if defined(SYZ_EXECUTOR) || (defined(SYZ_REPEAT) && defined(SYZ_WAIT_REPEAT)) || \ - defined(SYZ_USE_TMP_DIR) || defined(SYZ_TUN_ENABLE) || defined(SYZ_SANDBOX_NAMESPACE) || \ - defined(SYZ_SANDBOX_NONE) || defined(SYZ_SANDBOX_SETUID) || defined(__NR_syz_kvm_setup_cpu) || \ - defined(__NR_syz_init_net_socket) && \ - (defined(SYZ_SANDBOX_NONE) || defined(SYZ_SANDBOX_SETUID) || defined(SYZ_SANDBOX_NAMESPACE)) +#if defined(SYZ_EXECUTOR) || (defined(SYZ_REPEAT) && defined(SYZ_WAIT_REPEAT)) || \ + defined(SYZ_USE_TMP_DIR) || defined(SYZ_TUN_ENABLE) || defined(SYZ_SANDBOX_NAMESPACE) || \ + defined(SYZ_SANDBOX_NONE) || defined(SYZ_SANDBOX_SETUID) || defined(__NR_syz_kvm_setup_cpu) || \ + defined(__NR_syz_init_net_socket) && \ + (defined(SYZ_SANDBOX_NONE) || defined(SYZ_SANDBOX_SETUID) || defined(SYZ_SANDBOX_NAMESPACE)) || \ + defined(__NR_syz_mmap) NORETURN PRINTF static void fail(const char* msg, ...) { int e = errno; diff --git a/pkg/csource/common.go b/pkg/csource/common.go index 11eff8384..a0312f619 100644 --- a/pkg/csource/common.go +++ b/pkg/csource/common.go @@ -13,12 +13,12 @@ import ( "github.com/google/syzkaller/sys/targets" ) -func createCommonHeader(p *prog.Prog, opts Options) ([]byte, error) { +func createCommonHeader(p, mmapProg *prog.Prog, opts Options) ([]byte, error) { commonHeader, err := getCommonHeader(p.Target.OS) if err != nil { return nil, err } - defines, err := defineList(p, opts) + defines, err := defineList(p, mmapProg, opts) if err != nil { return nil, err } @@ -53,7 +53,7 @@ func createCommonHeader(p *prog.Prog, opts Options) ([]byte, error) { return src, nil } -func defineList(p *prog.Prog, opts Options) ([]string, error) { +func defineList(p, mmapProg *prog.Prog, opts Options) ([]string, error) { var defines []string bitmasks, csums := prog.RequiredFeatures(p) if bitmasks { @@ -113,6 +113,9 @@ func defineList(p *prog.Prog, opts Options) ([]string, error) { for _, c := range p.Calls { defines = append(defines, "__NR_"+c.Meta.CallName) } + for _, c := range mmapProg.Calls { + defines = append(defines, "__NR_"+c.Meta.CallName) + } defines = append(defines, targets.Get(p.Target.OS, p.Target.Arch).CArch...) return defines, nil } diff --git a/pkg/csource/csource.go b/pkg/csource/csource.go index ddb1fbf3f..a3c1b00b7 100644 --- a/pkg/csource/csource.go +++ b/pkg/csource/csource.go @@ -15,7 +15,7 @@ import ( ) func Write(p *prog.Prog, opts Options) ([]byte, error) { - if err := opts.Check(); err != nil { + if err := opts.Check(p.Target.OS); err != nil { return nil, fmt.Errorf("csource: invalid opts: %v", err) } ctx := &context{ @@ -44,7 +44,7 @@ func Write(p *prog.Prog, opts Options) ([]byte, error) { ctx.print("// autogenerated by syzkaller (http://github.com/google/syzkaller)\n\n") - hdr, err := createCommonHeader(p, opts) + hdr, err := createCommonHeader(p, mmapProg, opts) if err != nil { return nil, err } @@ -87,17 +87,7 @@ func Write(p *prog.Prog, opts Options) ([]byte, error) { if opts.UseTmpDir { ctx.printf("\tuse_temporary_dir();\n") } - if opts.Sandbox != "" { - ctx.printf("\tint pid = do_sandbox_%v();\n", opts.Sandbox) - ctx.print("\tint status = 0;\n") - ctx.print("\twhile (waitpid(pid, &status, __WALL) != pid) {}\n") - } else { - if opts.EnableTun { - ctx.printf("\tinitialize_tun();\n") - ctx.printf("\tinitialize_netdevices();\n") - } - ctx.print("\tloop();\n") - } + ctx.writeLoopCall() ctx.print("\treturn 0;\n}\n") } else { ctx.generateTestFunc(calls, len(vars) != 0, "execute_one") @@ -118,17 +108,7 @@ func Write(p *prog.Prog, opts Options) ([]byte, error) { ctx.print("\t\t\tfail(\"failed to chdir\");\n") ctx.print("\t\tuse_temporary_dir();\n") } - if opts.Sandbox != "" { - ctx.printf("\t\tint pid = do_sandbox_%v();\n", opts.Sandbox) - ctx.print("\t\tint status = 0;\n") - ctx.print("\t\twhile (waitpid(pid, &status, __WALL) != pid) {}\n") - } else { - if opts.EnableTun { - ctx.printf("\t\tinitialize_tun();\n") - ctx.printf("\t\tinitialize_netdevices();\n") - } - ctx.print("\t\tloop();\n") - } + ctx.writeLoopCall() ctx.print("\t}\n}\n") } else { ctx.print("int main()\n{\n") @@ -149,17 +129,7 @@ func Write(p *prog.Prog, opts Options) ([]byte, error) { ctx.print("\t\t\t\t\tfail(\"failed to chdir\");\n") ctx.print("\t\t\t\tuse_temporary_dir();\n") } - if opts.Sandbox != "" { - ctx.printf("\t\t\t\tint pid = do_sandbox_%v();\n", opts.Sandbox) - ctx.print("\t\t\t\tint status = 0;\n") - ctx.print("\t\t\t\twhile (waitpid(pid, &status, __WALL) != pid) {}\n") - } else { - if opts.EnableTun { - ctx.printf("\t\t\t\tinitialize_tun();\n") - ctx.printf("\t\t\t\tinitialize_netdevices();\n") - } - ctx.print("\t\t\t\tloop();\n") - } + ctx.writeLoopCall() ctx.print("\t\t\t}\n") ctx.print("\t\t}\n") ctx.print("\t}\n") @@ -175,9 +145,9 @@ func Write(p *prog.Prog, opts Options) ([]byte, error) { result = re.ReplaceAll(result, []byte("$1;\n")) } if !opts.Debug { - re := regexp.MustCompile(`\t*debug\(.*\);\n`) + re := regexp.MustCompile(`\t*debug\((.*\n)*?.*\);\n`) result = re.ReplaceAll(result, nil) - re = regexp.MustCompile(`\t*debug_dump_data\(.*\);\n`) + re = regexp.MustCompile(`\t*debug_dump_data\((.*\n)*?.*\);\n`) result = re.ReplaceAll(result, nil) } result = bytes.Replace(result, []byte("NORETURN"), nil, -1) @@ -213,6 +183,20 @@ func (ctx *context) printf(str string, args ...interface{}) { ctx.print(fmt.Sprintf(str, args...)) } +func (ctx *context) writeLoopCall() { + if ctx.opts.Sandbox != "" { + ctx.printf("\tdo_sandbox_%v();\n", ctx.opts.Sandbox) + return + } + if ctx.opts.EnableTun { + ctx.printf("\tinitialize_tun();\n") + } + if ctx.opts.EnableNetdev { + ctx.printf("\tinitialize_netdevices();\n") + } + ctx.print("\tloop();\n") +} + func (ctx *context) generateTestFunc(calls []string, hasVars bool, name string) { opts := ctx.opts if !opts.Threaded && !opts.Collide { @@ -225,7 +209,7 @@ func (ctx *context) generateTestFunc(calls []string, hasVars bool, name string) ctx.printf("\tdebug(\"%v\\n\");\n", name) } if opts.Repro { - ctx.printf("\tsyscall(SYS_write, 1, \"executing program\\n\", strlen(\"executing program\\n\"));\n") + ctx.printf("\twrite(1, \"executing program\\n\", strlen(\"executing program\\n\"));\n") } for _, c := range calls { ctx.printf("%s", c) @@ -251,7 +235,7 @@ func (ctx *context) generateTestFunc(calls []string, hasVars bool, name string) ctx.printf("\tdebug(\"%v\\n\");\n", name) } if opts.Repro { - ctx.printf("\tsyscall(SYS_write, 1, \"executing program\\n\", strlen(\"executing program\\n\"));\n") + ctx.printf("\twrite(1, \"executing program\\n\", strlen(\"executing program\\n\"));\n") } ctx.printf("\texecute(%v);\n", len(calls)) if opts.Collide { @@ -265,7 +249,8 @@ func (ctx *context) generateTestFunc(calls []string, hasVars bool, name string) func (ctx *context) generateSyscallDefines() { prefix := ctx.sysTarget.SyscallPrefix for name, nr := range ctx.calls { - if strings.HasPrefix(name, "syz_") || !ctx.sysTarget.NeedSyscallDefine(nr) { + if !ctx.sysTarget.SyscallNumbers || + strings.HasPrefix(name, "syz_") || !ctx.sysTarget.NeedSyscallDefine(nr) { continue } ctx.printf("#ifndef %v%v\n", prefix, name) @@ -346,15 +331,21 @@ func (ctx *context) generateCalls(p prog.ExecProg) ([]string, []uint64) { // TODO: if we don't emit the call we must also not emit copyin, copyout and fault injection. // However, simply skipping whole iteration breaks tests due to unused static functions. if emitCall { - native := !strings.HasPrefix(callName, "syz_") + native := ctx.sysTarget.SyscallNumbers && !strings.HasPrefix(callName, "syz_") fmt.Fprintf(w, "\t") if resCopyout || argCopyout { fmt.Fprintf(w, "res = ") } if native { fmt.Fprintf(w, "syscall(%v%v", ctx.sysTarget.SyscallPrefix, callName) - } else { + } else if strings.HasPrefix(callName, "syz_") { fmt.Fprintf(w, "%v(", callName) + } else { + args := strings.Repeat(",long", len(call.Args)) + if args != "" { + args = args[1:] + } + fmt.Fprintf(w, "((long(*)(%v))%v)(", args, callName) } for ai, arg := range call.Args { if native || ai > 0 { @@ -374,7 +365,13 @@ func (ctx *context) generateCalls(p prog.ExecProg) ([]string, []uint64) { // Copyout. if resCopyout || argCopyout { - fmt.Fprintf(w, "\tif (res != -1)") + cast := "" + if !ctx.sysTarget.SyscallNumbers { + // On fuchsia we call libc calls to function returning long, + // as the result int -1 is returned as 0x00000000ffffffff rather than full -1. + cast = "(int)" + } + fmt.Fprintf(w, "\tif (%vres != -1)", cast) copyoutMultiple := len(call.Copyout) > 1 || resCopyout && len(call.Copyout) > 0 if copyoutMultiple { fmt.Fprintf(w, " {") diff --git a/pkg/csource/csource_test.go b/pkg/csource/csource_test.go index dabf88407..204cfa59a 100644 --- a/pkg/csource/csource_test.go +++ b/pkg/csource/csource_test.go @@ -8,6 +8,7 @@ import ( "math/rand" "os" "runtime" + "strings" "testing" "time" @@ -15,19 +16,6 @@ import ( _ "github.com/google/syzkaller/sys" ) -func initTest(t *testing.T) (*prog.Target, rand.Source, int) { - t.Parallel() - iters := 1 - seed := int64(time.Now().UnixNano()) - rs := rand.NewSource(seed) - t.Logf("seed=%v", seed) - target, err := prog.GetTarget("linux", runtime.GOARCH) - if err != nil { - t.Fatal(err) - } - return target, rs, iters -} - func TestGenerateOne(t *testing.T) { t.Parallel() opts := Options{ @@ -35,7 +23,7 @@ func TestGenerateOne(t *testing.T) { Collide: true, Repeat: true, Procs: 2, - Sandbox: "namespace", + Sandbox: "none", Repro: true, UseTmpDir: true, } @@ -43,8 +31,8 @@ func TestGenerateOne(t *testing.T) { if target.OS == "test" { continue } - if target.OS == "fuchsia" { - continue // TODO(dvyukov): support fuchsia + if target.OS == "fuchsia" && !strings.Contains(os.Getenv("SOURCEDIR"), "fuchsia") { + continue } if target.OS == "windows" { continue // TODO(dvyukov): support windows @@ -73,14 +61,36 @@ func TestGenerateOne(t *testing.T) { } } -func TestGenerateOptions(t *testing.T) { - target, rs, _ := initTest(t) +func TestGenerateOptionsHost(t *testing.T) { + target, err := prog.GetTarget(runtime.GOOS, runtime.GOARCH) + if err != nil { + t.Fatal(err) + } + testGenerateOptions(t, target) +} + +func TestGenerateOptionsFuchsia(t *testing.T) { + if !strings.Contains(os.Getenv("SOURCEDIR"), "fuchsia") { + t.Skip("SOURCEDIR is not set") + } + target, err := prog.GetTarget("fuchsia", runtime.GOARCH) + if err != nil { + t.Fatal(err) + } + testGenerateOptions(t, target) +} + +func testGenerateOptions(t *testing.T, target *prog.Target) { + t.Parallel() + seed := int64(time.Now().UnixNano()) + rs := rand.NewSource(seed) + t.Logf("seed=%v", seed) + r := rand.New(rs) syzProg := target.GenerateAllSyzProg(rs) t.Logf("syz program:\n%s\n", syzProg.Serialize()) - permutations := allOptionsSingle() - allPermutations := allOptionsPermutations() + permutations := allOptionsSingle(target.OS) + allPermutations := allOptionsPermutations(target.OS) if testing.Short() { - r := rand.New(rs) for i := 0; i < 16; i++ { permutations = append(permutations, allPermutations[r.Intn(len(allPermutations))]) } @@ -89,14 +99,13 @@ func TestGenerateOptions(t *testing.T) { } for i, opts := range permutations { opts := opts + rs1 := rand.NewSource(r.Int63()) t.Run(fmt.Sprintf("%v", i), func(t *testing.T) { - target, rs, iters := initTest(t) + t.Parallel() t.Logf("opts: %+v", opts) if !testing.Short() { - for i := 0; i < iters; i++ { - p := target.Generate(rs, 10, nil) - testOne(t, p, opts) - } + p := target.Generate(rs1, 10, nil) + testOne(t, p, opts) } testOne(t, syzProg, opts) }) diff --git a/pkg/csource/freebsd_common.go b/pkg/csource/freebsd_common.go index 79baeb37d..1cf0a8143 100644 --- a/pkg/csource/freebsd_common.go +++ b/pkg/csource/freebsd_common.go @@ -42,7 +42,8 @@ __attribute__((noreturn)) static void doexit(int status) #if defined(SYZ_EXECUTOR) || (defined(SYZ_REPEAT) && defined(SYZ_WAIT_REPEAT)) || \ defined(SYZ_USE_TMP_DIR) || defined(SYZ_TUN_ENABLE) || defined(SYZ_SANDBOX_NAMESPACE) || \ defined(SYZ_SANDBOX_NONE) || defined(SYZ_SANDBOX_SETUID) || defined(SYZ_FAULT_INJECTION) || \ - defined(__NR_syz_kvm_setup_cpu) || defined(__NR_syz_init_net_socket) + defined(__NR_syz_kvm_setup_cpu) || defined(__NR_syz_init_net_socket) || \ + defined(__NR_syz_mmap) #include <errno.h> #include <stdarg.h> #include <stdio.h> @@ -100,7 +101,7 @@ struct call_t { #if defined(SYZ_EXECUTOR) || (defined(SYZ_REPEAT) && defined(SYZ_WAIT_REPEAT)) || \ defined(SYZ_USE_TMP_DIR) || defined(SYZ_TUN_ENABLE) || defined(SYZ_SANDBOX_NAMESPACE) || \ defined(SYZ_SANDBOX_NONE) || defined(SYZ_SANDBOX_SETUID) || defined(SYZ_FAULT_INJECTION) || \ - defined(__NR_syz_kvm_setup_cpu) + defined(__NR_syz_kvm_setup_cpu) || defined(__NR_syz_mmap) const int kFailStatus = 67; const int kRetryStatus = 69; #endif @@ -109,11 +110,12 @@ const int kRetryStatus = 69; const int kErrorStatus = 68; #endif -#if defined(SYZ_EXECUTOR) || (defined(SYZ_REPEAT) && defined(SYZ_WAIT_REPEAT)) || \ - defined(SYZ_USE_TMP_DIR) || defined(SYZ_TUN_ENABLE) || defined(SYZ_SANDBOX_NAMESPACE) || \ - defined(SYZ_SANDBOX_NONE) || defined(SYZ_SANDBOX_SETUID) || defined(__NR_syz_kvm_setup_cpu) || \ - defined(__NR_syz_init_net_socket) && \ - (defined(SYZ_SANDBOX_NONE) || defined(SYZ_SANDBOX_SETUID) || defined(SYZ_SANDBOX_NAMESPACE)) +#if defined(SYZ_EXECUTOR) || (defined(SYZ_REPEAT) && defined(SYZ_WAIT_REPEAT)) || \ + defined(SYZ_USE_TMP_DIR) || defined(SYZ_TUN_ENABLE) || defined(SYZ_SANDBOX_NAMESPACE) || \ + defined(SYZ_SANDBOX_NONE) || defined(SYZ_SANDBOX_SETUID) || defined(__NR_syz_kvm_setup_cpu) || \ + defined(__NR_syz_init_net_socket) && \ + (defined(SYZ_SANDBOX_NONE) || defined(SYZ_SANDBOX_SETUID) || defined(SYZ_SANDBOX_NAMESPACE)) || \ + defined(__NR_syz_mmap) NORETURN PRINTF static void fail(const char* msg, ...) { int e = errno; diff --git a/pkg/csource/fuchsia_common.go b/pkg/csource/fuchsia_common.go new file mode 100644 index 000000000..5fe83bb72 --- /dev/null +++ b/pkg/csource/fuchsia_common.go @@ -0,0 +1,657 @@ +// AUTOGENERATED FROM executor/common_fuchsia.h + +package csource + +var commonHeaderFuchsia = ` + + +#ifndef _GNU_SOURCE +#define _GNU_SOURCE +#endif + +#include <fcntl.h> +#include <poll.h> +#include <signal.h> +#include <sys/file.h> +#include <sys/stat.h> +#include <sys/time.h> +#include <sys/types.h> +#include <sys/uio.h> +#include <time.h> +#include <unistd.h> +#include <utime.h> +#include <zircon/process.h> +#include <zircon/syscalls.h> +#if defined(SYZ_EXECUTOR) || defined(SYZ_THREADED) || defined(SYZ_COLLIDE) || defined(SYZ_HANDLE_SEGV) +#include <pthread.h> +#include <stdlib.h> +#endif +#if defined(SYZ_EXECUTOR) || (defined(SYZ_REPEAT) && defined(SYZ_WAIT_REPEAT) && defined(SYZ_USE_TMP_DIR)) +#include <dirent.h> +#endif +#if defined(SYZ_EXECUTOR) || (defined(SYZ_REPEAT) && defined(SYZ_WAIT_REPEAT)) +#include <errno.h> +#include <stdarg.h> +#include <stdio.h> +#include <sys/time.h> +#include <sys/wait.h> +#endif +#if defined(SYZ_EXECUTOR) || defined(SYZ_HANDLE_SEGV) +#include <zircon/syscalls/debug.h> +#include <zircon/syscalls/exception.h> +#include <zircon/syscalls/object.h> +#include <zircon/syscalls/port.h> +#endif + +#if defined(SYZ_EXECUTOR) || (defined(SYZ_REPEAT) && defined(SYZ_WAIT_REPEAT)) || \ + defined(SYZ_USE_TMP_DIR) || defined(SYZ_HANDLE_SEGV) || defined(SYZ_TUN_ENABLE) || \ + defined(SYZ_SANDBOX_NAMESPACE) || defined(SYZ_SANDBOX_SETUID) || \ + defined(SYZ_SANDBOX_NONE) || defined(SYZ_FAULT_INJECTION) || \ + defined(__NR_syz_mmap) +__attribute__((noreturn)) static void doexit(int status) +{ + _exit(status); + for (;;) { + } +} +#endif + + + +#include <stdint.h> +#include <string.h> +#if defined(SYZ_EXECUTOR) || (defined(SYZ_REPEAT) && defined(SYZ_WAIT_REPEAT)) || \ + defined(SYZ_USE_TMP_DIR) || defined(SYZ_TUN_ENABLE) || defined(SYZ_SANDBOX_NAMESPACE) || \ + defined(SYZ_SANDBOX_NONE) || defined(SYZ_SANDBOX_SETUID) || defined(SYZ_FAULT_INJECTION) || \ + defined(__NR_syz_kvm_setup_cpu) || defined(__NR_syz_init_net_socket) || \ + defined(__NR_syz_mmap) +#include <errno.h> +#include <stdarg.h> +#include <stdio.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_HANDLE_SEGV) +#include <setjmp.h> +#include <signal.h> +#include <string.h> +#endif +#if defined(SYZ_EXECUTOR) || defined(SYZ_DEBUG) +#include <stdarg.h> +#include <stdio.h> +#endif + +#if defined(SYZ_EXECUTOR) +#define exit vsnprintf +#define _exit vsnprintf + +typedef unsigned long long uint64; +typedef unsigned int uint32; +typedef unsigned short uint16; +typedef unsigned char uint8; + +#ifdef SYZ_EXECUTOR +const int kInPipeFd = 250; +const int kOutPipeFd = 251; +#endif + +#if defined(__GNUC__) +#define SYSCALLAPI +#define NORETURN __attribute__((noreturn)) +#define ALIGNED(N) __attribute__((aligned(N))) +#define PRINTF __attribute__((format(printf, 1, 2))) +#else +#define SYSCALLAPI WINAPI +#define NORETURN __declspec(noreturn) +#define ALIGNED(N) __declspec(align(N)) +#define PRINTF +#endif + +typedef long(SYSCALLAPI* syscall_t)(long, long, long, long, long, long, long, long, long); + +struct call_t { + const char* name; + int sys_nr; + syscall_t call; +}; + +#endif + +#if defined(SYZ_EXECUTOR) || (defined(SYZ_REPEAT) && defined(SYZ_WAIT_REPEAT)) || \ + defined(SYZ_USE_TMP_DIR) || defined(SYZ_TUN_ENABLE) || defined(SYZ_SANDBOX_NAMESPACE) || \ + defined(SYZ_SANDBOX_NONE) || defined(SYZ_SANDBOX_SETUID) || defined(SYZ_FAULT_INJECTION) || \ + defined(__NR_syz_kvm_setup_cpu) || defined(__NR_syz_mmap) +const int kFailStatus = 67; +const int kRetryStatus = 69; +#endif + +#if defined(SYZ_EXECUTOR) +const int kErrorStatus = 68; +#endif + +#if defined(SYZ_EXECUTOR) || (defined(SYZ_REPEAT) && defined(SYZ_WAIT_REPEAT)) || \ + defined(SYZ_USE_TMP_DIR) || defined(SYZ_TUN_ENABLE) || defined(SYZ_SANDBOX_NAMESPACE) || \ + defined(SYZ_SANDBOX_NONE) || defined(SYZ_SANDBOX_SETUID) || defined(__NR_syz_kvm_setup_cpu) || \ + defined(__NR_syz_init_net_socket) && \ + (defined(SYZ_SANDBOX_NONE) || defined(SYZ_SANDBOX_SETUID) || defined(SYZ_SANDBOX_NAMESPACE)) || \ + defined(__NR_syz_mmap) +NORETURN PRINTF static void fail(const char* msg, ...) +{ + int e = errno; + va_list args; + va_start(args, msg); + vfprintf(stderr, msg, args); + va_end(args); + fprintf(stderr, " (errno %d)\n", e); + doexit((e == ENOMEM || e == EAGAIN) ? kRetryStatus : kFailStatus); +} +#endif + +#if defined(SYZ_EXECUTOR) +NORETURN PRINTF static void error(const char* msg, ...) +{ + va_list args; + va_start(args, msg); + vfprintf(stderr, msg, args); + va_end(args); + fprintf(stderr, "\n"); + doexit(kErrorStatus); +} +#endif + +#if defined(SYZ_EXECUTOR) || (defined(SYZ_REPEAT) && defined(SYZ_WAIT_REPEAT) && defined(SYZ_USE_TMP_DIR)) || defined(SYZ_FAULT_INJECTION) +NORETURN PRINTF static void exitf(const char* msg, ...) +{ + int e = errno; + va_list args; + va_start(args, msg); + vfprintf(stderr, msg, args); + va_end(args); + fprintf(stderr, " (errno %d)\n", e); + doexit(kRetryStatus); +} +#endif + +#if defined(SYZ_EXECUTOR) || defined(SYZ_DEBUG) +static int flag_debug; + +PRINTF static void debug(const char* msg, ...) +{ + if (!flag_debug) + return; + va_list args; + va_start(args, msg); + vfprintf(stderr, msg, args); + va_end(args); + fflush(stderr); +} +#endif + +#if defined(SYZ_EXECUTOR) || defined(SYZ_USE_BITMASKS) +#define BITMASK_LEN(type, bf_len) (type)((1ull << (bf_len)) - 1) + +#define BITMASK_LEN_OFF(type, bf_off, bf_len) (type)(BITMASK_LEN(type, (bf_len)) << (bf_off)) + +#define STORE_BY_BITMASK(type, addr, val, bf_off, bf_len) \ + if ((bf_off) == 0 && (bf_len) == 0) { \ + *(type*)(addr) = (type)(val); \ + } else { \ + type new_val = *(type*)(addr); \ + new_val &= ~BITMASK_LEN_OFF(type, (bf_off), (bf_len)); \ + new_val |= ((type)(val)&BITMASK_LEN(type, (bf_len))) << (bf_off); \ + *(type*)(addr) = new_val; \ + } +#endif + +#if defined(SYZ_EXECUTOR) || defined(SYZ_USE_CHECKSUMS) +struct csum_inet { + uint32 acc; +}; + +static void csum_inet_init(struct csum_inet* csum) +{ + csum->acc = 0; +} + +static void csum_inet_update(struct csum_inet* csum, const uint8* data, size_t length) +{ + if (length == 0) + return; + + size_t i; + for (i = 0; i < length - 1; i += 2) + csum->acc += *(uint16*)&data[i]; + + if (length & 1) + csum->acc += (uint16)data[length - 1]; + + while (csum->acc > 0xffff) + csum->acc = (csum->acc & 0xffff) + (csum->acc >> 16); +} + +static uint16 csum_inet_digest(struct csum_inet* csum) +{ + return ~csum->acc; +} +#endif + +#if defined(SYZ_EXECUTOR) || defined(SYZ_HANDLE_SEGV) +static __thread int skip_segv; +static __thread jmp_buf segv_env; + +static void segv_handler() +{ + if (__atomic_load_n(&skip_segv, __ATOMIC_RELAXED)) { + debug("recover: skipping\n"); + longjmp(segv_env, 1); + } + debug("recover: exiting\n"); + doexit(1); +} + +static void* ex_handler(void* arg) +{ + zx_handle_t port = (zx_handle_t)(long)arg; + for (int i = 0; i < 10000; i++) { + zx_port_packet_t packet = {}; + zx_status_t status = zx_port_wait(port, ZX_TIME_INFINITE, &packet); + if (status != ZX_OK) { + debug("zx_port_wait failed: %d\n", status); + continue; + } + debug("got exception packet: type=%d status=%d tid=%llu\n", + packet.type, packet.status, (unsigned long long)(packet.exception.tid)); + zx_handle_t thread; + status = zx_object_get_child(zx_process_self(), packet.exception.tid, + ZX_RIGHT_SAME_RIGHTS, &thread); + if (status != ZX_OK) { + debug("zx_object_get_child failed: %d\n", status); + continue; + } + zx_thread_state_general_regs_t regs; + status = zx_thread_read_state(thread, ZX_THREAD_STATE_GENERAL_REGS, + ®s, sizeof(regs)); + if (status != ZX_OK) { + debug("zx_thread_read_state failed: %d (%d)\n", + (int)sizeof(regs), status); + } else { +#if defined(__x86_64__) + regs.rip = (uint64)(void*)&segv_handler; +#elif defined(__aarch64__) + regs.pc = (uint64)(void*)&segv_handler; +#else +#error "unsupported arch" +#endif + status = zx_thread_write_state(thread, ZX_THREAD_STATE_GENERAL_REGS, ®s, sizeof(regs)); + if (status != ZX_OK) { + debug("zx_thread_write_state failed: %d\n", status); + } + } + status = zx_task_resume(thread, ZX_RESUME_EXCEPTION); + if (status != ZX_OK) { + debug("zx_task_resume failed: %d\n", status); + } + zx_handle_close(thread); + } + doexit(1); + return 0; +} + +static void install_segv_handler() +{ + zx_status_t status; + zx_handle_t port; + if ((status = zx_port_create(0, &port)) != ZX_OK) + fail("zx_port_create failed: %d", status); + if ((status = zx_task_bind_exception_port(zx_process_self(), port, 0, 0)) != ZX_OK) + fail("zx_task_bind_exception_port failed: %d", status); + pthread_t th; + if (pthread_create(&th, 0, ex_handler, (void*)(long)port)) + fail("pthread_create failed"); +} + +#define NONFAILING(...) \ + { \ + __atomic_fetch_add(&skip_segv, 1, __ATOMIC_SEQ_CST); \ + if (sigsetjmp(segv_env, 0) == 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 current_time_ms() +{ + struct timespec ts; + + if (clock_gettime(CLOCK_MONOTONIC, &ts)) + fail("clock_gettime failed"); + return (uint64)ts.tv_sec * 1000 + (uint64)ts.tv_nsec / 1000000; +} +#endif + +#if defined(SYZ_EXECUTOR) +static void sleep_ms(uint64 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(__NR_syz_mmap) +long syz_mmap(size_t addr, size_t size) +{ + zx_handle_t root = zx_vmar_root_self(); + zx_info_vmar_t info; + zx_status_t status = zx_object_get_info(root, ZX_INFO_VMAR, &info, sizeof(info), 0, 0); + if (status != ZX_OK) + fail("zx_object_get_info(ZX_INFO_VMAR) failed: %d", status); + zx_handle_t vmo; + status = zx_vmo_create(size, 0, &vmo); + if (status != ZX_OK) + return status; + uintptr_t mapped_addr; + status = zx_vmar_map(root, addr - info.base, vmo, 0, size, + ZX_VM_FLAG_SPECIFIC_OVERWRITE | ZX_VM_FLAG_PERM_READ | + ZX_VM_FLAG_PERM_WRITE | ZX_VM_FLAG_PERM_EXECUTE, + &mapped_addr); + return status; +} +#endif + +#if defined(SYZ_EXECUTOR) || defined(__NR_syz_process_self) +long syz_process_self() +{ + return zx_process_self(); +} +#endif + +#if defined(SYZ_EXECUTOR) || defined(__NR_syz_thread_self) +long syz_thread_self() +{ + return zx_thread_self(); +} +#endif + +#if defined(SYZ_EXECUTOR) || defined(__NR_syz_vmar_root_self) +long syz_vmar_root_self() +{ + return zx_vmar_root_self(); +} +#endif + +#if defined(SYZ_EXECUTOR) || defined(__NR_syz_job_default) +long syz_job_default() +{ + return zx_job_default(); +} +#endif + +#if defined(SYZ_EXECUTOR) || defined(__NR_syz_future_time) +long syz_future_time(long when) +{ + zx_time_t delta_ms; + switch (when) { + case 0: + delta_ms = 5; + case 1: + delta_ms = 30; + default: + delta_ms = 10000; + } + zx_time_t now = zx_clock_get(ZX_CLOCK_MONOTONIC); + return now + delta_ms * 1000 * 1000; +} +#endif + +#if defined(SYZ_SANDBOX_NONE) +static void loop(); +static int do_sandbox_none(void) +{ + loop(); + return 0; +} +#endif + +#if 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_REPEAT) && defined(SYZ_WAIT_REPEAT) && defined(SYZ_USE_TMP_DIR)) +static void remove_dir(const char* dir) +{ + struct dirent* ep; + DIR* dp = opendir(dir); + if (dp == NULL) + exitf("opendir(%s) failed", dir); + 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)) + exitf("lstat(%s) failed", filename); + if (S_ISDIR(st.st_mode)) { + remove_dir(filename); + continue; + } + if (unlink(filename)) + exitf("unlink(%s) failed", filename); + } + closedir(dp); + if (rmdir(dir)) + exitf("rmdir(%s) failed", dir); +} +#endif + +#if defined(SYZ_EXECUTOR) || defined(SYZ_REPEAT) +static void execute_one(); +extern unsigned long long procid; + +#if defined(SYZ_EXECUTOR) +void reply_handshake(); +void receive_execute(); +void reply_execute(int status); +extern uint32* output_data; +extern uint32* output_pos; +#endif + +#if defined(SYZ_WAIT_REPEAT) +static void loop() +{ +#if defined(SYZ_EXECUTOR) + reply_handshake(); +#endif + int iter; + for (iter = 0;; iter++) { +#if defined(SYZ_EXECUTOR) || defined(SYZ_USE_TMP_DIR) + char cwdbuf[32]; + sprintf(cwdbuf, "./%d", iter); + if (mkdir(cwdbuf, 0777)) + fail("failed to mkdir"); +#endif +#if defined(SYZ_EXECUTOR) + receive_execute(); +#endif + int pid = fork(); + if (pid < 0) + fail("clone failed"); + if (pid == 0) { +#if defined(SYZ_EXECUTOR) || defined(SYZ_USE_TMP_DIR) + if (chdir(cwdbuf)) + fail("failed to chdir"); +#endif +#if defined(SYZ_EXECUTOR) + close(kInPipeFd); + close(kOutPipeFd); +#endif +#if defined(SYZ_EXECUTOR) + output_pos = output_data; +#endif + execute_one(); + debug("worker exiting\n"); + doexit(0); + } + debug("spawned worker pid %d\n", pid); + + int status = 0; + uint64 start = current_time_ms(); +#if defined(SYZ_EXECUTOR) + uint64 last_executed = start; + uint32 executed_calls = __atomic_load_n(output_data, __ATOMIC_RELAXED); +#endif + for (;;) { + int res = waitpid(-1, &status, WNOHANG); + if (res == pid) { + debug("waitpid(%d)=%d\n", pid, res); + break; + } + usleep(1000); +#if defined(SYZ_EXECUTOR) + uint64 now = current_time_ms(); + uint32 now_executed = __atomic_load_n(output_data, __ATOMIC_RELAXED); + if (executed_calls != now_executed) { + executed_calls = now_executed; + last_executed = now; + } + if ((now - start < 3 * 1000) && (now - start < 1000 || now - last_executed < 500)) + continue; +#else + if (current_time_ms() - start < 3 * 1000) + continue; +#endif + debug("waitpid(%d)=%d\n", pid, res); + debug("killing\n"); + kill(-pid, SIGKILL); + kill(pid, SIGKILL); + while (waitpid(-1, &status, 0) != pid) { + } + break; + } +#if defined(SYZ_EXECUTOR) + status = WEXITSTATUS(status); + if (status == kFailStatus) + fail("child failed"); + if (status == kErrorStatus) + error("child errored"); + reply_execute(0); +#endif +#if defined(SYZ_EXECUTOR) || defined(SYZ_USE_TMP_DIR) + remove_dir(cwdbuf); +#endif + } +} +#else +void loop() +{ + while (1) { + execute_one(); + } +} +#endif +#endif + +#if defined(SYZ_THREADED) +struct thread_t { + int created, running, call; + pthread_t th; + pthread_mutex_t mu; + pthread_cond_t cv; +}; + +static struct thread_t threads[16]; +static void execute_call(int call); +static int running; +#if defined(SYZ_COLLIDE) +static int collide; +#endif + +static void* thr(void* arg) +{ + struct thread_t* th = (struct thread_t*)arg; + for (;;) { + pthread_mutex_lock(&th->mu); + while (!th->running) + pthread_cond_wait(&th->cv, &th->mu); + pthread_mutex_unlock(&th->mu); + execute_call(th->call); + __atomic_fetch_sub(&running, 1, __ATOMIC_RELAXED); + pthread_mutex_lock(&th->mu); + __atomic_store_n(&th->running, 0, __ATOMIC_RELEASE); + pthread_mutex_unlock(&th->mu); + pthread_cond_signal(&th->cv); + } + return 0; +} + +static void execute(int num_calls) +{ + int call, thread; + running = 0; + for (call = 0; call < num_calls; call++) { + for (thread = 0; thread < sizeof(threads) / sizeof(threads[0]); thread++) { + struct thread_t* th = &threads[thread]; + if (!th->created) { + th->created = 1; + pthread_attr_t attr; + pthread_attr_init(&attr); + pthread_attr_setstacksize(&attr, 128 << 10); + pthread_create(&th->th, &attr, thr, th); + pthread_mutex_init(&th->mu, 0); + pthread_cond_init(&th->cv, 0); + } + if (!__atomic_load_n(&th->running, __ATOMIC_ACQUIRE)) { + th->call = call; + __atomic_fetch_add(&running, 1, __ATOMIC_RELAXED); + + pthread_mutex_lock(&th->mu); + th->running = 1; + pthread_mutex_unlock(&th->mu); + pthread_cond_signal(&th->cv); +#if defined(SYZ_COLLIDE) + if (collide && call % 2) + break; +#endif + struct timespec ts; + ts.tv_sec = 0; + ts.tv_nsec = 20 * 1000 * 1000; + pthread_mutex_lock(&th->mu); + while (th->running && ts.tv_nsec >= 5 * 1000 * 1000) { + pthread_cond_timedwait(&th->cv, &th->mu, &ts); + ts.tv_nsec /= 2; + } + pthread_mutex_unlock(&th->mu); + if (__atomic_load_n(&running, __ATOMIC_RELAXED)) + usleep((call == num_calls - 1) ? 10000 : 1000); + break; + } + } + } +} +#endif +` diff --git a/pkg/csource/gen.go b/pkg/csource/gen.go index dad3b6659..424789e0f 100644 --- a/pkg/csource/gen.go +++ b/pkg/csource/gen.go @@ -15,22 +15,35 @@ //go:generate bash -c "echo -e '// AUTOGENERATED FROM executor/common_bsd.h\n\npackage csource\nvar commonHeaderNetbsd = `' > netbsd_common.go; cat ../../executor/common_bsd.h | sed -e '/#include \"common.h\"/ {' -e 'r ../../executor/common.h' -e 'd' -e '}' | egrep -v '^[ ]*//' | sed '/^[ ]*\\/\\/.*/d' | sed 's#[ ]*//.*##g' >> netbsd_common.go; echo '`' >> netbsd_common.go" //go:generate go fmt netbsd_common.go +//go:generate bash -c "echo -e '// AUTOGENERATED FROM executor/common_fuchsia.h\n\npackage csource\nvar commonHeaderFuchsia = `' > fuchsia_common.go; cat ../../executor/common_fuchsia.h | sed -e '/#include \"common.h\"/ {' -e 'r ../../executor/common.h' -e 'd' -e '}' | egrep -v '^[ ]*//' | sed '/^[ ]*\\/\\/.*/d' | sed 's#[ ]*//.*##g' >> fuchsia_common.go; echo '`' >> fuchsia_common.go" +//go:generate go fmt fuchsia_common.go + package csource import ( "fmt" ) +const ( + linux = "linux" + akaros = "akaros" + freebsd = "freebsd" + netbsd = "netbsd" + fuchsia = "fuchsia" +) + func getCommonHeader(os string) (string, error) { switch os { - case "linux": + case linux: return commonHeaderLinux, nil - case "akaros": + case akaros: return commonHeaderAkaros, nil - case "freebsd": + case freebsd: return commonHeaderFreebsd, nil - case "netbsd": + case netbsd: return commonHeaderNetbsd, nil + case fuchsia: + return commonHeaderFuchsia, nil default: return "", fmt.Errorf("unsupported OS: %v", os) } diff --git a/pkg/csource/linux_common.go b/pkg/csource/linux_common.go index 4bc98c820..2ab93fcfe 100644 --- a/pkg/csource/linux_common.go +++ b/pkg/csource/linux_common.go @@ -168,7 +168,8 @@ __attribute__((noreturn)) static void doexit(int status) #if defined(SYZ_EXECUTOR) || (defined(SYZ_REPEAT) && defined(SYZ_WAIT_REPEAT)) || \ defined(SYZ_USE_TMP_DIR) || defined(SYZ_TUN_ENABLE) || defined(SYZ_SANDBOX_NAMESPACE) || \ defined(SYZ_SANDBOX_NONE) || defined(SYZ_SANDBOX_SETUID) || defined(SYZ_FAULT_INJECTION) || \ - defined(__NR_syz_kvm_setup_cpu) || defined(__NR_syz_init_net_socket) + defined(__NR_syz_kvm_setup_cpu) || defined(__NR_syz_init_net_socket) || \ + defined(__NR_syz_mmap) #include <errno.h> #include <stdarg.h> #include <stdio.h> @@ -226,7 +227,7 @@ struct call_t { #if defined(SYZ_EXECUTOR) || (defined(SYZ_REPEAT) && defined(SYZ_WAIT_REPEAT)) || \ defined(SYZ_USE_TMP_DIR) || defined(SYZ_TUN_ENABLE) || defined(SYZ_SANDBOX_NAMESPACE) || \ defined(SYZ_SANDBOX_NONE) || defined(SYZ_SANDBOX_SETUID) || defined(SYZ_FAULT_INJECTION) || \ - defined(__NR_syz_kvm_setup_cpu) + defined(__NR_syz_kvm_setup_cpu) || defined(__NR_syz_mmap) const int kFailStatus = 67; const int kRetryStatus = 69; #endif @@ -235,11 +236,12 @@ const int kRetryStatus = 69; const int kErrorStatus = 68; #endif -#if defined(SYZ_EXECUTOR) || (defined(SYZ_REPEAT) && defined(SYZ_WAIT_REPEAT)) || \ - defined(SYZ_USE_TMP_DIR) || defined(SYZ_TUN_ENABLE) || defined(SYZ_SANDBOX_NAMESPACE) || \ - defined(SYZ_SANDBOX_NONE) || defined(SYZ_SANDBOX_SETUID) || defined(__NR_syz_kvm_setup_cpu) || \ - defined(__NR_syz_init_net_socket) && \ - (defined(SYZ_SANDBOX_NONE) || defined(SYZ_SANDBOX_SETUID) || defined(SYZ_SANDBOX_NAMESPACE)) +#if defined(SYZ_EXECUTOR) || (defined(SYZ_REPEAT) && defined(SYZ_WAIT_REPEAT)) || \ + defined(SYZ_USE_TMP_DIR) || defined(SYZ_TUN_ENABLE) || defined(SYZ_SANDBOX_NAMESPACE) || \ + defined(SYZ_SANDBOX_NONE) || defined(SYZ_SANDBOX_SETUID) || defined(__NR_syz_kvm_setup_cpu) || \ + defined(__NR_syz_init_net_socket) && \ + (defined(SYZ_SANDBOX_NONE) || defined(SYZ_SANDBOX_SETUID) || defined(SYZ_SANDBOX_NAMESPACE)) || \ + defined(__NR_syz_mmap) NORETURN PRINTF static void fail(const char* msg, ...) { int e = errno; @@ -2229,6 +2231,17 @@ static void sandbox_common() debug("unshare(CLONE_SYSVSEM): %d\n", errno); } } + +int wait_for_loop(int pid) +{ + if (pid < 0) + fail("sandbox fork failed"); + debug("spawned loop pid %d\n", pid); + int status = 0; + while (waitpid(-1, &status, __WALL) != pid) { + } + return WEXITSTATUS(status); +} #endif #if defined(SYZ_EXECUTOR) || defined(SYZ_SANDBOX_NONE) @@ -2238,10 +2251,8 @@ static int do_sandbox_none(void) debug("unshare(CLONE_NEWPID): %d\n", errno); } int pid = fork(); - if (pid < 0) - fail("sandbox fork failed"); - if (pid) - return pid; + if (pid <= 0) + return wait_for_loop(pid); #if defined(SYZ_EXECUTOR) || defined(SYZ_ENABLE_CGROUPS) setup_cgroups(); @@ -2268,10 +2279,8 @@ static int do_sandbox_setuid(void) if (unshare(CLONE_NEWPID)) fail("unshare(CLONE_NEWPID)"); int pid = fork(); - if (pid < 0) - fail("sandbox fork failed"); - if (pid) - return pid; + if (pid <= 0) + return wait_for_loop(pid); #if defined(SYZ_EXECUTOR) || defined(SYZ_ENABLE_CGROUPS) setup_cgroups(); @@ -2420,9 +2429,7 @@ static int do_sandbox_namespace(void) mprotect(sandbox_stack, 4096, PROT_NONE); pid = clone(namespace_sandbox_proc, &sandbox_stack[sizeof(sandbox_stack) - 64], CLONE_NEWUSER | CLONE_NEWPID, 0); - if (pid < 0) - fail("sandbox clone failed"); - return pid; + return wait_for_loop(pid); } #endif @@ -3182,7 +3189,7 @@ static void execute(int num_calls) ts.tv_sec = 0; ts.tv_nsec = 20 * 1000 * 1000; syscall(SYS_futex, &th->running, FUTEX_WAIT, 1, &ts); - if (running) + if (__atomic_load_n(&running, __ATOMIC_RELAXED)) usleep((call == num_calls - 1) ? 10000 : 1000); break; } diff --git a/pkg/csource/netbsd_common.go b/pkg/csource/netbsd_common.go index 994edd8b3..9c6c7785f 100644 --- a/pkg/csource/netbsd_common.go +++ b/pkg/csource/netbsd_common.go @@ -42,7 +42,8 @@ __attribute__((noreturn)) static void doexit(int status) #if defined(SYZ_EXECUTOR) || (defined(SYZ_REPEAT) && defined(SYZ_WAIT_REPEAT)) || \ defined(SYZ_USE_TMP_DIR) || defined(SYZ_TUN_ENABLE) || defined(SYZ_SANDBOX_NAMESPACE) || \ defined(SYZ_SANDBOX_NONE) || defined(SYZ_SANDBOX_SETUID) || defined(SYZ_FAULT_INJECTION) || \ - defined(__NR_syz_kvm_setup_cpu) || defined(__NR_syz_init_net_socket) + defined(__NR_syz_kvm_setup_cpu) || defined(__NR_syz_init_net_socket) || \ + defined(__NR_syz_mmap) #include <errno.h> #include <stdarg.h> #include <stdio.h> @@ -100,7 +101,7 @@ struct call_t { #if defined(SYZ_EXECUTOR) || (defined(SYZ_REPEAT) && defined(SYZ_WAIT_REPEAT)) || \ defined(SYZ_USE_TMP_DIR) || defined(SYZ_TUN_ENABLE) || defined(SYZ_SANDBOX_NAMESPACE) || \ defined(SYZ_SANDBOX_NONE) || defined(SYZ_SANDBOX_SETUID) || defined(SYZ_FAULT_INJECTION) || \ - defined(__NR_syz_kvm_setup_cpu) + defined(__NR_syz_kvm_setup_cpu) || defined(__NR_syz_mmap) const int kFailStatus = 67; const int kRetryStatus = 69; #endif @@ -109,11 +110,12 @@ const int kRetryStatus = 69; const int kErrorStatus = 68; #endif -#if defined(SYZ_EXECUTOR) || (defined(SYZ_REPEAT) && defined(SYZ_WAIT_REPEAT)) || \ - defined(SYZ_USE_TMP_DIR) || defined(SYZ_TUN_ENABLE) || defined(SYZ_SANDBOX_NAMESPACE) || \ - defined(SYZ_SANDBOX_NONE) || defined(SYZ_SANDBOX_SETUID) || defined(__NR_syz_kvm_setup_cpu) || \ - defined(__NR_syz_init_net_socket) && \ - (defined(SYZ_SANDBOX_NONE) || defined(SYZ_SANDBOX_SETUID) || defined(SYZ_SANDBOX_NAMESPACE)) +#if defined(SYZ_EXECUTOR) || (defined(SYZ_REPEAT) && defined(SYZ_WAIT_REPEAT)) || \ + defined(SYZ_USE_TMP_DIR) || defined(SYZ_TUN_ENABLE) || defined(SYZ_SANDBOX_NAMESPACE) || \ + defined(SYZ_SANDBOX_NONE) || defined(SYZ_SANDBOX_SETUID) || defined(__NR_syz_kvm_setup_cpu) || \ + defined(__NR_syz_init_net_socket) && \ + (defined(SYZ_SANDBOX_NONE) || defined(SYZ_SANDBOX_SETUID) || defined(SYZ_SANDBOX_NAMESPACE)) || \ + defined(__NR_syz_mmap) NORETURN PRINTF static void fail(const char* msg, ...) { int e = errno; diff --git a/pkg/csource/options.go b/pkg/csource/options.go index fd89abcb6..608aee21a 100644 --- a/pkg/csource/options.go +++ b/pkg/csource/options.go @@ -8,6 +8,8 @@ import ( "encoding/json" "errors" "fmt" + + "github.com/google/syzkaller/syz-manager/mgrconfig" ) // Options control various aspects of source generation. @@ -41,7 +43,28 @@ type Options struct { // Check checks if the opts combination is valid or not. // For example, Collide without Threaded is not valid. // Invalid combinations must not be passed to Write. -func (opts Options) Check() error { +func (opts Options) Check(OS string) error { + switch OS { + case fuchsia: + if opts.Fault { + return errors.New("Fault is not supported on fuchsia") + } + if opts.EnableTun { + return errors.New("EnableTun is not supported on fuchsia") + } + if opts.EnableCgroups { + return errors.New("EnableCgroups is not supported on fuchsia") + } + if opts.EnableNetdev { + return errors.New("EnableNetdev is not supported on fuchsia") + } + if opts.ResetNet { + return errors.New("ResetNet is not supported on fuchsia") + } + if opts.Sandbox != "" && opts.Sandbox != "none" { + return fmt.Errorf("Sandbox=%v is not supported on fuchsia", opts.Sandbox) + } + } if !opts.Threaded && opts.Collide { // Collide requires threaded. return errors.New("Collide without Threaded") @@ -83,6 +106,35 @@ func (opts Options) Check() error { return nil } +func DefaultOpts(cfg *mgrconfig.Config) Options { + opts := Options{ + Threaded: true, + Collide: true, + Repeat: true, + Procs: cfg.Procs, + Sandbox: cfg.Sandbox, + EnableTun: true, + EnableCgroups: true, + EnableNetdev: true, + ResetNet: true, + UseTmpDir: true, + HandleSegv: true, + WaitRepeat: true, + Repro: true, + } + switch cfg.TargetOS { + case fuchsia: + opts.EnableTun = false + opts.EnableCgroups = false + opts.EnableNetdev = false + opts.ResetNet = false + } + if err := opts.Check(cfg.TargetOS); err != nil { + panic(fmt.Sprintf("DefaultOpts created bad opts: %v", err)) + } + return opts +} + func (opts Options) Serialize() []byte { data, err := json.Marshal(opts) if err != nil { diff --git a/pkg/csource/options_test.go b/pkg/csource/options_test.go index 4cccf1c6e..87ee31a1b 100644 --- a/pkg/csource/options_test.go +++ b/pkg/csource/options_test.go @@ -10,7 +10,7 @@ import ( ) func TestParseOptions(t *testing.T) { - for _, opts := range allOptionsSingle() { + for _, opts := range allOptionsSingle("linux") { data := opts.Serialize() got, err := DeserializeOptions(data) if err != nil { @@ -112,7 +112,7 @@ func TestParseOptionsCanned(t *testing.T) { } } -func allOptionsSingle() []Options { +func allOptionsSingle(OS string) []Options { var opts []Options fields := reflect.TypeOf(Options{}).NumField() for i := 0; i < fields; i++ { @@ -124,25 +124,25 @@ func allOptionsSingle() []Options { Sandbox: "none", UseTmpDir: true, } - opts = append(opts, enumerateField(opt, i)...) + opts = append(opts, enumerateField(OS, opt, i)...) } return opts } -func allOptionsPermutations() []Options { +func allOptionsPermutations(OS string) []Options { opts := []Options{Options{}} fields := reflect.TypeOf(Options{}).NumField() for i := 0; i < fields; i++ { var newOpts []Options for _, opt := range opts { - newOpts = append(newOpts, enumerateField(opt, i)...) + newOpts = append(newOpts, enumerateField(OS, opt, i)...) } opts = newOpts } return opts } -func enumerateField(opt Options, field int) []Options { +func enumerateField(OS string, opt Options, field int) []Options { var opts []Options s := reflect.ValueOf(&opt).Elem() fldName := s.Type().Field(field).Name @@ -171,7 +171,7 @@ func enumerateField(opt Options, field int) []Options { } var checked []Options for _, opt := range opts { - if err := opt.Check(); err == nil { + if err := opt.Check(OS); err == nil { checked = append(checked, opt) } } diff --git a/pkg/repro/repro.go b/pkg/repro/repro.go index 2501d1672..5df344a69 100644 --- a/pkg/repro/repro.go +++ b/pkg/repro/repro.go @@ -289,30 +289,10 @@ func (ctx *context) extractProg(entries []*prog.LogEntry) (*Result, error) { return nil, nil } -func (ctx *context) createDefaultOps() csource.Options { - opts := csource.Options{ - Threaded: true, - Collide: true, - Repeat: true, - Procs: ctx.cfg.Procs, - Sandbox: ctx.cfg.Sandbox, - EnableTun: true, - EnableCgroups: true, - EnableNetdev: true, - ResetNet: true, - UseTmpDir: true, - HandleSegv: true, - WaitRepeat: true, - Repro: true, - } - return opts -} - func (ctx *context) extractProgSingle(entries []*prog.LogEntry, duration time.Duration) (*Result, error) { ctx.reproLog(3, "single: executing %d programs separately with timeout %s", len(entries), duration) - opts := ctx.createDefaultOps() - + opts := csource.DefaultOpts(ctx.cfg) for _, ent := range entries { opts.Fault = ent.Fault opts.FaultCall = ent.FaultCall @@ -342,8 +322,7 @@ func (ctx *context) extractProgSingle(entries []*prog.LogEntry, duration time.Du func (ctx *context) extractProgBisect(entries []*prog.LogEntry, baseDuration time.Duration) (*Result, error) { ctx.reproLog(3, "bisect: bisecting %d programs with base timeout %s", len(entries), baseDuration) - opts := ctx.createDefaultOps() - + opts := csource.DefaultOpts(ctx.cfg) duration := func(entries int) time.Duration { return baseDuration + time.Duration((entries/4))*time.Second } diff --git a/pkg/repro/repro_test.go b/pkg/repro/repro_test.go index 0a437e713..91d71e84d 100644 --- a/pkg/repro/repro_test.go +++ b/pkg/repro/repro_test.go @@ -83,7 +83,7 @@ func TestSimplifies(t *testing.T) { } var check func(opts csource.Options, i int) check = func(opts csource.Options, i int) { - if err := opts.Check(); err != nil { + if err := opts.Check("linux"); err != nil { t.Fatalf("opts are invalid: %v", err) } if i == len(cSimplifies) { |
