aboutsummaryrefslogtreecommitdiffstats
path: root/executor
diff options
context:
space:
mode:
authorDmitry Vyukov <dvyukov@google.com>2015-11-10 20:30:50 +0100
committerDmitry Vyukov <dvyukov@google.com>2015-11-10 20:30:50 +0100
commit0165a4b2e4384f44c8042dcacaf52563ec74bb8c (patch)
treea8f35be5a7ec82b5b54c601da878763c9fb05bf1 /executor
parentd3e457e2853c2f72c06b964034f239c34510fa37 (diff)
use fork server in executor
This avoids exec per test. Also allows to pre-map shared memory regions. And will allow to pre-map coverage regions, etc. Seems to work already, but probably there are still some bugs.
Diffstat (limited to 'executor')
-rw-r--r--executor/executor.cc113
1 files changed, 83 insertions, 30 deletions
diff --git a/executor/executor.cc b/executor/executor.cc
index e126d466d..2c2aa9841 100644
--- a/executor/executor.cc
+++ b/executor/executor.cc
@@ -11,11 +11,14 @@
#include <unistd.h>
#include <fcntl.h>
#include <time.h>
+#include <signal.h>
+#include <limits.h>
#include <sys/types.h>
#include <sys/time.h>
#include <sys/syscall.h>
#include <sys/stat.h>
#include <sys/mman.h>
+#include <sys/wait.h>
#include <linux/futex.h>
#include <pthread.h>
#include <grp.h>
@@ -24,6 +27,8 @@
const int kInFd = 3;
const int kOutFd = 4;
+const int kInPipeFd = 5;
+const int kOutPipeFd = 6;
const int kCoverFd = 5;
const int kMaxInput = 1 << 20;
const int kMaxOutput = 16 << 20;
@@ -39,6 +44,9 @@ const uint64_t arg_const = 0;
const uint64_t arg_result = 1;
const uint64_t arg_data = 2;
+const int kFailStatus = 67;
+const int kErrorStatus = 68;
+
// We use the default value instead of results of failed syscalls.
// -1 is an invalid fd and an invalid address and deterministic,
// so good enough for our purposes.
@@ -89,6 +97,7 @@ __attribute__((noreturn)) void fail(const char* msg, ...);
__attribute__((noreturn)) void error(const char* msg, ...);
__attribute__((noreturn)) void exitf(const char* msg, ...);
void debug(const char* msg, ...);
+void execute_one();
uint64_t read_input(uint64_t** input_posp, bool peek = false);
uint64_t read_arg(uint64_t** input_posp);
uint64_t read_result(uint64_t** input_posp);
@@ -112,35 +121,84 @@ int main()
fail("mmap of input file failed");
if (mmap(&output_data[0], kMaxOutput, PROT_READ | PROT_WRITE, MAP_SHARED | MAP_FIXED, kOutFd, 0) != &output_data[0])
fail("mmap of output file failed");
-retry:
- uint64_t* input_pos = (uint64_t*)&input_data[0];
- uint64_t flags = read_input(&input_pos);
+ char cwdbuf[64 << 10];
+ char* cwd = getcwd(cwdbuf, sizeof(cwdbuf));
+
+ sigset_t sigchldset;
+ sigemptyset(&sigchldset);
+ sigaddset(&sigchldset, SIGCHLD);
+ if (sigprocmask(SIG_BLOCK, &sigchldset, NULL))
+ fail("sigprocmask failed");
+
+ uint64_t flags = *(uint64_t*)input_data;
flag_debug = flags & (1 << 0);
flag_cover = flags & (1 << 1);
flag_threaded = flags & (1 << 2);
flag_collide = flags & (1 << 3);
flag_deduplicate = flags & (1 << 4);
flag_drop_privs = flags & (1 << 5);
- output_pos = (uint32_t*)&output_data[0];
- write_output(0); // Number of executed syscalls (updated later).
-
if (flag_collide)
flag_threaded = true;
- if (!collide) {
- cover_open();
- if (!flag_threaded)
- cover_init(&threads[0]);
-
- if (flag_drop_privs) {
- // TODO: 65534 is meant to be nobody
- if (setgroups(0, NULL))
- fail("failed to setgroups");
- if (setresgid(65534, 65534, 65534))
- fail("failed to setresgid");
- if (setresuid(65534, 65534, 65534))
- fail("failed to setresuid");
+
+ cover_open();
+
+ for (;;) {
+ char tmp;
+ if (read(kInPipeFd, &tmp, 1) != 1)
+ fail("control pipe read failed");
+ // The dir may have been recreated.
+ if (chdir(cwd))
+ fail("failed to chdir");
+
+ int pid = fork();
+ if (pid < 0)
+ fail("fork failed");
+ if (pid == 0) {
+ setpgid(0, 0);
+ if (flag_drop_privs) {
+ // TODO: 65534 is meant to be nobody
+ if (setgroups(0, NULL))
+ fail("failed to setgroups");
+ if (setresgid(65534, 65534, 65534))
+ fail("failed to setresgid");
+ if (setresuid(65534, 65534, 65534))
+ fail("failed to setresuid");
+ }
+ execute_one();
+ debug("exiting\n");
+ return 0;
}
+
+ timespec ts = {};
+ ts.tv_sec = 5;
+ ts.tv_nsec = 0;
+ if (sigtimedwait(&sigchldset, NULL, &ts) < 0) {
+ kill(-pid, SIGKILL);
+ kill(pid, SIGKILL);
+ }
+ int status = 0;
+ if (waitpid(pid, &status, 0) != pid)
+ fail("waitpid failed");
+ status = WEXITSTATUS(status);
+ if (status == kFailStatus)
+ fail("child failed");
+ if (status == kErrorStatus)
+ error("child errored");
+ if (write(kOutPipeFd, &tmp, 1) != 1)
+ fail("control pipe write failed");
}
+}
+
+void execute_one()
+{
+retry:
+ uint64_t* input_pos = (uint64_t*)&input_data[0];
+ read_input(&input_pos); // flags
+ output_pos = (uint32_t*)&output_data[0];
+ write_output(0); // Number of executed syscalls (updated later).
+
+ if (!collide && !flag_threaded)
+ cover_init(&threads[0]);
int call_index = 0;
for (int n = 0;; n++) {
@@ -236,16 +294,11 @@ retry:
}
}
- if (flag_collide) {
- if (!collide) {
- debug("enabling collider\n");
- collide = true;
- goto retry;
- }
+ if (flag_collide && !collide) {
+ debug("enabling collider\n");
+ collide = true;
+ goto retry;
}
-
- debug("exiting\n");
- return 0;
}
thread_t* schedule_call(int n, int call_index, int call_num, uint64_t num_args, uint64_t* args, uint64_t* pos)
@@ -537,7 +590,7 @@ void fail(const char* msg, ...)
vfprintf(stderr, msg, args);
va_end(args);
fprintf(stderr, " (errno %d)\n", e);
- exit(67);
+ exit(kFailStatus);
}
// kernel error (e.g. wrong syscall return value)
@@ -549,7 +602,7 @@ void error(const char* msg, ...)
vfprintf(stderr, msg, args);
va_end(args);
fprintf(stderr, "\n");
- exit(68);
+ exit(kErrorStatus);
}
// just exit (e.g. due to temporal ENOMEM error)