From b89d7d70732deb53683e0462d3247ed847b52120 Mon Sep 17 00:00:00 2001 From: Alex Groce Date: Sat, 15 Dec 2018 13:28:15 -0700 Subject: [PATCH 1/3] add a simple no fork mode for replay and fuzzing --- src/include/deepstate/DeepState.h | 6 +++++- src/lib/DeepState.c | 3 ++- 2 files changed, 7 insertions(+), 2 deletions(-) diff --git a/src/include/deepstate/DeepState.h b/src/include/deepstate/DeepState.h index 163d921..03b0438 100644 --- a/src/include/deepstate/DeepState.h +++ b/src/include/deepstate/DeepState.h @@ -66,6 +66,7 @@ DECLARE_bool(abort_on_fail); DECLARE_bool(verbose_reads); DECLARE_bool(fuzz); DECLARE_bool(fuzz_save_passing); +DECLARE_bool(no_fork); DECLARE_int(log_level); DECLARE_int(seed); @@ -533,7 +534,7 @@ static void DeepState_RunTest(struct DeepState_TestInfo *test) { } /* Run a test case, but in libFuzzer, so not inside a fork. */ -static int DeepState_RunTestLLVM(struct DeepState_TestInfo *test) { +static int DeepState_RunTestNoFork(struct DeepState_TestInfo *test) { /* Run the test. */ if (!setjmp(DeepState_ReturnToRun)) { /* Convert uncaught C++ exceptions into a test failure. */ @@ -577,6 +578,9 @@ static int DeepState_RunTestLLVM(struct DeepState_TestInfo *test) { /* Fork and run `test`. */ static enum DeepState_TestRunResult DeepState_ForkAndRunTest(struct DeepState_TestInfo *test) { + if (FLAG_no_fork) { + return DeepState_RunTestNoFork(test); + } pid_t test_pid = fork(); if (!test_pid) { DeepState_RunTest(test); diff --git a/src/lib/DeepState.c b/src/lib/DeepState.c index b35c2b8..1e162ad 100644 --- a/src/lib/DeepState.c +++ b/src/lib/DeepState.c @@ -39,6 +39,7 @@ DEFINE_bool(abort_on_fail, false, "Abort on file replay failure (useful in file DEFINE_bool(verbose_reads, false, "Report on bytes being read during execution of test."); DEFINE_bool(fuzz, false, "Perform brute force unguided fuzzing."); DEFINE_bool(fuzz_save_passing, false, "Save passing tests during fuzzing."); +DEFINE_bool(no_fork, false, "Don't fork when running a test.") DEFINE_int(log_level, 0, "Minimum level of logging to output."); DEFINE_int(seed, 0, "Seed for brute force fuzzing (uses time if not set)."); @@ -660,7 +661,7 @@ extern int LLVMFuzzerTestOneInput(const uint8_t *Data, size_t Size) { DeepState_Begin(test); - enum DeepState_TestRunResult result = DeepState_RunTestLLVM(test); + enum DeepState_TestRunResult result = DeepState_RunTestNoFork(test); const char* abort_check = getenv("LIBFUZZER_ABORT_ON_FAIL"); if (abort_check != NULL) { From 950da4a78935b47d303d27df907641774d9fdeef Mon Sep 17 00:00:00 2001 From: Alex Groce Date: Sat, 15 Dec 2018 13:40:17 -0700 Subject: [PATCH 2/3] fix various issues --- src/include/deepstate/DeepState.h | 20 ++++++++++++-------- src/lib/DeepState.c | 2 +- 2 files changed, 13 insertions(+), 9 deletions(-) diff --git a/src/include/deepstate/DeepState.h b/src/include/deepstate/DeepState.h index 03b0438..51c3cee 100644 --- a/src/include/deepstate/DeepState.h +++ b/src/include/deepstate/DeepState.h @@ -578,16 +578,20 @@ static int DeepState_RunTestNoFork(struct DeepState_TestInfo *test) { /* Fork and run `test`. */ static enum DeepState_TestRunResult DeepState_ForkAndRunTest(struct DeepState_TestInfo *test) { - if (FLAG_no_fork) { - return DeepState_RunTestNoFork(test); - } - pid_t test_pid = fork(); - if (!test_pid) { - DeepState_RunTest(test); + pid_t test_pid; + if (!FLAGS_no_fork) { + test_pid = fork(); + if (!test_pid) { + DeepState_RunTest(test); + } } int wstatus; - waitpid(test_pid, &wstatus, 0); - + if (!FLAGS_no_fork) { + waitpid(test_pid, &wstatus, 0); + } else { + wstatus = DeepState_RunTestNoFork(test); + } + /* If we exited normally, the status code tells us if the test passed. */ if (WIFEXITED(wstatus)) { uint8_t status = WEXITSTATUS(wstatus); diff --git a/src/lib/DeepState.c b/src/lib/DeepState.c index 1e162ad..cfd1657 100644 --- a/src/lib/DeepState.c +++ b/src/lib/DeepState.c @@ -39,7 +39,7 @@ DEFINE_bool(abort_on_fail, false, "Abort on file replay failure (useful in file DEFINE_bool(verbose_reads, false, "Report on bytes being read during execution of test."); DEFINE_bool(fuzz, false, "Perform brute force unguided fuzzing."); DEFINE_bool(fuzz_save_passing, false, "Save passing tests during fuzzing."); -DEFINE_bool(no_fork, false, "Don't fork when running a test.") +DEFINE_bool(no_fork, false, "Don't fork when running a test."); DEFINE_int(log_level, 0, "Minimum level of logging to output."); DEFINE_int(seed, 0, "Seed for brute force fuzzing (uses time if not set)."); From 6e0c09f83539886d68f1b569e7c03bc611ee7e47 Mon Sep 17 00:00:00 2001 From: Alex Groce Date: Sat, 15 Dec 2018 13:46:53 -0700 Subject: [PATCH 3/3] Note fork issues in README --- README.md | 9 +++++++++ 1 file changed, 9 insertions(+) diff --git a/README.md b/README.md index 6d8bdbe..cf5420c 100644 --- a/README.md +++ b/README.md @@ -115,6 +115,15 @@ argument to see all DeepState options. DeepState consists of a static library, used to write test harnesses, and command-line _executors_ written in Python. At this time, the best documentation is in the [examples](/examples) and in our [paper](https://agroce.github.io/bar18.pdf). A more extensive example, using DeepState and libFuzzer to test a user-mode file system, is available [here](https://github.com/agroce/testfs); in particular the [Tests.cpp](https://github.com/agroce/testfs/blob/master/Tests.cpp) file and CMakeLists.txt show DeepState usage. +## A Note on Mac OS and Forking + +Normally, when running a test for replay or fuzzing, DeepState forks +in order to cleanly handle crashes of a test. Unfortunately, `fork()` +on mac OS is extremely slow. When using the built-in fuzzer or +replaying tests, it is highly recommended to add the `--no_fork` +option on mac OS, unless you need the added crash handling (that is, +things aren't working without that option). + ## Built-In Fuzzer Every DeepState executable provides a simple built-in fuzzer that