diff --git a/dom/ipc/StructuredCloneData.h b/dom/ipc/StructuredCloneData.h index b5dc0d5b32f7..0566a3b07f8b 100644 --- a/dom/ipc/StructuredCloneData.h +++ b/dom/ipc/StructuredCloneData.h @@ -127,12 +127,11 @@ class SharedJSAllocatedData final { * - Steal: Steal the buffers from the underlying JSStructuredCloneData so that * it's safe for the StructuredCloneData to outlive the source data. This is * safe to use with IPC-provided ClonedMessageData instances because - * JSStructuredCloneData's IPC ParamTraits::Read method uses ExtractBuffers, - * returning a fatal false if unable to extract. (And - * SerializedStructuredCloneBuffer wraps/defers to it.) But if it's possible - * the ClonedMessageData came from a different source that might have borrowed - * the buffers itself, then things will crash. That would be a pretty strange - * implementation; if you see one, change it to use SharedJSAllocatedData. + * JSStructuredCloneData's IPC ParamTraits::Read method copies the relevant + * data into owned buffers. But if it's possible the ClonedMessageData came + * from a different source that might have borrowed the buffers itself, then + * things will crash. That would be a pretty strange implementation; if you + * see one, change it to use SharedJSAllocatedData. * * 1: Specifically, in the Write() case an owning SharedJSAllocatedData is * created efficiently (by stealing from StructuredCloneHolder). The diff --git a/ipc/glue/SerializedStructuredCloneBuffer.cpp b/ipc/glue/SerializedStructuredCloneBuffer.cpp new file mode 100644 index 000000000000..fb52b9472ac5 --- /dev/null +++ b/ipc/glue/SerializedStructuredCloneBuffer.cpp @@ -0,0 +1,78 @@ +/* -*- 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 "mozilla/ipc/SerializedStructuredCloneBuffer.h" +#include "js/StructuredClone.h" + +namespace IPC { + +void ParamTraits::Write(MessageWriter* aWriter, + const paramType& aParam) { + MOZ_ASSERT(!(aParam.Size() % sizeof(uint64_t))); + + // We can only construct shared memory regions up to 4Gb in size, making that + // the maximum possible JSStructuredCloneData size. + mozilla::CheckedUint32 size = aParam.Size(); + if (!size.isValid()) { + aWriter->FatalError("JSStructuredCloneData over 4Gb in size"); + return; + } + WriteParam(aWriter, size.value()); + + MessageBufferWriter bufWriter(aWriter, size.value()); + aParam.ForEachDataChunk([&](const char* aData, size_t aSize) { + return bufWriter.WriteBytes(aData, aSize); + }); +} + +bool ParamTraits::Read(MessageReader* aReader, + paramType* aResult) { + uint32_t length = 0; + if (!ReadParam(aReader, &length)) { + aReader->FatalError("JSStructuredCloneData length read failed"); + return false; + } + MOZ_ASSERT(!(length % sizeof(uint64_t))); + + // Borrowing is not suitable to use for IPC to hand out data because we often + // want to store the data somewhere for processing after IPC has released the + // underlying buffers. + // + // This deserializer previously used a mechanism to transfer ownership over + // the underlying buffers from IPC into the JSStructuredCloneData. This was + // removed when support for serializing over shared memory was added, as the + // benefit for avoiding copies was limited due to it only functioning for + // buffers under 64k in size (as larger buffers would be serialized using + // shared memory), and it added substantial complexity to the BufferList type + // and the IPC serialization layer due to things like buffer alignment. This + // can be revisited in the future if it turns out to be a noticable + // performance regression. (bug 1783242) + + mozilla::BufferList buffers(0, 0, 4096); + MessageBufferReader bufReader(aReader, length); + uint32_t read = 0; + while (read < length) { + size_t bufLen; + char* buf = buffers.AllocateBytes(length - read, &bufLen); + if (!buf) { + // Would be nice to allow actor to control behaviour here (bug 1784307) + NS_ABORT_OOM(length - read); + return false; + } + if (!bufReader.ReadBytesInto(buf, bufLen)) { + aReader->FatalError("JSStructuredCloneData ReadBytesInto failed"); + return false; + } + read += bufLen; + } + + MOZ_ASSERT(read == length); + *aResult = JSStructuredCloneData(std::move(buffers), + JS::StructuredCloneScope::DifferentProcess); + return true; +} + +} // namespace IPC diff --git a/ipc/glue/SerializedStructuredCloneBuffer.h b/ipc/glue/SerializedStructuredCloneBuffer.h index 839d7ee266f4..ed4e404dbc5a 100644 --- a/ipc/glue/SerializedStructuredCloneBuffer.h +++ b/ipc/glue/SerializedStructuredCloneBuffer.h @@ -64,46 +64,9 @@ template <> struct ParamTraits { typedef JSStructuredCloneData paramType; - static void Write(MessageWriter* aWriter, const paramType& aParam) { - MOZ_ASSERT(!(aParam.Size() % sizeof(uint64_t))); - WriteParam(aWriter, aParam.Size()); - aParam.ForEachDataChunk([&](const char* aData, size_t aSize) { - return aWriter->WriteBytes(aData, aSize, sizeof(uint64_t)); - }); - } + static void Write(MessageWriter* aWriter, const paramType& aParam); - static bool Read(MessageReader* aReader, paramType* aResult) { - size_t length = 0; - if (!ReadParam(aReader, &length)) { - return false; - } - MOZ_ASSERT(!(length % sizeof(uint64_t))); - - mozilla::BufferList buffers(0, 0, 4096); - - // Borrowing is not suitable to use for IPC to hand out data - // because we often want to store the data somewhere for - // processing after IPC has released the underlying buffers. One - // case is PContentChild::SendGetXPCOMProcessAttributes. We can't - // return a borrowed buffer because the out param outlives the - // IPDL callback. - if (length && - !aReader->ExtractBuffers(length, &buffers, sizeof(uint64_t))) { - return false; - } - - bool success; - mozilla::BufferList out = - buffers.MoveFallible(&success); - if (!success) { - return false; - } - - *aResult = JSStructuredCloneData( - std::move(out), JS::StructuredCloneScope::DifferentProcess); - - return true; - } + static bool Read(MessageReader* aReader, paramType* aResult); }; template <> diff --git a/ipc/glue/moz.build b/ipc/glue/moz.build index b86ba0a9f382..37f6b5563e02 100644 --- a/ipc/glue/moz.build +++ b/ipc/glue/moz.build @@ -180,6 +180,7 @@ UNIFIED_SOURCES += [ "ProtocolUtils.cpp", "ScopedPort.cpp", "ScopedXREEmbed.cpp", + "SerializedStructuredCloneBuffer.cpp", "SharedMemory.cpp", "Shmem.cpp", "StringUtil.cpp",