Home | History | Annotate | Line # | Download | only in fuzzer
      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 #include "FuzzerCommand.h"
     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 Command &Cmd) {
     42   std::string CmdLine = Cmd.toString();
     43   posix_spawnattr_t SpawnAttributes;
     44   if (posix_spawnattr_init(&SpawnAttributes))
     45     return -1;
     46   // Block and ignore signals of the current process when the first thread
     47   // enters.
     48   {
     49     std::lock_guard<std::mutex> Lock(SignalMutex);
     50     if (ActiveThreadCount == 0) {
     51       static struct sigaction IgnoreSignalAction;
     52       sigset_t BlockedSignalsSet;
     53       memset(&IgnoreSignalAction, 0, sizeof(IgnoreSignalAction));
     54       IgnoreSignalAction.sa_handler = SIG_IGN;
     55 
     56       if (sigaction(SIGINT, &IgnoreSignalAction, &OldSigIntAction) == -1) {
     57         Printf("Failed to ignore SIGINT\n");
     58         (void)posix_spawnattr_destroy(&SpawnAttributes);
     59         return -1;
     60       }
     61       if (sigaction(SIGQUIT, &IgnoreSignalAction, &OldSigQuitAction) == -1) {
     62         Printf("Failed to ignore SIGQUIT\n");
     63         // Try our best to restore the signal handlers.
     64         (void)sigaction(SIGINT, &OldSigIntAction, NULL);
     65         (void)posix_spawnattr_destroy(&SpawnAttributes);
     66         return -1;
     67       }
     68 
     69       (void)sigemptyset(&BlockedSignalsSet);
     70       (void)sigaddset(&BlockedSignalsSet, SIGCHLD);
     71       if (sigprocmask(SIG_BLOCK, &BlockedSignalsSet, &OldBlockedSignalsSet) ==
     72           -1) {
     73         Printf("Failed to block SIGCHLD\n");
     74         // Try our best to restore the signal handlers.
     75         (void)sigaction(SIGQUIT, &OldSigQuitAction, NULL);
     76         (void)sigaction(SIGINT, &OldSigIntAction, NULL);
     77         (void)posix_spawnattr_destroy(&SpawnAttributes);
     78         return -1;
     79       }
     80     }
     81     ++ActiveThreadCount;
     82   }
     83 
     84   // NOTE: Do not introduce any new `return` statements past this
     85   // point. It is important that `ActiveThreadCount` always be decremented
     86   // when leaving this function.
     87 
     88   // Make sure the child process uses the default handlers for the
     89   // following signals rather than inheriting what the parent has.
     90   sigset_t DefaultSigSet;
     91   (void)sigemptyset(&DefaultSigSet);
     92   (void)sigaddset(&DefaultSigSet, SIGQUIT);
     93   (void)sigaddset(&DefaultSigSet, SIGINT);
     94   (void)posix_spawnattr_setsigdefault(&SpawnAttributes, &DefaultSigSet);
     95   // Make sure the child process doesn't block SIGCHLD
     96   (void)posix_spawnattr_setsigmask(&SpawnAttributes, &OldBlockedSignalsSet);
     97   short SpawnFlags = POSIX_SPAWN_SETSIGDEF | POSIX_SPAWN_SETSIGMASK;
     98   (void)posix_spawnattr_setflags(&SpawnAttributes, SpawnFlags);
     99 
    100   pid_t Pid;
    101   char **Environ = environ; // Read from global
    102   const char *CommandCStr = CmdLine.c_str();
    103   char *const Argv[] = {
    104     strdup("sh"),
    105     strdup("-c"),
    106     strdup(CommandCStr),
    107     NULL
    108   };
    109   int ErrorCode = 0, ProcessStatus = 0;
    110   // FIXME: We probably shouldn't hardcode the shell path.
    111   ErrorCode = posix_spawn(&Pid, "/bin/sh", NULL, &SpawnAttributes,
    112                           Argv, Environ);
    113   (void)posix_spawnattr_destroy(&SpawnAttributes);
    114   if (!ErrorCode) {
    115     pid_t SavedPid = Pid;
    116     do {
    117       // Repeat until call completes uninterrupted.
    118       Pid = waitpid(SavedPid, &ProcessStatus, /*options=*/0);
    119     } while (Pid == -1 && errno == EINTR);
    120     if (Pid == -1) {
    121       // Fail for some other reason.
    122       ProcessStatus = -1;
    123     }
    124   } else if (ErrorCode == ENOMEM || ErrorCode == EAGAIN) {
    125     // Fork failure.
    126     ProcessStatus = -1;
    127   } else {
    128     // Shell execution failure.
    129     ProcessStatus = W_EXITCODE(127, 0);
    130   }
    131   for (unsigned i = 0, n = sizeof(Argv) / sizeof(Argv[0]); i < n; ++i)
    132     free(Argv[i]);
    133 
    134   // Restore the signal handlers of the current process when the last thread
    135   // using this function finishes.
    136   {
    137     std::lock_guard<std::mutex> Lock(SignalMutex);
    138     --ActiveThreadCount;
    139     if (ActiveThreadCount == 0) {
    140       bool FailedRestore = false;
    141       if (sigaction(SIGINT, &OldSigIntAction, NULL) == -1) {
    142         Printf("Failed to restore SIGINT handling\n");
    143         FailedRestore = true;
    144       }
    145       if (sigaction(SIGQUIT, &OldSigQuitAction, NULL) == -1) {
    146         Printf("Failed to restore SIGQUIT handling\n");
    147         FailedRestore = true;
    148       }
    149       if (sigprocmask(SIG_BLOCK, &OldBlockedSignalsSet, NULL) == -1) {
    150         Printf("Failed to unblock SIGCHLD\n");
    151         FailedRestore = true;
    152       }
    153       if (FailedRestore)
    154         ProcessStatus = -1;
    155     }
    156   }
    157   return ProcessStatus;
    158 }
    159 
    160 } // namespace fuzzer
    161 
    162 #endif // LIBFUZZER_APPLE
    163