FuzzerUtilDarwin.cpp 5.9 KB

123456789101112131415161718192021222324252627282930313233343536373839404142434445464748495051525354555657585960616263646566676869707172737475767778798081828384858687888990919293949596979899100101102103104105106107108109110111112113114115116117118119120121122123124125126127128129130131132133134135136137138139140141142143144145146147148149150151152153154155156157158159160161162163164165166167168169170
  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. // There is no header for this on macOS so declare here
  22. extern "C" char **environ;
  23. namespace fuzzer {
  24. static std::mutex SignalMutex;
  25. // Global variables used to keep track of how signal handling should be
  26. // restored. They should **not** be accessed without holding `SignalMutex`.
  27. static int ActiveThreadCount = 0;
  28. static struct sigaction OldSigIntAction;
  29. static struct sigaction OldSigQuitAction;
  30. static sigset_t OldBlockedSignalsSet;
  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.
  37. int ExecuteCommand(const Command &Cmd) {
  38. std::string CmdLine = Cmd.toString();
  39. posix_spawnattr_t SpawnAttributes;
  40. if (posix_spawnattr_init(&SpawnAttributes))
  41. return -1;
  42. // Block and ignore signals of the current process when the first thread
  43. // enters.
  44. {
  45. std::lock_guard<std::mutex> Lock(SignalMutex);
  46. if (ActiveThreadCount == 0) {
  47. static struct sigaction IgnoreSignalAction;
  48. sigset_t BlockedSignalsSet;
  49. memset(&IgnoreSignalAction, 0, sizeof(IgnoreSignalAction));
  50. IgnoreSignalAction.sa_handler = SIG_IGN;
  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. (void)sigemptyset(&BlockedSignalsSet);
  64. (void)sigaddset(&BlockedSignalsSet, SIGCHLD);
  65. if (sigprocmask(SIG_BLOCK, &BlockedSignalsSet, &OldBlockedSignalsSet) ==
  66. -1) {
  67. Printf("Failed to block SIGCHLD\n");
  68. // Try our best to restore the signal handlers.
  69. (void)sigaction(SIGQUIT, &OldSigQuitAction, NULL);
  70. (void)sigaction(SIGINT, &OldSigIntAction, NULL);
  71. (void)posix_spawnattr_destroy(&SpawnAttributes);
  72. return -1;
  73. }
  74. }
  75. ++ActiveThreadCount;
  76. }
  77. // NOTE: Do not introduce any new `return` statements past this
  78. // point. It is important that `ActiveThreadCount` always be decremented
  79. // when leaving this function.
  80. // Make sure the child process uses the default handlers for the
  81. // following signals rather than inheriting what the parent has.
  82. sigset_t DefaultSigSet;
  83. (void)sigemptyset(&DefaultSigSet);
  84. (void)sigaddset(&DefaultSigSet, SIGQUIT);
  85. (void)sigaddset(&DefaultSigSet, SIGINT);
  86. (void)posix_spawnattr_setsigdefault(&SpawnAttributes, &DefaultSigSet);
  87. // Make sure the child process doesn't block SIGCHLD
  88. (void)posix_spawnattr_setsigmask(&SpawnAttributes, &OldBlockedSignalsSet);
  89. short SpawnFlags = POSIX_SPAWN_SETSIGDEF | POSIX_SPAWN_SETSIGMASK;
  90. (void)posix_spawnattr_setflags(&SpawnAttributes, SpawnFlags);
  91. pid_t Pid;
  92. char **Environ = environ; // Read from global
  93. const char *CommandCStr = CmdLine.c_str();
  94. char *const Argv[] = {
  95. strdup("sh"),
  96. strdup("-c"),
  97. strdup(CommandCStr),
  98. NULL
  99. };
  100. int ErrorCode = 0, ProcessStatus = 0;
  101. // FIXME: We probably shouldn't hardcode the shell path.
  102. ErrorCode = posix_spawn(&Pid, "/bin/sh", NULL, &SpawnAttributes,
  103. Argv, Environ);
  104. (void)posix_spawnattr_destroy(&SpawnAttributes);
  105. if (!ErrorCode) {
  106. pid_t SavedPid = Pid;
  107. do {
  108. // Repeat until call completes uninterrupted.
  109. Pid = waitpid(SavedPid, &ProcessStatus, /*options=*/0);
  110. } while (Pid == -1 && errno == EINTR);
  111. if (Pid == -1) {
  112. // Fail for some other reason.
  113. ProcessStatus = -1;
  114. }
  115. } else if (ErrorCode == ENOMEM || ErrorCode == EAGAIN) {
  116. // Fork failure.
  117. ProcessStatus = -1;
  118. } else {
  119. // Shell execution failure.
  120. ProcessStatus = W_EXITCODE(127, 0);
  121. }
  122. for (unsigned i = 0, n = sizeof(Argv) / sizeof(Argv[0]); i < n; ++i)
  123. free(Argv[i]);
  124. // Restore the signal handlers of the current process when the last thread
  125. // using this function finishes.
  126. {
  127. std::lock_guard<std::mutex> Lock(SignalMutex);
  128. --ActiveThreadCount;
  129. if (ActiveThreadCount == 0) {
  130. bool FailedRestore = false;
  131. if (sigaction(SIGINT, &OldSigIntAction, NULL) == -1) {
  132. Printf("Failed to restore SIGINT handling\n");
  133. FailedRestore = true;
  134. }
  135. if (sigaction(SIGQUIT, &OldSigQuitAction, NULL) == -1) {
  136. Printf("Failed to restore SIGQUIT handling\n");
  137. FailedRestore = true;
  138. }
  139. if (sigprocmask(SIG_BLOCK, &OldBlockedSignalsSet, NULL) == -1) {
  140. Printf("Failed to unblock SIGCHLD\n");
  141. FailedRestore = true;
  142. }
  143. if (FailedRestore)
  144. ProcessStatus = -1;
  145. }
  146. }
  147. return ProcessStatus;
  148. }
  149. void DiscardOutput(int Fd) {
  150. FILE* Temp = fopen("/dev/null", "w");
  151. if (!Temp)
  152. return;
  153. dup2(fileno(Temp), Fd);
  154. fclose(Temp);
  155. }
  156. } // namespace fuzzer
  157. #endif // LIBFUZZER_APPLE