Book a Demo!
CoCalc Logo Icon
StoreFeaturesDocsShareSupportNewsAboutPoliciesSign UpSign In
freebsd
GitHub Repository: freebsd/freebsd-src
Path: blob/main/contrib/llvm-project/compiler-rt/lib/fuzzer/FuzzerUtilDarwin.cpp
35262 views
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 "FuzzerPlatform.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
#include <unistd.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 Command &Cmd) {
42
std::string CmdLine = Cmd.toString();
43
posix_spawnattr_t SpawnAttributes;
44
if (posix_spawnattr_init(&SpawnAttributes))
45
return -1;
46
// Block and ignore signals of the current process when the first thread
47
// enters.
48
{
49
std::lock_guard<std::mutex> Lock(SignalMutex);
50
if (ActiveThreadCount == 0) {
51
static struct sigaction IgnoreSignalAction;
52
sigset_t BlockedSignalsSet;
53
memset(&IgnoreSignalAction, 0, sizeof(IgnoreSignalAction));
54
IgnoreSignalAction.sa_handler = SIG_IGN;
55
56
if (sigaction(SIGINT, &IgnoreSignalAction, &OldSigIntAction) == -1) {
57
Printf("Failed to ignore SIGINT\n");
58
(void)posix_spawnattr_destroy(&SpawnAttributes);
59
return -1;
60
}
61
if (sigaction(SIGQUIT, &IgnoreSignalAction, &OldSigQuitAction) == -1) {
62
Printf("Failed to ignore SIGQUIT\n");
63
// Try our best to restore the signal handlers.
64
(void)sigaction(SIGINT, &OldSigIntAction, NULL);
65
(void)posix_spawnattr_destroy(&SpawnAttributes);
66
return -1;
67
}
68
69
(void)sigemptyset(&BlockedSignalsSet);
70
(void)sigaddset(&BlockedSignalsSet, SIGCHLD);
71
if (sigprocmask(SIG_BLOCK, &BlockedSignalsSet, &OldBlockedSignalsSet) ==
72
-1) {
73
Printf("Failed to block SIGCHLD\n");
74
// Try our best to restore the signal handlers.
75
(void)sigaction(SIGQUIT, &OldSigQuitAction, NULL);
76
(void)sigaction(SIGINT, &OldSigIntAction, NULL);
77
(void)posix_spawnattr_destroy(&SpawnAttributes);
78
return -1;
79
}
80
}
81
++ActiveThreadCount;
82
}
83
84
// NOTE: Do not introduce any new `return` statements past this
85
// point. It is important that `ActiveThreadCount` always be decremented
86
// when leaving this function.
87
88
// Make sure the child process uses the default handlers for the
89
// following signals rather than inheriting what the parent has.
90
sigset_t DefaultSigSet;
91
(void)sigemptyset(&DefaultSigSet);
92
(void)sigaddset(&DefaultSigSet, SIGQUIT);
93
(void)sigaddset(&DefaultSigSet, SIGINT);
94
(void)posix_spawnattr_setsigdefault(&SpawnAttributes, &DefaultSigSet);
95
// Make sure the child process doesn't block SIGCHLD
96
(void)posix_spawnattr_setsigmask(&SpawnAttributes, &OldBlockedSignalsSet);
97
short SpawnFlags = POSIX_SPAWN_SETSIGDEF | POSIX_SPAWN_SETSIGMASK;
98
(void)posix_spawnattr_setflags(&SpawnAttributes, SpawnFlags);
99
100
pid_t Pid;
101
char **Environ = environ; // Read from global
102
const char *CommandCStr = CmdLine.c_str();
103
char *const Argv[] = {
104
strdup("sh"),
105
strdup("-c"),
106
strdup(CommandCStr),
107
NULL
108
};
109
int ErrorCode = 0, ProcessStatus = 0;
110
// FIXME: We probably shouldn't hardcode the shell path.
111
ErrorCode = posix_spawn(&Pid, "/bin/sh", NULL, &SpawnAttributes,
112
Argv, Environ);
113
(void)posix_spawnattr_destroy(&SpawnAttributes);
114
if (!ErrorCode) {
115
pid_t SavedPid = Pid;
116
do {
117
// Repeat until call completes uninterrupted.
118
Pid = waitpid(SavedPid, &ProcessStatus, /*options=*/0);
119
} while (Pid == -1 && errno == EINTR);
120
if (Pid == -1) {
121
// Fail for some other reason.
122
ProcessStatus = -1;
123
}
124
} else if (ErrorCode == ENOMEM || ErrorCode == EAGAIN) {
125
// Fork failure.
126
ProcessStatus = -1;
127
} else {
128
// Shell execution failure.
129
ProcessStatus = W_EXITCODE(127, 0);
130
}
131
for (unsigned i = 0, n = sizeof(Argv) / sizeof(Argv[0]); i < n; ++i)
132
free(Argv[i]);
133
134
// Restore the signal handlers of the current process when the last thread
135
// using this function finishes.
136
{
137
std::lock_guard<std::mutex> Lock(SignalMutex);
138
--ActiveThreadCount;
139
if (ActiveThreadCount == 0) {
140
bool FailedRestore = false;
141
if (sigaction(SIGINT, &OldSigIntAction, NULL) == -1) {
142
Printf("Failed to restore SIGINT handling\n");
143
FailedRestore = true;
144
}
145
if (sigaction(SIGQUIT, &OldSigQuitAction, NULL) == -1) {
146
Printf("Failed to restore SIGQUIT handling\n");
147
FailedRestore = true;
148
}
149
if (sigprocmask(SIG_BLOCK, &OldBlockedSignalsSet, NULL) == -1) {
150
Printf("Failed to unblock SIGCHLD\n");
151
FailedRestore = true;
152
}
153
if (FailedRestore)
154
ProcessStatus = -1;
155
}
156
}
157
return ProcessStatus;
158
}
159
160
void DiscardOutput(int Fd) {
161
FILE* Temp = fopen("/dev/null", "w");
162
if (!Temp)
163
return;
164
dup2(fileno(Temp), Fd);
165
fclose(Temp);
166
}
167
168
void SetThreadName(std::thread &thread, const std::string &name) {
169
// TODO ?
170
// Darwin allows to set the name only on the current thread it seems
171
}
172
173
} // namespace fuzzer
174
175
#endif // LIBFUZZER_APPLE
176
177