From 6c9a2c06a5d1cbdf7ae8a2a39e8c12bc7fec52aa Mon Sep 17 00:00:00 2001 From: Jed Davis Date: Sat, 2 Dec 2023 01:49:58 +0000 Subject: [PATCH] Bug 1858279 - Rework how process launch interacts with Linux sandboxing and the fork server. r=nika,gcp Currently, process launch interfaces with Linux sandboxing via the ForkDelegate abstraction, basically replacing `fork` with an opaque stateful callback, configured using various info from the parent process (prefs, gfxInfo, etc.). Unfortunately, the fork server effectively needs to move that object into another process, and this is accomplished in a way that's complicated and difficult to deal with and causes some problems. Instead, this patch makes the sandboxing state transparent: fields are added to LaunchOptions which are serialized/deserialized, and the sandbox launcher object is now exposed in a header and used directly by LaunchApp (and its fork server equivalent). There are a few other changes that follow from this. In particular, the pipe for the chroot server is now created later, during LaunchApp but before `FileDescriptorShuffle::Init`, so LaunchApp will side-effect `LaunchOptions::fds_to_remap`. (But this also means we're no longer using a fake mapping of fd 10 which isn't actually used, and we're no longer creating a socketpair in one process and sending both ends to another process that could have just created it itself.) For more details, see the comments in `SandboxLaunch.h` for the member functions `Configure`, `Prepare`, and `Fork`. As a convenient side effect of this change, `Prepare` is now fallible, so we can handle certain error cases (like failing to create a socket pair) more gracefully. Differential Revision: https://phabricator.services.mozilla.com/D194456 --- ipc/chromium/src/base/process_util.h | 19 +- ipc/chromium/src/base/process_util_linux.cc | 62 +++++-- ipc/glue/ForkServer.cpp | 10 +- ipc/glue/ForkServiceChild.cpp | 13 +- ipc/glue/ForkServiceChild.h | 14 +- ipc/glue/GeckoChildProcessHost.cpp | 2 +- security/sandbox/linux/SandboxChrootProto.h | 3 - .../sandbox/linux/launch/SandboxLaunch.cpp | 170 +++++++----------- security/sandbox/linux/launch/SandboxLaunch.h | 59 +++++- 9 files changed, 194 insertions(+), 158 deletions(-) diff --git a/ipc/chromium/src/base/process_util.h b/ipc/chromium/src/base/process_util.h index 75f7fe9c5248..45426d616e40 100644 --- a/ipc/chromium/src/base/process_util.h +++ b/ipc/chromium/src/base/process_util.h @@ -163,15 +163,16 @@ struct LaunchOptions { bool use_forkserver = false; #endif -#if defined(XP_LINUX) - struct ForkDelegate { - virtual ~ForkDelegate() {} - virtual pid_t Fork() = 0; - }; - - // If non-null, the fork delegate will be called instead of fork(). - // It is not required to call pthread_atfork hooks. - mozilla::UniquePtr fork_delegate = nullptr; +#if defined(XP_LINUX) && defined(MOZ_SANDBOX) + // These fields are used by the sandboxing code in SandboxLaunch.cpp. + // It's not ideal to have them here, but trying to abstract them makes + // it harder to serialize LaunchOptions for the fork server. + // + // (fork_flags holds extra flags for the clone() syscall, and + // sandbox_chroot indicates whether the child process will be given + // the ability to chroot() itself to an empty directory.) + int fork_flags = 0; + bool sandbox_chroot = false; #endif #ifdef XP_DARWIN diff --git a/ipc/chromium/src/base/process_util_linux.cc b/ipc/chromium/src/base/process_util_linux.cc index bf03a3c8dccb..1586bc34342d 100644 --- a/ipc/chromium/src/base/process_util_linux.cc +++ b/ipc/chromium/src/base/process_util_linux.cc @@ -16,6 +16,10 @@ #include "mozilla/ipc/LaunchError.h" +#if defined(XP_LINUX) && defined(MOZ_SANDBOX) +# include "mozilla/SandboxLaunch.h" +#endif + #if defined(MOZ_ENABLE_FORKSERVER) # include # include @@ -93,6 +97,17 @@ bool AppProcessBuilder::ForkProcess(const std::vector& argv, } }); +# if defined(XP_LINUX) && defined(MOZ_SANDBOX) + mozilla::SandboxLaunch launcher; + if (!launcher.Prepare(&options)) { + return false; + } +# else + struct { + pid_t Fork() { return fork(); } + } launcher; +# endif + argv_ = argv; if (!shuffle_.Init(options.fds_to_remap)) { return false; @@ -103,16 +118,12 @@ bool AppProcessBuilder::ForkProcess(const std::vector& argv, fflush(stdout); fflush(stderr); -# ifdef XP_LINUX - pid_t pid = options.fork_delegate ? options.fork_delegate->Fork() : fork(); + pid_t pid = launcher.Fork(); // WARNING: if pid == 0, only async signal safe operations are permitted from // here until exec or _exit. // // Specifically, heap allocation is not safe: the sandbox's fork substitute // won't run the pthread_atfork handlers that fix up the malloc locks. -# else - pid_t pid = fork(); -# endif if (pid < 0) { return false; @@ -209,23 +220,31 @@ static Result LaunchAppWithForkServer( ProcessHandle* process_handle) { MOZ_ASSERT(ForkServiceChild::Get()); - nsTArray _argv(argv.size()); - nsTArray env(options.env_map.size()); - nsTArray fdsremap(options.fds_to_remap.size()); + // Check for unsupported options + MOZ_ASSERT(options.workdir.empty()); + MOZ_ASSERT(!options.full_env); + MOZ_ASSERT(!options.wait); + + ForkServiceChild::Args forkArgs; + +# if defined(XP_LINUX) && defined(MOZ_SANDBOX) + forkArgs.mForkFlags = options.fork_flags; + forkArgs.mChroot = options.sandbox_chroot; +# endif for (auto& arg : argv) { - _argv.AppendElement(arg.c_str()); + forkArgs.mArgv.AppendElement(arg.c_str()); } for (auto& vv : options.env_map) { - env.AppendElement(mozilla::EnvVar(nsCString(vv.first.c_str()), - nsCString(vv.second.c_str()))); + forkArgs.mEnv.AppendElement(mozilla::EnvVar(nsCString(vv.first.c_str()), + nsCString(vv.second.c_str()))); } for (auto& fdmapping : options.fds_to_remap) { - fdsremap.AppendElement(mozilla::FdMapping( + forkArgs.mFdsRemap.AppendElement(mozilla::FdMapping( mozilla::ipc::FileDescriptor(fdmapping.first), fdmapping.second)); } - return ForkServiceChild::Get()->SendForkNewSubprocess(_argv, env, fdsremap, + return ForkServiceChild::Get()->SendForkNewSubprocess(forkArgs, process_handle); } #endif // MOZ_ENABLE_FORKSERVER @@ -241,6 +260,17 @@ Result LaunchApp(const std::vector& argv, mozilla::UniquePtr argv_cstr(new char*[argv.size() + 1]); +#if defined(XP_LINUX) && defined(MOZ_SANDBOX) + mozilla::SandboxLaunch launcher; + if (!launcher.Prepare(&options)) { + return Err(LaunchError("SL::Prepare", errno)); + } +#else + struct { + pid_t Fork() { return fork(); } + } launcher; +#endif + EnvironmentArray env_storage; const EnvironmentArray& envp = options.full_env ? options.full_env @@ -267,16 +297,12 @@ Result LaunchApp(const std::vector& argv, const char* gcov_child_prefix = PR_GetEnv("GCOV_CHILD_PREFIX"); #endif -#ifdef XP_LINUX - pid_t pid = options.fork_delegate ? options.fork_delegate->Fork() : fork(); + pid_t pid = launcher.Fork(); // WARNING: if pid == 0, only async signal safe operations are permitted from // here until exec or _exit. // // Specifically, heap allocation is not safe: the sandbox's fork substitute // won't run the pthread_atfork handlers that fix up the malloc locks. -#else - pid_t pid = fork(); -#endif if (pid < 0) { CHROMIUM_LOG(WARNING) << "fork() failed: " << strerror(errno); diff --git a/ipc/glue/ForkServer.cpp b/ipc/glue/ForkServer.cpp index 5755152374ae..b70fdbf2c003 100644 --- a/ipc/glue/ForkServer.cpp +++ b/ipc/glue/ForkServer.cpp @@ -155,6 +155,12 @@ inline bool ParseForkNewSubprocess(IPC::Message& aMsg, nsTArray env_map; nsTArray fds_remap; +#if defined(XP_LINUX) && defined(MOZ_SANDBOX) + ReadParamInfallible(&reader, &aOptions->fork_flags, + "Error deserializing 'int'"); + ReadParamInfallible(&reader, &aOptions->sandbox_chroot, + "Error deserializing 'bool'"); +#endif ReadParamInfallible(&reader, &argv_array, "Error deserializing 'nsCString[]'"); ReadParamInfallible(&reader, &env_map, "Error deserializing 'EnvVar[]'"); @@ -203,10 +209,6 @@ void ForkServer::OnMessageReceived(UniquePtr message) { return; } -#if defined(XP_LINUX) && defined(MOZ_SANDBOX) - mozilla::SandboxLaunchForkServerPrepare(argv, options); -#endif - base::ProcessHandle child_pid = -1; mAppProcBuilder = MakeUnique(); if (!mAppProcBuilder->ForkProcess(argv, std::move(options), &child_pid)) { diff --git a/ipc/glue/ForkServiceChild.cpp b/ipc/glue/ForkServiceChild.cpp index db130343cc59..65560dfaea20 100644 --- a/ipc/glue/ForkServiceChild.cpp +++ b/ipc/glue/ForkServiceChild.cpp @@ -72,15 +72,18 @@ ForkServiceChild::~ForkServiceChild() { } Result ForkServiceChild::SendForkNewSubprocess( - const nsTArray& aArgv, const nsTArray& aEnvMap, - const nsTArray& aFdsRemap, pid_t* aPid) { + const Args& aArgs, pid_t* aPid) { mRecvPid = -1; IPC::Message msg(MSG_ROUTING_CONTROL, Msg_ForkNewSubprocess__ID); IPC::MessageWriter writer(msg); - WriteIPDLParam(&writer, nullptr, aArgv); - WriteIPDLParam(&writer, nullptr, aEnvMap); - WriteIPDLParam(&writer, nullptr, aFdsRemap); +#if defined(XP_LINUX) && defined(MOZ_SANDBOX) + WriteIPDLParam(&writer, nullptr, aArgs.mForkFlags); + WriteIPDLParam(&writer, nullptr, aArgs.mChroot); +#endif + WriteIPDLParam(&writer, nullptr, aArgs.mArgv); + WriteIPDLParam(&writer, nullptr, aArgs.mEnv); + WriteIPDLParam(&writer, nullptr, aArgs.mFdsRemap); if (!mTcver->Send(msg)) { MOZ_LOG(gForkServiceLog, LogLevel::Verbose, ("the pipe to the fork server is closed or having errors")); diff --git a/ipc/glue/ForkServiceChild.h b/ipc/glue/ForkServiceChild.h index 40699df061a2..73d090d556a5 100644 --- a/ipc/glue/ForkServiceChild.h +++ b/ipc/glue/ForkServiceChild.h @@ -33,6 +33,16 @@ class ForkServiceChild { ForkServiceChild(int aFd, GeckoChildProcessHost* aProcess); virtual ~ForkServiceChild(); + struct Args { +#if defined(XP_LINUX) && defined(MOZ_SANDBOX) + int mForkFlags = 0; + bool mChroot = false; +#endif + nsTArray mArgv; + nsTArray mEnv; + nsTArray mFdsRemap; + }; + /** * Ask the fork server to create a new process with given parameters. * @@ -45,9 +55,7 @@ class ForkServiceChild { * \param aPid returns the PID of the content process created. * \return true if success. */ - Result SendForkNewSubprocess( - const nsTArray& aArgv, const nsTArray& aEnvMap, - const nsTArray& aFdsRemap, pid_t* aPid); + Result SendForkNewSubprocess(const Args& aArgs, pid_t* aPid); /** * Create a fork server process and the singleton of this class. diff --git a/ipc/glue/GeckoChildProcessHost.cpp b/ipc/glue/GeckoChildProcessHost.cpp index be78f9369eca..8e76ea48a586 100644 --- a/ipc/glue/GeckoChildProcessHost.cpp +++ b/ipc/glue/GeckoChildProcessHost.cpp @@ -614,7 +614,7 @@ void GeckoChildProcessHost::PrepareLaunch() { } #if defined(XP_LINUX) && defined(MOZ_SANDBOX) - SandboxLaunchPrepare(mProcessType, mLaunchOptions.get(), mSandbox); + SandboxLaunch::Configure(mProcessType, mSandbox, mLaunchOptions.get()); #endif #ifdef XP_WIN diff --git a/security/sandbox/linux/SandboxChrootProto.h b/security/sandbox/linux/SandboxChrootProto.h index 6afc5311a13f..362e15a219f0 100644 --- a/security/sandbox/linux/SandboxChrootProto.h +++ b/security/sandbox/linux/SandboxChrootProto.h @@ -12,9 +12,6 @@ namespace mozilla { static const int kSandboxChrootClientFd = 6; -#if defined(MOZ_ENABLE_FORKSERVER) -static const int kSandboxChrootServerFd = 10; -#endif static const char kSandboxChrootRequest = 'C'; static const char kSandboxChrootResponse = 'O'; static const char kSandboxChrootEnvFlag[] = "MOZ_SANDBOX_USE_CHROOT"; diff --git a/security/sandbox/linux/launch/SandboxLaunch.cpp b/security/sandbox/linux/launch/SandboxLaunch.cpp index 4165bb3f15cc..bec94f3c4c11 100644 --- a/security/sandbox/linux/launch/SandboxLaunch.cpp +++ b/security/sandbox/linux/launch/SandboxLaunch.cpp @@ -220,47 +220,30 @@ static void AttachSandboxReporter(base::file_handle_mapping_vector* aFdMap) { aFdMap->push_back({srcFd, dstFd}); } -class SandboxFork : public base::LaunchOptions::ForkDelegate { - public: - explicit SandboxFork(int aFlags, bool aChroot, int aServerFd = -1, - int aClientFd = -1); - virtual ~SandboxFork(); - - void PrepareMapping(base::file_handle_mapping_vector* aMap); - pid_t Fork() override; - - private: - int mFlags; - int mChrootServer; - int mChrootClient; - - void StartChrootServer(); - SandboxFork(const SandboxFork&) = delete; - SandboxFork& operator=(const SandboxFork&) = delete; -}; - static int GetEffectiveSandboxLevel(GeckoProcessType aType, ipc::SandboxingKind aKind) { auto info = SandboxInfo::Get(); switch (aType) { - case GeckoProcessType_GMPlugin: - if (info.Test(SandboxInfo::kEnabledForMedia)) { - return 1; - } - return 0; - case GeckoProcessType_Content: #ifdef MOZ_ENABLE_FORKSERVER // With this env MOZ_SANDBOXED will be set, and mozsandbox will - // be preloaded for the fork server. The content processes rely - // on wrappers defined by mozsandbox to work properly. + // be preloaded for the fork server. Sandboxed child processes + // rely on wrappers defined by mozsandbox to work properly. case GeckoProcessType_ForkServer: + return 1; + break; #endif + case GeckoProcessType_Content: // GetEffectiveContentSandboxLevel is main-thread-only due to prefs. MOZ_ASSERT(NS_IsMainThread()); if (info.Test(SandboxInfo::kEnabledForContent)) { return GetEffectiveContentSandboxLevel(); } return 0; + case GeckoProcessType_GMPlugin: + if (info.Test(SandboxInfo::kEnabledForMedia)) { + return 1; + } + return 0; case GeckoProcessType_RDD: return PR_GetEnv("MOZ_DISABLE_RDD_SANDBOX") == nullptr ? 1 : 0; case GeckoProcessType_Socket: @@ -274,8 +257,10 @@ static int GetEffectiveSandboxLevel(GeckoProcessType aType, } } -void SandboxLaunchPrepare(GeckoProcessType aType, base::LaunchOptions* aOptions, - ipc::SandboxingKind aKind) { +// static +void SandboxLaunch::Configure(GeckoProcessType aType, SandboxingKind aKind, + LaunchOptions* aOptions) { + MOZ_ASSERT(aOptions->fork_flags == 0 && !aOptions->sandbox_chroot); auto info = SandboxInfo::Get(); // We won't try any kind of sandboxing without seccomp-bpf. @@ -364,82 +349,18 @@ void SandboxLaunchPrepare(GeckoProcessType aType, base::LaunchOptions* aOptions, if (canChroot || flags != 0) { flags |= CLONE_NEWUSER; - auto forker = MakeUnique(flags, canChroot); - forker->PrepareMapping(&aOptions->fds_to_remap); - aOptions->fork_delegate = std::move(forker); - // Pass to |SandboxLaunchForkServerPrepare()| in the fork server. - aOptions->env_map[kSandboxChrootEnvFlag] = - std::to_string(canChroot ? 1 : 0) + std::to_string(flags); } + + aOptions->env_map[kSandboxChrootEnvFlag] = std::to_string(canChroot ? 1 : 0); + + aOptions->sandbox_chroot = canChroot; + aOptions->fork_flags = flags; } -#if defined(MOZ_ENABLE_FORKSERVER) -/** - * Called by the fork server to install a fork delegator. - * - * In the case of fork server, the value of the flags of |SandboxFork| - * are passed as an env variable to the fork server so that we can - * recreate a |SandboxFork| as a fork delegator at the fork server. - */ -void SandboxLaunchForkServerPrepare(const std::vector& aArgv, - base::LaunchOptions& aOptions) { - auto chroot = std::find_if( - aOptions.env_map.begin(), aOptions.env_map.end(), - [](auto& elt) { return elt.first == kSandboxChrootEnvFlag; }); - if (chroot == aOptions.env_map.end()) { - return; - } - bool canChroot = chroot->second.c_str()[0] == '1'; - int flags = atoi(chroot->second.c_str() + 1); - MOZ_ASSERT(flags || canChroot); +SandboxLaunch::SandboxLaunch() + : mFlags(0), mChrootServer(-1), mChrootClient(-1) {} - // Find chroot server fd. It is supposed to be map to - // kSandboxChrootServerFd so that we find it out from the mapping. - auto fdmap = std::find_if( - aOptions.fds_to_remap.begin(), aOptions.fds_to_remap.end(), - [](auto& elt) { return elt.second == kSandboxChrootServerFd; }); - MOZ_ASSERT(fdmap != aOptions.fds_to_remap.end(), - "ChrootServerFd is not found with sandbox chroot"); - int chrootserverfd = fdmap->first; - aOptions.fds_to_remap.erase(fdmap); - - // Set only the chroot server fd, not the client fd. Because, the - // client fd is already in |fds_to_remap|, we don't need the forker - // to do it again. And, the forker need only the server fd, that - // chroot server uses it to sync with the client (content). See - // |SandboxFox::StartChrootServer()|. - auto forker = MakeUnique(flags, canChroot, chrootserverfd); - aOptions.fork_delegate = std::move(forker); -} -#endif - -SandboxFork::SandboxFork(int aFlags, bool aChroot, int aServerFd, int aClientFd) - : mFlags(aFlags), mChrootServer(aServerFd), mChrootClient(aClientFd) { - if (aChroot && mChrootServer < 0) { - int fds[2]; - int rv = socketpair(AF_UNIX, SOCK_STREAM | SOCK_CLOEXEC, 0, fds); - if (rv != 0) { - SANDBOX_LOG_ERRNO("socketpair"); - MOZ_CRASH("socketpair failed"); - } - mChrootClient = fds[0]; - mChrootServer = fds[1]; - } -} - -void SandboxFork::PrepareMapping(base::file_handle_mapping_vector* aMap) { - MOZ_ASSERT(XRE_GetProcessType() != GeckoProcessType_ForkServer); - if (mChrootClient >= 0) { - aMap->push_back({mChrootClient, kSandboxChrootClientFd}); - } -#if defined(MOZ_ENABLE_FORKSERVER) - if (mChrootServer >= 0) { - aMap->push_back({mChrootServer, kSandboxChrootServerFd}); - } -#endif -} - -SandboxFork::~SandboxFork() { +SandboxLaunch::~SandboxLaunch() { if (mChrootClient >= 0) { close(mChrootClient); } @@ -448,6 +369,31 @@ SandboxFork::~SandboxFork() { } } +bool SandboxLaunch::Prepare(LaunchOptions* aOptions) { + MOZ_ASSERT(mChrootClient < 0 && mChrootServer < 0); + + mFlags = aOptions->fork_flags; + + // Create the socket for communication between the child process and + // the chroot helper process. The client end is passed to the child + // via `fds_to_remap` and the server end is inherited and used in + // `StartChrootServer`. + if (aOptions->sandbox_chroot) { + int fds[2]; + int rv = socketpair(AF_UNIX, SOCK_STREAM | SOCK_CLOEXEC, 0, fds); + if (rv != 0) { + SANDBOX_LOG_ERRNO("socketpair"); + return false; + } + mChrootClient = fds[0]; + mChrootServer = fds[1]; + + aOptions->fds_to_remap.push_back({mChrootClient, kSandboxChrootClientFd}); + } + + return true; +} + static void BlockAllSignals(sigset_t* aOldSigs) { sigset_t allSigs; int rv = sigfillset(&allSigs); @@ -613,7 +559,7 @@ static void DropAllCaps() { } } -pid_t SandboxFork::Fork() { +pid_t SandboxLaunch::Fork() { if (mFlags == 0) { MOZ_ASSERT(mChrootServer < 0); return fork(); @@ -649,16 +595,28 @@ pid_t SandboxFork::Fork() { if (mChrootServer >= 0) { StartChrootServer(); + // Don't close the client fd when this object is destroyed. At + // this point we're in the child process proper, so it's "owned" + // by the FileDescriptorShuffle / CloseSuperfluous code (i.e., + // that's what will consume it and close it). + mChrootClient = -1; } - // execve() will drop capabilities, but it seems best to also drop - // them here in case they'd do something unexpected in the generic - // post-fork code. + // execve() will drop capabilities, but the fork server case doesn't + // exec so we need to do this directly. (Also, it's a good idea to + // follow the principle of least privilege even when not strictly + // necessary.) + // + // Note that, while capabilities within an unprivileged user + // namespace are constrained in theory, in practice they expose a + // lot of attack surface and there have been exploitable kernel bugs + // related to that in the past, so we really want to drop them + // before doing anything that needs sandboxing. DropAllCaps(); return 0; } -void SandboxFork::StartChrootServer() { +void SandboxLaunch::StartChrootServer() { // Run the rest of this function in a separate process that can // chroot() on behalf of this process after it's sandboxed. pid_t pid = ForkWithFlags(CLONE_FS); diff --git a/security/sandbox/linux/launch/SandboxLaunch.h b/security/sandbox/linux/launch/SandboxLaunch.h index bbe52f070236..988709dcdbc3 100644 --- a/security/sandbox/linux/launch/SandboxLaunch.h +++ b/security/sandbox/linux/launch/SandboxLaunch.h @@ -14,15 +14,56 @@ namespace mozilla { -// Called in the parent process to set up launch-time aspects of -// sandboxing. If aType is GeckoProcessType_Content, this must be -// called on the main thread in order to access prefs. -void SandboxLaunchPrepare(GeckoProcessType aType, base::LaunchOptions* aOptions, - ipc::SandboxingKind aKind); -#if defined(MOZ_ENABLE_FORKSERVER) -void SandboxLaunchForkServerPrepare(const std::vector& aArgv, - base::LaunchOptions& aOptions); -#endif +class SandboxLaunch final { + public: + SandboxLaunch(); + ~SandboxLaunch(); + + SandboxLaunch(const SandboxLaunch&) = delete; + SandboxLaunch& operator=(const SandboxLaunch&) = delete; + + using LaunchOptions = base::LaunchOptions; + using SandboxingKind = ipc::SandboxingKind; + + // Decide what sandboxing features will be used for a process, and + // modify `*aOptions` accordingly. This does not allocate fds or + // other OS resources (other than memory for strings). + // + // This is meant to be called in the parent process (even if the + // fork server will be used), and if `aType` is Content then it must + // be called on the main thread in order to access prefs. + static void Configure(GeckoProcessType aType, SandboxingKind aKind, + LaunchOptions* aOptions); + + // Finish setting up for process launch, based on the information + // from `Configure(...)`. Called in the process that will do the + // launch (fork server if applicable, otherwise parent), and before + // calling `FileDescriptorShuffle::Init`. + // + // This can allocate fds (owned by `*this`) and modify + // `aOptions->fds_to_remap`, but does not access the + // environment-related fields of `*aOptions`. + bool Prepare(LaunchOptions* aOptions); + + // Launch the child process, similarly to `::fork()`; called after + // `Configure` and `Prepare`. + // + // If launch-time sandboxing features are used, `pthread_atfork` + // hooks are not currently supported in that case, and signal + // handlers are reset in the child process. If sandboxing is not + // used, this is equivalent to `::fork()`. + pid_t Fork(); + + private: + int mFlags; + int mChrootServer; + int mChrootClient; + + void StartChrootServer(); +}; + +// This doesn't really belong in this header but it's used in both +// SandboxLaunch and SandboxBrokerPolicyFactory. bool HasAtiDrivers(); } // namespace mozilla