Index: lib/Fuzzer/FuzzerDriver.cpp =================================================================== --- lib/Fuzzer/FuzzerDriver.cpp +++ lib/Fuzzer/FuzzerDriver.cpp @@ -17,6 +17,7 @@ #include #include #include +#include // FIXME: This shouldn't be here! #include #include #include @@ -228,10 +229,30 @@ std::vector V; std::thread Pulse(PulseThread); Pulse.detach(); + + // FIXME: This is a gross layering violation but we have to do this here + // because we can't do this the threads because that would be racey. + // The threads use ExecuteCommand() which executes waitpid() (or some variant + // of it) so we should block SIGINT so that pressing CTRL+C in a terminal + // doesn't kill us which would leave behind zombie processes. + struct sigaction IgnoreSigIntAction; + struct sigaction OldSigIntAction;; + memset(&IgnoreSigIntAction, 0, sizeof(IgnoreSigIntAction)); + IgnoreSigIntAction.sa_handler = SIG_IGN; + if (sigaction(SIGINT, &IgnoreSigIntAction, &OldSigIntAction) == -1) { + Printf("Failed to block SIGINT\n"); + exit(1); + } + for (int i = 0; i < NumWorkers; i++) V.push_back(std::thread(WorkerThread, Cmd, &Counter, NumJobs, &HasErrors)); for (auto &T : V) T.join(); + + // Restore normal handling of SIGINT + if (sigaction(SIGINT, &OldSigIntAction, 0) == -1) { + Printf("Failed to restore handling of SIGINT\n"); + } return HasErrors ? 1 : 0; } Index: lib/Fuzzer/FuzzerUtil.cpp =================================================================== --- lib/Fuzzer/FuzzerUtil.cpp +++ lib/Fuzzer/FuzzerUtil.cpp @@ -12,10 +12,12 @@ #include "FuzzerInternal.h" #include #include +#include #include #include #include #include +#include #include #include #include @@ -25,6 +27,11 @@ #include #include +// There is no header for this on macOS so declare here +#if LIBFUZZER_APPLE +extern char** environ; +#endif + namespace fuzzer { void PrintHexArray(const uint8_t *Data, size_t Size, @@ -146,8 +153,62 @@ return N; } +/* This is a reimplementation of Libc's `system()`. + * On Darwin the Libc implementation contains a mutex + * which prevents it from being used in parallel. This implementation + * does not lock and so can be used in parallel. However unlike + * `system()` this method does not try to modify signal handlers + * of the current process to avoid being racey. + */ +int NonLockingSystem(const char* Command) { + pid_t Pid; + int ErrorCode = 0, ProcessStatus = 0; + char** Environ = environ; // Read from global + const char* Argv[] = { "sh", "-c", Command, NULL}; + sigset_t DefaultSigSet; + posix_spawnattr_t SpawnAttributes; + short SpawnFlags = POSIX_SPAWN_SETSIGDEF; + sigemptyset(&DefaultSigSet); + if (posix_spawnattr_init(&SpawnAttributes)) return -1; + + // Make sure the child process uses the default handlers for the + // following signals rather than inheriting what the parent has. + sigaddset(&DefaultSigSet, SIGQUIT); + sigaddset(&DefaultSigSet, SIGINT); + posix_spawnattr_setsigdefault(&SpawnAttributes, &DefaultSigSet); + posix_spawnattr_setflags(&SpawnAttributes, SpawnFlags); + + // FIXME: We probably shouldn't hardcode the shell path. + ErrorCode = posix_spawn(&Pid, "/bin/sh", NULL, &SpawnAttributes, (char *const *)Argv, Environ); + posix_spawnattr_destroy(&SpawnAttributes); + if (!ErrorCode) { + pid_t SavedPid = Pid; + do { + // Repeat until call completes uninterrupted. + Pid = waitpid(SavedPid, &ProcessStatus, /*options=*/0); + } while (Pid == -1 && errno == EINTR); + if (Pid == -1) { + // Fail for some other reason. + ProcessStatus = -1; + } + } + else if (ErrorCode == ENOMEM || ErrorCode == EAGAIN) { + // Fork failure. + ProcessStatus = -1; + } else { + // Shell execution failure. + ProcessStatus = W_EXITCODE(127, 0); + } + return ProcessStatus; +} + int ExecuteCommand(const std::string &Command) { - return system(Command.c_str()); + if (LIBFUZZER_LINUX) + return system(Command.c_str()); + else if (LIBFUZZER_APPLE) + return NonLockingSystem(Command.c_str()); + assert(0 && "ExecuteCommand() is not implemented for your platform"); + return -1; } bool ToASCII(uint8_t *Data, size_t Size) {