OSDN Git Service

Try to deflake fuzzer-oom.test on Windows
[android-x86/external-llvm.git] / lib / Fuzzer / FuzzerUtilDarwin.cpp
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 <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 std::string &Command) {
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 = Command.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