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