tor-browser

The Tor Browser
git clone https://git.dasho.dev/tor-browser.git
Log | Files | Refs | README | LICENSE

ForkServer.cpp (13306B)


      1 /* -*- Mode: C++; tab-width: 8; indent-tabs-mode: nil; c-basic-offset: 2 -*- */
      2 /* vim: set ts=8 sts=2 et sw=2 tw=80: */
      3 /* This Source Code Form is subject to the terms of the Mozilla Public
      4 * License, v. 2.0. If a copy of the MPL was not distributed with this
      5 * file, You can obtain one at http://mozilla.org/MPL/2.0/. */
      6 
      7 #include "mozilla/ipc/ForkServer.h"
      8 
      9 #include "base/eintr_wrapper.h"
     10 #include "chrome/common/chrome_switches.h"
     11 #include "ipc/IPCMessageUtilsSpecializations.h"
     12 #include "mozilla/BlockingResourceBase.h"
     13 #include "mozilla/GeckoArgs.h"
     14 #include "mozilla/Logging.h"
     15 #include "mozilla/Omnijar.h"
     16 #include "mozilla/ProcessType.h"
     17 #include "mozilla/ipc/FileDescriptor.h"
     18 #include "mozilla/ipc/ProcessUtils.h"
     19 #include "mozilla/ipc/ProtocolMessageUtils.h"
     20 #include "mozilla/ipc/SetProcessTitle.h"
     21 #include "nsTraceRefcnt.h"
     22 
     23 #include <fcntl.h>
     24 #include <string.h>
     25 #include <sys/wait.h>
     26 #include <unistd.h>
     27 
     28 #if defined(XP_LINUX) && defined(MOZ_SANDBOX)
     29 #  include "mozilla/SandboxLaunch.h"
     30 #endif
     31 
     32 #if defined(XP_OPENBSD)
     33 #  include "BinaryPath.h"
     34 #  include <err.h>
     35 #endif
     36 
     37 #include <algorithm>
     38 
     39 namespace mozilla {
     40 namespace ipc {
     41 
     42 LazyLogModule gForkServiceLog("ForkService");
     43 
     44 static int gSignalPipe = -1;
     45 static void HandleSigChld(int aSignal) {
     46  MOZ_ASSERT(aSignal == SIGCHLD);
     47  const char msg = 0;
     48  HANDLE_EINTR(write(gSignalPipe, &msg, 1));
     49 }
     50 
     51 ForkServer::ForkServer(int* aArgc, char*** aArgv) : mArgc(aArgc), mArgv(aArgv) {
     52  SetThisProcessName("forkserver");
     53 
     54  Maybe<UniqueFileHandle> ipcHandle = geckoargs::sIPCHandle.Get(*aArgc, *aArgv);
     55  if (!ipcHandle) {
     56    MOZ_CRASH("forkserver missing ipcHandle argument");
     57  }
     58 
     59  // Hold our IPC FD while our MiniTransceiver is alive.
     60  mIpcFd = ipcHandle.extract();
     61  mTcver = MakeUnique<MiniTransceiver>(mIpcFd.get(),
     62                                       DataBufferClear::AfterReceiving);
     63 
     64  auto signalPipe = geckoargs::sSignalPipe.Get(*aArgc, *aArgv);
     65  if (signalPipe) {
     66    gSignalPipe = signalPipe->release();
     67    signal(SIGCHLD, HandleSigChld);
     68  } else {
     69    signal(SIGCHLD, SIG_IGN);
     70  }
     71 }
     72 
     73 /**
     74 * Preload any resources that the forked child processes might need,
     75 * and which might change incompatibly or become unavailable by the
     76 * time they're started.  For example: the omnijar files, or certain
     77 * shared libraries.
     78 */
     79 static void ForkServerPreload(int& aArgc, char** aArgv) {
     80  Omnijar::ChildProcessInit(aArgc, aArgv);
     81 #if defined(XP_OPENBSD)
     82  char binaryPath[MAXPATHLEN];
     83  nsresult rv = mozilla::BinaryPath::Get(binaryPath);
     84  if (NS_FAILED(rv)) {
     85    errx(1, "failed to cache binary path ?");
     86  }
     87 #endif
     88 }
     89 
     90 /**
     91 * Start providing the service at the IPC channel.
     92 */
     93 bool ForkServer::HandleMessages() {
     94  while (true) {
     95    UniquePtr<IPC::Message> msg;
     96    if (!mTcver->Recv(msg)) {
     97      break;
     98    }
     99 
    100    switch (msg->type()) {
    101      case Msg_ForkNewSubprocess__ID:
    102        if (HandleForkNewSubprocess(std::move(msg))) {
    103          // New process - child
    104          return false;
    105        }
    106        break;
    107      case Msg_WaitPid__ID:
    108        HandleWaitPid(std::move(msg));
    109        break;
    110      default:
    111        MOZ_LOG(gForkServiceLog, LogLevel::Verbose,
    112                ("unknown message type %d\n", msg->type()));
    113    }
    114  }
    115  // Stop the server
    116  return true;
    117 }
    118 
    119 template <class P>
    120 static void ReadParamInfallible(IPC::MessageReader* aReader, P* aResult,
    121                                const char* aCrashMessage) {
    122  if (!IPC::ReadParam(aReader, aResult)) {
    123    MOZ_CRASH_UNSAFE(aCrashMessage);
    124  }
    125 }
    126 
    127 /**
    128 * Parse a Message to obtain a `LaunchOptions` and the attached fd
    129 * that the child will use to receive its `SubprocessExecInfo`.
    130 */
    131 static bool ParseForkNewSubprocess(IPC::Message& aMsg,
    132                                   UniqueFileHandle* aExecFd,
    133                                   base::LaunchOptions* aOptions) {
    134  // The type was already checked in HandleMessages
    135  MOZ_ASSERT(aMsg.type() == Msg_ForkNewSubprocess__ID);
    136  IPC::MessageReader reader(aMsg);
    137 
    138 #if defined(XP_LINUX) && defined(MOZ_SANDBOX)
    139  ReadParamInfallible(&reader, &aOptions->fork_flags,
    140                      "Error deserializing 'int'");
    141  ReadParamInfallible(&reader, &aOptions->sandbox_chroot_server,
    142                      "Error deserializing 'UniqueFileHandle'");
    143 #endif
    144  ReadParamInfallible(&reader, aExecFd,
    145                      "Error deserializing 'UniqueFileHandle'");
    146  reader.EndRead();
    147 
    148  return true;
    149 }
    150 
    151 /**
    152 * Parse a `Message`, in the forked child process, to get the argument
    153 * and environment strings.
    154 */
    155 static bool ParseSubprocessExecInfo(IPC::Message& aMsg,
    156                                    geckoargs::ChildProcessArgs* aArgs,
    157                                    base::environment_map* aEnv) {
    158  if (aMsg.type() != Msg_SubprocessExecInfo__ID) {
    159    MOZ_LOG(gForkServiceLog, LogLevel::Verbose,
    160            ("unknown message type %d (!= %d)\n", aMsg.type(),
    161             Msg_SubprocessExecInfo__ID));
    162    return false;
    163  }
    164 
    165  IPC::MessageReader reader(aMsg);
    166 
    167  ReadParamInfallible(&reader, aEnv, "Error deserializing 'env_map'");
    168  ReadParamInfallible(&reader, &aArgs->mArgs, "Error deserializing 'mArgs'");
    169  ReadParamInfallible(&reader, &aArgs->mFiles, "Error deserializing 'mFiles'");
    170  reader.EndRead();
    171 
    172  return true;
    173 }
    174 
    175 // Run in the forked child process. Receives a message on `aExecFd` containing
    176 // the new process configuration, and updates the environment, command line, and
    177 // passed file handles to reflect the new process.
    178 static void ForkedChildProcessInit(int aExecFd, int* aArgc, char*** aArgv) {
    179  // Remove the fork-server-specific SIGCHLD handler.
    180  signal(SIGCHLD, SIG_DFL);
    181  // This process is currently single-threaded, so the fd used by the
    182  // signal handler can be safely closed once the handler is removed.
    183  if (gSignalPipe >= 0) {
    184    close(gSignalPipe);
    185    gSignalPipe = -1;
    186  }
    187 
    188  // Content process
    189  MiniTransceiver execTcver(aExecFd);
    190  UniquePtr<IPC::Message> execMsg;
    191  if (!execTcver.Recv(execMsg)) {
    192    // Crashing here isn't great, because the crash reporter isn't
    193    // set up, but we don't have a lot of options currently.  Also,
    194    // receive probably won't fail unless the parent also crashes.
    195    printf_stderr("ForkServer: SubprocessExecInfo receive error\n");
    196    MOZ_CRASH();
    197  }
    198 
    199  geckoargs::ChildProcessArgs args;
    200  base::environment_map env;
    201  if (!ParseSubprocessExecInfo(*execMsg, &args, &env)) {
    202    printf_stderr("ForkServer: SubprocessExecInfo parse error\n");
    203    MOZ_CRASH();
    204  }
    205 
    206  // Set environment variables as specified in env_map.
    207  for (auto& elt : env) {
    208    setenv(elt.first.c_str(), elt.second.c_str(), 1);
    209  }
    210 
    211  // Initialize passed file handles.
    212  geckoargs::SetPassedFileHandles(std::move(args.mFiles));
    213 
    214  // Change argc & argv of main() with the arguments passing
    215  // through IPC.
    216  char** argv = new char*[args.mArgs.size() + 1];
    217  char** p = argv;
    218  for (auto& elt : args.mArgs) {
    219    *p++ = strdup(elt.c_str());
    220  }
    221  *p = nullptr;
    222  *aArgv = argv;
    223  *aArgc = args.mArgs.size();
    224  mozilla::SetProcessTitle(args.mArgs);
    225 }
    226 
    227 /**
    228 * Extract parameters from the |Message| to create a
    229 * |base::AppProcessBuilder| as |mAppProcBuilder|.
    230 *
    231 * It will return in both the fork server process and the new content
    232 * process.  |mAppProcBuilder| is null for the fork server.
    233 */
    234 bool ForkServer::HandleForkNewSubprocess(UniquePtr<IPC::Message> aMessage) {
    235  UniqueFileHandle execFd;
    236  base::LaunchOptions options;
    237  if (!ParseForkNewSubprocess(*aMessage, &execFd, &options)) {
    238    return false;
    239  }
    240 
    241 #if defined(MOZ_MEMORY) && defined(DEBUG)
    242  jemalloc_stats_t stats;
    243  jemalloc_stats(&stats);
    244  // What we actually want to assert is that there are 0 thread-local arenas
    245  // (threads may exist but thread-local arenas are opt-in) that would be leaked
    246  // (because the threads wont exist in the new process), and 0 private
    247  // main-thread-only arenas and this is not the main thread (as those might be
    248  // inconsistent in the new process).  Instead we check that there's exactly
    249  // one arena - the default public arena).
    250  MOZ_ASSERT(stats.narenas == 1,
    251             "ForkServer before fork()/clone() should have a single arena.");
    252 #endif
    253 
    254 #if defined(XP_LINUX) && defined(MOZ_SANDBOX)
    255  mozilla::SandboxLaunch launcher;
    256  if (!launcher.Prepare(&options)) {
    257    MOZ_CRASH("SandboxLaunch::Prepare failed");
    258  }
    259 #else
    260  struct {
    261    pid_t Fork() { return fork(); }
    262  } launcher;
    263 #endif
    264 
    265  // Avoid any contents of buffered stdout/stderr being sent by forked
    266  // children.
    267  fflush(stdout);
    268  fflush(stderr);
    269 
    270  pid_t pid = launcher.Fork();
    271  if (pid < 0) {
    272    MOZ_CRASH("failed to fork");
    273  }
    274 
    275  // NOTE: After this point, if pid == 0, we're in the newly forked child
    276  // process.
    277 
    278  if (pid == 0) {
    279    // Re-configure to a child process, and return to our caller.
    280    ForkedChildProcessInit(execFd.get(), mArgc, mArgv);
    281    return true;
    282  }
    283 
    284  // Fork server process
    285 
    286  IPC::Message reply(MSG_ROUTING_CONTROL, Reply_ForkNewSubprocess__ID);
    287  IPC::MessageWriter writer(reply);
    288  WriteParam(&writer, pid);
    289  mTcver->SendInfallible(reply, "failed to send a reply message");
    290 
    291  return false;
    292 }
    293 
    294 void ForkServer::HandleWaitPid(UniquePtr<IPC::Message> aMessage) {
    295  MOZ_ASSERT(aMessage->type() == Msg_WaitPid__ID);
    296  IPC::MessageReader reader(*aMessage);
    297 
    298  pid_t pid;
    299  bool block;
    300  ReadParamInfallible(&reader, &pid, "Error deserializing 'pid_t'");
    301  ReadParamInfallible(&reader, &block, "Error deserializing 'bool'");
    302 
    303  // It's safe to use plain waitpid here (and not the waitid/WNOWAIT
    304  // contraption used in the parent process) because this process is
    305  // single-threaded so there's no possibility of another thread
    306  // trying to ptrace the same child process.
    307  int status;
    308  pid_t rv = HANDLE_EINTR(waitpid(pid, &status, block ? 0 : WNOHANG));
    309  // Three possibilities here:
    310  //  Terminated: rv > 0; return {false, status}
    311  //     Running: rv = 0; return {true, 0}
    312  //       Error: rv < 0; return {true, errno}
    313  bool isErr = rv <= 0;
    314  int err = rv < 0 ? errno : 0;
    315  MOZ_ASSERT(isErr || rv == pid);
    316 
    317  IPC::Message reply(MSG_ROUTING_CONTROL, Reply_WaitPid__ID);
    318  IPC::MessageWriter writer(reply);
    319  WriteParam(&writer, isErr);
    320  WriteParam(&writer, isErr ? err : status);
    321  mTcver->SendInfallible(reply, "failed to send a reply message");
    322 }
    323 
    324 /**
    325 * Setup and run a fork server at the main thread.
    326 *
    327 * This function returns for two reasons:
    328 *  - the fork server is stopped normally, or
    329 *  - a new process is forked from the fork server and this function
    330 *    returned in the child, the new process.
    331 *
    332 * For the later case, aArgc and aArgv are modified to pass the
    333 * arguments from the chrome process.
    334 */
    335 bool ForkServer::RunForkServer(int* aArgc, char*** aArgv) {
    336  MOZ_ASSERT(XRE_IsForkServerProcess(), "fork server process only");
    337 
    338 #ifdef DEBUG
    339  if (getenv("MOZ_FORKSERVER_WAIT_GDB")) {
    340    printf(
    341        "Waiting for 30 seconds."
    342        "  Attach the fork server with gdb %s %d\n",
    343        (*aArgv)[0], base::GetCurrentProcId());
    344    sleep(30);
    345  }
    346  bool sleep_newproc = !!getenv("MOZ_FORKSERVER_WAIT_GDB_NEWPROC");
    347 #endif
    348 
    349  SetProcessTitleInit(*aArgv);
    350 
    351  // Do this before NS_LogInit() to avoid log files taking lower
    352  // FDs.
    353  ForkServer forkserver(aArgc, aArgv);
    354 
    355  NS_LogInit();
    356  mozilla::LogModule::Init(0, nullptr);
    357  ForkServerPreload(*aArgc, *aArgv);
    358  MOZ_LOG(gForkServiceLog, LogLevel::Verbose, ("Start a fork server"));
    359  {
    360    DebugOnly<base::ProcessHandle> forkserver_pid = base::GetCurrentProcId();
    361    if (forkserver.HandleMessages()) {
    362      // In the fork server process
    363      // The server has stopped.
    364      MOZ_LOG(gForkServiceLog, LogLevel::Verbose,
    365              ("Terminate the fork server"));
    366      Omnijar::CleanUp();
    367      NS_LogTerm();
    368      return true;
    369    }
    370    // Now, we are running in a content process just forked from
    371    // the fork server process.
    372    MOZ_ASSERT(base::GetCurrentProcId() != forkserver_pid);
    373    MOZ_LOG(gForkServiceLog, LogLevel::Verbose, ("Fork a new content process"));
    374  }
    375 #ifdef DEBUG
    376  if (sleep_newproc) {
    377    printf(
    378        "Waiting for 30 seconds."
    379        "  Attach the new process with gdb %s %d\n",
    380        (*aArgv)[0], base::GetCurrentProcId());
    381    sleep(30);
    382  }
    383 #endif
    384  NS_LogTerm();
    385 
    386  nsTraceRefcnt::CloseLogFilesAfterFork();
    387 
    388  // Update our GeckoProcessType and GeckoChildID, removing the arguments.
    389  if (*aArgc < 2) {
    390    MOZ_CRASH("forked process missing process type and childid arguments");
    391  }
    392  SetGeckoProcessType((*aArgv)[--*aArgc]);
    393  SetGeckoChildID((*aArgv)[--*aArgc]);
    394  MOZ_ASSERT(!XRE_IsForkServerProcess(),
    395             "fork server created another fork server?");
    396 
    397  // This is now a child process, and it may even be a Content process.
    398  // It is required that the PRNG at least is re-initialized so the same state
    399  // is not shared accross all child processes, and in case of a Content process
    400  // it is also required that the small allocation are not being randomized ;
    401  // failing to do so will lead to performance regressions, e.g. as in
    402  // bug 1912262.
    403 #if defined(MOZ_MEMORY)
    404  jemalloc_reset_small_alloc_randomization(
    405      /* aRandomizeSmall */ !XRE_IsContentProcess());
    406 #endif
    407 
    408  // Open log files again with right names and the new PID.
    409  nsTraceRefcnt::ReopenLogFilesAfterFork(XRE_GetProcessTypeString());
    410 
    411  return false;
    412 }
    413 
    414 }  // namespace ipc
    415 }  // namespace mozilla