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