mirror of
https://github.com/mozilla/gecko-dev.git
synced 2024-10-09 03:15:11 +00:00
Bug 1470591 - Part 3: AppForkBuilder to ceate a new content process. r=gsvelto
An instance of AppForkBuilder creates a new content process from the passed args and LaunchOptions. It bascally does the same thing as LaunchApp() for Linux, but it divides the procedure to two parts, - the 1st part forking a new process, and - the 2nd part initializing FDs, ENV, and message loops. Going two parts gives fork servers a chance to clean new processes before the initialization and running WEB content. For example, to clean sensitive data from memory. Depends on D46879 Differential Revision: https://phabricator.services.mozilla.com/D46880 --HG-- extra : moz-landing-system : lando
This commit is contained in:
parent
e9554bb05d
commit
3b1f4faef8
@ -43,6 +43,17 @@
|
||||
#include "mozilla/UniquePtr.h"
|
||||
#include "mozilla/ipc/EnvironmentMap.h"
|
||||
|
||||
#if defined(MOZ_ENABLE_FORKSERVER)
|
||||
#include "nsString.h"
|
||||
#include "mozilla/ipc/FileDescriptorShuffle.h"
|
||||
|
||||
namespace mozilla {
|
||||
namespace ipc {
|
||||
class FileDescriptor;
|
||||
}
|
||||
}
|
||||
#endif
|
||||
|
||||
#if defined(OS_MACOSX)
|
||||
struct kinfo_proc;
|
||||
#endif
|
||||
@ -165,6 +176,46 @@ typedef mozilla::UniquePtr<char*[], FreeEnvVarsArray> EnvironmentArray;
|
||||
EnvironmentArray BuildEnvironmentArray(const environment_map& env_vars_to_set);
|
||||
#endif
|
||||
|
||||
#if defined(MOZ_ENABLE_FORKSERVER)
|
||||
/**
|
||||
* Create and initialize a new process as a content process.
|
||||
*
|
||||
* This class is used only by the fork server.
|
||||
* To create a new content process, two steps are
|
||||
* - calling |ForkProcess()| to create a new process, and
|
||||
* - calling |InitAppProcess()| in the new process, the child
|
||||
* process, to initialize it for running WEB content later.
|
||||
*
|
||||
* The fork server can clean up it's resources in-between the first
|
||||
* and second step, that is why two steps.
|
||||
*/
|
||||
class AppProcessBuilder {
|
||||
public:
|
||||
AppProcessBuilder();
|
||||
// This function will fork a new process for use as a
|
||||
// content processes.
|
||||
bool ForkProcess(const std::vector<std::string>& argv,
|
||||
const LaunchOptions& options, ProcessHandle* process_handle);
|
||||
// This function will be called in the child process to initializes
|
||||
// the environment of the content process. It should be called
|
||||
// after the message loop of the main thread, to make sure the fork
|
||||
// server is destroyed properly in the child process.
|
||||
//
|
||||
// The message loop may allocate resources like file descriptors.
|
||||
// If this function is called before the end of the loop, the
|
||||
// reosurces may be destroyed while the loop is still alive.
|
||||
void InitAppProcess(int *argcp, char*** argvp);
|
||||
|
||||
private:
|
||||
void ReplaceArguments(int *argcp, char*** argvp);
|
||||
|
||||
mozilla::ipc::FileDescriptorShuffle shuffle_;
|
||||
std::vector<std::string> argv_;
|
||||
};
|
||||
|
||||
void RegisterForkServerNoCloseFD(int aFd);
|
||||
#endif
|
||||
|
||||
// Executes the application specified by cl. This function delegates to one
|
||||
// of the above two platform-specific functions.
|
||||
bool LaunchApp(const CommandLine& cl, const LaunchOptions&,
|
||||
|
@ -11,10 +11,24 @@
|
||||
#include <sys/wait.h>
|
||||
#include <unistd.h>
|
||||
|
||||
#include "algorithm"
|
||||
|
||||
#if defined(MOZ_ENABLE_FORKSERVER)
|
||||
#include <stdlib.h>
|
||||
#include <sys/types.h>
|
||||
# if defined(DEBUG)
|
||||
#include "base/message_loop.h"
|
||||
# endif
|
||||
#include "mozilla/DebugOnly.h"
|
||||
|
||||
using namespace mozilla::ipc;
|
||||
#endif
|
||||
|
||||
#include "base/eintr_wrapper.h"
|
||||
#include "base/logging.h"
|
||||
#include "mozilla/ipc/FileDescriptorShuffle.h"
|
||||
#include "mozilla/UniquePtr.h"
|
||||
#include "mozilla/StaticPtr.h"
|
||||
|
||||
// WARNING: despite the name, this file is also used on the BSDs and
|
||||
// Solaris (basically, Unixes that aren't Mac OS), not just Linux.
|
||||
@ -27,6 +41,119 @@ static mozilla::EnvironmentLog gProcessLog("MOZ_PROCESS_LOG");
|
||||
|
||||
namespace base {
|
||||
|
||||
#if defined(MOZ_ENABLE_FORKSERVER)
|
||||
static mozilla::StaticAutoPtr<std::vector<int> > sNoCloseFDs;
|
||||
|
||||
void
|
||||
RegisterForkServerNoCloseFD(int fd) {
|
||||
if (!sNoCloseFDs) {
|
||||
sNoCloseFDs = new std::vector<int>();
|
||||
}
|
||||
sNoCloseFDs->push_back(fd);
|
||||
}
|
||||
|
||||
static bool
|
||||
IsNoCloseFd(int fd) {
|
||||
if (!sNoCloseFDs) {
|
||||
return false;
|
||||
}
|
||||
return std::any_of(sNoCloseFDs->begin(), sNoCloseFDs->end(),
|
||||
[fd](int regfd) -> bool { return regfd == fd; });
|
||||
}
|
||||
|
||||
AppProcessBuilder::AppProcessBuilder() {
|
||||
}
|
||||
|
||||
static void
|
||||
ReplaceEnviroment(const LaunchOptions& options) {
|
||||
for (auto& elt : options.env_map) {
|
||||
setenv(elt.first.c_str(), elt.second.c_str(), 1);
|
||||
}
|
||||
}
|
||||
|
||||
bool
|
||||
AppProcessBuilder::ForkProcess(const std::vector<std::string>& argv,
|
||||
const LaunchOptions& options, ProcessHandle* process_handle) {
|
||||
argv_ = argv;
|
||||
if (!shuffle_.Init(options.fds_to_remap)) {
|
||||
return false;
|
||||
}
|
||||
|
||||
// Avoid the content of the buffer being sent out by child processes
|
||||
// repeatly.
|
||||
fflush(stdout);
|
||||
fflush(stderr);
|
||||
|
||||
#ifdef OS_LINUX
|
||||
pid_t pid = options.fork_delegate ? options.fork_delegate->Fork() : 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;
|
||||
}
|
||||
|
||||
if (pid == 0) {
|
||||
ReplaceEnviroment(options);
|
||||
} else {
|
||||
gProcessLog.print("==> process %d launched child process %d\n",
|
||||
GetCurrentProcId(), pid);
|
||||
if (options.wait) HANDLE_EINTR(waitpid(pid, 0, 0));
|
||||
}
|
||||
|
||||
if (process_handle) *process_handle = pid;
|
||||
|
||||
return true;
|
||||
}
|
||||
|
||||
void
|
||||
AppProcessBuilder::ReplaceArguments(int *argcp, char*** argvp) {
|
||||
// Change argc & argv of main() with the arguments passing
|
||||
// through IPC.
|
||||
char** argv = new char*[argv_.size() + 1];
|
||||
char** p = argv;
|
||||
for (auto& elt : argv_) {
|
||||
*p++ = strdup(elt.c_str());
|
||||
}
|
||||
*p = nullptr;
|
||||
*argvp = argv;
|
||||
*argcp = argv_.size();
|
||||
}
|
||||
|
||||
void
|
||||
AppProcessBuilder::InitAppProcess(int *argcp, char*** argvp) {
|
||||
MOZ_ASSERT(MessageLoop::current() == nullptr,
|
||||
"The message loop of the main thread should have been destroyed");
|
||||
|
||||
// The fork server handle SIGCHLD to read status of content
|
||||
// processes to handle Zombies. But, it is not necessary for
|
||||
// content processes.
|
||||
signal(SIGCHLD, SIG_DFL);
|
||||
|
||||
for (const auto& fds : shuffle_.Dup2Sequence()) {
|
||||
int fd = HANDLE_EINTR(dup2(fds.first, fds.second));
|
||||
MOZ_RELEASE_ASSERT(fd == fds.second, "dup2 failed");
|
||||
}
|
||||
|
||||
CloseSuperfluousFds(&shuffle_, [](void* ctx, int fd) {
|
||||
return static_cast<decltype(&shuffle_)>(ctx)->MapsTo(fd) ||
|
||||
IsNoCloseFd(fd);
|
||||
});
|
||||
// Without this, the destructor of |shuffle_| would try to close FDs
|
||||
// created by it, but they have been closed by
|
||||
// |CloseSuperfluousFds()|.
|
||||
shuffle_.Forget();
|
||||
|
||||
ReplaceArguments(argcp, argvp);
|
||||
}
|
||||
#endif // MOZ_ENABLE_FORKSERVER
|
||||
|
||||
bool LaunchApp(const std::vector<std::string>& argv,
|
||||
const LaunchOptions& options, ProcessHandle* process_handle) {
|
||||
mozilla::UniquePtr<char*[]> argv_cstr(new char*[argv.size() + 1]);
|
||||
|
@ -49,6 +49,12 @@ class FileDescriptorShuffle {
|
||||
// Can be used to close other fds after performing the dup2()s.
|
||||
bool MapsTo(int aFd) const;
|
||||
|
||||
// Forget the information, so that it's destructor will not try to
|
||||
// delete FDs duped by itself.
|
||||
void Forget() {
|
||||
mTempFds.Clear();
|
||||
}
|
||||
|
||||
private:
|
||||
nsTArray<std::pair<int, int>> mMapping;
|
||||
nsTArray<int> mTempFds;
|
||||
|
Loading…
Reference in New Issue
Block a user