Reland "posix: Replace DoubleForkAndExec() with ForkAndSpawn()"

This is a reland of 460943dd9a71dc76f68182a8ede766d5543e5341

Original change's description:
> The DoubleForkAndExec() function was taking over 622 milliseconds to run
> on macOS 11 (BigSur) on Intel i5-1038NG7. I did some debugging by adding
> some custom traces and found that the fork() syscall is the bottleneck
> here, i.e., the first fork() takes around 359 milliseconds and the
> nested fork() takes around 263 milliseconds. Replacing the nested fork()
> and exec() with posix_spawn() reduces the time consumption to 257
> milliseconds!
>
> See https://github.com/libuv/libuv/pull/3064 to know why fork() is so
> slow on macOS and why posix_spawn() is a better replacement.
>
> Another point to note is that even base::LaunchProcess() from Chromium
> calls posix_spawnp() on macOS -
> https://source.chromium.org/chromium/chromium/src/+/8f8d82dea0fa8f11f57c74dbb65126f8daba58f7:base/process/launch_mac.cc;l=295-296

The reland isolates the change to non-Android POSIX systems because
posix_spawn and posix_spawnp are available in Android NDK 28, but
Chromium is building with version 23.

Change-Id: I5a9253ad7d21a18dc74158d74bdec96beef78c20
Reviewed-on: https://chromium-review.googlesource.com/c/crashpad/crashpad/+/3721655
Reviewed-by: Mark Mentovai <mark@chromium.org>
Commit-Queue: Mark Mentovai <mark@chromium.org>
GitOrigin-RevId: 1c37daa5acd500633cdffc7f6db61d8c6b32b771
diff --git a/AUTHORS b/AUTHORS
index 8dcac32..0210392 100644
--- a/AUTHORS
+++ b/AUTHORS
@@ -12,3 +12,4 @@
 Vewd Software AS
 LG Electronics, Inc.
 MIPS Technologies, Inc.
+Darshan Sen <raisinten@gmail.com>
diff --git a/client/crashpad_client_linux.cc b/client/crashpad_client_linux.cc
index 295ec16..9cd452b 100644
--- a/client/crashpad_client_linux.cc
+++ b/client/crashpad_client_linux.cc
@@ -45,9 +45,9 @@
 #include "util/linux/socket.h"
 #include "util/misc/address_sanitizer.h"
 #include "util/misc/from_pointer_cast.h"
-#include "util/posix/double_fork_and_exec.h"
 #include "util/posix/scoped_mmap.h"
 #include "util/posix/signals.h"
+#include "util/posix/spawn_subprocess.h"
 
 namespace crashpad {
 
@@ -459,7 +459,7 @@
 
   argv.push_back(FormatArgumentInt("initial-client-fd", handler_sock.get()));
   argv.push_back("--shared-client-connection");
-  if (!DoubleForkAndExec(argv, nullptr, handler_sock.get(), false, nullptr)) {
+  if (!SpawnSubprocess(argv, nullptr, handler_sock.get(), false, nullptr)) {
     return false;
   }
 
@@ -614,7 +614,7 @@
     int socket) {
   std::vector<std::string> argv = BuildAppProcessArgs(
       class_name, database, metrics_dir, url, annotations, arguments, socket);
-  return DoubleForkAndExec(argv, env, socket, false, nullptr);
+  return SpawnSubprocess(argv, env, socket, false, nullptr);
 }
 
 bool CrashpadClient::StartHandlerWithLinkerAtCrash(
@@ -663,7 +663,7 @@
                                   annotations,
                                   arguments,
                                   socket);
-  return DoubleForkAndExec(argv, env, socket, false, nullptr);
+  return SpawnSubprocess(argv, env, socket, false, nullptr);
 }
 
 #endif
@@ -697,7 +697,7 @@
 
   argv.push_back(FormatArgumentInt("initial-client-fd", socket));
 
