1*0b57cec5SDimitry Andric //===- FuzzerUtilDarwin.cpp - Misc utils ----------------------------------===// 2*0b57cec5SDimitry Andric // 3*0b57cec5SDimitry Andric // Part of the LLVM Project, under the Apache License v2.0 with LLVM Exceptions. 4*0b57cec5SDimitry Andric // See https://llvm.org/LICENSE.txt for license information. 5*0b57cec5SDimitry Andric // SPDX-License-Identifier: Apache-2.0 WITH LLVM-exception 6*0b57cec5SDimitry Andric // 7*0b57cec5SDimitry Andric //===----------------------------------------------------------------------===// 8*0b57cec5SDimitry Andric // Misc utils for Darwin. 9*0b57cec5SDimitry Andric //===----------------------------------------------------------------------===// 10*0b57cec5SDimitry Andric #include "FuzzerDefs.h" 11*0b57cec5SDimitry Andric #if LIBFUZZER_APPLE 12*0b57cec5SDimitry Andric #include "FuzzerCommand.h" 13*0b57cec5SDimitry Andric #include "FuzzerIO.h" 14*0b57cec5SDimitry Andric #include <mutex> 15*0b57cec5SDimitry Andric #include <signal.h> 16*0b57cec5SDimitry Andric #include <spawn.h> 17*0b57cec5SDimitry Andric #include <stdlib.h> 18*0b57cec5SDimitry Andric #include <string.h> 19*0b57cec5SDimitry Andric #include <sys/wait.h> 20*0b57cec5SDimitry Andric 21*0b57cec5SDimitry Andric // There is no header for this on macOS so declare here 22*0b57cec5SDimitry Andric extern "C" char **environ; 23*0b57cec5SDimitry Andric 24*0b57cec5SDimitry Andric namespace fuzzer { 25*0b57cec5SDimitry Andric 26*0b57cec5SDimitry Andric static std::mutex SignalMutex; 27*0b57cec5SDimitry Andric // Global variables used to keep track of how signal handling should be 28*0b57cec5SDimitry Andric // restored. They should **not** be accessed without holding `SignalMutex`. 29*0b57cec5SDimitry Andric static int ActiveThreadCount = 0; 30*0b57cec5SDimitry Andric static struct sigaction OldSigIntAction; 31*0b57cec5SDimitry Andric static struct sigaction OldSigQuitAction; 32*0b57cec5SDimitry Andric static sigset_t OldBlockedSignalsSet; 33*0b57cec5SDimitry Andric 34*0b57cec5SDimitry Andric // This is a reimplementation of Libc's `system()`. On Darwin the Libc 35*0b57cec5SDimitry Andric // implementation contains a mutex which prevents it from being used 36*0b57cec5SDimitry Andric // concurrently. This implementation **can** be used concurrently. It sets the 37*0b57cec5SDimitry Andric // signal handlers when the first thread enters and restores them when the last 38*0b57cec5SDimitry Andric // thread finishes execution of the function and ensures this is not racey by 39*0b57cec5SDimitry Andric // using a mutex. 40*0b57cec5SDimitry Andric int ExecuteCommand(const Command &Cmd) { 41*0b57cec5SDimitry Andric std::string CmdLine = Cmd.toString(); 42*0b57cec5SDimitry Andric posix_spawnattr_t SpawnAttributes; 43*0b57cec5SDimitry Andric if (posix_spawnattr_init(&SpawnAttributes)) 44*0b57cec5SDimitry Andric return -1; 45*0b57cec5SDimitry Andric // Block and ignore signals of the current process when the first thread 46*0b57cec5SDimitry Andric // enters. 47*0b57cec5SDimitry Andric { 48*0b57cec5SDimitry Andric std::lock_guard<std::mutex> Lock(SignalMutex); 49*0b57cec5SDimitry Andric if (ActiveThreadCount == 0) { 50*0b57cec5SDimitry Andric static struct sigaction IgnoreSignalAction; 51*0b57cec5SDimitry Andric sigset_t BlockedSignalsSet; 52*0b57cec5SDimitry Andric memset(&IgnoreSignalAction, 0, sizeof(IgnoreSignalAction)); 53*0b57cec5SDimitry Andric IgnoreSignalAction.sa_handler = SIG_IGN; 54*0b57cec5SDimitry Andric 55*0b57cec5SDimitry Andric if (sigaction(SIGINT, &IgnoreSignalAction, &OldSigIntAction) == -1) { 56*0b57cec5SDimitry Andric Printf("Failed to ignore SIGINT\n"); 57*0b57cec5SDimitry Andric (void)posix_spawnattr_destroy(&SpawnAttributes); 58*0b57cec5SDimitry Andric return -1; 59*0b57cec5SDimitry Andric } 60*0b57cec5SDimitry Andric if (sigaction(SIGQUIT, &IgnoreSignalAction, &OldSigQuitAction) == -1) { 61*0b57cec5SDimitry Andric Printf("Failed to ignore SIGQUIT\n"); 62*0b57cec5SDimitry Andric // Try our best to restore the signal handlers. 63*0b57cec5SDimitry Andric (void)sigaction(SIGINT, &OldSigIntAction, NULL); 64*0b57cec5SDimitry Andric (void)posix_spawnattr_destroy(&SpawnAttributes); 65*0b57cec5SDimitry Andric return -1; 66*0b57cec5SDimitry Andric } 67*0b57cec5SDimitry Andric 68*0b57cec5SDimitry Andric (void)sigemptyset(&BlockedSignalsSet); 69*0b57cec5SDimitry Andric (void)sigaddset(&BlockedSignalsSet, SIGCHLD); 70*0b57cec5SDimitry Andric if (sigprocmask(SIG_BLOCK, &BlockedSignalsSet, &OldBlockedSignalsSet) == 71*0b57cec5SDimitry Andric -1) { 72*0b57cec5SDimitry Andric Printf("Failed to block SIGCHLD\n"); 73*0b57cec5SDimitry Andric // Try our best to restore the signal handlers. 74*0b57cec5SDimitry Andric (void)sigaction(SIGQUIT, &OldSigQuitAction, NULL); 75*0b57cec5SDimitry Andric (void)sigaction(SIGINT, &OldSigIntAction, NULL); 76*0b57cec5SDimitry Andric (void)posix_spawnattr_destroy(&SpawnAttributes); 77*0b57cec5SDimitry Andric return -1; 78*0b57cec5SDimitry Andric } 79*0b57cec5SDimitry Andric } 80*0b57cec5SDimitry Andric ++ActiveThreadCount; 81*0b57cec5SDimitry Andric } 82*0b57cec5SDimitry Andric 83*0b57cec5SDimitry Andric // NOTE: Do not introduce any new `return` statements past this 84*0b57cec5SDimitry Andric // point. It is important that `ActiveThreadCount` always be decremented 85*0b57cec5SDimitry Andric // when leaving this function. 86*0b57cec5SDimitry Andric 87*0b57cec5SDimitry Andric // Make sure the child process uses the default handlers for the 88*0b57cec5SDimitry Andric // following signals rather than inheriting what the parent has. 89*0b57cec5SDimitry Andric sigset_t DefaultSigSet; 90*0b57cec5SDimitry Andric (void)sigemptyset(&DefaultSigSet); 91*0b57cec5SDimitry Andric (void)sigaddset(&DefaultSigSet, SIGQUIT); 92*0b57cec5SDimitry Andric (void)sigaddset(&DefaultSigSet, SIGINT); 93*0b57cec5SDimitry Andric (void)posix_spawnattr_setsigdefault(&SpawnAttributes, &DefaultSigSet); 94*0b57cec5SDimitry Andric // Make sure the child process doesn't block SIGCHLD 95*0b57cec5SDimitry Andric (void)posix_spawnattr_setsigmask(&SpawnAttributes, &OldBlockedSignalsSet); 96*0b57cec5SDimitry Andric short SpawnFlags = POSIX_SPAWN_SETSIGDEF | POSIX_SPAWN_SETSIGMASK; 97*0b57cec5SDimitry Andric (void)posix_spawnattr_setflags(&SpawnAttributes, SpawnFlags); 98*0b57cec5SDimitry Andric 99*0b57cec5SDimitry Andric pid_t Pid; 100*0b57cec5SDimitry Andric char **Environ = environ; // Read from global 101*0b57cec5SDimitry Andric const char *CommandCStr = CmdLine.c_str(); 102*0b57cec5SDimitry Andric char *const Argv[] = { 103*0b57cec5SDimitry Andric strdup("sh"), 104*0b57cec5SDimitry Andric strdup("-c"), 105*0b57cec5SDimitry Andric strdup(CommandCStr), 106*0b57cec5SDimitry Andric NULL 107*0b57cec5SDimitry Andric }; 108*0b57cec5SDimitry Andric int ErrorCode = 0, ProcessStatus = 0; 109*0b57cec5SDimitry Andric // FIXME: We probably shouldn't hardcode the shell path. 110*0b57cec5SDimitry Andric ErrorCode = posix_spawn(&Pid, "/bin/sh", NULL, &SpawnAttributes, 111*0b57cec5SDimitry Andric Argv, Environ); 112*0b57cec5SDimitry Andric (void)posix_spawnattr_destroy(&SpawnAttributes); 113*0b57cec5SDimitry Andric if (!ErrorCode) { 114*0b57cec5SDimitry Andric pid_t SavedPid = Pid; 115*0b57cec5SDimitry Andric do { 116*0b57cec5SDimitry Andric // Repeat until call completes uninterrupted. 117*0b57cec5SDimitry Andric Pid = waitpid(SavedPid, &ProcessStatus, /*options=*/0); 118*0b57cec5SDimitry Andric } while (Pid == -1 && errno == EINTR); 119*0b57cec5SDimitry Andric if (Pid == -1) { 120*0b57cec5SDimitry Andric // Fail for some other reason. 121*0b57cec5SDimitry Andric ProcessStatus = -1; 122*0b57cec5SDimitry Andric } 123*0b57cec5SDimitry Andric } else if (ErrorCode == ENOMEM || ErrorCode == EAGAIN) { 124*0b57cec5SDimitry Andric // Fork failure. 125*0b57cec5SDimitry Andric ProcessStatus = -1; 126*0b57cec5SDimitry Andric } else { 127*0b57cec5SDimitry Andric // Shell execution failure. 128*0b57cec5SDimitry Andric ProcessStatus = W_EXITCODE(127, 0); 129*0b57cec5SDimitry Andric } 130*0b57cec5SDimitry Andric for (unsigned i = 0, n = sizeof(Argv) / sizeof(Argv[0]); i < n; ++i) 131*0b57cec5SDimitry Andric free(Argv[i]); 132*0b57cec5SDimitry Andric 133*0b57cec5SDimitry Andric // Restore the signal handlers of the current process when the last thread 134*0b57cec5SDimitry Andric // using this function finishes. 135*0b57cec5SDimitry Andric { 136*0b57cec5SDimitry Andric std::lock_guard<std::mutex> Lock(SignalMutex); 137*0b57cec5SDimitry Andric --ActiveThreadCount; 138*0b57cec5SDimitry Andric if (ActiveThreadCount == 0) { 139*0b57cec5SDimitry Andric bool FailedRestore = false; 140*0b57cec5SDimitry Andric if (sigaction(SIGINT, &OldSigIntAction, NULL) == -1) { 141*0b57cec5SDimitry Andric Printf("Failed to restore SIGINT handling\n"); 142*0b57cec5SDimitry Andric FailedRestore = true; 143*0b57cec5SDimitry Andric } 144*0b57cec5SDimitry Andric if (sigaction(SIGQUIT, &OldSigQuitAction, NULL) == -1) { 145*0b57cec5SDimitry Andric Printf("Failed to restore SIGQUIT handling\n"); 146*0b57cec5SDimitry Andric FailedRestore = true; 147*0b57cec5SDimitry Andric } 148*0b57cec5SDimitry Andric if (sigprocmask(SIG_BLOCK, &OldBlockedSignalsSet, NULL) == -1) { 149*0b57cec5SDimitry Andric Printf("Failed to unblock SIGCHLD\n"); 150*0b57cec5SDimitry Andric FailedRestore = true; 151*0b57cec5SDimitry Andric } 152*0b57cec5SDimitry Andric if (FailedRestore) 153*0b57cec5SDimitry Andric ProcessStatus = -1; 154*0b57cec5SDimitry Andric } 155*0b57cec5SDimitry Andric } 156*0b57cec5SDimitry Andric return ProcessStatus; 157*0b57cec5SDimitry Andric } 158*0b57cec5SDimitry Andric 159*0b57cec5SDimitry Andric } // namespace fuzzer 160*0b57cec5SDimitry Andric 161*0b57cec5SDimitry Andric #endif // LIBFUZZER_APPLE 162