tor-browser

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

process_util_posix.cc (11999B)


      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) 2006-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 <dirent.h>
      8 #include <errno.h>
      9 #include <fcntl.h>
     10 #include <signal.h>
     11 #include <stdio.h>
     12 #include <stdlib.h>
     13 #include <string.h>
     14 #include <sys/resource.h>
     15 #include <sys/time.h>
     16 #include <sys/types.h>
     17 #include <sys/wait.h>
     18 #include <unistd.h>
     19 
     20 #include "base/basictypes.h"
     21 #include "base/command_line.h"
     22 #include "base/eintr_wrapper.h"
     23 #include "base/logging.h"
     24 #include "base/platform_thread.h"
     25 #include "base/process_util.h"
     26 #include "base/time.h"
     27 #include "base/waitable_event.h"
     28 #include "base/dir_reader_posix.h"
     29 
     30 #include "mozilla/UniquePtr.h"
     31 // For PR_DuplicateEnvironment:
     32 #include "prenv.h"
     33 #include "prmem.h"
     34 
     35 #ifdef MOZ_ENABLE_FORKSERVER
     36 #  include "mozilla/ipc/ForkServiceChild.h"
     37 #  include "mozilla/Printf.h"
     38 #endif
     39 
     40 // We could configure-test for `waitid`, but it's been in POSIX for a
     41 // long time and OpenBSD seems to be the only Unix we target that
     42 // doesn't have it.  Note that `waitid` is used to resolve a conflict
     43 // with the crash reporter, which isn't available on OpenBSD.
     44 #ifndef __OpenBSD__
     45 #  define HAVE_WAITID
     46 #endif
     47 
     48 #ifdef DEBUG
     49 #  define LOG_AND_ASSERT CHROMIUM_LOG(FATAL)
     50 #else
     51 #  define LOG_AND_ASSERT CHROMIUM_LOG(ERROR)
     52 #endif
     53 
     54 namespace base {
     55 
     56 ProcessId GetCurrentProcId() { return getpid(); }
     57 
     58 ProcessHandle GetCurrentProcessHandle() { return GetCurrentProcId(); }
     59 
     60 bool OpenProcessHandle(ProcessId pid, ProcessHandle* handle) {
     61  // On Posix platforms, process handles are the same as PIDs, so we
     62  // don't need to do anything.
     63  *handle = pid;
     64  return true;
     65 }
     66 
     67 bool OpenPrivilegedProcessHandle(ProcessId pid, ProcessHandle* handle) {
     68  // On POSIX permissions are checked for each operation on process,
     69  // not when opening a "handle".
     70  return OpenProcessHandle(pid, handle);
     71 }
     72 
     73 void CloseProcessHandle(ProcessHandle process) {
     74  // See OpenProcessHandle, nothing to do.
     75  return;
     76 }
     77 
     78 ProcessId GetProcId(ProcessHandle process) { return process; }
     79 
     80 // Attempts to kill the process identified by the given process
     81 // entry structure.  Ignores specified exit_code; posix can't force that.
     82 // Returns true if this is successful, false otherwise.
     83 bool KillProcess(ProcessHandle process_id, int exit_code) {
     84  // It's too easy to accidentally kill pid 0 (meaning the caller's
     85  // process group) or pid -1 (all other processes killable by this
     86  // user), and neither they nor other negative numbers (process
     87  // groups) are legitimately used by this function's callers, so
     88  // reject them all.
     89  if (process_id <= 0) {
     90    CHROMIUM_LOG(WARNING) << "base::KillProcess refusing to kill pid "
     91                          << process_id;
     92    return false;
     93  }
     94 
     95  bool result = kill(process_id, SIGTERM) == 0;
     96 
     97  if (!result && (errno == ESRCH)) {
     98    result = true;
     99  }
    100 
    101  if (!result) DLOG(ERROR) << "Unable to terminate process.";
    102 
    103  return result;
    104 }
    105 
    106 #ifdef ANDROID
    107 typedef unsigned long int rlim_t;
    108 #endif
    109 
    110 // A class to handle auto-closing of DIR*'s.
    111 class ScopedDIRClose {
    112 public:
    113  inline void operator()(DIR* x) const {
    114    if (x) {
    115      closedir(x);
    116    }
    117  }
    118 };
    119 typedef mozilla::UniquePtr<DIR, ScopedDIRClose> ScopedDIR;
    120 
    121 void CloseSuperfluousFds(void* aCtx, bool (*aShouldPreserve)(void*, int)) {
    122  // DANGER: no calls to malloc (or locks, etc.) are allowed from now on:
    123  // https://crbug.com/36678
    124  // Also, beware of STL iterators: https://crbug.com/331459
    125 #if defined(ANDROID)
    126  static const rlim_t kSystemDefaultMaxFds = 1024;
    127  static const char kFDDir[] = "/proc/self/fd";
    128 #elif defined(XP_LINUX) || defined(XP_SOLARIS)
    129  static const rlim_t kSystemDefaultMaxFds = 8192;
    130  static const char kFDDir[] = "/proc/self/fd";
    131 #elif defined(XP_DARWIN)
    132  static const rlim_t kSystemDefaultMaxFds = 256;
    133  static const char kFDDir[] = "/dev/fd";
    134 #elif defined(__DragonFly__) || defined(XP_FREEBSD) || defined(XP_NETBSD) || \
    135    defined(XP_OPENBSD)
    136  // the getrlimit below should never fail, so whatever ..
    137  static const rlim_t kSystemDefaultMaxFds = 1024;
    138  // at least /dev/fd will exist
    139  static const char kFDDir[] = "/dev/fd";
    140 #endif
    141 
    142  // Get the maximum number of FDs possible.
    143  struct rlimit nofile;
    144  rlim_t max_fds;
    145  if (getrlimit(RLIMIT_NOFILE, &nofile)) {
    146    // getrlimit failed. Take a best guess.
    147    max_fds = kSystemDefaultMaxFds;
    148    DLOG(ERROR) << "getrlimit(RLIMIT_NOFILE) failed: " << errno;
    149  } else {
    150    max_fds = nofile.rlim_cur;
    151  }
    152 
    153  if (max_fds > INT_MAX) max_fds = INT_MAX;
    154 
    155  DirReaderPosix fd_dir(kFDDir);
    156 
    157  if (!fd_dir.IsValid()) {
    158    // Fallback case: Try every possible fd.
    159    for (rlim_t i = 0; i < max_fds; ++i) {
    160      const int fd = static_cast<int>(i);
    161      if (fd == STDIN_FILENO || fd == STDOUT_FILENO || fd == STDERR_FILENO ||
    162          aShouldPreserve(aCtx, fd)) {
    163        continue;
    164      }
    165 
    166      // Since we're just trying to close anything we can find,
    167      // ignore any error return values of close().
    168      close(fd);
    169    }
    170    return;
    171  }
    172 
    173  const int dir_fd = fd_dir.fd();
    174 
    175  for (; fd_dir.Next();) {
    176    // Skip . and .. entries.
    177    if (fd_dir.name()[0] == '.') continue;
    178 
    179    char* endptr;
    180    errno = 0;
    181    const long int fd = strtol(fd_dir.name(), &endptr, 10);
    182    if (fd_dir.name()[0] == 0 || *endptr || fd < 0 || errno) continue;
    183    if (fd == dir_fd) continue;
    184    if (fd == STDIN_FILENO || fd == STDOUT_FILENO || fd == STDERR_FILENO ||
    185        aShouldPreserve(aCtx, fd)) {
    186      continue;
    187    }
    188 
    189    // When running under Valgrind, Valgrind opens several FDs for its
    190    // own use and will complain if we try to close them.  All of
    191    // these FDs are >= |max_fds|, so we can check against that here
    192    // before closing.  See https://bugs.kde.org/show_bug.cgi?id=191758
    193    if (fd < static_cast<int>(max_fds)) {
    194      int ret = IGNORE_EINTR(close(fd));
    195      if (ret != 0) {
    196        DLOG(ERROR) << "Problem closing fd";
    197      }
    198    }
    199  }
    200 }
    201 
    202 ProcessStatus WaitForProcess(ProcessHandle handle, BlockingWait blocking,
    203                             int* info_out) {
    204  *info_out = 0;
    205 
    206 #if defined(MOZ_ENABLE_FORKSERVER) || !defined(HAVE_WAITID)
    207  auto handleStatus = [&](int status) -> ProcessStatus {
    208    if (WIFEXITED(status)) {
    209      *info_out = WEXITSTATUS(status);
    210      return ProcessStatus::Exited;
    211    }
    212    if (WIFSIGNALED(status)) {
    213      *info_out = WTERMSIG(status);
    214      return ProcessStatus::Killed;
    215    }
    216    LOG_AND_ASSERT << "unexpected wait status: " << status;
    217    return ProcessStatus::Error;
    218  };
    219 #endif
    220 
    221  auto handleForkServer = [&]() -> mozilla::Maybe<ProcessStatus> {
    222 #ifdef MOZ_ENABLE_FORKSERVER
    223    if (errno != ECHILD || !mozilla::ipc::ForkServiceChild::WasUsed()) {
    224      return mozilla::Nothing();
    225    }
    226 
    227    auto forkService = mozilla::ipc::ForkServiceChild::Get();
    228    if (!forkService) {
    229      DLOG(WARNING) << "fork server exited too soon";
    230      return mozilla::Nothing();
    231    }
    232 
    233    auto result =
    234        forkService->SendWaitPid(handle, blocking == BlockingWait::Yes);
    235    if (result.isOk()) {
    236      return mozilla::Some(handleStatus(result.unwrap().status));
    237    }
    238 
    239    int err = result.unwrapErr();
    240    if (err == ECHILD) {
    241      return mozilla::Nothing();
    242    }
    243 
    244    *info_out = err;
    245    return mozilla::Some(err == 0 ? ProcessStatus::Running
    246                                  : ProcessStatus::Error);
    247 #else
    248    return mozilla::Nothing();
    249 #endif
    250  };
    251 
    252  const int maybe_wnohang = (blocking == BlockingWait::No) ? WNOHANG : 0;
    253 
    254 #ifdef HAVE_WAITID
    255  // We use `WNOWAIT` to read the process status without
    256  // side-effecting it, in case it's something unexpected like a
    257  // ptrace-stop for the crash reporter.  If is an exit, the call is
    258  // reissued (see the end of this function) without that flag in
    259  // order to collect the process.
    260  siginfo_t si{};
    261  const int wflags = WEXITED | WNOWAIT | maybe_wnohang;
    262  int result = HANDLE_EINTR(waitid(P_PID, handle, &si, wflags));
    263  if (result == -1) {
    264    int wait_err = errno;
    265    if (auto forkServerReturn = handleForkServer()) {
    266      return *forkServerReturn;
    267    }
    268 
    269    CHROMIUM_LOG(INFO) << "waitid failed pid:" << handle
    270                       << " errno:" << wait_err;
    271    *info_out = wait_err;
    272    return ProcessStatus::Error;
    273  }
    274 
    275  if (si.si_pid == 0) {
    276    // the child hasn't exited yet.
    277    return ProcessStatus::Running;
    278  }
    279 
    280  ProcessStatus status;
    281  DCHECK(si.si_pid == handle);
    282  switch (si.si_code) {
    283    case CLD_STOPPED:
    284    case CLD_CONTINUED:
    285      LOG_AND_ASSERT << "waitid returned an event type that it shouldn't have";
    286      [[fallthrough]];
    287    case CLD_TRAPPED:
    288      CHROMIUM_LOG(WARNING) << "ignoring non-exit event for process " << handle;
    289      return ProcessStatus::Running;
    290 
    291    case CLD_KILLED:
    292    case CLD_DUMPED:
    293      status = ProcessStatus::Killed;
    294      *info_out = si.si_status;
    295      break;
    296 
    297    case CLD_EXITED:
    298      status = ProcessStatus::Exited;
    299      *info_out = si.si_status;
    300      break;
    301 
    302    default:
    303      LOG_AND_ASSERT << "unexpected waitid si_code value: " << si.si_code;
    304      // This shouldn't happen, but assume that the process exited to
    305      // avoid the caller possibly ending up in a loop.
    306      *info_out = 0;
    307      return ProcessStatus::Exited;
    308  }
    309 
    310  // Now consume the status / collect the dead process
    311  const int old_si_code = si.si_code;
    312  si.si_pid = 0;
    313  // In theory it shouldn't matter either way if we use `WNOHANG` at
    314  // this point, but just in case, avoid unexpected blocking.
    315  result = HANDLE_EINTR(waitid(P_PID, handle, &si, WEXITED | WNOHANG));
    316  DCHECK(result == 0);
    317  DCHECK(si.si_pid == handle);
    318  DCHECK(si.si_code == old_si_code);
    319  return status;
    320 
    321 #else   // no waitid
    322 
    323  int status;
    324  const int result = waitpid(handle, &status, maybe_wnohang);
    325  if (result == -1) {
    326    int wait_err = errno;
    327    if (auto forkServerReturn = handleForkServer()) {
    328      return *forkServerReturn;
    329    }
    330 
    331    CHROMIUM_LOG(INFO) << "waitpid failed pid:" << handle
    332                       << " errno:" << wait_err;
    333    *info_out = wait_err;
    334    return ProcessStatus::Error;
    335  }
    336  if (result == 0) {
    337    return ProcessStatus::Running;
    338  }
    339 
    340  return handleStatus(status);
    341 #endif  // waitid
    342 }
    343 
    344 void FreeEnvVarsArray::operator()(char** array) {
    345  for (char** varPtr = array; *varPtr != nullptr; ++varPtr) {
    346    free(*varPtr);
    347  }
    348  delete[] array;
    349 }
    350 
    351 EnvironmentArray BuildEnvironmentArray(const environment_map& env_vars_to_set) {
    352  base::environment_map combined_env_vars = env_vars_to_set;
    353  char** environ = PR_DuplicateEnvironment();
    354  for (char** varPtr = environ; *varPtr != nullptr; ++varPtr) {
    355    std::string varString = *varPtr;
    356    size_t equalPos = varString.find_first_of('=');
    357    std::string varName = varString.substr(0, equalPos);
    358    std::string varValue = varString.substr(equalPos + 1);
    359    if (combined_env_vars.find(varName) == combined_env_vars.end()) {
    360      combined_env_vars[varName] = varValue;
    361    }
    362    PR_Free(*varPtr);  // PR_DuplicateEnvironment() uses PR_Malloc().
    363  }
    364  PR_Free(environ);  // PR_DuplicateEnvironment() uses PR_Malloc().
    365 
    366  EnvironmentArray array(new char*[combined_env_vars.size() + 1]);
    367  size_t i = 0;
    368  for (const auto& key_val : combined_env_vars) {
    369    std::string entry(key_val.first);
    370    entry += "=";
    371    entry += key_val.second;
    372    array[i] = strdup(entry.c_str());
    373    i++;
    374  }
    375  array[i] = nullptr;
    376  return array;
    377 }
    378 
    379 }  // namespace base
    380 
    381 namespace mozilla {
    382 
    383 EnvironmentLog::EnvironmentLog(const char* varname, size_t len) {
    384  const char* e = getenv(varname);
    385  if (e && *e) {
    386    fname_ = e;
    387  }
    388 }
    389 
    390 void EnvironmentLog::print(const char* format, ...) {
    391  if (!fname_.size()) return;
    392 
    393  FILE* f;
    394  if (fname_.compare("-") == 0) {
    395    f = fdopen(dup(STDOUT_FILENO), "a");
    396  } else {
    397    f = fopen(fname_.c_str(), "a");
    398  }
    399 
    400  if (!f) return;
    401 
    402  va_list a;
    403  va_start(a, format);
    404  vfprintf(f, format, a);
    405  va_end(a);
    406  fclose(f);
    407 }
    408 
    409 }  // namespace mozilla