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 diff --git a/src/include/deepstate/DeepState.h b/src/include/deepstate/DeepState.h index 163d921..51c3cee 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,13 +578,20 @@ static int DeepState_RunTestLLVM(struct DeepState_TestInfo *test) { /* Fork and run `test`. */ static enum DeepState_TestRunResult DeepState_ForkAndRunTest(struct DeepState_TestInfo *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 b35c2b8..cfd1657 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) {