]> CyberLeo.Net >> Repos - FreeBSD/FreeBSD.git/blob - contrib/compiler-rt/lib/fuzzer/FuzzerUtilDarwin.cpp
Fix a memory leak in if_delgroups() introduced in r334118.
[FreeBSD/FreeBSD.git] / contrib / compiler-rt / lib / fuzzer / FuzzerUtilDarwin.cpp
1 //===- FuzzerUtilDarwin.cpp - Misc utils ----------------------------------===//
2 //
3 // Part of the LLVM Project, under the Apache License v2.0 with LLVM Exceptions.
4 // See https://llvm.org/LICENSE.txt for license information.
5 // SPDX-License-Identifier: Apache-2.0 WITH LLVM-exception
6 //
7 //===----------------------------------------------------------------------===//
8 // Misc utils for Darwin.
9 //===----------------------------------------------------------------------===//
10 #include "FuzzerDefs.h"
11 #if LIBFUZZER_APPLE
12 #include "FuzzerCommand.h"
13 #include "FuzzerIO.h"
14 #include <mutex>
15 #include <signal.h>
16 #include <spawn.h>
17 #include <stdlib.h>
18 #include <string.h>
19 #include <sys/wait.h>
20
21 // There is no header for this on macOS so declare here
22 extern "C" char **environ;
23
24 namespace fuzzer {
25
26 static std::mutex SignalMutex;
27 // Global variables used to keep track of how signal handling should be
28 // restored. They should **not** be accessed without holding `SignalMutex`.
29 static int ActiveThreadCount = 0;
30 static struct sigaction OldSigIntAction;
31 static struct sigaction OldSigQuitAction;
32 static sigset_t OldBlockedSignalsSet;
33
34 // This is a reimplementation of Libc's `system()`. On Darwin the Libc
35 // implementation contains a mutex which prevents it from being used
36 // concurrently. This implementation **can** be used concurrently. It sets the
37 // signal handlers when the first thread enters and restores them when the last
38 // thread finishes execution of the function and ensures this is not racey by
39 // using a mutex.
40 int ExecuteCommand(const Command &Cmd) {
41   std::string CmdLine = Cmd.toString();
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 = CmdLine.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