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