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