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