george.karpenkov | 29efa6d | 2017-08-21 23:25:50 +0000 | [diff] [blame] | 1 | //===- FuzzerUtilDarwin.cpp - Misc utils ----------------------------------===// |
| 2 | // |
chandlerc | 4028449 | 2019-01-19 08:50:56 +0000 | [diff] [blame] | 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 |
george.karpenkov | 29efa6d | 2017-08-21 23:25:50 +0000 | [diff] [blame] | 6 | // |
| 7 | //===----------------------------------------------------------------------===// |
| 8 | // Misc utils for Darwin. |
| 9 | //===----------------------------------------------------------------------===// |
| 10 | #include "FuzzerDefs.h" |
| 11 | #if LIBFUZZER_APPLE |
morehouse | a80f645 | 2017-12-04 19:25:59 +0000 | [diff] [blame] | 12 | #include "FuzzerCommand.h" |
george.karpenkov | 29efa6d | 2017-08-21 23:25:50 +0000 | [diff] [blame] | 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. |
morehouse | a80f645 | 2017-12-04 19:25:59 +0000 | [diff] [blame] | 40 | int ExecuteCommand(const Command &Cmd) { |
| 41 | std::string CmdLine = Cmd.toString(); |
george.karpenkov | 29efa6d | 2017-08-21 23:25:50 +0000 | [diff] [blame] | 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 |
morehouse | a80f645 | 2017-12-04 19:25:59 +0000 | [diff] [blame] | 101 | const char *CommandCStr = CmdLine.c_str(); |
george.karpenkov | 29efa6d | 2017-08-21 23:25:50 +0000 | [diff] [blame] | 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 |