summaryrefslogtreecommitdiffstats
path: root/ipc/glue/IPCStreamUtils.cpp
diff options
context:
space:
mode:
Diffstat (limited to 'ipc/glue/IPCStreamUtils.cpp')
-rw-r--r--ipc/glue/IPCStreamUtils.cpp560
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