aboutsummaryrefslogtreecommitdiffstats
path: root/executor
diff options
context:
space:
mode:
authorDmitry Vyukov <dvyukov@google.com>2017-04-07 10:24:54 +0200
committerDmitry Vyukov <dvyukov@google.com>2017-05-26 17:22:57 +0200
commit8f58526cb8e159721342f3880658a1a2547adab8 (patch)
treef90aba54ce0bfa3372128c1fbea0414ad17abe5b /executor
parent145e067777cb0d21644412548e67dcb934f1da5e (diff)
all: add fault injection capability
Systematically inject faults during smashing. Requires kernel patch: "fault-inject: support systematic fault injection" (currently in linux-next).
Diffstat (limited to 'executor')
-rw-r--r--executor/executor.cc55
1 files changed, 48 insertions, 7 deletions
diff --git a/executor/executor.cc b/executor/executor.cc
index 02792f82f..bdda612ca 100644
--- a/executor/executor.cc
+++ b/executor/executor.cc
@@ -76,9 +76,14 @@ bool flag_collide;
bool flag_sandbox_privs;
sandbox_type flag_sandbox;
bool flag_enable_tun;
+bool flag_enable_fault_injection;
bool flag_collect_cover;
bool flag_dedup_cover;
+// Inject fault into flag_fault_nth-th operation in flag_fault_call-th syscall.
+bool flag_inject_fault;
+int flag_fault_call;
+int flag_fault_nth;
__attribute__((aligned(64 << 10))) char input_data[kMaxInput];
uint32_t* output_data;
@@ -111,6 +116,7 @@ struct thread_t {
uint64_t res;
uint64_t reserrno;
uint64_t cover_size;
+ bool fault_injected;
int cover_fd;
};
@@ -135,7 +141,6 @@ void execute_call(thread_t* th);
void handle_completion(thread_t* th);
void thread_create(thread_t* th, int id);
void* worker_thread(void* arg);
-bool write_file(const char* file, const char* what, ...);
void cover_open();
void cover_enable(thread_t* th);
void cover_reset(thread_t* th);
@@ -176,8 +181,9 @@ int main(int argc, char** argv)
if (!flag_threaded)
flag_collide = false;
flag_enable_tun = flags & (1 << 6);
- uint64_t executor_pid = *((uint64_t*)input_data + 1);
+ flag_enable_fault_injection = flags & (1 << 7);
+ uint64_t executor_pid = *((uint64_t*)input_data + 1);
cover_open();
setup_main_process();
@@ -240,11 +246,16 @@ void loop()
// 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.
- char flags = 0;
- if (read(kInPipeFd, &flags, 1) != 1)
+ uint64_t in_cmd[3] = {};
+ if (read(kInPipeFd, &in_cmd[0], sizeof(in_cmd)) != (ssize_t)sizeof(in_cmd))
fail("control pipe read failed");
- flag_collect_cover = flags & (1 << 0);
- flag_dedup_cover = flags & (1 << 1);
+ flag_collect_cover = in_cmd[0] & (1 << 0);
+ flag_dedup_cover = in_cmd[0] & (1 << 1);
+ flag_inject_fault = in_cmd[0] & (1 << 2);
+ flag_fault_call = in_cmd[1];
+ flag_fault_nth = in_cmd[2];
+ debug("exec opts: cover=%d dedup=%d fault=%d/%d/%d\n", flag_collect_cover, flag_dedup_cover,
+ flag_inject_fault, flag_fault_call, flag_fault_nth);
int pid = fork();
if (pid < 0)
@@ -482,7 +493,7 @@ retry:
}
}
- if (flag_collide && !collide) {
+ if (flag_collide && !flag_inject_fault && !collide) {
debug("enabling collider\n");
collide = true;
goto retry;
@@ -561,6 +572,7 @@ void handle_completion(thread_t* th)
write_output(th->call_num);
uint32_t reserrno = th->res != (uint64_t)-1 ? 0 : th->reserrno;
write_output(reserrno);
+ write_output(th->fault_injected);
uint32_t* signal_count_pos = write_output(0); // filled in later
uint32_t* cover_count_pos = write_output(0); // filled in later
@@ -651,10 +663,39 @@ void execute_call(thread_t* th)
}
debug(")\n");
+ int fail_fd = -1;
+ if (flag_inject_fault && th->call_index == flag_fault_call) {
+ if (collide)
+ fail("both collide and fault injection are enabled");
+ debug("injecting fault into %d-th operation\n", flag_fault_nth);
+ char buf[128];
+ sprintf(buf, "/proc/self/task/%d/fail-nth", (int)syscall(SYS_gettid));
+ fail_fd = open(buf, O_RDWR);
+ if (fail_fd == -1)
+ fail("failed to open /proc/self/task/tid/fail-nth");
+ sprintf(buf, "%d", flag_fault_nth + 1);
+ if (write(fail_fd, buf, strlen(buf)) != (ssize_t)strlen(buf))
+ fail("failed to write /proc/self/task/tid/fail-nth");
+ }
+
cover_reset(th);
th->res = execute_syscall(call->sys_nr, 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]);
th->reserrno = errno;
th->cover_size = cover_read(th);
+ th->fault_injected = false;
+
+ if (flag_inject_fault && th->call_index == flag_fault_call) {
+ char buf[16];
+ int n = read(fail_fd, buf, sizeof(buf) - 1);
+ if (n <= 0)
+ fail("failed to read /proc/self/task/tid/fail-nth");
+ th->fault_injected = n == 2 && buf[0] == '0' && buf[1] == '\n';
+ buf[0] = '0';
+ if (write(fail_fd, buf, 1) != 1)
+ fail("failed to write /proc/self/task/tid/fail-nth");
+ close(fail_fd);
+ debug("fault injected: %d\n", th->fault_injected);
+ }
if (th->res == (uint64_t)-1)
debug("#%d: %s = errno(%ld)\n", th->id, call->name, th->reserrno);