13cab2bb3Spatrick //===- FuzzerUtilDarwin.cpp - Misc utils ----------------------------------===//
23cab2bb3Spatrick //
33cab2bb3Spatrick // Part of the LLVM Project, under the Apache License v2.0 with LLVM Exceptions.
43cab2bb3Spatrick // See https://llvm.org/LICENSE.txt for license information.
53cab2bb3Spatrick // SPDX-License-Identifier: Apache-2.0 WITH LLVM-exception
63cab2bb3Spatrick //
73cab2bb3Spatrick //===----------------------------------------------------------------------===//
83cab2bb3Spatrick // Misc utils for Darwin.
93cab2bb3Spatrick //===----------------------------------------------------------------------===//
10*1f9cb04fSpatrick #include "FuzzerPlatform.h"
113cab2bb3Spatrick #if LIBFUZZER_APPLE
123cab2bb3Spatrick #include "FuzzerCommand.h"
133cab2bb3Spatrick #include "FuzzerIO.h"
143cab2bb3Spatrick #include <mutex>
153cab2bb3Spatrick #include <signal.h>
163cab2bb3Spatrick #include <spawn.h>
173cab2bb3Spatrick #include <stdlib.h>
183cab2bb3Spatrick #include <string.h>
193cab2bb3Spatrick #include <sys/wait.h>
203cab2bb3Spatrick #include <unistd.h>
213cab2bb3Spatrick
223cab2bb3Spatrick // There is no header for this on macOS so declare here
233cab2bb3Spatrick extern "C" char **environ;
243cab2bb3Spatrick
253cab2bb3Spatrick namespace fuzzer {
263cab2bb3Spatrick
273cab2bb3Spatrick static std::mutex SignalMutex;
283cab2bb3Spatrick // Global variables used to keep track of how signal handling should be
293cab2bb3Spatrick // restored. They should **not** be accessed without holding `SignalMutex`.
303cab2bb3Spatrick static int ActiveThreadCount = 0;
313cab2bb3Spatrick static struct sigaction OldSigIntAction;
323cab2bb3Spatrick static struct sigaction OldSigQuitAction;
333cab2bb3Spatrick static sigset_t OldBlockedSignalsSet;
343cab2bb3Spatrick
353cab2bb3Spatrick // This is a reimplementation of Libc's `system()`. On Darwin the Libc
363cab2bb3Spatrick // implementation contains a mutex which prevents it from being used
373cab2bb3Spatrick // concurrently. This implementation **can** be used concurrently. It sets the
383cab2bb3Spatrick // signal handlers when the first thread enters and restores them when the last
393cab2bb3Spatrick // thread finishes execution of the function and ensures this is not racey by
403cab2bb3Spatrick // using a mutex.
ExecuteCommand(const Command & Cmd)413cab2bb3Spatrick int ExecuteCommand(const Command &Cmd) {
423cab2bb3Spatrick std::string CmdLine = Cmd.toString();
433cab2bb3Spatrick posix_spawnattr_t SpawnAttributes;
443cab2bb3Spatrick if (posix_spawnattr_init(&SpawnAttributes))
453cab2bb3Spatrick return -1;
463cab2bb3Spatrick // Block and ignore signals of the current process when the first thread
473cab2bb3Spatrick // enters.
483cab2bb3Spatrick {
493cab2bb3Spatrick std::lock_guard<std::mutex> Lock(SignalMutex);
503cab2bb3Spatrick if (ActiveThreadCount == 0) {
513cab2bb3Spatrick static struct sigaction IgnoreSignalAction;
523cab2bb3Spatrick sigset_t BlockedSignalsSet;
533cab2bb3Spatrick memset(&IgnoreSignalAction, 0, sizeof(IgnoreSignalAction));
543cab2bb3Spatrick IgnoreSignalAction.sa_handler = SIG_IGN;
553cab2bb3Spatrick
563cab2bb3Spatrick if (sigaction(SIGINT, &IgnoreSignalAction, &OldSigIntAction) == -1) {
573cab2bb3Spatrick Printf("Failed to ignore SIGINT\n");
583cab2bb3Spatrick (void)posix_spawnattr_destroy(&SpawnAttributes);
593cab2bb3Spatrick return -1;
603cab2bb3Spatrick }
613cab2bb3Spatrick if (sigaction(SIGQUIT, &IgnoreSignalAction, &OldSigQuitAction) == -1) {
623cab2bb3Spatrick Printf("Failed to ignore SIGQUIT\n");
633cab2bb3Spatrick // Try our best to restore the signal handlers.
643cab2bb3Spatrick (void)sigaction(SIGINT, &OldSigIntAction, NULL);
653cab2bb3Spatrick (void)posix_spawnattr_destroy(&SpawnAttributes);
663cab2bb3Spatrick return -1;
673cab2bb3Spatrick }
683cab2bb3Spatrick
693cab2bb3Spatrick (void)sigemptyset(&BlockedSignalsSet);
703cab2bb3Spatrick (void)sigaddset(&BlockedSignalsSet, SIGCHLD);
713cab2bb3Spatrick if (sigprocmask(SIG_BLOCK, &BlockedSignalsSet, &OldBlockedSignalsSet) ==
723cab2bb3Spatrick -1) {
733cab2bb3Spatrick Printf("Failed to block SIGCHLD\n");
743cab2bb3Spatrick // Try our best to restore the signal handlers.
753cab2bb3Spatrick (void)sigaction(SIGQUIT, &OldSigQuitAction, NULL);
763cab2bb3Spatrick (void)sigaction(SIGINT, &OldSigIntAction, NULL);
773cab2bb3Spatrick (void)posix_spawnattr_destroy(&SpawnAttributes);
783cab2bb3Spatrick return -1;
793cab2bb3Spatrick }
803cab2bb3Spatrick }
813cab2bb3Spatrick ++ActiveThreadCount;
823cab2bb3Spatrick }
833cab2bb3Spatrick
843cab2bb3Spatrick // NOTE: Do not introduce any new `return` statements past this
853cab2bb3Spatrick // point. It is important that `ActiveThreadCount` always be decremented
863cab2bb3Spatrick // when leaving this function.
873cab2bb3Spatrick
883cab2bb3Spatrick // Make sure the child process uses the default handlers for the
893cab2bb3Spatrick // following signals rather than inheriting what the parent has.
903cab2bb3Spatrick sigset_t DefaultSigSet;
913cab2bb3Spatrick (void)sigemptyset(&DefaultSigSet);
923cab2bb3Spatrick (void)sigaddset(&DefaultSigSet, SIGQUIT);
933cab2bb3Spatrick (void)sigaddset(&DefaultSigSet, SIGINT);
943cab2bb3Spatrick (void)posix_spawnattr_setsigdefault(&SpawnAttributes, &DefaultSigSet);
953cab2bb3Spatrick // Make sure the child process doesn't block SIGCHLD
963cab2bb3Spatrick (void)posix_spawnattr_setsigmask(&SpawnAttributes, &OldBlockedSignalsSet);
973cab2bb3Spatrick short SpawnFlags = POSIX_SPAWN_SETSIGDEF | POSIX_SPAWN_SETSIGMASK;
983cab2bb3Spatrick (void)posix_spawnattr_setflags(&SpawnAttributes, SpawnFlags);
993cab2bb3Spatrick
1003cab2bb3Spatrick pid_t Pid;
1013cab2bb3Spatrick char **Environ = environ; // Read from global
1023cab2bb3Spatrick const char *CommandCStr = CmdLine.c_str();
1033cab2bb3Spatrick char *const Argv[] = {
1043cab2bb3Spatrick strdup("sh"),
1053cab2bb3Spatrick strdup("-c"),
1063cab2bb3Spatrick strdup(CommandCStr),
1073cab2bb3Spatrick NULL
1083cab2bb3Spatrick };
1093cab2bb3Spatrick int ErrorCode = 0, ProcessStatus = 0;
1103cab2bb3Spatrick // FIXME: We probably shouldn't hardcode the shell path.
1113cab2bb3Spatrick ErrorCode = posix_spawn(&Pid, "/bin/sh", NULL, &SpawnAttributes,
1123cab2bb3Spatrick Argv, Environ);
1133cab2bb3Spatrick (void)posix_spawnattr_destroy(&SpawnAttributes);
1143cab2bb3Spatrick if (!ErrorCode) {
1153cab2bb3Spatrick pid_t SavedPid = Pid;
1163cab2bb3Spatrick do {
1173cab2bb3Spatrick // Repeat until call completes uninterrupted.
1183cab2bb3Spatrick Pid = waitpid(SavedPid, &ProcessStatus, /*options=*/0);
1193cab2bb3Spatrick } while (Pid == -1 && errno == EINTR);
1203cab2bb3Spatrick if (Pid == -1) {
1213cab2bb3Spatrick // Fail for some other reason.
1223cab2bb3Spatrick ProcessStatus = -1;
1233cab2bb3Spatrick }
1243cab2bb3Spatrick } else if (ErrorCode == ENOMEM || ErrorCode == EAGAIN) {
1253cab2bb3Spatrick // Fork failure.
1263cab2bb3Spatrick ProcessStatus = -1;
1273cab2bb3Spatrick } else {
1283cab2bb3Spatrick // Shell execution failure.
1293cab2bb3Spatrick ProcessStatus = W_EXITCODE(127, 0);
1303cab2bb3Spatrick }
1313cab2bb3Spatrick for (unsigned i = 0, n = sizeof(Argv) / sizeof(Argv[0]); i < n; ++i)
1323cab2bb3Spatrick free(Argv[i]);
1333cab2bb3Spatrick
1343cab2bb3Spatrick // Restore the signal handlers of the current process when the last thread
1353cab2bb3Spatrick // using this function finishes.
1363cab2bb3Spatrick {
1373cab2bb3Spatrick std::lock_guard<std::mutex> Lock(SignalMutex);
1383cab2bb3Spatrick --ActiveThreadCount;
1393cab2bb3Spatrick if (ActiveThreadCount == 0) {
1403cab2bb3Spatrick bool FailedRestore = false;
1413cab2bb3Spatrick if (sigaction(SIGINT, &OldSigIntAction, NULL) == -1) {
1423cab2bb3Spatrick Printf("Failed to restore SIGINT handling\n");
1433cab2bb3Spatrick FailedRestore = true;
1443cab2bb3Spatrick }
1453cab2bb3Spatrick if (sigaction(SIGQUIT, &OldSigQuitAction, NULL) == -1) {
1463cab2bb3Spatrick Printf("Failed to restore SIGQUIT handling\n");
1473cab2bb3Spatrick FailedRestore = true;
1483cab2bb3Spatrick }
1493cab2bb3Spatrick if (sigprocmask(SIG_BLOCK, &OldBlockedSignalsSet, NULL) == -1) {
1503cab2bb3Spatrick Printf("Failed to unblock SIGCHLD\n");
1513cab2bb3Spatrick FailedRestore = true;
1523cab2bb3Spatrick }
1533cab2bb3Spatrick if (FailedRestore)
1543cab2bb3Spatrick ProcessStatus = -1;
1553cab2bb3Spatrick }
1563cab2bb3Spatrick }
1573cab2bb3Spatrick return ProcessStatus;
1583cab2bb3Spatrick }
1593cab2bb3Spatrick
DiscardOutput(int Fd)1603cab2bb3Spatrick void DiscardOutput(int Fd) {
1613cab2bb3Spatrick FILE* Temp = fopen("/dev/null", "w");
1623cab2bb3Spatrick if (!Temp)
1633cab2bb3Spatrick return;
1643cab2bb3Spatrick dup2(fileno(Temp), Fd);
1653cab2bb3Spatrick fclose(Temp);
1663cab2bb3Spatrick }
1673cab2bb3Spatrick
1683cab2bb3Spatrick } // namespace fuzzer
1693cab2bb3Spatrick
1703cab2bb3Spatrick #endif // LIBFUZZER_APPLE
171