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