diff options
author | Daniel Baumann <daniel.baumann@progress-linux.org> | 2024-04-28 14:29:10 +0000 |
---|---|---|
committer | Daniel Baumann <daniel.baumann@progress-linux.org> | 2024-04-28 14:29:10 +0000 |
commit | 2aa4a82499d4becd2284cdb482213d541b8804dd (patch) | |
tree | b80bf8bf13c3766139fbacc530efd0dd9d54394c /ipc/glue/IPCStreamUtils.cpp | |
parent | Initial commit. (diff) | |
download | firefox-2aa4a82499d4becd2284cdb482213d541b8804dd.tar.xz firefox-2aa4a82499d4becd2284cdb482213d541b8804dd.zip |
Adding upstream version 86.0.1.upstream/86.0.1upstream
Signed-off-by: Daniel Baumann <daniel.baumann@progress-linux.org>
Diffstat (limited to 'ipc/glue/IPCStreamUtils.cpp')
-rw-r--r-- | ipc/glue/IPCStreamUtils.cpp | 560 |
1 files changed, 560 insertions, 0 deletions
diff --git a/ipc/glue/IPCStreamUtils.cpp b/ipc/glue/IPCStreamUtils.cpp new file mode 100644 index 0000000000..fba98891fe --- /dev/null +++ b/ipc/glue/IPCStreamUtils.cpp @@ -0,0 +1,560 @@ +/* -*- 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/. */ + +#include "IPCStreamUtils.h" + +#include "nsIIPCSerializableInputStream.h" + +#include "mozilla/Assertions.h" +#include "mozilla/dom/ContentChild.h" +#include "mozilla/dom/ContentParent.h" +#include "mozilla/dom/File.h" +#include "mozilla/ipc/FileDescriptorSetChild.h" +#include "mozilla/ipc/FileDescriptorSetParent.h" +#include "mozilla/ipc/InputStreamUtils.h" +#include "mozilla/net/SocketProcessChild.h" +#include "mozilla/net/SocketProcessParent.h" +#include "mozilla/Unused.h" +#include "nsNetCID.h" +#include "BackgroundParentImpl.h" +#include "BackgroundChildImpl.h" + +using namespace mozilla::dom; + +namespace mozilla { +namespace ipc { + +namespace { + +// These serialization and cleanup functions could be externally exposed. For +// now, though, keep them private to encourage use of the safer RAII +// AutoIPCStream class. + +template <typename M> +bool SerializeInputStreamWithFdsChild(nsIIPCSerializableInputStream* aStream, + IPCStream& aValue, bool aDelayedStart, + M* aManager) { + MOZ_RELEASE_ASSERT(aStream); + MOZ_ASSERT(aManager); + + const uint64_t kTooLargeStream = 1024 * 1024; + + uint32_t sizeUsed = 0; + AutoTArray<FileDescriptor, 4> fds; + aStream->Serialize(aValue.stream(), fds, aDelayedStart, kTooLargeStream, + &sizeUsed, aManager); + + MOZ_ASSERT(sizeUsed <= kTooLargeStream); + + if (aValue.stream().type() == InputStreamParams::T__None) { + MOZ_CRASH("Serialize failed!"); + } + + if (fds.IsEmpty()) { + aValue.optionalFds() = void_t(); + } else { + PFileDescriptorSetChild* fdSet = + aManager->SendPFileDescriptorSetConstructor(fds[0]); + for (uint32_t i = 1; i < fds.Length(); ++i) { + Unused << fdSet->SendAddFileDescriptor(fds[i]); + } + + aValue.optionalFds() = fdSet; + } + + return true; +} + +template <typename M> +bool SerializeInputStreamWithFdsParent(nsIIPCSerializableInputStream* aStream, + IPCStream& aValue, bool aDelayedStart, + M* aManager) { + MOZ_RELEASE_ASSERT(aStream); + MOZ_ASSERT(aManager); + + const uint64_t kTooLargeStream = 1024 * 1024; + + uint32_t sizeUsed = 0; + AutoTArray<FileDescriptor, 4> fds; + aStream->Serialize(aValue.stream(), fds, aDelayedStart, kTooLargeStream, + &sizeUsed, aManager); + + MOZ_ASSERT(sizeUsed <= kTooLargeStream); + + if (aValue.stream().type() == InputStreamParams::T__None) { + MOZ_CRASH("Serialize failed!"); + } + + aValue.optionalFds() = void_t(); + if (!fds.IsEmpty()) { + PFileDescriptorSetParent* fdSet = + aManager->SendPFileDescriptorSetConstructor(fds[0]); + for (uint32_t i = 1; i < fds.Length(); ++i) { + if (NS_WARN_IF(!fdSet->SendAddFileDescriptor(fds[i]))) { + Unused << PFileDescriptorSetParent::Send__delete__(fdSet); + fdSet = nullptr; + break; + } + } + + if (fdSet) { + aValue.optionalFds() = fdSet; + } + } + + return true; +} + +template <typename M> +bool SerializeInputStream(nsIInputStream* aStream, IPCStream& aValue, + M* aManager, bool aDelayedStart) { + MOZ_ASSERT(aStream); + MOZ_ASSERT(aManager); + + InputStreamParams params; + InputStreamHelper::SerializeInputStreamAsPipe(aStream, params, aDelayedStart, + aManager); + + if (params.type() == InputStreamParams::T__None) { + return false; + } + + aValue.stream() = params; + aValue.optionalFds() = void_t(); + + return true; +} + +template <typename M> +bool SerializeInputStreamChild(nsIInputStream* aStream, M* aManager, + IPCStream* aValue, + Maybe<IPCStream>* aOptionalValue, + bool aDelayedStart) { + MOZ_ASSERT(aStream); + MOZ_ASSERT(aManager); + MOZ_ASSERT(aValue || aOptionalValue); + + nsCOMPtr<nsIIPCSerializableInputStream> serializable = + do_QueryInterface(aStream); + + if (serializable) { + if (aValue) { + return SerializeInputStreamWithFdsChild(serializable, *aValue, + aDelayedStart, aManager); + } + + return SerializeInputStreamWithFdsChild(serializable, aOptionalValue->ref(), + aDelayedStart, aManager); + } + + if (aValue) { + return SerializeInputStream(aStream, *aValue, aManager, aDelayedStart); + } + + return SerializeInputStream(aStream, aOptionalValue->ref(), aManager, + aDelayedStart); +} + +template <typename M> +bool SerializeInputStreamParent(nsIInputStream* aStream, M* aManager, + IPCStream* aValue, + Maybe<IPCStream>* aOptionalValue, + bool aDelayedStart) { + MOZ_ASSERT(aStream); + MOZ_ASSERT(aManager); + MOZ_ASSERT(aValue || aOptionalValue); + + nsCOMPtr<nsIIPCSerializableInputStream> serializable = + do_QueryInterface(aStream); + + if (serializable) { + if (aValue) { + return SerializeInputStreamWithFdsParent(serializable, *aValue, + aDelayedStart, aManager); + } + + return SerializeInputStreamWithFdsParent( + serializable, aOptionalValue->ref(), aDelayedStart, aManager); + } + + if (aValue) { + return SerializeInputStream(aStream, *aValue, aManager, aDelayedStart); + } + + return SerializeInputStream(aStream, aOptionalValue->ref(), aManager, + aDelayedStart); +} + +void ActivateAndCleanupIPCStream(IPCStream& aValue, bool aConsumedByIPC, + bool aDelayedStart) { + // Cleanup file descriptors if necessary + if (aValue.optionalFds().type() == + OptionalFileDescriptorSet::TPFileDescriptorSetChild) { + AutoTArray<FileDescriptor, 4> fds; + + auto fdSetActor = static_cast<FileDescriptorSetChild*>( + aValue.optionalFds().get_PFileDescriptorSetChild()); + MOZ_ASSERT(fdSetActor); + + // FileDescriptorSet doesn't clear its fds in its ActorDestroy, so we + // unconditionally forget them here. The fds themselves are auto-closed + // in ~FileDescriptor since they originated in this process. + fdSetActor->ForgetFileDescriptors(fds); + + if (!aConsumedByIPC) { + Unused << FileDescriptorSetChild::Send__delete__(fdSetActor); + } + + } else if (aValue.optionalFds().type() == + OptionalFileDescriptorSet::TPFileDescriptorSetParent) { + AutoTArray<FileDescriptor, 4> fds; + + auto fdSetActor = static_cast<FileDescriptorSetParent*>( + aValue.optionalFds().get_PFileDescriptorSetParent()); + MOZ_ASSERT(fdSetActor); + + // FileDescriptorSet doesn't clear its fds in its ActorDestroy, so we + // unconditionally forget them here. The fds themselves are auto-closed + // in ~FileDescriptor since they originated in this process. + fdSetActor->ForgetFileDescriptors(fds); + + if (!aConsumedByIPC) { + Unused << FileDescriptorSetParent::Send__delete__(fdSetActor); + } + } + + // Activate IPCRemoteStreamParams. + InputStreamHelper::PostSerializationActivation(aValue.stream(), + aConsumedByIPC, aDelayedStart); +} + +void ActivateAndCleanupIPCStream(Maybe<IPCStream>& aValue, bool aConsumedByIPC, + bool aDelayedStart) { + if (aValue.isNothing()) { + return; + } + + ActivateAndCleanupIPCStream(aValue.ref(), aConsumedByIPC, aDelayedStart); +} + +// Returns false if the serialization should not proceed. This means that the +// inputStream is null. +bool NormalizeOptionalValue(nsIInputStream* aStream, IPCStream* aValue, + Maybe<IPCStream>* aOptionalValue) { + if (aValue) { + // if aStream is null, we will crash when serializing. + return true; + } + + if (!aStream) { + aOptionalValue->reset(); + return false; + } + + aOptionalValue->emplace(); + return true; +} + +} // anonymous namespace + +already_AddRefed<nsIInputStream> DeserializeIPCStream(const IPCStream& aValue) { + // Note, we explicitly do not support deserializing the PChildToParentStream + // actor on the child side nor the PParentToChildStream actor on the parent + // side. + + AutoTArray<FileDescriptor, 4> fds; + if (aValue.optionalFds().type() == + OptionalFileDescriptorSet::TPFileDescriptorSetParent) { + auto fdSetActor = static_cast<FileDescriptorSetParent*>( + aValue.optionalFds().get_PFileDescriptorSetParent()); + MOZ_ASSERT(fdSetActor); + + fdSetActor->ForgetFileDescriptors(fds); + MOZ_ASSERT(!fds.IsEmpty()); + + if (!FileDescriptorSetParent::Send__delete__(fdSetActor)) { + // child process is gone, warn and allow actor to clean up normally + NS_WARNING("Failed to delete fd set actor."); + } + } else if (aValue.optionalFds().type() == + OptionalFileDescriptorSet::TPFileDescriptorSetChild) { + auto fdSetActor = static_cast<FileDescriptorSetChild*>( + aValue.optionalFds().get_PFileDescriptorSetChild()); + MOZ_ASSERT(fdSetActor); + + fdSetActor->ForgetFileDescriptors(fds); + MOZ_ASSERT(!fds.IsEmpty()); + + Unused << FileDescriptorSetChild::Send__delete__(fdSetActor); + } + + return InputStreamHelper::DeserializeInputStream(aValue.stream(), fds); +} + +already_AddRefed<nsIInputStream> DeserializeIPCStream( + const Maybe<IPCStream>& aValue) { + if (aValue.isNothing()) { + return nullptr; + } + + return DeserializeIPCStream(aValue.ref()); +} + +AutoIPCStream::AutoIPCStream(bool aDelayedStart) + : mOptionalValue(&mInlineValue), mDelayedStart(aDelayedStart) {} + +AutoIPCStream::AutoIPCStream(IPCStream& aTarget, bool aDelayedStart) + : mValue(&aTarget), mDelayedStart(aDelayedStart) {} + +AutoIPCStream::AutoIPCStream(Maybe<IPCStream>& aTarget, bool aDelayedStart) + : mOptionalValue(&aTarget), mDelayedStart(aDelayedStart) { + mOptionalValue->reset(); +} + +AutoIPCStream::~AutoIPCStream() { + MOZ_ASSERT(mValue || mOptionalValue); + if (mValue && IsSet()) { + ActivateAndCleanupIPCStream(*mValue, mTaken, mDelayedStart); + } else { + ActivateAndCleanupIPCStream(*mOptionalValue, mTaken, mDelayedStart); + } +} + +bool AutoIPCStream::Serialize(nsIInputStream* aStream, + dom::ContentChild* aManager) { + MOZ_ASSERT(aStream || !mValue); + MOZ_ASSERT(aManager); + MOZ_ASSERT(mValue || mOptionalValue); + MOZ_ASSERT(!mTaken); + MOZ_ASSERT(!IsSet()); + + // If NormalizeOptionalValue returns false, we don't have to proceed. + if (!NormalizeOptionalValue(aStream, mValue, mOptionalValue)) { + return true; + } + + if (!SerializeInputStreamChild(aStream, aManager, mValue, mOptionalValue, + mDelayedStart)) { + MOZ_CRASH("IPCStream creation failed!"); + } + + return true; +} + +bool AutoIPCStream::Serialize(nsIInputStream* aStream, + PBackgroundChild* aManager) { + MOZ_ASSERT(aStream || !mValue); + MOZ_ASSERT(aManager); + MOZ_ASSERT(mValue || mOptionalValue); + MOZ_ASSERT(!mTaken); + MOZ_ASSERT(!IsSet()); + + // If NormalizeOptionalValue returns false, we don't have to proceed. + if (!NormalizeOptionalValue(aStream, mValue, mOptionalValue)) { + return true; + } + + BackgroundChildImpl* impl = static_cast<BackgroundChildImpl*>(aManager); + if (!SerializeInputStreamChild(aStream, impl, mValue, mOptionalValue, + mDelayedStart)) { + MOZ_CRASH("IPCStream creation failed!"); + } + + return true; +} + +bool AutoIPCStream::Serialize(nsIInputStream* aStream, + net::SocketProcessChild* aManager) { + MOZ_ASSERT(aStream || !mValue); + MOZ_ASSERT(aManager); + MOZ_ASSERT(mValue || mOptionalValue); + MOZ_ASSERT(!mTaken); + MOZ_ASSERT(!IsSet()); + + // If NormalizeOptionalValue returns false, we don't have to proceed. + if (!NormalizeOptionalValue(aStream, mValue, mOptionalValue)) { + return true; + } + + if (!SerializeInputStreamChild(aStream, aManager, mValue, mOptionalValue, + mDelayedStart)) { + MOZ_CRASH("IPCStream creation failed!"); + } + + return true; +} + +bool AutoIPCStream::Serialize(nsIInputStream* aStream, + dom::ContentParent* aManager) { + MOZ_ASSERT(aStream || !mValue); + MOZ_ASSERT(aManager); + MOZ_ASSERT(mValue || mOptionalValue); + MOZ_ASSERT(!mTaken); + MOZ_ASSERT(!IsSet()); + + // If NormalizeOptionalValue returns false, we don't have to proceed. + if (!NormalizeOptionalValue(aStream, mValue, mOptionalValue)) { + return true; + } + + if (!SerializeInputStreamParent(aStream, aManager, mValue, mOptionalValue, + mDelayedStart)) { + return false; + } + + return true; +} + +bool AutoIPCStream::Serialize(nsIInputStream* aStream, + PBackgroundParent* aManager) { + MOZ_ASSERT(aStream || !mValue); + MOZ_ASSERT(aManager); + MOZ_ASSERT(mValue || mOptionalValue); + MOZ_ASSERT(!mTaken); + MOZ_ASSERT(!IsSet()); + + // If NormalizeOptionalValue returns false, we don't have to proceed. + if (!NormalizeOptionalValue(aStream, mValue, mOptionalValue)) { + return true; + } + + BackgroundParentImpl* impl = static_cast<BackgroundParentImpl*>(aManager); + if (!SerializeInputStreamParent(aStream, impl, mValue, mOptionalValue, + mDelayedStart)) { + return false; + } + + return true; +} + +bool AutoIPCStream::Serialize(nsIInputStream* aStream, + net::SocketProcessParent* aManager) { + MOZ_ASSERT(aStream || !mValue); + MOZ_ASSERT(aManager); + MOZ_ASSERT(mValue || mOptionalValue); + MOZ_ASSERT(!mTaken); + MOZ_ASSERT(!IsSet()); + + // If NormalizeOptionalValue returns false, we don't have to proceed. + if (!NormalizeOptionalValue(aStream, mValue, mOptionalValue)) { + return true; + } + + if (!SerializeInputStreamParent(aStream, aManager, mValue, mOptionalValue, + mDelayedStart)) { + return false; + } + + return true; +} + +bool AutoIPCStream::IsSet() const { + MOZ_ASSERT(mValue || mOptionalValue); + if (mValue) { + return mValue->stream().type() != InputStreamParams::T__None; + } else { + return mOptionalValue->isSome() && + mOptionalValue->ref().stream().type() != InputStreamParams::T__None; + } +} + +IPCStream& AutoIPCStream::TakeValue() { + MOZ_ASSERT(mValue || mOptionalValue); + MOZ_ASSERT(!mTaken); + MOZ_ASSERT(IsSet()); + + mTaken = true; + + if (mValue) { + return *mValue; + } + + IPCStream& value = mOptionalValue->ref(); + return value; +} + +Maybe<IPCStream>& AutoIPCStream::TakeOptionalValue() { + MOZ_ASSERT(!mTaken); + MOZ_ASSERT(!mValue); + MOZ_ASSERT(mOptionalValue); + mTaken = true; + return *mOptionalValue; +} + +void IPDLParamTraits<nsIInputStream*>::Write(IPC::Message* aMsg, + IProtocol* aActor, + nsIInputStream* aParam) { + auto autoStream = MakeRefPtr<HoldIPCStream>(); + + bool ok = false; + bool found = false; + + // We can only serialize our nsIInputStream if it's going to be sent over one + // of the protocols we support, or a protocol which is managed by one of the + // protocols we support. + IProtocol* actor = aActor; + while (!found && actor) { + switch (actor->GetProtocolId()) { + case PContentMsgStart: + if (actor->GetSide() == mozilla::ipc::ParentSide) { + ok = autoStream->Serialize( + aParam, static_cast<mozilla::dom::ContentParent*>(actor)); + } else { + MOZ_RELEASE_ASSERT(actor->GetSide() == mozilla::ipc::ChildSide); + ok = autoStream->Serialize( + aParam, static_cast<mozilla::dom::ContentChild*>(actor)); + } + found = true; + break; + case PBackgroundMsgStart: + if (actor->GetSide() == mozilla::ipc::ParentSide) { + ok = autoStream->Serialize( + aParam, static_cast<mozilla::ipc::PBackgroundParent*>(actor)); + } else { + MOZ_RELEASE_ASSERT(actor->GetSide() == mozilla::ipc::ChildSide); + ok = autoStream->Serialize( + aParam, static_cast<mozilla::ipc::PBackgroundChild*>(actor)); + } + found = true; + break; + default: + break; + } + + // Try the actor's manager. + actor = actor->Manager(); + } + + if (!found) { + aActor->FatalError( + "Attempt to send nsIInputStream over an unsupported ipdl protocol"); + } + MOZ_RELEASE_ASSERT(ok, "Failed to serialize nsIInputStream"); + + WriteIPDLParam(aMsg, aActor, autoStream->TakeOptionalValue()); + + // Dispatch the autoStream to an async runnable, so that we guarantee it + // outlives this callstack, and doesn't shut down any actors we created + // until after we've finished sending the current message. + NS_ProxyRelease("IPDLParamTraits<nsIInputStream*>::Write::autoStream", + NS_GetCurrentThread(), autoStream.forget(), true); +} + +bool IPDLParamTraits<nsIInputStream*>::Read(const IPC::Message* aMsg, + PickleIterator* aIter, + IProtocol* aActor, + RefPtr<nsIInputStream>* aResult) { + mozilla::Maybe<mozilla::ipc::IPCStream> ipcStream; + if (!ReadIPDLParam(aMsg, aIter, aActor, &ipcStream)) { + return false; + } + + *aResult = mozilla::ipc::DeserializeIPCStream(ipcStream); + return true; +} + +} // namespace ipc +} // namespace mozilla |