1 //===- FuzzerUtilDarwin.cpp - Misc utils ----------------------------------===// 2 // 3 // Part of the LLVM Project, under the Apache License v2.0 with LLVM Exceptions. 4 // See https://llvm.org/LICENSE.txt for license information. 5 // SPDX-License-Identifier: Apache-2.0 WITH LLVM-exception 6 // 7 //===----------------------------------------------------------------------===// 8 // Misc utils for Darwin. 9 //===----------------------------------------------------------------------===// 10 #include "FuzzerDefs.h" 11 #if LIBFUZZER_APPLE 12 #include "FuzzerCommand.h" 13 #include "FuzzerIO.h" 14 #include <mutex> 15 #include <signal.h> 16 #include <spawn.h> 17 #include <stdlib.h> 18 #include <string.h> 19 #include <sys/wait.h> 20 21 // There is no header for this on macOS so declare here 22 extern "C" char **environ; 23 24 namespace fuzzer { 25 26 static std::mutex SignalMutex; 27 // Global variables used to keep track of how signal handling should be 28 // restored. They should **not** be accessed without holding `SignalMutex`. 29 static int ActiveThreadCount = 0; 30 static struct sigaction OldSigIntAction; 31 static struct sigaction OldSigQuitAction; 32 static sigset_t OldBlockedSignalsSet; 33 34 // This is a reimplementation of Libc's `system()`. On Darwin the Libc 35 // implementation contains a mutex which prevents it from being used 36 // concurrently. This implementation **can** be used concurrently. It sets the 37 // signal handlers when the first thread enters and restores them when the last 38 // thread finishes execution of the function and ensures this is not racey by 39 // using a mutex. 40 int ExecuteCommand(const Command &Cmd) { 41 std::string CmdLine = Cmd.toString(); 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 = CmdLine.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