tor-browser

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

UtilityProcessHost.cpp (12868B)


      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 "UtilityProcessHost.h"
      8 
      9 #include "mozilla/dom/ContentParent.h"
     10 #include "mozilla/ipc/Endpoint.h"
     11 #include "mozilla/ipc/UtilityProcessManager.h"
     12 #include "mozilla/ipc/UtilityProcessSandboxing.h"
     13 #include "mozilla/Telemetry.h"
     14 
     15 #include "chrome/common/process_watcher.h"
     16 #include "mozilla/Preferences.h"
     17 #include "mozilla/StaticPrefs_general.h"
     18 
     19 #if defined(XP_MACOSX) && defined(MOZ_SANDBOX)
     20 #  include "mozilla/Sandbox.h"
     21 #endif
     22 
     23 #if defined(XP_LINUX) && defined(MOZ_SANDBOX)
     24 #  include "mozilla/SandboxBrokerPolicyFactory.h"
     25 #endif
     26 
     27 #if defined(XP_WIN)
     28 #  include "mozilla/WinDllServices.h"
     29 #endif  // defined(XP_WIN)
     30 
     31 #if defined(MOZ_WMF_CDM) && defined(MOZ_SANDBOX) && !defined(MOZ_ASAN)
     32 #  define MOZ_WMF_CDM_LPAC_SANDBOX true
     33 #endif
     34 
     35 #ifdef MOZ_WMF_CDM_LPAC_SANDBOX
     36 #  include "GMPServiceParent.h"
     37 #  include "mozilla/dom/KeySystemNames.h"
     38 #  include "mozilla/GeckoArgs.h"
     39 #  include "mozilla/MFMediaEngineUtils.h"
     40 #  include "mozilla/StaticPrefs_media.h"
     41 #  include "nsIFile.h"
     42 #  include "sandboxBroker.h"
     43 #endif
     44 
     45 #include "ProfilerParent.h"
     46 #include "mozilla/PProfilerChild.h"
     47 
     48 namespace mozilla::ipc {
     49 
     50 LazyLogModule gUtilityProcessLog("utilityproc");
     51 #define LOGD(...) MOZ_LOG(gUtilityProcessLog, LogLevel::Debug, (__VA_ARGS__))
     52 
     53 #ifdef MOZ_WMF_CDM_LPAC_SANDBOX
     54 #  define WMF_LOG(msg, ...)                     \
     55    MOZ_LOG(gMFMediaEngineLog, LogLevel::Debug, \
     56            ("UtilityProcessHost=%p, " msg, this, ##__VA_ARGS__))
     57 #endif
     58 
     59 #if defined(XP_MACOSX) && defined(MOZ_SANDBOX)
     60 bool UtilityProcessHost::sLaunchWithMacSandbox = false;
     61 #endif
     62 
     63 UtilityProcessHost::UtilityProcessHost(SandboxingKind aSandbox,
     64                                       RefPtr<Listener> aListener)
     65    : GeckoChildProcessHost(GeckoProcessType_Utility),
     66      mListener(std::move(aListener)),
     67      mLiveToken(new media::Refcountable<bool>(true)),
     68      mLaunchPromise(MakeRefPtr<LaunchPromiseType::Private>(__func__)) {
     69  MOZ_COUNT_CTOR(UtilityProcessHost);
     70  LOGD("[%p] UtilityProcessHost::UtilityProcessHost sandboxingKind=%" PRIu64,
     71       this, aSandbox);
     72 
     73 #if defined(XP_MACOSX) && defined(MOZ_SANDBOX)
     74  if (!sLaunchWithMacSandbox) {
     75    sLaunchWithMacSandbox = IsUtilitySandboxEnabled(aSandbox);
     76  }
     77  mDisableOSActivityMode = sLaunchWithMacSandbox;
     78 #endif
     79 #if defined(MOZ_SANDBOX)
     80  mSandbox = aSandbox;
     81 #endif
     82 }
     83 
     84 UtilityProcessHost::~UtilityProcessHost() {
     85  MOZ_COUNT_DTOR(UtilityProcessHost);
     86 #if defined(MOZ_SANDBOX)
     87  LOGD("[%p] UtilityProcessHost::~UtilityProcessHost sandboxingKind=%" PRIu64,
     88       this, mSandbox);
     89 #else
     90  LOGD("[%p] UtilityProcessHost::~UtilityProcessHost", this);
     91 #endif
     92 }
     93 
     94 bool UtilityProcessHost::Launch(geckoargs::ChildProcessArgs aExtraOpts) {
     95  MOZ_ASSERT(NS_IsMainThread());
     96 
     97  MOZ_ASSERT(mLaunchPhase == LaunchPhase::Unlaunched);
     98  MOZ_ASSERT(!mUtilityProcessParent);
     99 
    100  LOGD("[%p] UtilityProcessHost::Launch", this);
    101 
    102  mPrefSerializer = MakeUnique<ipc::SharedPreferenceSerializer>();
    103  if (!mPrefSerializer->SerializeToSharedMemory(GeckoProcessType_Utility,
    104                                                /* remoteType */ ""_ns)) {
    105    return false;
    106  }
    107  mPrefSerializer->AddSharedPrefCmdLineArgs(*this, aExtraOpts);
    108 
    109 #ifdef MOZ_WMF_CDM_LPAC_SANDBOX
    110  EnsureWidevineL1PathForSandbox(aExtraOpts);
    111 #endif
    112 
    113  mLaunchPhase = LaunchPhase::Waiting;
    114 
    115  if (!GeckoChildProcessHost::AsyncLaunch(std::move(aExtraOpts))) {
    116    NS_WARNING("UtilityProcess AsyncLaunch failed, aborting.");
    117    mLaunchPhase = LaunchPhase::Complete;
    118    mPrefSerializer = nullptr;
    119    return false;
    120  }
    121  LOGD("[%p] UtilityProcessHost::Launch launching async", this);
    122  return true;
    123 }
    124 
    125 RefPtr<UtilityProcessHost::LaunchPromiseType>
    126 UtilityProcessHost::LaunchPromise() {
    127  MOZ_ASSERT(NS_IsMainThread());
    128 
    129  if (mLaunchPromiseLaunched) {
    130    return mLaunchPromise;
    131  }
    132 
    133  WhenProcessHandleReady()->Then(
    134      GetCurrentSerialEventTarget(), __func__,
    135      [this, liveToken = mLiveToken](
    136          const ipc::ProcessHandlePromise::ResolveOrRejectValue& aResult) {
    137        if (!*liveToken) {
    138          // The UtilityProcessHost got deleted. Abort. The promise would have
    139          // already been rejected.
    140          return;
    141        }
    142        if (mLaunchCompleted) {
    143          return;
    144        }
    145        mLaunchCompleted = true;
    146        if (aResult.IsReject()) {
    147          RejectPromise(aResult.RejectValue());
    148        }
    149        // If aResult.IsResolve() then we have succeeded in launching the
    150        // Utility process. The promise will be resolved once the channel has
    151        // connected (or failed to) later.
    152      });
    153 
    154  mLaunchPromiseLaunched = true;
    155  return mLaunchPromise;
    156 }
    157 
    158 void UtilityProcessHost::OnChannelConnected(base::ProcessId peer_pid) {
    159  MOZ_ASSERT(!NS_IsMainThread());
    160  LOGD("[%p] UtilityProcessHost::OnChannelConnected", this);
    161 
    162  GeckoChildProcessHost::OnChannelConnected(peer_pid);
    163 
    164  NS_DispatchToMainThread(NS_NewRunnableFunction(
    165      "UtilityProcessHost::OnChannelConnected",
    166      [this, liveToken = mLiveToken]() {
    167        if (*liveToken && mLaunchPhase == LaunchPhase::Waiting) {
    168          InitAfterConnect(true);
    169        }
    170      }));
    171 }
    172 
    173 void UtilityProcessHost::InitAfterConnect(bool aSucceeded) {
    174  MOZ_ASSERT(NS_IsMainThread());
    175 
    176  MOZ_ASSERT(mLaunchPhase == LaunchPhase::Waiting);
    177  MOZ_ASSERT(!mUtilityProcessParent);
    178 
    179  // This function is patterned after other `FooProcessHost` functions, but we
    180  // never actually call it with `false`.
    181  MOZ_ASSERT(aSucceeded);
    182 
    183  mLaunchPhase = LaunchPhase::Complete;
    184 
    185  mUtilityProcessParent = MakeRefPtr<UtilityProcessParent>(this);
    186  DebugOnly<bool> rv = TakeInitialEndpoint().Bind(mUtilityProcessParent.get());
    187  MOZ_ASSERT(rv);
    188 
    189  // Only clear mPrefSerializer in the success case to avoid a
    190  // possible race in the case case of a timeout on Windows launch.
    191  // See Bug 1555076 comment 7:
    192  // https://bugzilla.mozilla.org/show_bug.cgi?id=1555076#c7
    193  mPrefSerializer = nullptr;
    194 
    195  Maybe<FileDescriptor> brokerFd;
    196 
    197 #if defined(XP_LINUX) && defined(MOZ_SANDBOX)
    198  UniquePtr<SandboxBroker::Policy> policy;
    199  switch (mSandbox) {
    200    case SandboxingKind::GENERIC_UTILITY:
    201      policy = SandboxBrokerPolicyFactory::GetUtilityProcessPolicy(
    202          GetActor()->OtherPid());
    203      break;
    204 
    205    default:
    206      MOZ_ASSERT(false, "Invalid SandboxingKind");
    207      break;
    208  }
    209  if (policy != nullptr) {
    210    brokerFd = Some(FileDescriptor());
    211    mSandboxBroker = SandboxBroker::Create(
    212        std::move(policy), GetActor()->OtherPid(), brokerFd.ref());
    213    // This is unlikely to fail and probably indicates OS resource
    214    // exhaustion, but we can at least try to recover.
    215    (void)NS_WARN_IF(mSandboxBroker == nullptr);
    216    MOZ_ASSERT(brokerFd.ref().IsValid());
    217  }
    218 #endif  // XP_LINUX && MOZ_SANDBOX
    219 
    220  bool isReadyForBackgroundProcessing = false;
    221 #if defined(XP_WIN)
    222  RefPtr<DllServices> dllSvc(DllServices::Get());
    223  isReadyForBackgroundProcessing = dllSvc->IsReadyForBackgroundProcessing();
    224 #endif
    225 
    226  (void)GetActor()->SendInit(brokerFd, Telemetry::CanRecordReleaseData(),
    227                             isReadyForBackgroundProcessing);
    228 
    229  (void)GetActor()->SendInitProfiler(
    230      ProfilerParent::CreateForProcess(GetActor()->OtherPid()));
    231 
    232  LOGD("[%p] UtilityProcessHost::InitAfterConnect succeeded", this);
    233 
    234  // Promise will be resolved later, from UtilityProcessParent when the child
    235  // will send the InitCompleted message.
    236 }
    237 
    238 void UtilityProcessHost::Shutdown() {
    239  MOZ_ASSERT(NS_IsMainThread());
    240  MOZ_ASSERT(!mShutdownRequested);
    241  LOGD("[%p] UtilityProcessHost::Shutdown", this);
    242 
    243  RejectPromise(LaunchError("aborted by UtilityProcessHost::Shutdown"));
    244 
    245  if (mUtilityProcessParent) {
    246    LOGD("[%p] UtilityProcessHost::Shutdown not destroying utility process.",
    247         this);
    248 
    249    // OnChannelClosed uses this to check if the shutdown was expected or
    250    // unexpected.
    251    mShutdownRequested = true;
    252 
    253    // The channel might already be closed if we got here unexpectedly.
    254    if (mUtilityProcessParent->CanSend()) {
    255      mUtilityProcessParent->Close();
    256    }
    257 
    258 #ifndef NS_FREE_PERMANENT_DATA
    259    // No need to communicate shutdown, the Utility process doesn't need to
    260    // communicate anything back.
    261    KillHard("NormalShutdown");
    262 #endif
    263 
    264    // If we're shutting down unexpectedly, we're in the middle of handling an
    265    // ActorDestroy for PUtilityProcessParent, which is still on the stack.
    266    // We'll return back to OnChannelClosed.
    267    //
    268    // Otherwise, we'll wait for OnChannelClose to be called whenever
    269    // PUtilityProcessParent acknowledges shutdown.
    270    return;
    271  }
    272 
    273  DestroyProcess();
    274 }
    275 
    276 void UtilityProcessHost::OnChannelClosed(
    277    IProtocol::ActorDestroyReason aReason) {
    278  MOZ_ASSERT(NS_IsMainThread());
    279  LOGD("[%p] UtilityProcessHost::OnChannelClosed", this);
    280 
    281  // `aReason` was not originally passed into this function; a value of 0 for
    282  // the `why` in telemetry means it's from an older build with no info
    283  RejectPromise(
    284      LaunchError("UtilityProcessHost::OnChannelClosed", 1 + (long)aReason));
    285 
    286  if (!mShutdownRequested && mListener) {
    287    // This is an unclean shutdown. Notify our listener that we're going away.
    288    mListener->OnProcessUnexpectedShutdown(this);
    289  }
    290 
    291  DestroyProcess();
    292 
    293  // Release the actor.
    294  UtilityProcessParent::Destroy(std::move(mUtilityProcessParent));
    295 }
    296 
    297 void UtilityProcessHost::KillHard(const char* aReason) {
    298  MOZ_ASSERT(NS_IsMainThread());
    299  LOGD("[%p] UtilityProcessHost::KillHard", this);
    300 
    301  ProcessHandle handle = GetChildProcessHandle();
    302  if (!base::KillProcess(handle, base::PROCESS_END_KILLED_BY_USER)) {
    303    NS_WARNING("failed to kill subprocess!");
    304  }
    305 
    306  SetAlreadyDead();
    307 }
    308 
    309 void UtilityProcessHost::DestroyProcess() {
    310  MOZ_ASSERT(NS_IsMainThread());
    311  LOGD("[%p] UtilityProcessHost::DestroyProcess", this);
    312 
    313  RejectPromise(LaunchError("UtilityProcessHost::DestroyProcess"));
    314 
    315  // Any pending tasks will be cancelled from now on.
    316  *mLiveToken = false;
    317 
    318  NS_DispatchToMainThread(
    319      NS_NewRunnableFunction("DestroyProcessRunnable", [this] { Destroy(); }));
    320 }
    321 
    322 void UtilityProcessHost::ResolvePromise() {
    323  MOZ_ASSERT(NS_IsMainThread());
    324  LOGD("[%p] UtilityProcessHost connected - resolving launch promise", this);
    325 
    326  if (!mLaunchPromiseSettled) {
    327    mLaunchPromise->Resolve(Ok{}, __func__);
    328    mLaunchPromiseSettled = true;
    329  }
    330 
    331  mLaunchCompleted = true;
    332 }
    333 
    334 void UtilityProcessHost::RejectPromise(LaunchError err) {
    335  MOZ_ASSERT(NS_IsMainThread());
    336  LOGD("[%p] UtilityProcessHost connection failed - rejecting launch promise",
    337       this);
    338 
    339  if (!mLaunchPromiseSettled) {
    340    mLaunchPromise->Reject(std::move(err), __func__);
    341    mLaunchPromiseSettled = true;
    342  }
    343 
    344  mLaunchCompleted = true;
    345 }
    346 
    347 #if defined(XP_MACOSX) && defined(MOZ_SANDBOX)
    348 bool UtilityProcessHost::FillMacSandboxInfo(MacSandboxInfo& aInfo) {
    349  GeckoChildProcessHost::FillMacSandboxInfo(aInfo);
    350  if (!aInfo.shouldLog && PR_GetEnv("MOZ_SANDBOX_UTILITY_LOGGING")) {
    351    aInfo.shouldLog = true;
    352  }
    353  return true;
    354 }
    355 
    356 /* static */
    357 MacSandboxType UtilityProcessHost::GetMacSandboxType() {
    358  return MacSandboxType_Utility;
    359 }
    360 #endif
    361 
    362 #ifdef MOZ_WMF_CDM_LPAC_SANDBOX
    363 void UtilityProcessHost::EnsureWidevineL1PathForSandbox(
    364    geckoargs::ChildProcessArgs& aExtraOpts) {
    365  if (mSandbox != SandboxingKind::MF_MEDIA_ENGINE_CDM) {
    366    return;
    367  }
    368 
    369  RefPtr<mozilla::gmp::GeckoMediaPluginServiceParent> gmps =
    370      mozilla::gmp::GeckoMediaPluginServiceParent::GetSingleton();
    371  if (NS_WARN_IF(!gmps)) {
    372    WMF_LOG("Failed to get GeckoMediaPluginServiceParent!");
    373    return;
    374  }
    375 
    376  if (!StaticPrefs::media_eme_widevine_experiment_enabled()) {
    377    return;
    378  }
    379 
    380  // If Widevine L1 is installed after the MFCDM process starts, we will set it
    381  // path later via MFCDMService::UpdateWideivineL1Path().
    382  nsString widevineL1Path;
    383  nsCOMPtr<nsIFile> pluginFile;
    384  if (NS_WARN_IF(NS_FAILED(gmps->FindPluginDirectoryForAPI(
    385          nsCString(kWidevineExperimentAPIName),
    386          {nsCString(kWidevineExperimentKeySystemName)},
    387          getter_AddRefs(pluginFile))))) {
    388    WMF_LOG("Widevine L1 is not installed yet");
    389    return;
    390  }
    391 
    392  if (!pluginFile) {
    393    WMF_LOG("No plugin file found!");
    394    return;
    395  }
    396 
    397  if (NS_WARN_IF(NS_FAILED(pluginFile->GetTarget(widevineL1Path)))) {
    398    WMF_LOG("Failed to get L1 path!");
    399    return;
    400  }
    401 
    402  WMF_LOG("Set Widevine L1 path=%s",
    403          NS_ConvertUTF16toUTF8(widevineL1Path).get());
    404  geckoargs::sPluginPath.Put(NS_ConvertUTF16toUTF8(widevineL1Path).get(),
    405                             aExtraOpts);
    406  SandboxBroker::EnsureLpacPermsissionsOnDir(widevineL1Path);
    407 }
    408 
    409 #  undef WMF_LOG
    410 
    411 #endif
    412 
    413 }  // namespace mozilla::ipc