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