/* -*- Mode: C++; tab-width: 8; indent-tabs-mode: nil; c-basic-offset: 2 -*- */ /* vim: set ts=8 sts=2 et sw=2 tw=80: */ /* This Source Code Form is subject to the terms of the Mozilla Public * License, v. 2.0. If a copy of the MPL was not distributed with this
* file, You can obtain one at http://mozilla.org/MPL/2.0/. */
#ifdefined(DEBUG) && !defined(FUZZING) // We do not expect IPC_FAIL to ever happen in normal operations. If this // happens in DEBUG, we most likely see some behavior during a test we should // really investigate.
nsPrintfCString crashMsg( "Use IPC_FAIL only in an " "unrecoverable, unexpected state: %s",
errorMsg.get()); // We already leak the same information potentially on child process failures // even in release, and here we are only in DEBUG.
MOZ_CRASH_UNSAFE(crashMsg.get()); #else return IPCResult(false); #endif
}
#ifdefined(DEBUG) || defined(FUZZING) // If aTopLevelProtocol matches any token in aFilter, return true. // // aTopLevelProtocol is a protocol name, without the "Parent" / "Child" suffix. // aSide indicates whether we're logging parent-side or child-side activity. // // aFilter is a list of protocol names separated by commas and/or // spaces. These may include the "Child" / "Parent" suffix, or omit // the suffix to log activity on both sides. // // This overload is for testability; application code should use the single- // argument version (defined in the ProtocolUtils.h) which takes the filter from // the environment. bool LoggingEnabledFor(constchar* aTopLevelProtocol, Side aSide, constchar* aFilter) { if (!aFilter) { returnfalse;
} if (strcmp(aFilter, "1") == 0) { returntrue;
}
constchar kDelimiters[] = ", ";
Tokenizer tokens(aFilter, kDelimiters);
Tokenizer::Token t; while (tokens.Next(t)) { if (t.Type() == Tokenizer::TOKEN_WORD) { auto filter = t.AsString();
// Since aTopLevelProtocol never includes the "Parent" / "Child" suffix, // this will only occur when filter doesn't include it either, meaning // that we should log activity on both sides. if (filter == aTopLevelProtocol) { returntrue;
}
void ProtocolErrorBreakpoint(constchar* aMsg) { // Bugs that generate these error messages can be tough to // reproduce. Log always in the hope that someone finds the error // message.
printf_stderr("IPDL protocol error: %s\n", aMsg);
}
nsAutoCString formattedMessage("IPDL error: \"");
formattedMessage.AppendASCII(aMsg); if (aIsParent) { // We're going to crash the parent process because at this time // there's no other really nice way of getting a minidump out of // this process if we're off the main thread.
formattedMessage.AppendLiteral("\". Intentionally crashing.");
NS_ERROR(formattedMessage.get());
CrashReporter::RecordAnnotationCString(
CrashReporter::Annotation::IPCFatalErrorMsg, aMsg);
AnnotateSystemError(); #ifndef FUZZING
MOZ_CRASH("IPC FatalError in the parent process!"); #endif
} else {
formattedMessage.AppendLiteral("\". abort()ing as a result."); #ifndef FUZZING
MOZ_CRASH_UNSAFE(formattedMessage.get()); #endif
}
}
void ActorIdReadError(constchar* aActorDescription) { #ifndef FUZZING
MOZ_CRASH_UNSAFE_PRINTF("Error deserializing id for %s", aActorDescription); #endif
}
void BadActorIdError(constchar* aActorDescription) {
nsPrintfCString message("bad id for %s", aActorDescription);
ProtocolErrorBreakpoint(message.get());
}
void ActorLookupError(constchar* aActorDescription) {
nsPrintfCString message("could not lookup id for %s", aActorDescription);
ProtocolErrorBreakpoint(message.get());
}
void MismatchedActorTypeError(constchar* aActorDescription) {
nsPrintfCString message("actor that should be of type %s has different type",
aActorDescription);
ProtocolErrorBreakpoint(message.get());
}
void UnionTypeReadError(constchar* aUnionName) {
MOZ_CRASH_UNSAFE_PRINTF("error deserializing type of union %s", aUnionName);
}
// When the LifecycleProxy's lifetime has come to an end, it means that the // actor should have its `Dealloc` method called on it. In a well-behaved // actor, this will release the IPC-held reference to the actor. // // If the actor has already died before the `LifecycleProxy`, the `IProtocol` // destructor below will clear our reference to it, preventing us from // performing a use-after-free here. if (!mActor) { return;
}
// Clear our actor's state back to inactive, and then invoke ActorDealloc.
MOZ_ASSERT(mActor->mLinkStatus == LinkStatus::Destroyed, "Deallocating non-destroyed actor!");
mActor->mLifecycleProxy = nullptr;
mActor->mLinkStatus = LinkStatus::Inactive;
mActor->ActorDealloc();
mActor = nullptr;
}
IProtocol::~IProtocol() { // If the actor still has a lifecycle proxy when it is being torn down, it // means that IPC was not given control over the lifecycle of the actor // correctly. Usually this means that the actor was destroyed while IPC is // calling a message handler for it, and the actor incorrectly frees itself // during that operation. // // As this happens unfortunately frequently, due to many odd protocols in // Gecko, simply emit a warning and clear the weak backreference from our // LifecycleProxy back to us. if (mLifecycleProxy) {
MOZ_ASSERT(mLinkStatus != LinkStatus::Inactive);
NS_WARNING(
nsPrintfCString("Actor destructor for '%s%s' called before IPC " "lifecycle complete!\n" "References to this actor may unexpectedly dangle!",
GetProtocolName(), StringFromIPCSide(GetSide()))
.get());
// The following methods either directly forward to the toplevel protocol, or // almost directly do.
IProtocol* IProtocol::Lookup(int32_t aId) { return mToplevel->Lookup(aId); }
mozilla::ipc::FatalError(aErrorMsg, mSide == ParentSide); if (CanSend()) {
GetIPCChannel()->InduceConnectionError();
}
}
bool IProtocol::AllocShmem(size_t aSize, Shmem* aOutMem) { if (!CanSend()) {
NS_WARNING( "Shmem not allocated. Cannot communicate with the other actor."); returnfalse;
}
bool IProtocol::AllocUnsafeShmem(size_t aSize, Shmem* aOutMem) { if (!CanSend()) {
NS_WARNING( "Shmem not allocated. Cannot communicate with the other actor."); returnfalse;
}
bool IProtocol::SetManagerAndRegister(IRefCountedProtocol* aManager,
int32_t aId) {
MOZ_RELEASE_ASSERT(mLinkStatus == LinkStatus::Inactive, "Actor must be inactive to SetManagerAndRegister");
// Set to `false` if the actor is to be torn down after registration. bool success = true;
// Set the manager prior to registering so registering properly inherits // the manager's event target.
SetManager(aManager);
mId = aId == kNullActorId ? mToplevel->NextId() : aId; while (mToplevel->mActorMap.Contains(mId)) { // The ID already existing is an error case, but we want to proceed with // registration so that we can tear down the actor cleanly - generate a new // ID for that case.
NS_WARNING("Actor already exists with the selected ID!");
mId = mToplevel->NextId();
success = false;
}
UntypedManagedContainer* container =
aManager->GetManagedActors(GetProtocolId()); if (container) {
container->Insert(this);
} else {
NS_WARNING("Manager does not manage actors with this ProtocolId");
success = false;
}
// If our manager is already dying, mark ourselves as doomed as well. if (aManager && aManager->mLinkStatus != LinkStatus::Connected) {
mLinkStatus = LinkStatus::Doomed; if (aManager->mLinkStatus != LinkStatus::Doomed) { // Our manager is already fully dead, make sure we call // `ActorDisconnected`.
success = false;
}
}
// If setting the manager failed, call `ActorDisconnected` and return false. if (!success) {
ActorDisconnected(FailedConstructor);
MOZ_ASSERT(mLinkStatus == LinkStatus::Destroyed); returnfalse;
} returntrue;
}
bool IProtocol::ChannelSend(UniquePtr<IPC::Message> aMsg, int32_t* aSeqno) { if (CanSend()) { // NOTE: This send call failing can only occur during toplevel channel // teardown. As this is an async call, this isn't reasonable to predict or // respond to, so just drop the message on the floor silently.
GetIPCChannel()->Send(std::move(aMsg), aSeqno); returntrue;
}
MOZ_ASSERT(!mLifecycleProxy, "double-connecting live actor");
RefPtr<ActorLifecycleProxy> proxy = new ActorLifecycleProxy(this);
mLifecycleProxy = proxy; return proxy.forget();
}
void IProtocol::ActorDisconnected(ActorDestroyReason aWhy) {
MOZ_ASSERT(mLifecycleProxy, "destroying zombie actor"); // If the actor has already been marked as `Destroyed`, there's nothing to do. if (mLinkStatus != LinkStatus::Connected &&
mLinkStatus != LinkStatus::Doomed) { return;
}
// Mark the entire subtree as doomed so that no further messages can be // sent/recieved, and newly created managed actors are immediately marked as // doomed on creation.
DoomSubtree();
// Perform the steps to fully destroy an actor after it has been unregistered // from its manager. auto doActorDestroy = [toplevel = mToplevel, ipcChannel = GetIPCChannel()](
IProtocol* actor, ActorDestroyReason why) {
MOZ_ASSERT(actor->mLinkStatus == LinkStatus::Doomed, "Actor must be doomed when calling doActorDestroy");
MOZ_ASSERT(actor->AllManagedActorsCount() == 0, "All managed actors must have been destroyed first");
// Mark the actor as Destroyed, ensuring we can't re-enter `ActorDestroy`, // even if an callback spins a nested event loop.
actor->mLinkStatus = LinkStatus::Destroyed;
int32_t id = actor->mId; if (IProtocol* manager = actor->Manager()) {
actor->mId = kFreedActorId; auto entry = toplevel->mActorMap.Lookup(id);
MOZ_DIAGNOSTIC_ASSERT(entry && *entry == actor->GetLifecycleProxy(), "ID must be present and reference this actor");
entry.Remove(); if (auto* container = manager->GetManagedActors(actor->GetProtocolId())) {
container->EnsureRemoved(actor);
}
}
// Hold all ActorLifecycleProxy instances for managed actors until we return.
nsTArray<RefPtr<ActorLifecycleProxy>> proxyHolder;
proxyHolder.AppendElement(GetLifecycleProxy());
// Invoke `ActorDestroy` for all managed actors in the subtree. These are // handled one at a time, so that new actors which are potentially registered // during `ActorDestroy` callbacks are not missed.
ActorDestroyReason subtreeWhy = aWhy; if (aWhy == Deletion || aWhy == FailedConstructor) {
subtreeWhy = AncestorDeletion;
} while (IProtocol* actor = PeekManagedActor()) { // If the selected actor manages other actors, destroy those first. while (IProtocol* inner = actor->PeekManagedActor()) {
actor = inner;
}
// Destroy ourselves if we were not not otherwise destroyed while destroying // managed actors. if (mLinkStatus == LinkStatus::Doomed) {
doActorDestroy(this, aWhy);
}
}
void IProtocol::DoomSubtree() {
MOZ_ASSERT(
mLinkStatus == LinkStatus::Connected || mLinkStatus == LinkStatus::Doomed, "Invalid link status for SetDoomed"); for (ProtocolId id : ManagedProtocolIds()) { for (IProtocol* actor : *GetManagedActors(id)) {
actor->DoomSubtree();
}
}
mLinkStatus = LinkStatus::Doomed;
}
IProtocol* IProtocol::PeekManagedActor() const { for (ProtocolId id : ManagedProtocolIds()) { const UntypedManagedContainer& container = *GetManagedActors(id); if (!container.IsEmpty()) { // Return the last element first, to reduce the copying required when // removing it. return *(container.end() - 1);
}
} return nullptr;
}
int32_t IToplevelProtocol::NextId() { // Generate the next ID to use for a shared memory or protocol. Parent and // Child sides of the protocol use different pools.
int32_t tag = 0; if (GetSide() == ParentSide) {
tag |= 1 << 1;
}
// Check any overflow
MOZ_RELEASE_ASSERT(mLastLocalId < (1 << 29));
// Compute the ID to use with the low two bits as our tag, and the remaining // bits as a monotonic. return (++mLastLocalId << 2) | tag;
}
IProtocol* actor = mWeakProxy->Get(); if (!actor) {
NS_WARNING(nsPrintfCString("Not resolving response '%s': actor is dead",
reply->name())
.get()); return;
}
void IPDLResolverInner::Destroy() { if (mReply) {
NS_PROXY_DELETE_TO_EVENT_TARGET(IPDLResolverInner,
mWeakProxy->ActorEventTarget());
} else { // If we've already been consumed, just delete without proxying. This avoids // leaking the resolver if the actor's thread is already dead. deletethis;
}
}
auto IPDLAsyncReturnsCallbacks::GotReply(IProtocol* aActor, const IPC::Message& aMessage)
-> Result { // Check if we have an entry for the given seqno and message type.
EntryKey key{aMessage.seqno(), aMessage.type()};
size_t index = mMap.BinaryIndexOf(key); if (index == nsTArray<Entry>::NoIndex) { return MsgProcessingError;
}
// Move the callbacks out of the map, as we will now be handling it.
Entry entry = std::move(mMap[index]);
mMap.RemoveElementAt(index);
MOZ_ASSERT(entry == key);
// Deserialize the message which was serialized by IPDLResolverInner.
IPC::MessageReader reader{aMessage, aActor}; bool resolve = false; if (!IPC::ReadParam(&reader, &resolve)) {
entry.mReject(ResponseRejectReason::HandlerRejected); return MsgValueError;
}
if (resolve) { // Hand off resolve-case deserialization & success to the callback.
Result rv = entry.mResolve(&reader); if (rv != MsgProcessed) { // If deserialization failed, we need to call the reject handler.
entry.mReject(ResponseRejectReason::HandlerRejected);
} return rv;
}
Die Informationen auf dieser Webseite wurden
nach bestem Wissen sorgfältig zusammengestellt. Es wird jedoch weder Vollständigkeit, noch Richtigkeit,
noch Qualität der bereit gestellten Informationen zugesichert.
Bemerkung:
Die farbliche Syntaxdarstellung ist noch experimentell.