-  return DoubleForkAndExec(argv, nullptr, socket, true, nullptr);
+  return SpawnSubprocess(argv, nullptr, socket, true, nullptr);
 }
 
 // static
diff --git a/client/crashpad_client_mac.cc b/client/crashpad_client_mac.cc
index d25bfb7..7e92cb8 100644
--- a/client/crashpad_client_mac.cc
+++ b/client/crashpad_client_mac.cc
@@ -36,7 +36,7 @@
 #include "util/mach/notify_server.h"
 #include "util/misc/clock.h"
 #include "util/misc/implicit_cast.h"
-#include "util/posix/double_fork_and_exec.h"
+#include "util/posix/spawn_subprocess.h"
 
 namespace crashpad {
 
@@ -343,7 +343,7 @@
     // this parent process, which was probably using the exception server now
     // being restarted. The handler can’t monitor itself for its own crashes via
     // this interface.
-    if (!DoubleForkAndExec(
+    if (!SpawnSubprocess(
             argv,
             nullptr,
             server_write_fd.get(),
diff --git a/handler/linux/cros_crash_report_exception_handler.cc b/handler/linux/cros_crash_report_exception_handler.cc
index 9e58d94..4804eac 100644
--- a/handler/linux/cros_crash_report_exception_handler.cc
+++ b/handler/linux/cros_crash_report_exception_handler.cc
@@ -29,7 +29,7 @@
 #include "util/linux/ptrace_client.h"
 #include "util/misc/metrics.h"
 #include "util/misc/uuid.h"
-#include "util/posix/double_fork_and_exec.h"
+#include "util/posix/spawn_subprocess.h"
 
 namespace crashpad {
 
@@ -266,12 +266,11 @@
     argv.push_back("--always_allow_feedback");
   }
 
-  if (!DoubleForkAndExec(argv,
-                         nullptr /* envp */,
-                         file_writer.fd() /* preserve_fd */,
-                         false /* use_path */,
-                         nullptr /* child_function */)) {
-    LOG(ERROR) << "DoubleForkAndExec failed";
+  if (!SpawnSubprocess(argv,
+                       nullptr /* envp */,
+                       file_writer.fd() /* preserve_fd */,
+                       false /* use_path */,
+                       nullptr /* child_function */)) {
     Metrics::ExceptionCaptureResult(
         Metrics::CaptureResult::kFinishedWritingCrashReportFailed);
     return false;
diff --git a/util/BUILD.gn b/util/BUILD.gn
index 3fe82c3..6e6a8ee 100644
--- a/util/BUILD.gn
+++ b/util/BUILD.gn
@@ -288,11 +288,11 @@
       sources += [
         "posix/close_multiple.cc",
         "posix/close_multiple.h",
-        "posix/double_fork_and_exec.cc",
-        "posix/double_fork_and_exec.h",
         "posix/drop_privileges.cc",
         "posix/drop_privileges.h",
         "posix/process_info.h",
+        "posix/spawn_subprocess.cc",
+        "posix/spawn_subprocess.h",
 
         # These map signals to and from strings. While Fuchsia defines some of
         # the common SIGx defines, signals are never raised on Fuchsia, so
diff --git a/util/posix/double_fork_and_exec.cc b/util/posix/double_fork_and_exec.cc
deleted file mode 100644
index 1960430..0000000
--- a/util/posix/double_fork_and_exec.cc
+++ /dev/null
@@ -1,166 +0,0 @@
-// Copyright 2017 The Crashpad Authors. All rights reserved.
-//
-// Licensed under the Apache License, Version 2.0 (the "License");
-// you may not use this file except in compliance with the License.
-// You may obtain a copy of the License at
-//
-//     http://www.apache.org/licenses/LICENSE-2.0
-//
-// Unless required by applicable law or agreed to in writing, software
-// distributed under the License is distributed on an "AS IS" BASIS,
-// WITHOUT WARRANTIES OR CONDITIONS OF ANY KIND, either express or implied.
-// See the License for the specific language governing permissions and
-// limitations under the License.
-
-#include "util/posix/double_fork_and_exec.h"
-
-#include <stdlib.h>
-#include <string.h>
-#include <sys/wait.h>
-#include <unistd.h>
-
-#include "base/check_op.h"
-#include "base/logging.h"
-#include "base/posix/eintr_wrapper.h"
-#include "base/strings/stringprintf.h"
-#include "util/posix/close_multiple.h"
-
-namespace crashpad {
-
-bool DoubleForkAndExec(const std::vector<std::string>& argv,
-                       const std::vector<std::string>* envp,
-                       int preserve_fd,
-                       bool use_path,
-                       void (*child_function)()) {
-  DCHECK(!envp || !use_path);
-
-  // argv_c contains const char* pointers and is terminated by nullptr. This is
-  // suitable for passing to execv(). Although argv_c is not used in the parent
-  // process, it must be built in the parent process because it’s unsafe to do
-  // so in the child or grandchild process.
-  std::vector<const char*> argv_c;
-  argv_c.reserve(argv.size() + 1);
-  for (const std::string& argument : argv) {
-    argv_c.push_back(argument.c_str());
-  }
-  argv_c.push_back(nullptr);
-
-  std::vector<const char*> envp_c;
-  if (envp) {
-    envp_c.reserve(envp->size() + 1);
-    for (const std::string& variable : *envp) {
-      envp_c.push_back(variable.c_str());
-    }
-    envp_c.push_back(nullptr);
-  }
-
-  // Double-fork(). The three processes involved are parent, child, and
-  // grandchild. The grandchild will call execv(). The child exits immediately
-  // after spawning the grandchild, so the grandchild becomes an orphan and its
-  // parent process ID becomes 1. This relieves the parent and child of the
-  // responsibility to reap the grandchild with waitpid() or similar. The
-  // grandchild is expected to outlive the parent process, so the parent
-  // shouldn’t be concerned with reaping it. This approach means that accidental
-  // early termination of the handler process will not result in a zombie
-  // process.
-  pid_t pid = fork();
-  if (pid < 0) {
-    PLOG(ERROR) << "fork";
-    return false;
-  }
-
-  if (pid == 0) {
-    // Child process.
-
-    if (child_function) {
-      child_function();
-    }
-
-    // Call setsid(), creating a new process group and a new session, both led
-    // by this process. The new process group has no controlling terminal. This
-    // disconnects it from signals generated by the parent process’ terminal.
-    //
-    // setsid() is done in the child instead of the grandchild so that the
-    // grandchild will not be a session leader. If it were a session leader, an
-    // accidental open() of a terminal device without O_NOCTTY would make that
-    // terminal the controlling terminal.
-    //
-    // It’s not desirable for the grandchild to have a controlling terminal. The
-    // grandchild manages its own lifetime, such as by monitoring clients on its
-    // own and exiting when it loses all clients and when it deems it
-    // appropraite to do so. It may serve clients in different process groups or
-    // sessions than its original client, and receiving signals intended for its
-    // original client’s process group could be harmful in that case.
-    PCHECK(setsid() != -1) << "setsid";
-
-    pid = fork();
-    if (pid < 0) {
-      PLOG(FATAL) << "fork";
-    }
-
-    if (pid > 0) {
-      // Child process.
-
-      // _exit() instead of exit(), because fork() was called.
-      _exit(EXIT_SUCCESS);
-    }
-
-    // Grandchild process.
-
-    CloseMultipleNowOrOnExec(STDERR_FILENO + 1, preserve_fd);
-
-    // &argv_c[0] is a pointer to a pointer to const char data, but because of
-    // how C (not C++) works, execvp() wants a pointer to a const pointer to
-    // char data. It modifies neither the data nor the pointers, so the
-    // const_cast is safe.
-    char* const* argv_for_execv = const_cast<char* const*>(&argv_c[0]);
-
-    if (envp) {
-      // This cast is safe for the same reason that the argv_for_execv cast is.
-      char* const* envp_for_execv = const_cast<char* const*>(&envp_c[0]);
-      execve(argv_for_execv[0], argv_for_execv, envp_for_execv);
-      PLOG(FATAL) << "execve " << argv_for_execv[0];
-    }
-
-    if (use_path) {
-      execvp(argv_for_execv[0], argv_for_execv);
-      PLOG(FATAL) << "execvp " << argv_for_execv[0];
-    }
-
-    execv(argv_for_execv[0], argv_for_execv);
-    PLOG(FATAL) << "execv " << argv_for_execv[0];
-  }
-
-  // waitpid() for the child, so that it does not become a zombie process. The
-  // child normally exits quickly.
-  //
-  // Failures from this point on may result in the accumulation of a zombie, but
-  // should not be considered fatal. Log only warnings, but don’t treat these
-  // failures as a failure of the function overall.
-  int status;
-  pid_t wait_pid = HANDLE_EINTR(waitpid(pid, &status, 0));
-  if (wait_pid == -1) {
-    PLOG(WARNING) << "waitpid";
-    return true;
-  }
-  DCHECK_EQ(wait_pid, pid);
-
-  if (WIFSIGNALED(status)) {
-    int sig = WTERMSIG(status);
-    LOG(WARNING) << base::StringPrintf(
-        "intermediate process terminated by signal %d (%s)%s",
-        sig,
-        strsignal(sig),
-        WCOREDUMP(status) ? " (core dumped)" : "");
-  } else if (!WIFEXITED(status)) {
-    LOG(WARNING) << base::StringPrintf(
-        "intermediate process: unknown termination 0x%x", status);
-  } else if (WEXITSTATUS(status) != EXIT_SUCCESS) {
-    LOG(WARNING) << "intermediate process exited with code "
-                 << WEXITSTATUS(status);
-  }
-
-  return true;
-}
-
-}  // namespace crashpad
diff --git a/util/posix/double_fork_and_exec.h b/util/posix/double_fork_and_exec.h
deleted file mode 100644
index 02fc0f2..0000000
--- a/util/posix/double_fork_and_exec.h
+++ /dev/null
@@ -1,74 +0,0 @@
-// Copyright 2017 The Crashpad Authors. All rights reserved.
-//
-// Licensed under the Apache License, Version 2.0 (the "License");
-// you may not use this file except in compliance with the License.
-// You may obtain a copy of the License at
-//
-//     http://www.apache.org/licenses/LICENSE-2.0
-//
-// Unless required by applicable law or agreed to in writing, software
-// distributed under the License is distributed on an "AS IS" BASIS,
-// WITHOUT WARRANTIES OR CONDITIONS OF ANY KIND, either express or implied.
-// See the License for the specific language governing permissions and
-// limitations under the License.
-
-#ifndef CRASHPAD_UTIL_POSIX_DOUBLE_FORK_AND_EXEC_H_
-#define CRASHPAD_UTIL_POSIX_DOUBLE_FORK_AND_EXEC_H_
-
-#include <string>
-#include <vector>
-
-namespace crashpad {
-
-//! \brief Executes a (grand-)child process.
-//!
-//! The grandchild process will be started through the
-//! double-`fork()`-and-`execv()` pattern. This allows the grandchild to fully
-//! disassociate from the parent. The grandchild will not be a member of the
-//! parent’s process group or session and will not have a controlling terminal,
-//! providing isolation from signals not intended for it. The grandchild’s
-//! parent process, in terms of the process tree hierarchy, will be the process
-//! with process ID 1, relieving any other process of the responsibility to reap
-//! it via `waitpid()`. Aside from the three file descriptors associated with
-//! the standard input/output streams and any file descriptor passed in \a
-//! preserve_fd, the grandchild will not inherit any file descriptors from the
-//! parent process.
-//!
-//! \param[in] argv The argument vector to start the grandchild process with.
-//!     `argv[0]` is used as the path to the executable.
-//! \param[in] envp A vector of environment variables of the form `var=value` to
-//!     be passed to `execve()`. If this value is `nullptr`, the current
-//!     environment is used.
-//! \param[in] preserve_fd A file descriptor to be inherited by the grandchild
-//!     process. This file descriptor is inherited in addition to the three file
-//!     descriptors associated with the standard input/output streams. Use `-1`
-//!     if no additional file descriptors are to be inherited.
-//! \param[in] use_path Whether to consult the `PATH` environment variable when
-//!     requested to start an executable at a non-absolute path. If `false`,
-//!     `execv()`, which does not consult `PATH`, will be used. If `true`,
-//!     `execvp()`, which does consult `PATH`, will be used.
-//! \param[in] child_function If not `nullptr`, this function will be called in
-//!     the intermediate child process, prior to the second `fork()`. Take note
-//!     that this function will run in the context of a forked process, and must
-//!     be safe for that purpose.
-//!
-//! Setting both \a envp to a value other than `nullptr` and \a use_path to
-//! `true` is not currently supported.
-//!
-//! \return `true` on success, and `false` on failure with a message logged.
-//!     Only failures that occur in the parent process that indicate a definite
-//!     failure to start the the grandchild are reported in the return value.
-//!     Failures in the intermediate child or grandchild processes cannot be
-//!     reported in the return value, and are addressed by logging a message and
-//!     terminating. The caller assumes the responsibility for detecting such
-//!     failures, for example, by observing a failure to perform a successful
-//!     handshake with the grandchild process.
-bool DoubleForkAndExec(const std::vector<std::string>& argv,
-                       const std::vector<std::string>* envp,
-                       int preserve_fd,
-                       bool use_path,
-                       void (*child_function)());
-
-}  // namespace crashpad
-
-#endif  // CRASHPAD_UTIL_POSIX_DOUBLE_FORK_AND_EXEC_H_
diff --git a/util/posix/spawn_subprocess.cc b/util/posix/spawn_subprocess.cc
new file mode 100644
index 0000000..43fca18
--- /dev/null
+++ b/util/posix/spawn_subprocess.cc
@@ -0,0 +1,261 @@
+// Copyright 2017 The Crashpad Authors. All rights reserved.
+//
+// Licensed under the Apache License, Version 2.0 (the "License");
+// you may not use this file except in compliance with the License.
+// You may obtain a copy of the License at
+//
+//     http://www.apache.org/licenses/LICENSE-2.0
+//
+// Unless required by applicable law or agreed to in writing, software
+// distributed under the License is distributed on an "AS IS" BASIS,
+// WITHOUT WARRANTIES OR CONDITIONS OF ANY KIND, either express or implied.
+// See the License for the specific language governing permissions and
+// limitations under the License.
+
+#include "util/posix/spawn_subprocess.h"
+
+#include <errno.h>
+#include <spawn.h>
+#include <stdlib.h>
+#include <string.h>
+#include <sys/wait.h>
+#include <unistd.h>
+
+#include "base/check.h"
+#include "base/check_op.h"
+#include "base/logging.h"
+#include "base/posix/eintr_wrapper.h"
+#include "base/strings/stringprintf.h"
+#include "build/build_config.h"
+#include "util/posix/close_multiple.h"
+
+#if BUILDFLAG(IS_ANDROID)
+#include <android/api-level.h>
+#endif
+
+extern char** environ;
+
+namespace crashpad {
+
+namespace {
+
+#if BUILDFLAG(IS_APPLE)
+
+class PosixSpawnAttr {
+ public:
+  PosixSpawnAttr() {
+    PCHECK((errno = posix_spawnattr_init(&attr_)) == 0)
+        << "posix_spawnattr_init";
+  }
+
+  PosixSpawnAttr(const PosixSpawnAttr&) = delete;
+  PosixSpawnAttr& operator=(const PosixSpawnAttr&) = delete;
+
+  ~PosixSpawnAttr() {
+    PCHECK((errno = posix_spawnattr_destroy(&attr_)) == 0)
+        << "posix_spawnattr_destroy";
+  }
+
+  void SetFlags(short flags) {
+    PCHECK((errno = posix_spawnattr_setflags(&attr_, flags)) == 0)
+        << "posix_spawnattr_setflags";
+  }
+
+  const posix_spawnattr_t* Get() const { return &attr_; }
+
+ private:
+  posix_spawnattr_t attr_;
+};
+
+class PosixSpawnFileActions {
+ public:
+  PosixSpawnFileActions() {
+    PCHECK((errno = posix_spawn_file_actions_init(&file_actions_)) == 0)
+        << "posix_spawn_file_actions_init";
+  }
+
+  PosixSpawnFileActions(const PosixSpawnFileActions&) = delete;
+  PosixSpawnFileActions& operator=(const PosixSpawnFileActions&) = delete;
+
+  ~PosixSpawnFileActions() {
+    PCHECK((errno = posix_spawn_file_actions_destroy(&file_actions_)) == 0)
+        << "posix_spawn_file_actions_destroy";
+  }
+
+  void AddInheritedFileDescriptor(int fd) {
+    PCHECK((errno = posix_spawn_file_actions_addinherit_np(&file_actions_,
+                                                           fd)) == 0)
+        << "posix_spawn_file_actions_addinherit_np";
+  }
+
+  const posix_spawn_file_actions_t* Get() const { return &file_actions_; }
+
+ private:
+  posix_spawn_file_actions_t file_actions_;
+};
+
+#endif
+
+}  // namespace
+
+bool SpawnSubprocess(const std::vector<std::string>& argv,
+                     const std::vector<std::string>* envp,
+                     int preserve_fd,
+                     bool use_path,
+                     void (*child_function)()) {
+  // argv_c contains const char* pointers and is terminated by nullptr. This is
+  // suitable for passing to posix_spawn*() and execv*(). Although argv_c is not
+  // used in the parent process, it must be built in the parent process because
+  // it’s unsafe to do so in the child or grandchild process.
+  std::vector<const char*> argv_c;
+  argv_c.reserve(argv.size() + 1);
+  for (const std::string& argument : argv) {
+    argv_c.push_back(argument.c_str());
+  }
+  argv_c.push_back(nullptr);
+
+  std::vector<const char*> envp_c;
+  if (envp) {
+    envp_c.reserve(envp->size() + 1);
+    for (const std::string& variable : *envp) {
+      envp_c.push_back(variable.c_str());
+    }
+    envp_c.push_back(nullptr);
+  }
+
+  // The three processes involved are parent, child, and grandchild. The child
+  // exits immediately after spawning the grandchild, so the grandchild becomes
+  // an orphan and its parent process ID becomes 1. This relieves the parent and
+  // child of the responsibility to reap the grandchild with waitpid() or
+  // similar. The grandchild is expected to outlive the parent process, so the
+  // parent shouldn’t be concerned with reaping it. This approach means that
+  // accidental early termination of the handler process will not result in a
+  // zombie process.
+  pid_t pid = fork();
+  if (pid < 0) {
+    PLOG(ERROR) << "fork";
+    return false;
+  }
+
+  if (pid == 0) {
+    // Child process.
+
+    if (child_function) {
+      child_function();
+    }
+
+    // Call setsid(), creating a new process group and a new session, both led
+    // by this process. The new process group has no controlling terminal. This
+    // disconnects it from signals generated by the parent process’ terminal.
+    //
+    // setsid() is done in the child instead of the grandchild so that the
+    // grandchild will not be a session leader. If it were a session leader, an
+    // accidental open() of a terminal device without O_NOCTTY would make that
+    // terminal the controlling terminal.
+    //
+    // It’s not desirable for the grandchild to have a controlling terminal. The
+    // grandchild manages its own lifetime, such as by monitoring clients on its
+    // own and exiting when it loses all clients and when it deems it
+    // appropraite to do so. It may serve clients in different process groups or
+    // sessions than its original client, and receiving signals intended for its
+    // original client’s process group could be harmful in that case.
+    PCHECK(setsid() != -1) << "setsid";
+
+    // &argv_c[0] is a pointer to a pointer to const char data, but because of
+    // how C (not C++) works, posix_spawn*() and execv*() want a pointer to
+    // a const pointer to char data. They modify neither the data nor the
+    // pointers, so the const_cast is safe.
+    char* const* argv_for_spawn = const_cast<char* const*>(argv_c.data());
+
+    // This cast is safe for the same reason that the argv_for_spawn cast is.
+    char* const* envp_for_spawn =
+        envp ? const_cast<char* const*>(envp_c.data()) : environ;
+
+#if BUILDFLAG(IS_ANDROID) && __ANDROID_API__ < 28
+    pid = fork();
+    if (pid < 0) {
+      PLOG(FATAL) << "fork";
+    }
+
+    if (pid > 0) {
+      // Child process.
+
+      // _exit() instead of exit(), because fork() was called.
+      _exit(EXIT_SUCCESS);
+    }
+
+    // Grandchild process.
+
+    CloseMultipleNowOrOnExec(STDERR_FILENO + 1, preserve_fd);
+
+    auto execve_fp = use_path ? execvpe : execve;
+    execve_fp(argv_for_spawn[0], argv_for_spawn, envp_for_spawn);
+    PLOG(FATAL) << (use_path ? "execvpe" : "execve");
+#else
+#if BUILDFLAG(IS_APPLE)
+    PosixSpawnAttr attr;
+    attr.SetFlags(POSIX_SPAWN_CLOEXEC_DEFAULT);
+
+    PosixSpawnFileActions file_actions;
+    for (int fd = 0; fd <= STDERR_FILENO; ++fd) {
+      file_actions.AddInheritedFileDescriptor(fd);
+    }
+    file_actions.AddInheritedFileDescriptor(preserve_fd);
+
+    const posix_spawnattr_t* attr_p = attr.Get();
+    const posix_spawn_file_actions_t* file_actions_p = file_actions.Get();
+#else
+    CloseMultipleNowOrOnExec(STDERR_FILENO + 1, preserve_fd);
+
+    const posix_spawnattr_t* attr_p = nullptr;
+    const posix_spawn_file_actions_t* file_actions_p = nullptr;
+#endif
+
+    auto posix_spawn_fp = use_path ? posix_spawnp : posix_spawn;
+    if ((errno = posix_spawn_fp(nullptr,
+                                argv_for_spawn[0],
+                                file_actions_p,
+                                attr_p,
+                                argv_for_spawn,
+                                envp_for_spawn)) != 0) {
+      PLOG(FATAL) << (use_path ? "posix_spawnp" : "posix_spawn");
+    }
+
+    // _exit() instead of exit(), because fork() was called.
+    _exit(EXIT_SUCCESS);
+#endif
+  }
+
+  // waitpid() for the child, so that it does not become a zombie process. The
+  // child normally exits quickly.
+  //
+  // Failures from this point on may result in the accumulation of a zombie, but
+  // should not be considered fatal. Log only warnings, but don’t treat these
+  // failures as a failure of the function overall.
+  int status;
+  pid_t wait_pid = HANDLE_EINTR(waitpid(pid, &status, 0));
+  if (wait_pid == -1) {
+    PLOG(WARNING) << "waitpid";
+    return true;
+  }
+  DCHECK_EQ(wait_pid, pid);
+
+  if (WIFSIGNALED(status)) {
+    int sig = WTERMSIG(status);
+    LOG(WARNING) << base::StringPrintf(
+        "intermediate process terminated by signal %d (%s)%s",
+        sig,
+        strsignal(sig),
+        WCOREDUMP(status) ? " (core dumped)" : "");
+  } else if (!WIFEXITED(status)) {
+    LOG(WARNING) << base::StringPrintf(
+        "intermediate process: unknown termination 0x%x", status);
+  } else if (WEXITSTATUS(status) != EXIT_SUCCESS) {
+    LOG(WARNING) << "intermediate process exited with code "
+                 << WEXITSTATUS(status);
+  }
+
+  return true;
+}
+
+}  // namespace crashpad
diff --git a/util/posix/spawn_subprocess.h b/util/posix/spawn_subprocess.h
new file mode 100644
index 0000000..2391081
--- /dev/null
+++ b/util/posix/spawn_subprocess.h
@@ -0,0 +1,69 @@
+// Copyright 2017 The Crashpad Authors. All rights reserved.
+//
+// Licensed under the Apache License, Version 2.0 (the "License");
+// you may not use this file except in compliance with the License.
+// You may obtain a copy of the License at
+//
+//     http://www.apache.org/licenses/LICENSE-2.0
+//
+// Unless required by applicable law or agreed to in writing, software
+// distributed under the License is distributed on an "AS IS" BASIS,
+// WITHOUT WARRANTIES OR CONDITIONS OF ANY KIND, either express or implied.
+// See the License for the specific language governing permissions and
+// limitations under the License.
+
+#ifndef CRASHPAD_UTIL_POSIX_SPAWN_SUBPROCESS_H_
+#define CRASHPAD_UTIL_POSIX_SPAWN_SUBPROCESS_H_
+
+#include <string>
+#include <vector>
+
+namespace crashpad {
+
+//! \brief Spawns a subprocess.
+//!
+//! A grandchild process will be started through the
+//! `fork()`-and-`posix_spawn()` pattern where supported, and
+//! double-`fork()`-and-`execv()` pattern elsewhere. This allows the grandchild
+//! to fully disassociate from the parent. The grandchild will not be a member
+//! of the parent’s process group or session and will not have a controlling
+//! terminal, providing isolation from signals not intended for it. The
+//! grandchild’s parent process, in terms of the process tree hierarchy, will be
+//! the process with process ID 1, relieving any other process of the
+//! responsibility to reap it via `waitpid()`. Aside from the three file
+//! descriptors associated with the standard input/output streams and any file
+//! descriptor passed in \a preserve_fd, the grandchild will not inherit any
+//! file descriptors from the parent process.
+//!
+//! \param[in] argv The argument vector to start the grandchild process with.
+//!     `argv[0]` is used as the path to the executable.
+//! \param[in] envp A vector of environment variables of the form `var=value` to
+//!     be passed to the spawned process. If this value is `nullptr`, the
+//!     current environment is used.
+//! \param[in] preserve_fd A file descriptor to be inherited by the grandchild
+//!     process. This file descriptor is inherited in addition to the three file
+//!     descriptors associated with the standard input/output streams. Use `-1`
+//!     if no additional file descriptors are to be inherited.
+//! \param[in] use_path Whether to consult the `PATH` environment variable when
+//!     requested to start an executable at a non-absolute path.
+//! \param[in] child_function If not `nullptr`, this function will be called in
+//!     the intermediate child process. Take note that this function will run in
+//!     the context of a forked process, and must be safe for that purpose.
+//!
+//! \return `true` on success, and `false` on failure with a message logged.
+//!     Only failures that occur in the parent process that indicate a definite
+//!     failure to start the the grandchild are reported in the return value.
+//!     Failures in the intermediate child or grandchild processes cannot be
+//!     reported in the return value, and are addressed by logging a message and
+//!     terminating. The caller assumes the responsibility for detecting such
+//!     failures, for example, by observing a failure to perform a successful
+//!     handshake with the grandchild process.
+bool SpawnSubprocess(const std::vector<std::string>& argv,
+                     const std::vector<std::string>* envp,
+                     int preserve_fd,
+                     bool use_path,
+                     void (*child_function)());
+
+}  // namespace crashpad
+
+#endif  // CRASHPAD_UTIL_POSIX_SPAWN_SUBPROCESS_H_