tor-browser

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

RDDProcessHost.cpp (8915B)


      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 #include "RDDProcessHost.h"
      7 
      8 #include "RDDChild.h"
      9 #include "chrome/common/process_watcher.h"
     10 #include "mozilla/Preferences.h"
     11 #include "mozilla/StaticPrefs_media.h"
     12 #include "mozilla/dom/ContentParent.h"
     13 #include "mozilla/ipc/ProcessUtils.h"
     14 
     15 #if defined(XP_MACOSX) && defined(MOZ_SANDBOX)
     16 #  include "mozilla/Sandbox.h"
     17 #endif
     18 
     19 namespace mozilla {
     20 
     21 using namespace ipc;
     22 
     23 #if defined(XP_MACOSX) && defined(MOZ_SANDBOX)
     24 bool RDDProcessHost::sLaunchWithMacSandbox = false;
     25 #endif
     26 
     27 RDDProcessHost::RDDProcessHost(Listener* aListener)
     28    : GeckoChildProcessHost(GeckoProcessType_RDD),
     29      mListener(aListener),
     30      mLiveToken(new media::Refcountable<bool>(true)) {
     31  MOZ_COUNT_CTOR(RDDProcessHost);
     32 
     33 #if defined(XP_MACOSX) && defined(MOZ_SANDBOX)
     34  if (!sLaunchWithMacSandbox) {
     35    sLaunchWithMacSandbox = (PR_GetEnv("MOZ_DISABLE_RDD_SANDBOX") == nullptr);
     36  }
     37  mDisableOSActivityMode = sLaunchWithMacSandbox;
     38 #endif
     39 }
     40 
     41 RDDProcessHost::~RDDProcessHost() { MOZ_COUNT_DTOR(RDDProcessHost); }
     42 
     43 bool RDDProcessHost::Launch(geckoargs::ChildProcessArgs aExtraOpts) {
     44  MOZ_ASSERT(NS_IsMainThread());
     45 
     46  MOZ_ASSERT(mLaunchPhase == LaunchPhase::Unlaunched);
     47  MOZ_ASSERT(!mRDDChild);
     48 
     49  mPrefSerializer = MakeUnique<ipc::SharedPreferenceSerializer>();
     50  if (!mPrefSerializer->SerializeToSharedMemory(GeckoProcessType_RDD,
     51                                                /* remoteType */ ""_ns)) {
     52    return false;
     53  }
     54  mPrefSerializer->AddSharedPrefCmdLineArgs(*this, aExtraOpts);
     55 
     56  mLaunchPhase = LaunchPhase::Waiting;
     57  mLaunchTime = TimeStamp::Now();
     58 
     59  int32_t timeoutMs = StaticPrefs::media_rdd_process_startup_timeout_ms();
     60 
     61  // If one of the following environment variables are set we can
     62  // effectively ignore the timeout - as we can guarantee the RDD
     63  // process will be terminated
     64  if (PR_GetEnv("MOZ_DEBUG_CHILD_PROCESS") ||
     65      PR_GetEnv("MOZ_DEBUG_CHILD_PAUSE")) {
     66    timeoutMs = 0;
     67  }
     68  if (timeoutMs) {
     69    // We queue a delayed task. If that task runs before the
     70    // WhenProcessHandleReady promise gets resolved, we will abort the launch.
     71    GetMainThreadSerialEventTarget()->DelayedDispatch(
     72        NS_NewRunnableFunction(
     73            "RDDProcessHost::Launchtimeout",
     74            [this, liveToken = mLiveToken]() {
     75              if (!*liveToken || mTimerChecked) {
     76                // We have been deleted or the runnable has already started, we
     77                // can abort.
     78                return;
     79              }
     80              InitAfterConnect(false);
     81              MOZ_ASSERT(mTimerChecked,
     82                         "InitAfterConnect must have acted on the promise");
     83            }),
     84        timeoutMs);
     85  }
     86 
     87  if (!GeckoChildProcessHost::AsyncLaunch(std::move(aExtraOpts))) {
     88    mLaunchPhase = LaunchPhase::Complete;
     89    mPrefSerializer = nullptr;
     90    return false;
     91  }
     92  return true;
     93 }
     94 
     95 RefPtr<GenericNonExclusivePromise> RDDProcessHost::LaunchPromise() {
     96  MOZ_ASSERT(NS_IsMainThread());
     97 
     98  if (mLaunchPromise) {
     99    return mLaunchPromise;
    100  }
    101  mLaunchPromise = MakeRefPtr<GenericNonExclusivePromise::Private>(__func__);
    102  WhenProcessHandleReady()->Then(
    103      GetCurrentSerialEventTarget(), __func__,
    104      [this, liveToken = mLiveToken](
    105          const ipc::ProcessHandlePromise::ResolveOrRejectValue& aResult) {
    106        if (!*liveToken) {
    107          // The RDDProcessHost got deleted. Abort. The promise would have
    108          // already been rejected.
    109          return;
    110        }
    111        if (mTimerChecked) {
    112          // We hit the timeout earlier, abort.
    113          return;
    114        }
    115        mTimerChecked = true;
    116        if (aResult.IsReject()) {
    117          RejectPromise();
    118        }
    119        // If aResult.IsResolve() then we have succeeded in launching the
    120        // RDD process. The promise will be resolved once the channel has
    121        // connected (or failed to) later.
    122      });
    123  return mLaunchPromise;
    124 }
    125 
    126 void RDDProcessHost::OnChannelConnected(base::ProcessId peer_pid) {
    127  MOZ_ASSERT(!NS_IsMainThread());
    128 
    129  GeckoChildProcessHost::OnChannelConnected(peer_pid);
    130 
    131  NS_DispatchToMainThread(NS_NewRunnableFunction(
    132      "RDDProcessHost::OnChannelConnected", [this, liveToken = mLiveToken]() {
    133        if (*liveToken && mLaunchPhase == LaunchPhase::Waiting) {
    134          InitAfterConnect(true);
    135        }
    136      }));
    137 }
    138 
    139 static uint64_t sRDDProcessTokenCounter = 0;
    140 
    141 void RDDProcessHost::InitAfterConnect(bool aSucceeded) {
    142  MOZ_ASSERT(NS_IsMainThread());
    143 
    144  MOZ_ASSERT(mLaunchPhase == LaunchPhase::Waiting);
    145  MOZ_ASSERT(!mRDDChild);
    146 
    147  mLaunchPhase = LaunchPhase::Complete;
    148 
    149  if (!aSucceeded) {
    150    RejectPromise();
    151    return;
    152  }
    153  mProcessToken = ++sRDDProcessTokenCounter;
    154  mRDDChild = MakeRefPtr<RDDChild>(this);
    155  DebugOnly<bool> rv = TakeInitialEndpoint().Bind(mRDDChild.get());
    156  MOZ_ASSERT(rv);
    157 
    158  // Only clear mPrefSerializer in the success case to avoid a
    159  // possible race in the case case of a timeout on Windows launch.
    160  // See Bug 1555076 comment 7:
    161  // https://bugzilla.mozilla.org/show_bug.cgi?id=1555076#c7
    162  mPrefSerializer = nullptr;
    163 
    164  if (!mRDDChild->Init()) {
    165    // Can't just kill here because it will create a timing race that
    166    // will crash the tab. We don't really want to crash the tab just
    167    // because RDD linux sandbox failed to initialize.  In this case,
    168    // we'll close the child channel which will cause the RDD process
    169    // to shutdown nicely avoiding the tab crash (which manifests as
    170    // Bug 1535335).
    171    mRDDChild->Close();
    172    RejectPromise();
    173  } else {
    174    ResolvePromise();
    175  }
    176 }
    177 
    178 void RDDProcessHost::Shutdown() {
    179  MOZ_ASSERT(NS_IsMainThread());
    180  MOZ_ASSERT(!mShutdownRequested);
    181 
    182  RejectPromise();
    183 
    184  if (mRDDChild) {
    185    // OnChannelClosed uses this to check if the shutdown was expected or
    186    // unexpected.
    187    mShutdownRequested = true;
    188 
    189    // The channel might already be closed if we got here unexpectedly.
    190    if (!mChannelClosed) {
    191      mRDDChild->Close();
    192    }
    193 
    194 #ifndef NS_FREE_PERMANENT_DATA
    195    // No need to communicate shutdown, the RDD process doesn't need to
    196    // communicate anything back.
    197    KillHard("NormalShutdown");
    198 #endif
    199 
    200    // If we're shutting down unexpectedly, we're in the middle of handling an
    201    // ActorDestroy for PRDDChild, which is still on the stack. We'll return
    202    // back to OnChannelClosed.
    203    //
    204    // Otherwise, we'll wait for OnChannelClose to be called whenever PRDDChild
    205    // acknowledges shutdown.
    206    return;
    207  }
    208 
    209  DestroyProcess();
    210 }
    211 
    212 void RDDProcessHost::OnChannelClosed() {
    213  MOZ_ASSERT(NS_IsMainThread());
    214 
    215  mChannelClosed = true;
    216  RejectPromise();
    217 
    218  if (!mShutdownRequested && mListener) {
    219    // This is an unclean shutdown. Notify our listener that we're going away.
    220    mListener->OnProcessUnexpectedShutdown(this);
    221  } else {
    222    DestroyProcess();
    223  }
    224 
    225  // Release the actor.
    226  RDDChild::Destroy(std::move(mRDDChild));
    227 }
    228 
    229 void RDDProcessHost::KillHard(const char* aReason) {
    230  MOZ_ASSERT(NS_IsMainThread());
    231 
    232  ProcessHandle handle = GetChildProcessHandle();
    233  if (!base::KillProcess(handle, base::PROCESS_END_KILLED_BY_USER)) {
    234    NS_WARNING("failed to kill subprocess!");
    235  }
    236 
    237  SetAlreadyDead();
    238 }
    239 
    240 uint64_t RDDProcessHost::GetProcessToken() const {
    241  MOZ_ASSERT(NS_IsMainThread());
    242  return mProcessToken;
    243 }
    244 
    245 void RDDProcessHost::DestroyProcess() {
    246  MOZ_ASSERT(NS_IsMainThread());
    247  RejectPromise();
    248 
    249  // Any pending tasks will be cancelled from now on.
    250  *mLiveToken = false;
    251 
    252  NS_DispatchToMainThread(
    253      NS_NewRunnableFunction("DestroyProcessRunnable", [this] { Destroy(); }));
    254 }
    255 
    256 void RDDProcessHost::ResolvePromise() {
    257  MOZ_ASSERT(NS_IsMainThread());
    258 
    259  if (mLaunchPromise && !mLaunchPromiseSettled) {
    260    mLaunchPromise->Resolve(true, __func__);
    261    mLaunchPromiseSettled = true;
    262  }
    263  // We have already acted on the promise; the timeout runnable no longer needs
    264  // to interrupt anything.
    265  mTimerChecked = true;
    266 }
    267 
    268 void RDDProcessHost::RejectPromise() {
    269  MOZ_ASSERT(NS_IsMainThread());
    270 
    271  if (mLaunchPromise && !mLaunchPromiseSettled) {
    272    mLaunchPromise->Reject(NS_ERROR_FAILURE, __func__);
    273    mLaunchPromiseSettled = true;
    274  }
    275  // We have already acted on the promise; the timeout runnable no longer needs
    276  // to interrupt anything.
    277  mTimerChecked = true;
    278 }
    279 
    280 #if defined(XP_MACOSX) && defined(MOZ_SANDBOX)
    281 bool RDDProcessHost::FillMacSandboxInfo(MacSandboxInfo& aInfo) {
    282  GeckoChildProcessHost::FillMacSandboxInfo(aInfo);
    283  if (!aInfo.shouldLog && PR_GetEnv("MOZ_SANDBOX_RDD_LOGGING")) {
    284    aInfo.shouldLog = true;
    285  }
    286  return true;
    287 }
    288 
    289 /* static */
    290 MacSandboxType RDDProcessHost::GetMacSandboxType() {
    291  return MacSandboxType_RDD;
    292 }
    293 #endif
    294 
    295 }  // namespace mozilla