Path: blob/main/contrib/llvm-project/compiler-rt/lib/fuzzer/FuzzerUtilDarwin.cpp
35262 views
//===- FuzzerUtilDarwin.cpp - Misc utils ----------------------------------===//1//2// Part of the LLVM Project, under the Apache License v2.0 with LLVM Exceptions.3// See https://llvm.org/LICENSE.txt for license information.4// SPDX-License-Identifier: Apache-2.0 WITH LLVM-exception5//6//===----------------------------------------------------------------------===//7// Misc utils for Darwin.8//===----------------------------------------------------------------------===//9#include "FuzzerPlatform.h"10#if LIBFUZZER_APPLE11#include "FuzzerCommand.h"12#include "FuzzerIO.h"13#include <mutex>14#include <signal.h>15#include <spawn.h>16#include <stdlib.h>17#include <string.h>18#include <sys/wait.h>19#include <unistd.h>2021// There is no header for this on macOS so declare here22extern "C" char **environ;2324namespace fuzzer {2526static std::mutex SignalMutex;27// Global variables used to keep track of how signal handling should be28// restored. They should **not** be accessed without holding `SignalMutex`.29static int ActiveThreadCount = 0;30static struct sigaction OldSigIntAction;31static struct sigaction OldSigQuitAction;32static sigset_t OldBlockedSignalsSet;3334// This is a reimplementation of Libc's `system()`. On Darwin the Libc35// implementation contains a mutex which prevents it from being used36// concurrently. This implementation **can** be used concurrently. It sets the37// signal handlers when the first thread enters and restores them when the last38// thread finishes execution of the function and ensures this is not racey by39// using a mutex.40int ExecuteCommand(const Command &Cmd) {41std::string CmdLine = Cmd.toString();42posix_spawnattr_t SpawnAttributes;43if (posix_spawnattr_init(&SpawnAttributes))44return -1;45// Block and ignore signals of the current process when the first thread46// enters.47{48std::lock_guard<std::mutex> Lock(SignalMutex);49if (ActiveThreadCount == 0) {50static struct sigaction IgnoreSignalAction;51sigset_t BlockedSignalsSet;52memset(&IgnoreSignalAction, 0, sizeof(IgnoreSignalAction));53IgnoreSignalAction.sa_handler = SIG_IGN;5455if (sigaction(SIGINT, &IgnoreSignalAction, &OldSigIntAction) == -1) {56Printf("Failed to ignore SIGINT\n");57(void)posix_spawnattr_destroy(&SpawnAttributes);58return -1;59}60if (sigaction(SIGQUIT, &IgnoreSignalAction, &OldSigQuitAction) == -1) {61Printf("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);65return -1;66}6768(void)sigemptyset(&BlockedSignalsSet);69(void)sigaddset(&BlockedSignalsSet, SIGCHLD);70if (sigprocmask(SIG_BLOCK, &BlockedSignalsSet, &OldBlockedSignalsSet) ==71-1) {72Printf("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);77return -1;78}79}80++ActiveThreadCount;81}8283// NOTE: Do not introduce any new `return` statements past this84// point. It is important that `ActiveThreadCount` always be decremented85// when leaving this function.8687// Make sure the child process uses the default handlers for the88// following signals rather than inheriting what the parent has.89sigset_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 SIGCHLD95(void)posix_spawnattr_setsigmask(&SpawnAttributes, &OldBlockedSignalsSet);96short SpawnFlags = POSIX_SPAWN_SETSIGDEF | POSIX_SPAWN_SETSIGMASK;97(void)posix_spawnattr_setflags(&SpawnAttributes, SpawnFlags);9899pid_t Pid;100char **Environ = environ; // Read from global101const char *CommandCStr = CmdLine.c_str();102char *const Argv[] = {103strdup("sh"),104strdup("-c"),105strdup(CommandCStr),106NULL107};108int ErrorCode = 0, ProcessStatus = 0;109// FIXME: We probably shouldn't hardcode the shell path.110ErrorCode = posix_spawn(&Pid, "/bin/sh", NULL, &SpawnAttributes,111Argv, Environ);112(void)posix_spawnattr_destroy(&SpawnAttributes);113if (!ErrorCode) {114pid_t SavedPid = Pid;115do {116// Repeat until call completes uninterrupted.117Pid = waitpid(SavedPid, &ProcessStatus, /*options=*/0);118} while (Pid == -1 && errno == EINTR);119if (Pid == -1) {120// Fail for some other reason.121ProcessStatus = -1;122}123} else if (ErrorCode == ENOMEM || ErrorCode == EAGAIN) {124// Fork failure.125ProcessStatus = -1;126} else {127// Shell execution failure.128ProcessStatus = W_EXITCODE(127, 0);129}130for (unsigned i = 0, n = sizeof(Argv) / sizeof(Argv[0]); i < n; ++i)131free(Argv[i]);132133// Restore the signal handlers of the current process when the last thread134// using this function finishes.135{136std::lock_guard<std::mutex> Lock(SignalMutex);137--ActiveThreadCount;138if (ActiveThreadCount == 0) {139bool FailedRestore = false;140if (sigaction(SIGINT, &OldSigIntAction, NULL) == -1) {141Printf("Failed to restore SIGINT handling\n");142FailedRestore = true;143}144if (sigaction(SIGQUIT, &OldSigQuitAction, NULL) == -1) {145Printf("Failed to restore SIGQUIT handling\n");146FailedRestore = true;147}148if (sigprocmask(SIG_BLOCK, &OldBlockedSignalsSet, NULL) == -1) {149Printf("Failed to unblock SIGCHLD\n");150FailedRestore = true;151}152if (FailedRestore)153ProcessStatus = -1;154}155}156return ProcessStatus;157}158159void DiscardOutput(int Fd) {160FILE* Temp = fopen("/dev/null", "w");161if (!Temp)162return;163dup2(fileno(Temp), Fd);164fclose(Temp);165}166167void SetThreadName(std::thread &thread, const std::string &name) {168// TODO ?169// Darwin allows to set the name only on the current thread it seems170}171172} // namespace fuzzer173174#endif // LIBFUZZER_APPLE175176177