tor-browser

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

process_util_linux.cc (5566B)


      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 // Copyright (c) 2008 The Chromium Authors. All rights reserved.
      4 // Use of this source code is governed by a BSD-style license that can be
      5 // found in the LICENSE file.
      6 
      7 #include "base/process_util.h"
      8 
      9 #include <string>
     10 #include <sys/wait.h>
     11 #include <unistd.h>
     12 
     13 #if defined(MOZ_CODE_COVERAGE)
     14 #  include "nsString.h"
     15 #endif
     16 
     17 #include "mozilla/ipc/LaunchError.h"
     18 
     19 #if defined(XP_LINUX) && defined(MOZ_SANDBOX)
     20 #  include "mozilla/SandboxLaunch.h"
     21 #endif
     22 
     23 #if defined(MOZ_CODE_COVERAGE)
     24 #  include "prenv.h"
     25 #  include "mozilla/ipc/EnvironmentMap.h"
     26 #endif
     27 
     28 #include "base/command_line.h"
     29 #include "base/eintr_wrapper.h"
     30 #include "base/logging.h"
     31 #include "mozilla/ipc/FileDescriptor.h"
     32 #include "mozilla/ipc/FileDescriptorShuffle.h"
     33 #include "mozilla/UniquePtr.h"
     34 #include "mozilla/StaticPtr.h"
     35 #include "mozilla/Result.h"
     36 
     37 // WARNING: despite the name, this file is also used on the BSDs and
     38 // Solaris (basically, Unixes that aren't Mac OS), not just Linux.
     39 
     40 namespace {
     41 
     42 MOZ_RUNINIT static mozilla::EnvironmentLog gProcessLog("MOZ_PROCESS_LOG");
     43 
     44 }  // namespace
     45 
     46 namespace base {
     47 
     48 Result<Ok, LaunchError> LaunchApp(const std::vector<std::string>& argv,
     49                                  LaunchOptions&& options,
     50                                  ProcessHandle* process_handle) {
     51  mozilla::UniquePtr<char*[]> argv_cstr(new char*[argv.size() + 1]);
     52 
     53 #if defined(XP_LINUX) && defined(MOZ_SANDBOX)
     54  mozilla::SandboxLaunch launcher;
     55  if (!launcher.Prepare(&options)) {
     56    return Err(LaunchError("SL::Prepare", errno));
     57  }
     58 #else
     59  struct {
     60    pid_t Fork() { return fork(); }
     61  } launcher;
     62 #endif
     63 
     64  EnvironmentArray env_storage;
     65  const EnvironmentArray& envp =
     66      options.full_env ? options.full_env
     67                       : (env_storage = BuildEnvironmentArray(options.env_map));
     68 
     69  // Init() there will call fcntl(F_DUPFD/F_DUPFD_CLOEXEC) under the hood in
     70  // https://searchfox.org/mozilla-central/rev/55d5c4b9dffe5e59eb6b019c1a930ec9ada47e10/ipc/glue/FileDescriptorShuffle.cpp#72
     71  // so it will set errno.
     72  mozilla::ipc::FileDescriptorShuffle shuffle;
     73  if (!shuffle.Init(options.fds_to_remap)) {
     74    CHROMIUM_LOG(WARNING) << "FileDescriptorShuffle::Init failed";
     75    return Err(LaunchError("FileDescriptorShuffle", errno));
     76  }
     77 
     78 #ifdef MOZ_CODE_COVERAGE
     79  // Before gcc/clang 10 there is a gcda dump before the fork.
     80  // This dump mustn't be interrupted by a SIGUSR1 else we may
     81  // have a dead lock (see bug 1637377).
     82  // So we just remove the handler and restore it after the fork
     83  // It's up the child process to set it up.
     84  // Once we switch to gcc/clang 10, we could just remove it in the child
     85  // process
     86  void (*ccovSigHandler)(int) = signal(SIGUSR1, SIG_IGN);
     87  const char* gcov_child_prefix = PR_GetEnv("GCOV_CHILD_PREFIX");
     88 #endif
     89 
     90  pid_t pid = launcher.Fork();
     91  // WARNING: if pid == 0, only async signal safe operations are permitted from
     92  // here until exec or _exit.
     93  //
     94  // Specifically, heap allocation is not safe: the sandbox's fork substitute
     95  // won't run the pthread_atfork handlers that fix up the malloc locks.
     96 
     97  if (pid < 0) {
     98    CHROMIUM_LOG(WARNING) << "fork() failed: " << strerror(errno);
     99    return Err(LaunchError("fork", errno));
    100  }
    101 
    102  if (pid == 0) {
    103    // In the child:
    104    if (!options.workdir.empty()) {
    105      if (chdir(options.workdir.c_str()) != 0) {
    106        // See under execve about logging unsafety.
    107        DLOG(ERROR) << "chdir failed " << options.workdir;
    108        _exit(127);
    109      }
    110    }
    111 
    112    for (const auto& fds : shuffle.Dup2Sequence()) {
    113      if (HANDLE_EINTR(dup2(fds.first, fds.second)) != fds.second) {
    114        // This shouldn't happen, but check for it.  And see below
    115        // about logging being unsafe here, so this is debug only.
    116        DLOG(ERROR) << "dup2 failed";
    117        _exit(127);
    118      }
    119    }
    120 
    121    CloseSuperfluousFds(&shuffle, [](void* aCtx, int aFd) {
    122      return static_cast<decltype(&shuffle)>(aCtx)->MapsTo(aFd);
    123    });
    124 
    125    for (size_t i = 0; i < argv.size(); i++)
    126      argv_cstr[i] = const_cast<char*>(argv[i].c_str());
    127    argv_cstr[argv.size()] = NULL;
    128 
    129 #ifdef MOZ_CODE_COVERAGE
    130    if (gcov_child_prefix && !options.full_env) {
    131      const pid_t child_pid = getpid();
    132      nsAutoCString new_gcov_prefix(gcov_child_prefix);
    133      new_gcov_prefix.Append(std::to_string((size_t)child_pid));
    134      EnvironmentMap new_map = options.env_map;
    135      new_map[ENVIRONMENT_LITERAL("GCOV_PREFIX")] =
    136          ENVIRONMENT_STRING(new_gcov_prefix.get());
    137      // FIXME(bug 1783305): this won't work if full_env is set, and
    138      // in general this block of code is doing things it shouldn't
    139      // be (async signal unsafety).
    140      env_storage = BuildEnvironmentArray(new_map);
    141    }
    142 #endif
    143 
    144    execve(argv_cstr[0], argv_cstr.get(), envp.get());
    145    // if we get here, we're in serious trouble and should complain loudly
    146    // NOTE: This is async signal unsafe; it could deadlock instead.  (But
    147    // only on debug builds; otherwise it's a signal-safe no-op.)
    148    DLOG(ERROR) << "FAILED TO exec() CHILD PROCESS, path: " << argv_cstr[0];
    149    _exit(127);
    150  }
    151 
    152  // In the parent:
    153 
    154 #ifdef MOZ_CODE_COVERAGE
    155  // Restore the handler for SIGUSR1
    156  signal(SIGUSR1, ccovSigHandler);
    157 #endif
    158 
    159  gProcessLog.print("==> process %d launched child process %d\n",
    160                    GetCurrentProcId(), pid);
    161  if (options.wait) HANDLE_EINTR(waitpid(pid, 0, 0));
    162 
    163  if (process_handle) *process_handle = pid;
    164 
    165  return Ok();
    166 }
    167 
    168 }  // namespace base