tor-browser

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

ProtocolUtils.cpp (30282B)


      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 "base/process_util.h"
      8 #include "base/task.h"
      9 
     10 #ifdef XP_UNIX
     11 #  include <errno.h>
     12 #endif
     13 
     14 #include "mozilla/IntegerPrintfMacros.h"
     15 
     16 #include "mozilla/ipc/ProtocolMessageUtils.h"
     17 #include "mozilla/ipc/ProtocolUtils.h"
     18 
     19 #include "mozilla/ipc/MessageChannel.h"
     20 #include "mozilla/StaticMutex.h"
     21 #if defined(DEBUG) || defined(FUZZING)
     22 #  include "mozilla/Tokenizer.h"
     23 #endif
     24 #include "nsPrintfCString.h"
     25 #include "nsReadableUtils.h"
     26 #include "prtime.h"
     27 
     28 #if defined(MOZ_SANDBOX) && defined(XP_WIN)
     29 #  include "mozilla/sandboxTarget.h"
     30 #endif
     31 
     32 #if defined(XP_WIN)
     33 #  include "aclapi.h"
     34 #  include "sddl.h"
     35 #endif
     36 
     37 #ifdef FUZZING_SNAPSHOT
     38 #  include "mozilla/fuzzing/IPCFuzzController.h"
     39 #endif
     40 
     41 using namespace IPC;
     42 
     43 using base::GetCurrentProcId;
     44 using base::ProcessHandle;
     45 using base::ProcessId;
     46 
     47 namespace mozilla {
     48 namespace ipc {
     49 
     50 /* static */
     51 EndpointProcInfo EndpointProcInfo::Current() {
     52  return EndpointProcInfo{.mPid = GetCurrentProcId(),
     53                          .mChildID = XRE_GetChildID()};
     54 }
     55 
     56 /* static */
     57 IPCResult IPCResult::FailImpl(NotNull<IProtocol*> actor, const char* where,
     58                              const char* why) {
     59  // Calls top-level protocol to handle the error.
     60  nsPrintfCString errorMsg("%s %s\n", where, why);
     61  actor->GetIPCChannel()->Listener()->ProcessingError(
     62      HasResultCodes::MsgProcessingError, errorMsg.get());
     63 
     64 #if defined(DEBUG) && !defined(FUZZING)
     65  // We do not expect IPC_FAIL to ever happen in normal operations. If this
     66  // happens in DEBUG, we most likely see some behavior during a test we should
     67  // really investigate.
     68  nsPrintfCString crashMsg(
     69      "Use IPC_FAIL only in an "
     70      "unrecoverable, unexpected state: %s",
     71      errorMsg.get());
     72  // We already leak the same information potentially on child process failures
     73  // even in release, and here we are only in DEBUG.
     74  MOZ_CRASH_UNSAFE(crashMsg.get());
     75 #else
     76  return IPCResult(false);
     77 #endif
     78 }
     79 
     80 /* static */
     81 IPCResult IPCResult::FailForTesting(NotNull<IProtocol*> actor,
     82                                    const char* where, const char* why) {
     83  return IPCResult(false);
     84 }
     85 
     86 void AnnotateSystemError() {
     87  uint32_t error = 0;
     88 #if defined(XP_WIN)
     89  error = ::GetLastError();
     90 #else
     91  error = errno;
     92 #endif
     93  if (error) {
     94    CrashReporter::RecordAnnotationU32(
     95        CrashReporter::Annotation::IPCSystemError, error);
     96  }
     97 }
     98 
     99 #if defined(XP_MACOSX)
    100 void AnnotateCrashReportWithErrno(CrashReporter::Annotation tag, int error) {
    101  CrashReporter::RecordAnnotationU32(tag, static_cast<uint32_t>(error));
    102 }
    103 #endif  // defined(XP_MACOSX)
    104 
    105 #if defined(DEBUG) || defined(FUZZING)
    106 // If aTopLevelProtocol matches any token in aFilter, return true.
    107 //
    108 // aTopLevelProtocol is a protocol name, without the "Parent" / "Child" suffix.
    109 // aSide indicates whether we're logging parent-side or child-side activity.
    110 //
    111 // aFilter is a list of protocol names separated by commas and/or
    112 // spaces. These may include the "Child" / "Parent" suffix, or omit
    113 // the suffix to log activity on both sides.
    114 //
    115 // This overload is for testability; application code should use the single-
    116 // argument version (defined in the ProtocolUtils.h) which takes the filter from
    117 // the environment.
    118 bool LoggingEnabledFor(const char* aTopLevelProtocol, Side aSide,
    119                       const char* aFilter) {
    120  if (!aFilter) {
    121    return false;
    122  }
    123  if (strcmp(aFilter, "1") == 0) {
    124    return true;
    125  }
    126 
    127  const char kDelimiters[] = ", ";
    128  Tokenizer tokens(aFilter, kDelimiters);
    129  Tokenizer::Token t;
    130  while (tokens.Next(t)) {
    131    if (t.Type() == Tokenizer::TOKEN_WORD) {
    132      auto filter = t.AsString();
    133 
    134      // Since aTopLevelProtocol never includes the "Parent" / "Child" suffix,
    135      // this will only occur when filter doesn't include it either, meaning
    136      // that we should log activity on both sides.
    137      if (filter == aTopLevelProtocol) {
    138        return true;
    139      }
    140 
    141      if (aSide == ParentSide &&
    142          StringEndsWith(filter, nsDependentCString("Parent")) &&
    143          Substring(filter, 0, filter.Length() - 6) == aTopLevelProtocol) {
    144        return true;
    145      }
    146 
    147      if (aSide == ChildSide &&
    148          StringEndsWith(filter, nsDependentCString("Child")) &&
    149          Substring(filter, 0, filter.Length() - 5) == aTopLevelProtocol) {
    150        return true;
    151      }
    152    }
    153  }
    154 
    155  return false;
    156 }
    157 #endif  // defined(DEBUG) || defined(FUZZING)
    158 
    159 void LogMessageForProtocol(const char* aTopLevelProtocol,
    160                           base::ProcessId aOtherPid,
    161                           const char* aContextDescription, uint32_t aMessageId,
    162                           MessageDirection aDirection) {
    163  nsPrintfCString logMessage(
    164      "[time: %" PRId64 "][%" PRIPID "%s%" PRIPID "] [%s] %s %s\n", PR_Now(),
    165      base::GetCurrentProcId(),
    166      aDirection == MessageDirection::eReceiving ? "<-" : "->", aOtherPid,
    167      aTopLevelProtocol, aContextDescription,
    168      StringFromIPCMessageType(aMessageId));
    169 #ifdef ANDROID
    170  __android_log_write(ANDROID_LOG_INFO, "GeckoIPC", logMessage.get());
    171 #endif
    172  fputs(logMessage.get(), stderr);
    173 }
    174 
    175 void ProtocolErrorBreakpoint(const char* aMsg) {
    176  // Bugs that generate these error messages can be tough to
    177  // reproduce.  Log always in the hope that someone finds the error
    178  // message.
    179  printf_stderr("IPDL protocol error: %s\n", aMsg);
    180 }
    181 
    182 void PickleFatalError(const char* aMsg, IProtocol* aActor) {
    183  if (aActor) {
    184    aActor->FatalError(aMsg);
    185  } else {
    186    FatalError(aMsg, false);
    187  }
    188 }
    189 
    190 void FatalError(const char* aMsg, bool aIsParent) {
    191 #ifndef FUZZING
    192  ProtocolErrorBreakpoint(aMsg);
    193 #endif
    194 
    195  nsAutoCString formattedMessage("IPDL error: \"");
    196  formattedMessage.AppendASCII(aMsg);
    197  if (aIsParent) {
    198    // We're going to crash the parent process because at this time
    199    // there's no other really nice way of getting a minidump out of
    200    // this process if we're off the main thread.
    201    formattedMessage.AppendLiteral("\". Intentionally crashing.");
    202    NS_ERROR(formattedMessage.get());
    203    CrashReporter::RecordAnnotationCString(
    204        CrashReporter::Annotation::IPCFatalErrorMsg, aMsg);
    205    AnnotateSystemError();
    206 #ifndef FUZZING
    207    MOZ_CRASH("IPC FatalError in the parent process!");
    208 #endif
    209  } else {
    210    formattedMessage.AppendLiteral("\". abort()ing as a result.");
    211 #ifndef FUZZING
    212    MOZ_CRASH_UNSAFE(formattedMessage.get());
    213 #endif
    214  }
    215 }
    216 
    217 void LogicError(const char* aMsg) { MOZ_CRASH_UNSAFE(aMsg); }
    218 
    219 void ActorIdReadError(const char* aActorDescription) {
    220 #ifndef FUZZING
    221  MOZ_CRASH_UNSAFE_PRINTF("Error deserializing id for %s", aActorDescription);
    222 #endif
    223 }
    224 
    225 void BadActorIdError(const char* aActorDescription) {
    226  nsPrintfCString message("bad id for %s", aActorDescription);
    227  ProtocolErrorBreakpoint(message.get());
    228 }
    229 
    230 void ActorLookupError(const char* aActorDescription) {
    231  nsPrintfCString message("could not lookup id for %s", aActorDescription);
    232  ProtocolErrorBreakpoint(message.get());
    233 }
    234 
    235 void MismatchedActorTypeError(const char* aActorDescription) {
    236  nsPrintfCString message("actor that should be of type %s has different type",
    237                          aActorDescription);
    238  ProtocolErrorBreakpoint(message.get());
    239 }
    240 
    241 void UnionTypeReadError(const char* aUnionName) {
    242  MOZ_CRASH_UNSAFE_PRINTF("error deserializing type of union %s", aUnionName);
    243 }
    244 
    245 void ArrayLengthReadError(const char* aElementName) {
    246  MOZ_CRASH_UNSAFE_PRINTF("error deserializing length of %s[]", aElementName);
    247 }
    248 
    249 void SentinelReadError(const char* aClassName) {
    250  MOZ_CRASH_UNSAFE_PRINTF("incorrect sentinel when reading %s", aClassName);
    251 }
    252 
    253 ActorLifecycleProxy::ActorLifecycleProxy(IProtocol* aActor) : mActor(aActor) {
    254  MOZ_ASSERT(mActor);
    255  MOZ_ASSERT(mActor->CanSend(),
    256             "Cannot create LifecycleProxy for non-connected actor!");
    257 
    258  // Record that we've taken our first reference to our actor.
    259  mActor->ActorAlloc();
    260 }
    261 
    262 WeakActorLifecycleProxy* ActorLifecycleProxy::GetWeakProxy() {
    263  if (!mWeakProxy) {
    264    mWeakProxy = new WeakActorLifecycleProxy(this);
    265  }
    266  return mWeakProxy;
    267 }
    268 
    269 ActorLifecycleProxy::~ActorLifecycleProxy() {
    270  if (mWeakProxy) {
    271    mWeakProxy->mProxy = nullptr;
    272    mWeakProxy = nullptr;
    273  }
    274 
    275  // When the LifecycleProxy's lifetime has come to an end, it means that the
    276  // actor should have its `Dealloc` method called on it. In a well-behaved
    277  // actor, this will release the IPC-held reference to the actor.
    278  //
    279  // If the actor has already died before the `LifecycleProxy`, the `IProtocol`
    280  // destructor below will clear our reference to it, preventing us from
    281  // performing a use-after-free here.
    282  if (!mActor) {
    283    return;
    284  }
    285 
    286  // Clear our actor's state back to inactive, and then invoke ActorDealloc.
    287  MOZ_ASSERT(mActor->mLinkStatus == LinkStatus::Destroyed,
    288             "Deallocating non-destroyed actor!");
    289  mActor->mLifecycleProxy = nullptr;
    290  mActor->mLinkStatus = LinkStatus::Inactive;
    291  mActor->ActorDealloc();
    292  mActor = nullptr;
    293 }
    294 
    295 WeakActorLifecycleProxy::WeakActorLifecycleProxy(ActorLifecycleProxy* aProxy)
    296    : mProxy(aProxy), mActorEventTarget(GetCurrentSerialEventTarget()) {}
    297 
    298 WeakActorLifecycleProxy::~WeakActorLifecycleProxy() {
    299  MOZ_DIAGNOSTIC_ASSERT(!mProxy, "Destroyed before mProxy was cleared?");
    300 }
    301 
    302 IProtocol* WeakActorLifecycleProxy::Get() const {
    303  MOZ_DIAGNOSTIC_ASSERT(mActorEventTarget->IsOnCurrentThread());
    304  return mProxy ? mProxy->Get() : nullptr;
    305 }
    306 
    307 WeakActorLifecycleProxy* IProtocol::GetWeakLifecycleProxy() {
    308  return mLifecycleProxy ? mLifecycleProxy->GetWeakProxy() : nullptr;
    309 }
    310 
    311 IProtocol::~IProtocol() {
    312  // If the actor still has a lifecycle proxy when it is being torn down, it
    313  // means that IPC was not given control over the lifecycle of the actor
    314  // correctly. Usually this means that the actor was destroyed while IPC is
    315  // calling a message handler for it, and the actor incorrectly frees itself
    316  // during that operation.
    317  //
    318  // As this happens unfortunately frequently, due to many odd protocols in
    319  // Gecko, simply emit a warning and clear the weak backreference from our
    320  // LifecycleProxy back to us.
    321  if (mLifecycleProxy) {
    322    MOZ_ASSERT(mLinkStatus != LinkStatus::Inactive);
    323    NS_WARNING(
    324        nsPrintfCString("Actor destructor for '%s%s' called before IPC "
    325                        "lifecycle complete!\n"
    326                        "References to this actor may unexpectedly dangle!",
    327                        GetProtocolName(), StringFromIPCSide(GetSide()))
    328            .get());
    329 
    330    mLifecycleProxy->mActor = nullptr;
    331    mLifecycleProxy = nullptr;
    332  }
    333 }
    334 
    335 // The following methods either directly forward to the toplevel protocol, or
    336 // almost directly do.
    337 IProtocol* IProtocol::Lookup(ActorId aId) { return mToplevel->Lookup(aId); }
    338 
    339 Shmem IProtocol::CreateSharedMemory(size_t aSize, bool aUnsafe) {
    340  return mToplevel->CreateSharedMemory(aSize, aUnsafe);
    341 }
    342 Shmem::Segment* IProtocol::LookupSharedMemory(Shmem::id_t aId) {
    343  return mToplevel->LookupSharedMemory(aId);
    344 }
    345 bool IProtocol::IsTrackingSharedMemory(const Shmem::Segment* aSegment) {
    346  return mToplevel->IsTrackingSharedMemory(aSegment);
    347 }
    348 bool IProtocol::DestroySharedMemory(Shmem& aShmem) {
    349  return mToplevel->DestroySharedMemory(aShmem);
    350 }
    351 
    352 MessageChannel* IProtocol::GetIPCChannel() {
    353  return mToplevel->GetIPCChannel();
    354 }
    355 const MessageChannel* IProtocol::GetIPCChannel() const {
    356  return mToplevel->GetIPCChannel();
    357 }
    358 
    359 nsISerialEventTarget* IProtocol::GetActorEventTarget() {
    360  return GetIPCChannel()->GetWorkerEventTarget();
    361 }
    362 
    363 void IProtocol::FatalError(const char* const aErrorMsg) {
    364  HandleFatalError(aErrorMsg);
    365 }
    366 
    367 void IProtocol::HandleFatalError(const char* aErrorMsg) {
    368  if (IProtocol* manager = Manager()) {
    369    manager->HandleFatalError(aErrorMsg);
    370    return;
    371  }
    372 
    373  mozilla::ipc::FatalError(aErrorMsg, mSide == ParentSide);
    374  if (CanSend()) {
    375    GetIPCChannel()->InduceConnectionError();
    376  }
    377 }
    378 
    379 bool IProtocol::AllocShmem(size_t aSize, Shmem* aOutMem) {
    380  if (!CanSend()) {
    381    NS_WARNING(
    382        "Shmem not allocated.  Cannot communicate with the other actor.");
    383    return false;
    384  }
    385 
    386  *aOutMem = CreateSharedMemory(aSize, false);
    387  return aOutMem->IsReadable();
    388 }
    389 
    390 bool IProtocol::AllocUnsafeShmem(size_t aSize, Shmem* aOutMem) {
    391  if (!CanSend()) {
    392    NS_WARNING(
    393        "Shmem not allocated.  Cannot communicate with the other actor.");
    394    return false;
    395  }
    396 
    397  *aOutMem = CreateSharedMemory(aSize, true);
    398  return aOutMem->IsReadable();
    399 }
    400 
    401 bool IProtocol::DeallocShmem(Shmem& aMem) {
    402  bool ok = DestroySharedMemory(aMem);
    403 #ifdef DEBUG
    404  if (!ok) {
    405    if (mSide == ChildSide) {
    406      FatalError("bad Shmem");
    407    } else {
    408      NS_WARNING("bad Shmem");
    409    }
    410    return false;
    411  }
    412 #endif  // DEBUG
    413  aMem.forget();
    414  return ok;
    415 }
    416 
    417 void IProtocol::SetManager(IRefCountedProtocol* aManager) {
    418  MOZ_RELEASE_ASSERT(!mManager || mManager == aManager);
    419  mManager = aManager;
    420  mToplevel = aManager->mToplevel;
    421 }
    422 
    423 bool IProtocol::SetManagerAndRegister(IRefCountedProtocol* aManager,
    424                                      ActorId aId) {
    425  MOZ_RELEASE_ASSERT(mLinkStatus == LinkStatus::Inactive,
    426                     "Actor must be inactive to SetManagerAndRegister");
    427 
    428  // Set to `false` if the actor is to be torn down after registration.
    429  bool success = true;
    430 
    431  // Set the manager prior to registering so registering properly inherits
    432  // the manager's event target.
    433  SetManager(aManager);
    434 
    435  mId = aId == kNullActorId ? mToplevel->NextId() : aId;
    436  while (mToplevel->mActorMap.Contains(mId)) {
    437    // The ID already existing is an error case, but we want to proceed with
    438    // registration so that we can tear down the actor cleanly - generate a new
    439    // ID for that case.
    440    NS_WARNING("Actor already exists with the selected ID!");
    441    mId = mToplevel->NextId();
    442    success = false;
    443  }
    444 
    445  RefPtr<ActorLifecycleProxy> proxy = ActorConnected();
    446  mToplevel->mActorMap.InsertOrUpdate(mId, proxy);
    447  MOZ_ASSERT(proxy->Get() == this);
    448 
    449  UntypedManagedContainer* container =
    450      aManager->GetManagedActors(GetProtocolId());
    451  if (container) {
    452    container->Insert(this);
    453  } else {
    454    NS_WARNING("Manager does not manage actors with this ProtocolId");
    455    success = false;
    456  }
    457 
    458  // If our manager is already dying, mark ourselves as doomed as well.
    459  if (aManager && aManager->mLinkStatus != LinkStatus::Connected) {
    460    mLinkStatus = LinkStatus::Doomed;
    461    if (aManager->mLinkStatus != LinkStatus::Doomed) {
    462      // Our manager is already fully dead, make sure we call
    463      // `ActorDisconnected`.
    464      success = false;
    465    }
    466  }
    467 
    468  // If setting the manager failed, call `ActorDisconnected` and return false.
    469  if (!success) {
    470    ActorDisconnected(FailedConstructor);
    471    MOZ_ASSERT(mLinkStatus == LinkStatus::Destroyed);
    472    return false;
    473  }
    474  return true;
    475 }
    476 
    477 void IProtocol::UnlinkManager() {
    478  mToplevel = nullptr;
    479  mManager = nullptr;
    480 }
    481 
    482 bool IProtocol::ChannelSend(UniquePtr<IPC::Message> aMsg,
    483                            IPC::Message::seqno_t* aSeqno) {
    484  if (CanSend()) {
    485    // NOTE: This send call failing can only occur during toplevel channel
    486    // teardown. As this is an async call, this isn't reasonable to predict or
    487    // respond to, so just drop the message on the floor silently.
    488    GetIPCChannel()->Send(std::move(aMsg), aSeqno);
    489    return true;
    490  }
    491 
    492  WarnMessageDiscarded(aMsg.get());
    493  return false;
    494 }
    495 
    496 bool IProtocol::ChannelSend(UniquePtr<IPC::Message> aMsg,
    497                            UniquePtr<IPC::Message>* aReply) {
    498  if (CanSend()) {
    499    return GetIPCChannel()->Send(std::move(aMsg), aReply);
    500  }
    501 
    502  WarnMessageDiscarded(aMsg.get());
    503  return false;
    504 }
    505 
    506 #ifdef DEBUG
    507 void IProtocol::WarnMessageDiscarded(IPC::Message* aMsg) {
    508  NS_WARNING(nsPrintfCString("IPC message '%s' discarded: actor cannot send",
    509                             aMsg->name())
    510                 .get());
    511 }
    512 #endif
    513 
    514 uint32_t IProtocol::AllManagedActorsCount() const {
    515  uint32_t total = 0;
    516  for (ProtocolId id : ManagedProtocolIds()) {
    517    total += GetManagedActors(id)->Count();
    518  }
    519  return total;
    520 }
    521 
    522 already_AddRefed<ActorLifecycleProxy> IProtocol::ActorConnected() {
    523  if (mLinkStatus != LinkStatus::Inactive) {
    524    return nullptr;
    525  }
    526 
    527 #ifdef FUZZING_SNAPSHOT
    528  fuzzing::IPCFuzzController::instance().OnActorConnected(this);
    529 #endif
    530 
    531  mLinkStatus = LinkStatus::Connected;
    532 
    533  MOZ_ASSERT(!mLifecycleProxy, "double-connecting live actor");
    534  RefPtr<ActorLifecycleProxy> proxy = new ActorLifecycleProxy(this);
    535  mLifecycleProxy = proxy;
    536  return proxy.forget();
    537 }
    538 
    539 void IProtocol::ActorDisconnected(ActorDestroyReason aWhy) {
    540  MOZ_ASSERT(mLifecycleProxy, "destroying zombie actor");
    541  // If the actor has already been marked as `Destroyed`, there's nothing to do.
    542  if (mLinkStatus != LinkStatus::Connected &&
    543      mLinkStatus != LinkStatus::Doomed) {
    544    return;
    545  }
    546 
    547  // Mark the entire subtree as doomed so that no further messages can be
    548  // sent/recieved, and newly created managed actors are immediately marked as
    549  // doomed on creation.
    550  DoomSubtree();
    551 
    552  // Perform the steps to fully destroy an actor after it has been unregistered
    553  // from its manager.
    554  auto doActorDestroy = [toplevel = mToplevel, ipcChannel = GetIPCChannel()](
    555                            IProtocol* actor, ActorDestroyReason why) {
    556    MOZ_ASSERT(actor->mLinkStatus == LinkStatus::Doomed,
    557               "Actor must be doomed when calling doActorDestroy");
    558    MOZ_ASSERT(actor->AllManagedActorsCount() == 0,
    559               "All managed actors must have been destroyed first");
    560 
    561    // Mark the actor as Destroyed, ensuring we can't re-enter `ActorDestroy`,
    562    // even if an callback spins a nested event loop.
    563    actor->mLinkStatus = LinkStatus::Destroyed;
    564 
    565 #ifdef FUZZING_SNAPSHOT
    566    fuzzing::IPCFuzzController::instance().OnActorDestroyed(actor);
    567 #endif
    568 
    569    ActorId id = actor->mId;
    570    if (IProtocol* manager = actor->Manager()) {
    571      auto entry = toplevel->mActorMap.Lookup(id);
    572      MOZ_DIAGNOSTIC_ASSERT(entry && *entry == actor->GetLifecycleProxy(),
    573                            "ID must be present and reference this actor");
    574      entry.Remove();
    575      if (auto* container = manager->GetManagedActors(actor->GetProtocolId())) {
    576        container->EnsureRemoved(actor);
    577      }
    578    }
    579 
    580    actor->RejectPendingResponses(ResponseRejectReason::ActorDestroyed);
    581    actor->ActorDestroy(why);
    582  };
    583 
    584  // Hold all ActorLifecycleProxy instances for managed actors until we return.
    585  nsTArray<RefPtr<ActorLifecycleProxy>> proxyHolder;
    586  proxyHolder.AppendElement(GetLifecycleProxy());
    587 
    588  // Invoke `ActorDestroy` for all managed actors in the subtree. These are
    589  // handled one at a time, so that new actors which are potentially registered
    590  // during `ActorDestroy` callbacks are not missed.
    591  ActorDestroyReason subtreeWhy = aWhy;
    592  if (aWhy == Deletion || aWhy == FailedConstructor) {
    593    subtreeWhy = AncestorDeletion;
    594  }
    595  while (IProtocol* actor = PeekManagedActor()) {
    596    // If the selected actor manages other actors, destroy those first.
    597    while (IProtocol* inner = actor->PeekManagedActor()) {
    598      actor = inner;
    599    }
    600 
    601    proxyHolder.AppendElement(actor->GetLifecycleProxy());
    602    doActorDestroy(actor, subtreeWhy);
    603  }
    604 
    605  // Destroy ourselves if we were not not otherwise destroyed while destroying
    606  // managed actors.
    607  if (mLinkStatus == LinkStatus::Doomed) {
    608    doActorDestroy(this, aWhy);
    609  }
    610 }
    611 
    612 void IProtocol::DoomSubtree() {
    613  MOZ_ASSERT(
    614      mLinkStatus == LinkStatus::Connected || mLinkStatus == LinkStatus::Doomed,
    615      "Invalid link status for SetDoomed");
    616  for (ProtocolId id : ManagedProtocolIds()) {
    617    for (IProtocol* actor : *GetManagedActors(id)) {
    618      actor->DoomSubtree();
    619    }
    620  }
    621  mLinkStatus = LinkStatus::Doomed;
    622 }
    623 
    624 IProtocol* IProtocol::PeekManagedActor() const {
    625  for (ProtocolId id : ManagedProtocolIds()) {
    626    const UntypedManagedContainer& container = *GetManagedActors(id);
    627    if (!container.IsEmpty()) {
    628      // Return the last element first, to reduce the copying required when
    629      // removing it.
    630      return *(container.end() - 1);
    631    }
    632  }
    633  return nullptr;
    634 }
    635 
    636 IToplevelProtocol::IToplevelProtocol(const char* aName, ProtocolId aProtoId,
    637                                     Side aSide)
    638    : IRefCountedProtocol(aProtoId, aSide),
    639      mOtherPid(base::kInvalidProcessId),
    640      mLastLocalId(kNullActorId),
    641      mChannel(aName, this) {
    642  mToplevel = this;
    643 }
    644 
    645 void IToplevelProtocol::SetOtherEndpointProcInfo(
    646    EndpointProcInfo aOtherProcInfo) {
    647  mOtherPid = aOtherProcInfo.mPid;
    648  mOtherChildID = aOtherProcInfo.mChildID;
    649 }
    650 
    651 bool IToplevelProtocol::Open(ScopedPort aPort, const nsID& aMessageChannelId,
    652                             EndpointProcInfo aOtherProcInfo,
    653                             nsISerialEventTarget* aEventTarget) {
    654  SetOtherEndpointProcInfo(aOtherProcInfo);
    655  return GetIPCChannel()->Open(std::move(aPort), mSide, aMessageChannelId,
    656                               aEventTarget);
    657 }
    658 
    659 bool IToplevelProtocol::Open(IToplevelProtocol* aTarget,
    660                             nsISerialEventTarget* aEventTarget,
    661                             mozilla::ipc::Side aSide) {
    662  SetOtherEndpointProcInfo(EndpointProcInfo::Current());
    663  aTarget->SetOtherEndpointProcInfo(EndpointProcInfo::Current());
    664  return GetIPCChannel()->Open(aTarget->GetIPCChannel(), aEventTarget, aSide);
    665 }
    666 
    667 bool IToplevelProtocol::OpenOnSameThread(IToplevelProtocol* aTarget,
    668                                         Side aSide) {
    669  SetOtherEndpointProcInfo(EndpointProcInfo::Current());
    670  aTarget->SetOtherEndpointProcInfo(EndpointProcInfo::Current());
    671  return GetIPCChannel()->OpenOnSameThread(aTarget->GetIPCChannel(), aSide);
    672 }
    673 
    674 void IToplevelProtocol::NotifyImpendingShutdown() {
    675  if (CanRecv()) {
    676    GetIPCChannel()->NotifyImpendingShutdown();
    677  }
    678 }
    679 
    680 void IToplevelProtocol::Close() { GetIPCChannel()->Close(); }
    681 
    682 void IToplevelProtocol::SetReplyTimeoutMs(int32_t aTimeoutMs) {
    683  GetIPCChannel()->SetReplyTimeoutMs(aTimeoutMs);
    684 }
    685 
    686 bool IToplevelProtocol::IsOnCxxStack() const {
    687  return GetIPCChannel()->IsOnCxxStack();
    688 }
    689 
    690 int64_t IToplevelProtocol::NextId() {
    691  // Generate the next ID to use for a shared memory or protocol. Parent and
    692  // Child sides of the protocol use different pools.
    693  MOZ_RELEASE_ASSERT(mozilla::Abs(mLastLocalId) < MSG_ROUTING_CONTROL - 1,
    694                     "actor id overflow");
    695  return (GetSide() == ChildSide) ? --mLastLocalId : ++mLastLocalId;
    696 }
    697 
    698 IProtocol* IToplevelProtocol::Lookup(ActorId aId) {
    699  if (auto entry = mActorMap.Lookup(aId)) {
    700    return entry.Data()->Get();
    701  }
    702  return nullptr;
    703 }
    704 
    705 Shmem IToplevelProtocol::CreateSharedMemory(size_t aSize, bool aUnsafe) {
    706  auto shmemBuilder = Shmem::Builder(aSize);
    707  if (!shmemBuilder) {
    708    return {};
    709  }
    710  auto [createdMessage, shmem] =
    711      shmemBuilder.Build(NextId(), aUnsafe, MSG_ROUTING_CONTROL);
    712  if (!createdMessage) {
    713    return {};
    714  }
    715  (void)GetIPCChannel()->Send(std::move(createdMessage));
    716 
    717  MOZ_ASSERT(!mShmemMap.Contains(shmem.Id()),
    718             "Don't insert with an existing ID");
    719  mShmemMap.InsertOrUpdate(shmem.Id(), shmem.GetSegment());
    720  return shmem;
    721 }
    722 
    723 Shmem::Segment* IToplevelProtocol::LookupSharedMemory(Shmem::id_t aId) {
    724  auto entry = mShmemMap.Lookup(aId);
    725  return entry ? entry.Data().get() : nullptr;
    726 }
    727 
    728 bool IToplevelProtocol::IsTrackingSharedMemory(const Shmem::Segment* segment) {
    729  for (const auto& shmem : mShmemMap.Values()) {
    730    if (segment == shmem) {
    731      return true;
    732    }
    733  }
    734  return false;
    735 }
    736 
    737 bool IToplevelProtocol::DestroySharedMemory(Shmem& shmem) {
    738  Shmem::id_t aId = shmem.Id();
    739  if (!LookupSharedMemory(aId)) {
    740    return false;
    741  }
    742 
    743  UniquePtr<Message> descriptor = shmem.MkDestroyedMessage(MSG_ROUTING_CONTROL);
    744 
    745  MOZ_ASSERT(mShmemMap.Contains(aId),
    746             "Attempting to remove an ID not in the shmem map");
    747  mShmemMap.Remove(aId);
    748 
    749  MessageChannel* channel = GetIPCChannel();
    750  if (!channel->CanSend()) {
    751    return true;
    752  }
    753 
    754  return descriptor && channel->Send(std::move(descriptor));
    755 }
    756 
    757 void IToplevelProtocol::DeallocShmems() { mShmemMap.Clear(); }
    758 
    759 bool IToplevelProtocol::ShmemCreated(const Message& aMsg) {
    760  Shmem::id_t id;
    761  RefPtr<Shmem::Segment> segment(Shmem::OpenExisting(aMsg, &id, true));
    762  if (!segment) {
    763    return false;
    764  }
    765  MOZ_ASSERT(!mShmemMap.Contains(id), "Don't insert with an existing ID");
    766  mShmemMap.InsertOrUpdate(id, std::move(segment));
    767  return true;
    768 }
    769 
    770 bool IToplevelProtocol::ShmemDestroyed(const Message& aMsg) {
    771  Shmem::id_t id;
    772  MessageReader reader(aMsg);
    773  if (!IPC::ReadParam(&reader, &id)) {
    774    return false;
    775  }
    776  reader.EndRead();
    777 
    778  mShmemMap.Remove(id);
    779  return true;
    780 }
    781 
    782 IPDLResolverInner::IPDLResolverInner(UniquePtr<IPC::Message> aReply,
    783                                     IProtocol* aActor)
    784    : mReply(std::move(aReply)),
    785      mWeakProxy(aActor->GetLifecycleProxy()->GetWeakProxy()) {}
    786 
    787 void IPDLResolverInner::ResolveOrReject(
    788    bool aResolve, FunctionRef<void(IPC::Message*, IProtocol*)> aWrite) {
    789  MOZ_ASSERT(mWeakProxy);
    790  MOZ_ASSERT(mWeakProxy->ActorEventTarget()->IsOnCurrentThread());
    791  MOZ_ASSERT(mReply);
    792 
    793  UniquePtr<IPC::Message> reply = std::move(mReply);
    794 
    795  IProtocol* actor = mWeakProxy->Get();
    796  if (!actor) {
    797    NS_WARNING(nsPrintfCString("Not resolving response '%s': actor is dead",
    798                               reply->name())
    799                   .get());
    800    return;
    801  }
    802 
    803  IPC::MessageWriter writer(*reply, actor);
    804  WriteParam(&writer, aResolve);
    805  aWrite(reply.get(), actor);
    806 
    807  actor->ChannelSend(std::move(reply));
    808 }
    809 
    810 void IPDLResolverInner::Destroy() {
    811  if (mReply) {
    812    NS_PROXY_DELETE_TO_EVENT_TARGET(IPDLResolverInner,
    813                                    mWeakProxy->ActorEventTarget());
    814  } else {
    815    // If we've already been consumed, just delete without proxying. This avoids
    816    // leaking the resolver if the actor's thread is already dead.
    817    delete this;
    818  }
    819 }
    820 
    821 IPDLResolverInner::~IPDLResolverInner() {
    822  if (mReply) {
    823    NS_WARNING(
    824        nsPrintfCString(
    825            "Rejecting reply '%s': resolver dropped without being called",
    826            mReply->name())
    827            .get());
    828    ResolveOrReject(false, [](IPC::Message* aMessage, IProtocol* aActor) {
    829      IPC::MessageWriter writer(*aMessage, aActor);
    830      ResponseRejectReason reason = ResponseRejectReason::ResolverDestroyed;
    831      WriteParam(&writer, reason);
    832    });
    833  }
    834 }
    835 
    836 bool IPDLAsyncReturnsCallbacks::EntryKey::operator==(
    837    const EntryKey& aOther) const {
    838  return mSeqno == aOther.mSeqno && mType == aOther.mType;
    839 }
    840 
    841 bool IPDLAsyncReturnsCallbacks::EntryKey::operator<(
    842    const EntryKey& aOther) const {
    843  return mSeqno < aOther.mSeqno ||
    844         (mSeqno == aOther.mSeqno && mType < aOther.mType);
    845 }
    846 
    847 void IPDLAsyncReturnsCallbacks::AddCallback(IPC::Message::seqno_t aSeqno,
    848                                            msgid_t aType, Callback aResolve,
    849                                            RejectCallback aReject) {
    850  Entry entry{{aSeqno, aType}, std::move(aResolve), std::move(aReject)};
    851  MOZ_ASSERT(!mMap.ContainsSorted(entry));
    852  mMap.InsertElementSorted(std::move(entry));
    853 }
    854 
    855 auto IPDLAsyncReturnsCallbacks::GotReply(IProtocol* aActor,
    856                                         const IPC::Message& aMessage)
    857    -> Result {
    858  // Check if we have an entry for the given seqno and message type.
    859  EntryKey key{aMessage.seqno(), aMessage.type()};
    860  size_t index = mMap.BinaryIndexOf(key);
    861  if (index == nsTArray<Entry>::NoIndex) {
    862    return MsgProcessingError;
    863  }
    864 
    865  // Move the callbacks out of the map, as we will now be handling it.
    866  Entry entry = std::move(mMap[index]);
    867  mMap.RemoveElementAt(index);
    868  MOZ_ASSERT(entry == key);
    869 
    870  // Deserialize the message which was serialized by IPDLResolverInner.
    871  IPC::MessageReader reader{aMessage, aActor};
    872  bool resolve = false;
    873  if (!IPC::ReadParam(&reader, &resolve)) {
    874    entry.mReject(ResponseRejectReason::HandlerRejected);
    875    return MsgValueError;
    876  }
    877 
    878  if (resolve) {
    879    // Hand off resolve-case deserialization & success to the callback.
    880    Result rv = entry.mResolve(&reader);
    881    if (rv != MsgProcessed) {
    882      // If deserialization failed, we need to call the reject handler.
    883      entry.mReject(ResponseRejectReason::HandlerRejected);
    884    }
    885    return rv;
    886  }
    887 
    888  ResponseRejectReason reason;
    889  if (!IPC::ReadParam(&reader, &reason)) {
    890    entry.mReject(ResponseRejectReason::HandlerRejected);
    891    return MsgValueError;
    892  }
    893  reader.EndRead();
    894 
    895  entry.mReject(reason);
    896  return MsgProcessed;
    897 }
    898 
    899 void IPDLAsyncReturnsCallbacks::RejectPendingResponses(
    900    ResponseRejectReason aReason) {
    901  nsTArray<Entry> pending = std::move(mMap);
    902  for (auto& entry : pending) {
    903    entry.mReject(aReason);
    904  }
    905 }
    906 
    907 }  // namespace ipc
    908 }  // namespace mozilla
    909 
    910 namespace IPC {
    911 
    912 void ParamTraits<mozilla::ipc::IProtocol*>::Write(MessageWriter* aWriter,
    913                                                  const paramType& aParam) {
    914  MOZ_RELEASE_ASSERT(aWriter->GetActor(),
    915                     "Cannot serialize managed actors without an actor");
    916 
    917  mozilla::ipc::ActorId id = mozilla::ipc::IProtocol::kNullActorId;
    918  if (aParam) {
    919    id = aParam->Id();
    920    MOZ_RELEASE_ASSERT(id != mozilla::ipc::IProtocol::kNullActorId,
    921                       "Actor has ID of 0?");
    922    MOZ_RELEASE_ASSERT(aParam->CanSend(),
    923                       "Actor must still be open when sending");
    924    MOZ_RELEASE_ASSERT(
    925        aWriter->GetActor()->GetIPCChannel() == aParam->GetIPCChannel(),
    926        "Actor must be from the same tree as the actor it is being sent over");
    927  }
    928 
    929  IPC::WriteParam(aWriter, id);
    930 }
    931 
    932 bool ParamTraits<mozilla::ipc::IProtocol*>::Read(MessageReader* aReader,
    933                                                 paramType* aResult) {
    934  MOZ_RELEASE_ASSERT(aReader->GetActor(),
    935                     "Cannot serialize managed actors without an actor");
    936 
    937  mozilla::ipc::ActorId id;
    938  if (!IPC::ReadParam(aReader, &id)) {
    939    return false;
    940  }
    941 
    942  if (id == mozilla::ipc::IProtocol::kNullActorId) {
    943    *aResult = nullptr;
    944    return true;
    945  }
    946 
    947  *aResult = aReader->GetActor()->Lookup(id);
    948  return *aResult != nullptr;
    949 }
    950 
    951 }  // namespace IPC