tor-browser

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

ForkServiceChild.cpp (9628B)


      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 "ForkServiceChild.h"
      8 #include "ForkServer.h"
      9 #include "chrome/common/process_watcher.h"
     10 #include "mozilla/Atomics.h"
     11 #include "mozilla/Logging.h"
     12 #include "mozilla/ipc/GeckoChildProcessHost.h"
     13 #include "mozilla/ipc/ProtocolMessageUtils.h"
     14 #include "mozilla/StaticPrefs_dom.h"
     15 #include "mozilla/Services.h"
     16 #include "ipc/IPCMessageUtilsSpecializations.h"
     17 #include "nsIObserverService.h"
     18 
     19 #include <unistd.h>
     20 #include <fcntl.h>
     21 
     22 namespace mozilla {
     23 namespace ipc {
     24 
     25 extern LazyLogModule gForkServiceLog;
     26 
     27 StaticMutex ForkServiceChild::sMutex;
     28 StaticRefPtr<ForkServiceChild> ForkServiceChild::sSingleton;
     29 Atomic<bool> ForkServiceChild::sForkServiceUsed;
     30 
     31 #ifndef SOCK_CLOEXEC
     32 static bool ConfigurePipeFd(int aFd) {
     33  int flags = fcntl(aFd, F_GETFD, 0);
     34  return flags != -1 && fcntl(aFd, F_SETFD, flags | FD_CLOEXEC) != -1;
     35 }
     36 #endif
     37 
     38 // Create a socketpair with both ends marked as close-on-exec
     39 static Result<Ok, LaunchError> CreateSocketPair(UniqueFileHandle& aFD0,
     40                                                UniqueFileHandle& aFD1) {
     41  int fds[2];
     42 #ifdef SOCK_CLOEXEC
     43  constexpr int type = SOCK_STREAM | SOCK_CLOEXEC;
     44 #else
     45  constexpr int type = SOCK_STREAM;
     46 #endif
     47 
     48  if (socketpair(AF_UNIX, type, 0, fds) < 0) {
     49    return Err(LaunchError("FSC::CSP::sp", errno));
     50  }
     51 
     52 #ifndef SOCK_CLOEXEC
     53  if (!ConfigurePipeFd(server.get()) || !ConfigurePipeFd(client.get())) {
     54    return Err(LaunchError("FSC::CSP::cfg", errno));
     55  }
     56 #endif
     57 
     58  aFD0.reset(fds[0]);
     59  aFD1.reset(fds[1]);
     60 
     61  return Ok();
     62 }
     63 
     64 void ForkServiceChild::StartForkServer() {
     65  UniqueFileHandle server;
     66  UniqueFileHandle client;
     67  if (CreateSocketPair(server, client).isErr()) {
     68    MOZ_LOG(gForkServiceLog, LogLevel::Error,
     69            ("failed to create fork server socket"));
     70    return;
     71  }
     72 
     73  GeckoChildProcessHost* subprocess =
     74      new GeckoChildProcessHost(GeckoProcessType_ForkServer, false);
     75 
     76  geckoargs::ChildProcessArgs extraOpts;
     77  geckoargs::sIPCHandle.Put(std::move(client), extraOpts);
     78  geckoargs::sSignalPipe.Put(ProcessWatcher::GetSignalPipe(), extraOpts);
     79 
     80  if (!subprocess->LaunchAndWaitForProcessHandle(std::move(extraOpts))) {
     81    MOZ_LOG(gForkServiceLog, LogLevel::Error, ("failed to launch fork server"));
     82    return;
     83  }
     84 
     85  sForkServiceUsed = true;
     86  StaticMutexAutoLock smal(sMutex);
     87  // Can't use MakeRefPtr; ctor is private.
     88  MOZ_ASSERT(sSingleton == nullptr);
     89  sSingleton = new ForkServiceChild(server.release(), subprocess);
     90 }
     91 
     92 void ForkServiceChild::StopForkServer() {
     93  RefPtr<ForkServiceChild> oldChild;
     94  {
     95    StaticMutexAutoLock smal(sMutex);
     96    oldChild = sSingleton.forget();
     97  }
     98  // Drop the old reference outside of the lock to avoid lock order
     99  // cycles via the GeckoChildProcessHost dtor.
    100 }
    101 
    102 RefPtr<ForkServiceChild> ForkServiceChild::Get() {
    103  RefPtr<ForkServiceChild> child;
    104  {
    105    StaticMutexAutoLock smal(sMutex);
    106    child = sSingleton;
    107  }
    108  return child;
    109 }
    110 
    111 ForkServiceChild::ForkServiceChild(int aFd, GeckoChildProcessHost* aProcess)
    112    : mMutex("mozilla.ipc.ForkServiceChild.mMutex"),
    113      mFailed(false),
    114      mProcess(aProcess) {
    115  mTcver = MakeUnique<MiniTransceiver>(aFd);
    116 }
    117 
    118 ForkServiceChild::~ForkServiceChild() {
    119  close(mTcver->GetFD());
    120  // This can be synchronous during browser shutdown, so do it *after*
    121  // causing the fork server to exit by closning the socket:
    122  mProcess->Destroy();
    123 }
    124 
    125 Result<Ok, LaunchError> ForkServiceChild::SendForkNewSubprocess(
    126    geckoargs::ChildProcessArgs&& aArgs, base::LaunchOptions&& aOptions,
    127    pid_t* aPid) {
    128  // Double-check there are no unsupported options.
    129  MOZ_ASSERT(aOptions.workdir.empty());
    130  MOZ_ASSERT(!aOptions.full_env);
    131  MOZ_ASSERT(!aOptions.wait);
    132  MOZ_ASSERT(aOptions.fds_to_remap.size() == aArgs.mFiles.size());
    133 
    134  MutexAutoLock lock(mMutex);
    135  if (mFailed) {
    136    return Err(LaunchError("FSC::SFNS::Failed"));
    137  }
    138 
    139  UniqueFileHandle execParent;
    140  {
    141    UniqueFileHandle execChild;
    142    IPC::Message msg(MSG_ROUTING_CONTROL, Msg_ForkNewSubprocess__ID);
    143 
    144    MOZ_TRY(CreateSocketPair(execParent, execChild));
    145 
    146    IPC::MessageWriter writer(msg);
    147 #if defined(XP_LINUX) && defined(MOZ_SANDBOX)
    148    WriteParam(&writer, aOptions.fork_flags);
    149    WriteParam(&writer, std::move(aOptions.sandbox_chroot_server));
    150 #endif
    151    WriteParam(&writer, std::move(execChild));
    152    if (!mTcver->Send(msg)) {
    153      MOZ_LOG(gForkServiceLog, LogLevel::Verbose,
    154              ("the pipe to the fork server is closed or having errors"));
    155      OnError();
    156      return Err(LaunchError("FSC::SFNS::Send"));
    157    }
    158  }
    159 
    160  {
    161    MiniTransceiver execTcver(execParent.get());
    162    IPC::Message execMsg(MSG_ROUTING_CONTROL, Msg_SubprocessExecInfo__ID);
    163    IPC::MessageWriter execWriter(execMsg);
    164    WriteParam(&execWriter, aOptions.env_map);
    165    WriteParam(&execWriter, aArgs.mArgs);
    166    WriteParam(&execWriter, std::move(aArgs.mFiles));
    167    if (!execTcver.Send(execMsg)) {
    168      MOZ_LOG(gForkServiceLog, LogLevel::Verbose,
    169              ("failed to send exec info to the fork server"));
    170      OnError();
    171      return Err(LaunchError("FSC::SFNS::Send2"));
    172    }
    173  }
    174  execParent = nullptr;
    175 
    176  UniquePtr<IPC::Message> reply;
    177  if (!mTcver->Recv(reply)) {
    178    MOZ_LOG(gForkServiceLog, LogLevel::Verbose,
    179            ("the pipe to the fork server is closed or having errors"));
    180    OnError();
    181    return Err(LaunchError("FSC::SFNS::Recv"));
    182  }
    183 
    184  if (reply->type() != Reply_ForkNewSubprocess__ID) {
    185    MOZ_LOG(gForkServiceLog, LogLevel::Verbose,
    186            ("unknown reply type %d", reply->type()));
    187    return Err(LaunchError("FSC::SFNS::Type"));
    188  }
    189  IPC::MessageReader reader(*reply);
    190 
    191  if (!ReadParam(&reader, aPid)) {
    192    MOZ_CRASH("Error deserializing 'pid_t'");
    193  }
    194  reader.EndRead();
    195 
    196  return Ok();
    197 }
    198 
    199 auto ForkServiceChild::SendWaitPid(pid_t aPid, bool aBlock)
    200    -> Result<ProcStatus, int> {
    201  MutexAutoLock lock(mMutex);
    202  if (mFailed) {
    203    return Err(ECONNRESET);
    204  }
    205 
    206  IPC::Message msg(MSG_ROUTING_CONTROL, Msg_WaitPid__ID);
    207  IPC::MessageWriter writer(msg);
    208  WriteParam(&writer, aPid);
    209  WriteParam(&writer, aBlock);
    210 
    211  if (!mTcver->Send(msg)) {
    212    MOZ_LOG(gForkServiceLog, LogLevel::Verbose,
    213            ("the pipe to the fork server is closed or having errors"));
    214    OnError();
    215    return Err(ECONNRESET);
    216  }
    217 
    218  UniquePtr<IPC::Message> reply;
    219  if (!mTcver->Recv(reply)) {
    220    MOZ_LOG(gForkServiceLog, LogLevel::Verbose,
    221            ("the pipe to the fork server is closed or having errors"));
    222    OnError();
    223    return Err(ECONNRESET);
    224  }
    225 
    226  if (reply->type() != Reply_WaitPid__ID) {
    227    MOZ_LOG(gForkServiceLog, LogLevel::Verbose,
    228            ("unknown reply type %d", reply->type()));
    229    OnError();
    230    return Err(EPROTO);
    231  }
    232  IPC::MessageReader reader(*reply);
    233 
    234  // Both sides of the Result are isomorphic to int.
    235  bool isErr = false;
    236  int value = 0;
    237  if (!ReadParam(&reader, &isErr) || !ReadParam(&reader, &value)) {
    238    MOZ_LOG(gForkServiceLog, LogLevel::Verbose,
    239            ("deserialization error in waitpid reply"));
    240    OnError();
    241    return Err(EPROTO);
    242  }
    243 
    244  // This can't use ?: because the types are different.
    245  if (isErr) {
    246    return Err(value);
    247  }
    248  return ProcStatus{value};
    249 }
    250 
    251 void ForkServiceChild::OnError() {
    252  mFailed = true;
    253  ForkServerLauncher::RestartForkServer();
    254 }
    255 
    256 NS_IMPL_ISUPPORTS(ForkServerLauncher, nsIObserver)
    257 
    258 bool ForkServerLauncher::sHaveStartedClient = false;
    259 StaticRefPtr<ForkServerLauncher> ForkServerLauncher::sSingleton;
    260 
    261 ForkServerLauncher::ForkServerLauncher() {}
    262 
    263 ForkServerLauncher::~ForkServerLauncher() {}
    264 
    265 already_AddRefed<ForkServerLauncher> ForkServerLauncher::Create() {
    266  if (sSingleton == nullptr) {
    267    sSingleton = new ForkServerLauncher();
    268  }
    269  RefPtr<ForkServerLauncher> launcher = sSingleton;
    270  return launcher.forget();
    271 }
    272 
    273 NS_IMETHODIMP
    274 ForkServerLauncher::Observe(nsISupports* aSubject, const char* aTopic,
    275                            const char16_t* aData) {
    276  if (strcmp(aTopic, NS_XPCOM_STARTUP_CATEGORY) == 0) {
    277    nsCOMPtr<nsIObserverService> obsSvc =
    278        mozilla::services::GetObserverService();
    279    MOZ_ASSERT(obsSvc != nullptr);
    280    // preferences are not available until final-ui-startup
    281    obsSvc->AddObserver(this, "final-ui-startup", false);
    282  } else if (!sHaveStartedClient && strcmp(aTopic, "final-ui-startup") == 0) {
    283    if (StaticPrefs::dom_ipc_forkserver_enable_AtStartup()) {
    284      sHaveStartedClient = true;
    285      ForkServiceChild::StartForkServer();
    286 
    287      nsCOMPtr<nsIObserverService> obsSvc =
    288          mozilla::services::GetObserverService();
    289      MOZ_ASSERT(obsSvc != nullptr);
    290      obsSvc->AddObserver(this, NS_XPCOM_SHUTDOWN_OBSERVER_ID, false);
    291    } else {
    292      sSingleton = nullptr;
    293    }
    294  }
    295 
    296  if (strcmp(aTopic, NS_XPCOM_SHUTDOWN_OBSERVER_ID) == 0) {
    297    // To make leak checker happy!
    298    sSingleton = nullptr;
    299  }
    300 
    301  return NS_OK;
    302 }
    303 
    304 void ForkServerLauncher::RestartForkServer() {
    305  // Restart fork server
    306  NS_SUCCEEDED(NS_DispatchToMainThreadQueue(
    307      NS_NewRunnableFunction("OnForkServerError",
    308                             [] {
    309                               if (sSingleton) {
    310                                 ForkServiceChild::StopForkServer();
    311                                 ForkServiceChild::StartForkServer();
    312                               }
    313                             }),
    314      EventQueuePriority::Idle));
    315 }
    316 
    317 }  // namespace ipc
    318 }  // namespace mozilla