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