aboutsummaryrefslogtreecommitdiffstats
path: root/executor
diff options
context:
space:
mode:
Diffstat (limited to 'executor')
-rw-r--r--executor/common_freebsd.h55
-rw-r--r--executor/executor.h1
-rw-r--r--executor/executor_freebsd.cc84
3 files changed, 136 insertions, 4 deletions
diff --git a/executor/common_freebsd.h b/executor/common_freebsd.h
index c072b12aa..0712942bd 100644
--- a/executor/common_freebsd.h
+++ b/executor/common_freebsd.h
@@ -17,6 +17,9 @@
#include <sys/wait.h>
#include <time.h>
#endif
+#if defined(SYZ_EXECUTOR) || (defined(SYZ_REPEAT) && defined(SYZ_WAIT_REPEAT) && defined(SYZ_USE_TMP_DIR))
+#include <dirent.h>
+#endif
#define doexit exit
@@ -86,6 +89,58 @@ static void sleep_ms(uint64_t ms)
}
#endif
+#if defined(SYZ_EXECUTOR) || (defined(SYZ_REPEAT) && defined(SYZ_WAIT_REPEAT) && defined(SYZ_USE_TMP_DIR))
+static void remove_dir(const char* dir)
+{
+ DIR* dp;
+ struct dirent* ep;
+ int iter = 0;
+retry:
+ dp = opendir(dir);
+ if (dp == NULL)
+ return;
+ while ((ep = readdir(dp))) {
+ if (strcmp(ep->d_name, ".") == 0 || strcmp(ep->d_name, "..") == 0)
+ continue;
+ char filename[FILENAME_MAX];
+ snprintf(filename, sizeof(filename), "%s/%s", dir, ep->d_name);
+ struct stat st;
+ if (lstat(filename, &st))
+ return;
+ if (S_ISDIR(st.st_mode)) {
+ remove_dir(filename);
+ continue;
+ }
+ int i;
+ for (i = 0;; i++) {
+ if (unlink(filename) == 0)
+ break;
+ if (errno == EROFS)
+ break;
+ if (errno != EBUSY || i > 100)
+ return;
+ }
+ }
+ closedir(dp);
+ int i;
+ for (i = 0;; i++) {
+ if (rmdir(dir) == 0)
+ break;
+ if (i < 100) {
+ if (errno == EROFS)
+ break;
+ if (errno == ENOTEMPTY) {
+ if (iter < 100) {
+ iter++;
+ goto retry;
+ }
+ }
+ }
+ return;
+ }
+}
+#endif
+
#if defined(SYZ_EXECUTOR) || defined(SYZ_FAULT_INJECTION)
static int inject_fault(int nth)
{
diff --git a/executor/executor.h b/executor/executor.h
index 1933c6cef..f3f0eb53a 100644
--- a/executor/executor.h
+++ b/executor/executor.h
@@ -616,6 +616,7 @@ void execute_call(thread_t* th)
}
cover_reset(th);
+ errno = 0;
th->res = execute_syscall(call, th->args[0], th->args[1], th->args[2],
th->args[3], th->args[4], th->args[5],
th->args[6], th->args[7], th->args[8]);
diff --git a/executor/executor_freebsd.cc b/executor/executor_freebsd.cc
index 78c48b693..a009da4cb 100644
--- a/executor/executor_freebsd.cc
+++ b/executor/executor_freebsd.cc
@@ -12,11 +12,17 @@
#include "syscalls_freebsd.h"
+#include <signal.h>
+#include <sys/mman.h>
#include <sys/resource.h>
#include <sys/time.h>
#include <sys/types.h>
-uint32_t output;
+const int kInFd = 3;
+const int kOutFd = 4;
+
+uint32_t* output_data;
+uint32_t* output_pos;
int main(int argc, char** argv)
{
@@ -25,6 +31,23 @@ int main(int argc, char** argv)
return 0;
}
+ if (mmap(&input_data[0], kMaxInput, PROT_READ, MAP_PRIVATE | MAP_FIXED, kInFd, 0) != &input_data[0])
+ fail("mmap of input file failed");
+ // The output region is the only thing in executor process for which consistency matters.
+ // If it is corrupted ipc package will fail to parse its contents and panic.
+ // But fuzzer constantly invents new ways of how to currupt the region,
+ // so we map the region at a (hopefully) hard to guess address surrounded by unmapped pages.
+ void* const kOutputDataAddr = (void*)0x1ddbc20000;
+ output_data = (uint32_t*)mmap(kOutputDataAddr, kMaxOutput, PROT_READ | PROT_WRITE, MAP_SHARED | MAP_FIXED, kOutFd, 0);
+ if (output_data != kOutputDataAddr)
+ fail("mmap of output file failed");
+ // Prevent random programs to mess with these fds.
+ // Due to races in collider mode, a program can e.g. ftruncate one of these fds,
+ // which will cause fuzzer to crash.
+ // That's also the reason why we close kInPipeFd/kOutPipeFd below.
+ close(kInFd);
+ close(kOutFd);
+
// Some minimal sandboxing.
struct rlimit rlim;
rlim.rlim_cur = rlim.rlim_max = 128 << 20;
@@ -40,8 +63,55 @@ int main(int argc, char** argv)
install_segv_handler();
setup_control_pipes();
- receive_execute(true);
- execute_one();
+ receive_handshake();
+ reply_handshake();
+
+ for (;;) {
+ receive_execute(false);
+ char cwdbuf[128] = "/syz-tmpXXXXXX";
+ mkdtemp(cwdbuf);
+ int pid = fork();
+ if (pid < 0)
+ fail("fork failed");
+ if (pid == 0) {
+ close(kInPipeFd);
+ close(kOutPipeFd);
+ if (chdir(cwdbuf))
+ fail("chdir failed");
+ output_pos = output_data;
+ execute_one();
+ doexit(0);
+ }
+ int status = 0;
+ uint64_t start = current_time_ms();
+ uint64_t last_executed = start;
+ uint32_t executed_calls = __atomic_load_n(output_data, __ATOMIC_RELAXED);
+ for (;;) {
+ int res = waitpid(pid, &status, WNOHANG);
+ if (res == pid)
+ break;
+ sleep_ms(1);
+ uint64_t now = current_time_ms();
+ uint32_t 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 - last_executed < 500))
+ continue;
+ kill(pid, SIGKILL);
+ while (waitpid(pid, &status, 0) != pid) {
+ }
+ break;
+ }
+ status = WEXITSTATUS(status);
+ if (status == kFailStatus)
+ fail("child failed");
+ if (status == kErrorStatus)
+ error("child errored");
+ remove_dir(cwdbuf);
+ reply_execute(0);
+ }
return 0;
}
@@ -71,9 +141,15 @@ uint64_t read_cover_size(thread_t* th)
uint32_t* write_output(uint32_t v)
{
- return &output;
+ if (collide)
+ return 0;
+ if (output_pos < output_data || (char*)output_pos >= (char*)output_data + kMaxOutput)
+ fail("output overflow");
+ *output_pos = v;
+ return output_pos++;
}
void write_completed(uint32_t completed)
{
+ __atomic_store_n(output_data, completed, __ATOMIC_RELEASE);
}