aboutsummaryrefslogtreecommitdiffstats
diff options
context:
space:
mode:
-rw-r--r--executor/common.h16
-rw-r--r--executor/common_fuchsia.h284
-rw-r--r--executor/common_linux.h29
-rw-r--r--executor/executor_fuchsia.cc19
-rw-r--r--executor/executor_fuchsia.h62
-rw-r--r--executor/executor_linux.cc15
-rw-r--r--pkg/csource/akaros_common.go16
-rw-r--r--pkg/csource/common.go9
-rw-r--r--pkg/csource/csource.go83
-rw-r--r--pkg/csource/csource_test.go61
-rw-r--r--pkg/csource/freebsd_common.go16
-rw-r--r--pkg/csource/fuchsia_common.go657
-rw-r--r--pkg/csource/gen.go21
-rw-r--r--pkg/csource/linux_common.go45
-rw-r--r--pkg/csource/netbsd_common.go16
-rw-r--r--pkg/csource/options.go54
-rw-r--r--pkg/csource/options_test.go14
-rw-r--r--pkg/repro/repro.go25
-rw-r--r--pkg/repro/repro_test.go2
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, &regs, 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,
+ &regs, 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, &regs, 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) {