tor-browser

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

UtilityProcessTest.cpp (8329B)


      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 #if defined(ENABLE_TESTS)
      8 #  include "mozilla/ipc/UtilityProcessManager.h"
      9 #  include "mozilla/ipc/UtilityProcessTest.h"
     10 #  include "mozilla/dom/ChromeUtilsBinding.h"
     11 #  include "mozilla/dom/Promise.h"
     12 #  include "mozilla/ProcInfo.h"
     13 #  include "mozilla/IntentionalCrash.h"
     14 
     15 #  ifdef XP_WIN
     16 #    include <handleapi.h>
     17 #    include <processthreadsapi.h>
     18 #    include <tlhelp32.h>
     19 
     20 #    include "mozilla/WinHandleWatcher.h"
     21 #    include "nsISupports.h"
     22 #    include "nsWindowsHelpers.h"
     23 #  endif
     24 
     25 namespace mozilla::ipc {
     26 
     27 static UtilityActorName UtilityActorNameFromString(
     28    const nsACString& aStringName) {
     29  using namespace mozilla::dom;
     30  auto idlName = StringToEnum<UtilityActorName>(aStringName);
     31  if (idlName.isSome()) {
     32    return idlName.value();
     33  }
     34  MOZ_CRASH("Unknown utility actor name");
     35 }
     36 
     37 // Find the utility process with the given actor or any utility process if
     38 // aActorName is Nothing().
     39 static SandboxingKind FindUtilityProcessWithActor(
     40    const Maybe<UtilityActorName>& aActorName) {
     41  RefPtr<UtilityProcessManager> utilityProc =
     42      UtilityProcessManager::GetSingleton();
     43  MOZ_ASSERT(utilityProc, "No UtilityprocessManager?");
     44 
     45  for (size_t i = 0; i < SandboxingKind::COUNT; ++i) {
     46    auto sbKind = static_cast<SandboxingKind>(i);
     47    if (!utilityProc->Process(sbKind)) {
     48      continue;
     49    }
     50    if (aActorName.isNothing()) {
     51      return sbKind;
     52    }
     53    for (auto actor : utilityProc->GetActors(sbKind)) {
     54      if (actor == aActorName.ref()) {
     55        return sbKind;
     56      }
     57    }
     58  }
     59 
     60  return SandboxingKind::COUNT;
     61 }
     62 
     63 #  ifdef XP_WIN
     64 namespace {
     65 // Promise implementation for `UntilChildProcessDead`.
     66 //
     67 // Resolves the provided JS promise when the provided Windows HANDLE becomes
     68 // signaled.
     69 class WinHandlePromiseImpl final {
     70 public:
     71  NS_INLINE_DECL_REFCOUNTING(WinHandlePromiseImpl)
     72 
     73  using HandlePtr = mozilla::UniqueFileHandle;
     74 
     75  // Takes ownership of aHandle.
     76  static void Create(mozilla::UniqueFileHandle handle,
     77                     RefPtr<mozilla::dom::Promise> promise) {
     78    MOZ_ASSERT(handle);
     79    MOZ_ASSERT(promise);
     80 
     81    RefPtr obj{new WinHandlePromiseImpl(std::move(handle), std::move(promise))};
     82 
     83    // WARNING: This creates an owning-reference cycle: (self -> HandleWatcher
     84    //    -> Runnable -> self). `obj` will therefore only be destroyed when and
     85    //    if the HANDLE is signaled.
     86    obj->watcher.Watch(obj->handle.get(), GetCurrentSerialEventTarget(),
     87                       NewRunnableMethod("WinHandlePromiseImpl::Resolve", obj,
     88                                         &WinHandlePromiseImpl::Resolve));
     89  }
     90 
     91 private:
     92  WinHandlePromiseImpl(mozilla::UniqueFileHandle handle,
     93                       RefPtr<mozilla::dom::Promise> promise)
     94      : handle(std::move(handle)), promise(std::move(promise)) {}
     95 
     96  ~WinHandlePromiseImpl() { watcher.Stop(); }
     97 
     98  void Resolve() { promise->MaybeResolveWithUndefined(); }
     99 
    100  mozilla::UniqueFileHandle handle;
    101  HandleWatcher watcher;
    102  RefPtr<mozilla::dom::Promise> promise;
    103 };
    104 
    105 }  // namespace
    106 #  endif
    107 
    108 NS_IMETHODIMP
    109 UtilityProcessTest::StartProcess(const nsTArray<nsCString>& aActorsToRegister,
    110                                 JSContext* aCx,
    111                                 mozilla::dom::Promise** aOutPromise) {
    112  NS_ENSURE_ARG(aOutPromise);
    113  *aOutPromise = nullptr;
    114  nsIGlobalObject* global = xpc::CurrentNativeGlobal(aCx);
    115  if (NS_WARN_IF(!global)) {
    116    return NS_ERROR_FAILURE;
    117  }
    118 
    119  ErrorResult erv;
    120  RefPtr<dom::Promise> promise = dom::Promise::Create(global, erv);
    121  if (NS_WARN_IF(erv.Failed())) {
    122    return erv.StealNSResult();
    123  }
    124 
    125  RefPtr<UtilityProcessManager> utilityProc =
    126      UtilityProcessManager::GetSingleton();
    127  MOZ_ASSERT(utilityProc, "No UtilityprocessManager?");
    128 
    129  auto actors = aActorsToRegister.Clone();
    130 
    131  utilityProc->LaunchProcess(SandboxingKind::GENERIC_UTILITY)
    132      ->Then(
    133          GetCurrentSerialEventTarget(), __func__,
    134          [promise, utilityProc, actors = std::move(actors)] {
    135            RefPtr<UtilityProcessParent> utilityParent =
    136                utilityProc->GetProcessParent(SandboxingKind::GENERIC_UTILITY);
    137            Maybe<int32_t> utilityPid =
    138                utilityProc->ProcessPid(SandboxingKind::GENERIC_UTILITY);
    139            for (size_t i = 0; i < actors.Length(); ++i) {
    140              auto uan = UtilityActorNameFromString(actors[i]);
    141              utilityProc->RegisterActor(utilityParent, uan);
    142            }
    143            if (utilityPid.isSome()) {
    144              promise->MaybeResolve(*utilityPid);
    145            } else {
    146              promise->MaybeReject(NS_ERROR_NOT_AVAILABLE);
    147            }
    148          },
    149          [promise](LaunchError aError) {
    150            MOZ_ASSERT_UNREACHABLE(
    151                "UtilityProcessTest; failure to get Utility process");
    152            promise->MaybeReject(NS_ERROR_FAILURE);
    153          });
    154 
    155  promise.forget(aOutPromise);
    156  return NS_OK;
    157 }
    158 
    159 NS_IMETHODIMP
    160 UtilityProcessTest::NoteIntentionalCrash(uint32_t aPid) {
    161  mozilla::NoteIntentionalCrash("utility", aPid);
    162  return NS_OK;
    163 }
    164 
    165 NS_IMETHODIMP
    166 UtilityProcessTest::UntilChildProcessDead(
    167    uint32_t pid, JSContext* cx, ::mozilla::dom::Promise** aOutPromise) {
    168  NS_ENSURE_ARG(aOutPromise);
    169  *aOutPromise = nullptr;
    170 
    171 #  ifdef XP_WIN
    172  if (pid == 0) {
    173    return NS_ERROR_INVALID_ARG;
    174  }
    175 
    176  nsIGlobalObject* global = xpc::CurrentNativeGlobal(cx);
    177  if (NS_WARN_IF(!global)) {
    178    return NS_ERROR_FAILURE;
    179  }
    180 
    181  ErrorResult erv;
    182  RefPtr<dom::Promise> promise = dom::Promise::Create(global, erv);
    183  if (NS_WARN_IF(erv.Failed())) {
    184    return erv.StealNSResult();
    185  }
    186 
    187  // Get a fresh handle to the child process with the specified PID.
    188  mozilla::UniqueFileHandle handle;
    189  {
    190    bool failed = false;
    191    GeckoChildProcessHost::GetAll([&](GeckoChildProcessHost* aProc) {
    192      if (handle || failed) {
    193        return;
    194      }
    195      if (aProc->GetChildProcessId() != pid) {
    196        return;
    197      }
    198 
    199      HANDLE handle_ = nullptr;
    200      if (!::DuplicateHandle(
    201              ::GetCurrentProcess(), aProc->GetChildProcessHandle(),
    202              ::GetCurrentProcess(), &handle_, SYNCHRONIZE, FALSE, 0)) {
    203        failed = true;
    204      } else {
    205        handle.reset(handle_);
    206      }
    207    });
    208 
    209    if (failed || !handle) {
    210      return NS_ERROR_FAILURE;
    211    }
    212  }
    213 
    214  // Create and attach the resolver for the promise, giving the handle over to
    215  // it.
    216  WinHandlePromiseImpl::Create(std::move(handle), promise);
    217 
    218  promise.forget(aOutPromise);
    219 
    220  return NS_OK;
    221 #  else  // !defined(XP_WIN)
    222  return NS_ERROR_NOT_IMPLEMENTED;
    223 #  endif
    224 }
    225 
    226 NS_IMETHODIMP
    227 UtilityProcessTest::StopProcess(const char* aActorName) {
    228  using namespace mozilla::dom;
    229 
    230  SandboxingKind sbKind;
    231  if (aActorName) {
    232    const nsDependentCString actorStringName(aActorName);
    233    UtilityActorName actorName = UtilityActorNameFromString(actorStringName);
    234    sbKind = FindUtilityProcessWithActor(Some(actorName));
    235  } else {
    236    sbKind = FindUtilityProcessWithActor(Nothing());
    237  }
    238 
    239  if (sbKind == SandboxingKind::COUNT) {
    240    MOZ_ASSERT_UNREACHABLE(
    241        "Attempted to stop process for actor when no "
    242        "such process exists");
    243    return NS_ERROR_FAILURE;
    244  }
    245 
    246  RefPtr<UtilityProcessManager> utilityProc =
    247      UtilityProcessManager::GetSingleton();
    248  MOZ_ASSERT(utilityProc, "No UtilityprocessManager?");
    249 
    250  utilityProc->CleanShutdown(sbKind);
    251  Maybe<int32_t> utilityPid = utilityProc->ProcessPid(sbKind);
    252  MOZ_RELEASE_ASSERT(utilityPid.isNothing(),
    253                     "Should not have a utility process PID anymore");
    254 
    255  return NS_OK;
    256 }
    257 
    258 NS_IMETHODIMP
    259 UtilityProcessTest::TestTelemetryProbes() {
    260  RefPtr<UtilityProcessManager> utilityProc =
    261      UtilityProcessManager::GetSingleton();
    262  MOZ_ASSERT(utilityProc, "No UtilityprocessManager?");
    263 
    264  for (RefPtr<UtilityProcessParent>& parent :
    265       utilityProc->GetAllProcessesProcessParent()) {
    266    (void)parent->SendTestTelemetryProbes();
    267  }
    268 
    269  return NS_OK;
    270 }
    271 
    272 NS_IMPL_ISUPPORTS(UtilityProcessTest, nsIUtilityProcessTest)
    273 
    274 }  // namespace mozilla::ipc
    275 #endif  // defined(ENABLE_TESTS)