Bug 1288997 - memory blob should not be shared across processes - part 3 - SendStream for memory blobs, r=bkelly a=gchang
authorAndrea Marchesini <amarchesini@mozilla.com>
Wed, 21 Sep 2016 12:27:26 +0200
changeset 358330 30a60c96657c94c2a83d6835fb9cb9552fc30437
parent 358329 d388cbcb82139adedbc40f46913fe5d29a351c81
child 358331 4dc5626ae64fead3bf40f480a3b1fc9061daf997
push id1324
push usermtabara@mozilla.com
push dateMon, 16 Jan 2017 13:07:44 +0000
treeherdermozilla-release@a01c49833940 [default view] [failures only]
perfherder[talos] [build metrics] [platform microbench] (compared to previous push)
reviewersbkelly, gchang
bugs1288997
milestone51.0a2
Bug 1288997 - memory blob should not be shared across processes - part 3 - SendStream for memory blobs, r=bkelly a=gchang
accessible/ipc/win/PDocAccessible.ipdl
dom/base/File.cpp
dom/base/File.h
dom/ipc/Blob.cpp
dom/ipc/DOMTypes.ipdlh
dom/ipc/PBlob.ipdl
--- a/accessible/ipc/win/PDocAccessible.ipdl
+++ b/accessible/ipc/win/PDocAccessible.ipdl
@@ -1,14 +1,15 @@
 /* -*- Mode: C++; tab-width: 2; indent-tabs-mode: nil; c-basic-offset: 2 -*- */
 /* vim: set ts=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 protocol PFileDescriptorSet;
 include protocol PBrowser;
 
 using mozilla::a11y::IAccessibleHolder from "mozilla/a11y/COMPtrTypes.h";
 
 namespace mozilla {
 namespace a11y {
 
 struct AccessibleData
--- a/dom/base/File.cpp
+++ b/dom/base/File.cpp
@@ -35,16 +35,18 @@
 #include "mozilla/Preferences.h"
 #include "mozilla/Attributes.h"
 #include "mozilla/dom/BlobBinding.h"
 #include "mozilla/dom/DOMError.h"
 #include "mozilla/dom/FileBinding.h"
 #include "mozilla/dom/WorkerPrivate.h"
 #include "mozilla/dom/WorkerRunnable.h"
 #include "nsThreadUtils.h"
+#include "nsStreamUtils.h"
+#include "SlicedInputStream.h"
 
 namespace mozilla {
 namespace dom {
 
 using namespace workers;
 
 // XXXkhuey the input stream that we pass out of a File
 // can outlive the actual File object.  Thus, we must
@@ -271,17 +273,17 @@ Blob::ToFile(const nsAString& aName, Err
 }
 
 already_AddRefed<Blob>
 Blob::CreateSlice(uint64_t aStart, uint64_t aLength,
                   const nsAString& aContentType,
                   ErrorResult& aRv)
 {
   RefPtr<BlobImpl> impl = mImpl->CreateSlice(aStart, aLength,
-                                               aContentType, aRv);
+                                             aContentType, aRv);
   if (aRv.Failed()) {
     return nullptr;
   }
 
   RefPtr<Blob> blob = Blob::Create(mParent, impl);
   return blob.forget();
 }
 
@@ -1188,10 +1190,80 @@ void
 BlobImplTemporaryBlob::GetInternalStream(nsIInputStream** aStream,
                                          ErrorResult& aRv)
 {
   nsCOMPtr<nsIInputStream> stream =
     new nsTemporaryFileInputStream(mFileDescOwner, mStartPos, mStartPos + mLength);
   stream.forget(aStream);
 }
 
+////////////////////////////////////////////////////////////////////////////
+// BlobImplStream implementation
+
+NS_IMPL_ISUPPORTS_INHERITED0(BlobImplStream, BlobImpl)
+
+BlobImplStream::BlobImplStream(nsIInputStream* aInputStream,
+                               const nsAString& aContentType,
+                               uint64_t aLength)
+  : BlobImplBase(aContentType, aLength)
+  , mInputStream(aInputStream)
+{
+  mImmutable = true;
+}
+
+BlobImplStream::BlobImplStream(BlobImplStream* aOther,
+                               const nsAString& aContentType,
+                               uint64_t aStart, uint64_t aLength)
+  : BlobImplBase(aContentType, aOther->mStart + aStart, aLength)
+  , mInputStream(new SlicedInputStream(aOther->mInputStream, aStart, aLength))
+{
+  mImmutable = true;
+}
+
+BlobImplStream::BlobImplStream(nsIInputStream* aInputStream,
+                               const nsAString& aName,
+                               const nsAString& aContentType,
+                               int64_t aLastModifiedDate,
+                               uint64_t aLength)
+  : BlobImplBase(aName, aContentType, aLength, aLastModifiedDate)
+  , mInputStream(aInputStream)
+{
+  mImmutable = true;
+}
+
+BlobImplStream::~BlobImplStream()
+{}
+
+void
+BlobImplStream::GetInternalStream(nsIInputStream** aStream, ErrorResult& aRv)
+{
+  nsCOMPtr<nsIInputStream> clonedStream;
+  nsCOMPtr<nsIInputStream> replacementStream;
+
+  aRv = NS_CloneInputStream(mInputStream, getter_AddRefs(clonedStream),
+                            getter_AddRefs(replacementStream));
+  if (NS_WARN_IF(aRv.Failed())) {
+    return;
+  }
+
+  if (replacementStream) {
+    mInputStream = replacementStream.forget();
+  }
+
+  clonedStream.forget(aStream);
+}
+
+already_AddRefed<BlobImpl>
+BlobImplStream::CreateSlice(uint64_t aStart, uint64_t aLength,
+                            const nsAString& aContentType, ErrorResult& aRv)
+{
+  if (!aLength) {
+    RefPtr<BlobImpl> impl = new EmptyBlobImpl(aContentType);
+    return impl.forget();
+  }
+
+  RefPtr<BlobImpl> impl =
+    new BlobImplStream(this, aContentType, aStart, aLength);
+  return impl.forget();
+}
+
 } // namespace dom
 } // namespace mozilla
--- a/dom/base/File.h
+++ b/dom/base/File.h
@@ -792,12 +792,50 @@ public:
   {
     return true;
   }
 
 private:
   ~EmptyBlobImpl() {}
 };
 
+class BlobImplStream final : public BlobImplBase
+{
+public:
+  NS_DECL_ISUPPORTS_INHERITED
+
+  BlobImplStream(nsIInputStream* aInputStream,
+                 const nsAString& aContentType,
+                 uint64_t aLength);
+
+  BlobImplStream(nsIInputStream* aInputStream,
+                 const nsAString& aName,
+                 const nsAString& aContentType,
+                 int64_t aLastModifiedDate,
+                 uint64_t aLength);
+
+  virtual void GetInternalStream(nsIInputStream** aStream,
+                                 ErrorResult& aRv) override;
+
+  virtual already_AddRefed<BlobImpl>
+  CreateSlice(uint64_t aStart, uint64_t aLength,
+              const nsAString& aContentType, ErrorResult& aRv) override;
+
+  virtual bool IsMemoryFile() const override
+  {
+    return true;
+  }
+
+private:
+  BlobImplStream(BlobImplStream* aOther,
+                 const nsAString& aContentType,
+                 uint64_t aStart,
+                 uint64_t aLength);
+
+  ~BlobImplStream();
+
+  nsCOMPtr<nsIInputStream> mInputStream;
+};
+
 } // namespace dom
 } // namespace mozilla
 
 #endif // mozilla_dom_File_h
--- a/dom/ipc/Blob.cpp
+++ b/dom/ipc/Blob.cpp
@@ -21,16 +21,17 @@
 #include "mozilla/dom/File.h"
 #include "mozilla/dom/nsIContentParent.h"
 #include "mozilla/dom/nsIContentChild.h"
 #include "mozilla/dom/PBlobStreamChild.h"
 #include "mozilla/dom/PBlobStreamParent.h"
 #include "mozilla/dom/indexedDB/FileSnapshot.h"
 #include "mozilla/dom/IndexedDatabaseManager.h"
 #include "mozilla/ipc/InputStreamUtils.h"
+#include "mozilla/ipc/IPCStreamUtils.h"
 #include "mozilla/ipc/PBackgroundChild.h"
 #include "mozilla/ipc/PBackgroundParent.h"
 #include "mozilla/ipc/PFileDescriptorSetParent.h"
 #include "MultipartBlobImpl.h"
 #include "nsDataHashtable.h"
 #include "nsHashKeys.h"
 #include "nsID.h"
 #include "nsIInputStream.h"
@@ -706,61 +707,48 @@ CreateBlobImpl(const nsID& aKnownBlobIDD
   DebugOnly<bool> isMutable;
   MOZ_ASSERT(NS_SUCCEEDED(blobImpl->GetMutable(&isMutable)));
   MOZ_ASSERT(!isMutable);
 
   return blobImpl.forget();
 }
 
 already_AddRefed<BlobImpl>
-CreateBlobImpl(const nsTArray<uint8_t>& aMemoryData,
+CreateBlobImpl(const IPCStream& aStream,
                const CreateBlobImplMetadata& aMetadata)
 {
-  static_assert(sizeof(aMemoryData.Length()) <= sizeof(size_t),
-                "String length won't fit in size_t!");
-  static_assert(sizeof(size_t) <= sizeof(uint64_t),
-                "size_t won't fit in uint64_t!");
-
   MOZ_ASSERT(gProcessType == GeckoProcessType_Default);
 
+  nsCOMPtr<nsIInputStream> inputStream = DeserializeIPCStream(aStream);
+  if (!inputStream) {
+    ASSERT_UNLESS_FUZZING();
+    return nullptr;
+  }
+
+  uint64_t available;
+  MOZ_ALWAYS_SUCCEEDS(inputStream->Available(&available));
+
   RefPtr<BlobImpl> blobImpl;
-
-  if (auto length = static_cast<size_t>(aMemoryData.Length())) {
-    static constexpr size_t elementSizeMultiplier =
-      sizeof(aMemoryData[0]) / sizeof(char);
-
-    if (!aMetadata.mHasRecursed &&
-        NS_WARN_IF(aMetadata.mLength != uint64_t(length))) {
-      ASSERT_UNLESS_FUZZING();
-      return nullptr;
-    }
-
-    void* buffer = malloc(length * elementSizeMultiplier);
-    if (NS_WARN_IF(!buffer)) {
-      return nullptr;
-    }
-
-    memcpy(buffer, aMemoryData.Elements(), length * elementSizeMultiplier);
-
-    if (!aMetadata.mHasRecursed && aMetadata.IsFile()) {
+  if (!aMetadata.mHasRecursed && aMetadata.IsFile()) {
+    if (available) {
       blobImpl =
-        new BlobImplMemory(buffer,
-                           uint64_t(length),
+        new BlobImplStream(inputStream,
                            aMetadata.mName,
                            aMetadata.mContentType,
-                           aMetadata.mLastModifiedDate);
+                           aMetadata.mLastModifiedDate,
+                           available);
     } else {
       blobImpl =
-        new BlobImplMemory(buffer, uint64_t(length), aMetadata.mContentType);
+        new EmptyBlobImpl(aMetadata.mName,
+                          aMetadata.mContentType,
+                          aMetadata.mLastModifiedDate);
     }
-  } else if (!aMetadata.mHasRecursed && aMetadata.IsFile()) {
+  } else if (available) {
     blobImpl =
-      new EmptyBlobImpl(aMetadata.mName,
-                        aMetadata.mContentType,
-                        aMetadata.mLastModifiedDate);
+      new BlobImplStream(inputStream, aMetadata.mContentType, available);
   } else {
     blobImpl = new EmptyBlobImpl(aMetadata.mContentType);
   }
 
   MOZ_ALWAYS_SUCCEEDS(blobImpl->SetMutable(false));
 
   return blobImpl.forget();
 }
@@ -778,18 +766,18 @@ CreateBlobImplFromBlobData(const BlobDat
   RefPtr<BlobImpl> blobImpl;
 
   switch (aBlobData.type()) {
     case BlobData::TnsID: {
       blobImpl = CreateBlobImpl(aBlobData.get_nsID(), aMetadata);
       break;
     }
 
-    case BlobData::TArrayOfuint8_t: {
-      blobImpl = CreateBlobImpl(aBlobData.get_ArrayOfuint8_t(), aMetadata);
+    case BlobData::TIPCStream: {
+      blobImpl = CreateBlobImpl(aBlobData.get_IPCStream(), aMetadata);
       break;
     }
 
     case BlobData::TArrayOfBlobData: {
       blobImpl = CreateBlobImpl(aBlobData.get_ArrayOfBlobData(), aMetadata);
       break;
     }
 
@@ -919,18 +907,20 @@ CreateBlobImpl(const ParentBlobConstruct
     metadata.mLastModifiedDate = params.modDate();
   }
 
   RefPtr<BlobImpl> blobImpl =
     CreateBlobImplFromBlobData(aBlobData, metadata);
   return blobImpl.forget();
 }
 
+template <class ChildManagerType>
 void
-BlobDataFromBlobImpl(BlobImpl* aBlobImpl, BlobData& aBlobData)
+BlobDataFromBlobImpl(ChildManagerType* aManager, BlobImpl* aBlobImpl,
+                     BlobData& aBlobData)
 {
   MOZ_ASSERT(gProcessType != GeckoProcessType_Default);
   MOZ_ASSERT(aBlobImpl);
 
   const nsTArray<RefPtr<BlobImpl>>* subBlobs = aBlobImpl->GetSubBlobImpls();
 
   if (subBlobs) {
     MOZ_ASSERT(subBlobs->Length());
@@ -938,17 +928,18 @@ BlobDataFromBlobImpl(BlobImpl* aBlobImpl
     aBlobData = nsTArray<BlobData>();
 
     nsTArray<BlobData>& subBlobDatas = aBlobData.get_ArrayOfBlobData();
     subBlobDatas.SetLength(subBlobs->Length());
 
     for (uint32_t count = subBlobs->Length(), index = 0;
          index < count;
          index++) {
-      BlobDataFromBlobImpl(subBlobs->ElementAt(index), subBlobDatas[index]);
+      BlobDataFromBlobImpl(aManager, subBlobs->ElementAt(index),
+                           subBlobDatas[index]);
     }
 
     return;
   }
 
   nsCOMPtr<nsIRemoteBlob> remoteBlob = do_QueryInterface(aBlobImpl);
   if (remoteBlob) {
     BlobChild* actor = remoteBlob->GetBlobChild();
@@ -958,36 +949,19 @@ BlobDataFromBlobImpl(BlobImpl* aBlobImpl
     return;
   }
 
   ErrorResult rv;
   nsCOMPtr<nsIInputStream> inputStream;
   aBlobImpl->GetInternalStream(getter_AddRefs(inputStream), rv);
   MOZ_ALWAYS_TRUE(!rv.Failed());
 
-  DebugOnly<bool> isNonBlocking;
-  MOZ_ASSERT(NS_SUCCEEDED(inputStream->IsNonBlocking(&isNonBlocking)));
-  MOZ_ASSERT(isNonBlocking);
-
-  uint64_t available;
-  MOZ_ALWAYS_SUCCEEDS(inputStream->Available(&available));
-
-  MOZ_ASSERT(available <= uint64_t(UINT32_MAX));
-
-  aBlobData = nsTArray<uint8_t>();
-
-  nsTArray<uint8_t>& blobData = aBlobData.get_ArrayOfuint8_t();
-
-  blobData.SetLength(size_t(available));
-
-  uint32_t readCount;
-  MOZ_ALWAYS_SUCCEEDS(
-    inputStream->Read(reinterpret_cast<char*>(blobData.Elements()),
-                      uint32_t(available),
-                      &readCount));
+  AutoIPCStream autoStream;
+  autoStream.Serialize(inputStream, aManager);
+  aBlobData = autoStream.TakeValue();
 }
 
 RemoteInputStream::RemoteInputStream(BlobImpl* aBlobImpl,
                                      uint64_t aStart,
                                      uint64_t aLength)
   : mMonitor("RemoteInputStream.mMonitor")
   , mActor(nullptr)
   , mBlobImpl(aBlobImpl)
@@ -3373,18 +3347,20 @@ BlobChild::GetOrCreateFromImpl(ChildMana
 
   if (gProcessType == GeckoProcessType_Default) {
     RefPtr<BlobImpl> sameProcessImpl = aBlobImpl;
     auto addRefedBlobImpl =
       reinterpret_cast<intptr_t>(sameProcessImpl.forget().take());
 
     blobParams = SameProcessBlobConstructorParams(addRefedBlobImpl);
   } else {
+    // BlobData is going to be populate here and it _must_ be send via IPC in
+    // order to avoid leaks.
     BlobData blobData;
-    BlobDataFromBlobImpl(aBlobImpl, blobData);
+    BlobDataFromBlobImpl(aManager, aBlobImpl, blobData);
 
     nsString contentType;
     aBlobImpl->GetType(contentType);
 
     ErrorResult rv;
     uint64_t length = aBlobImpl->GetSize(rv);
     MOZ_ASSERT(!rv.Failed());
 
--- a/dom/ipc/DOMTypes.ipdlh
+++ b/dom/ipc/DOMTypes.ipdlh
@@ -1,15 +1,17 @@
 /* -*- Mode: C++; c-basic-offset: 2; indent-tabs-mode: nil; tab-width: 8 -*- */
 /* vim: set sw=4 ts=8 et tw=80 ft=cpp : */
 /* 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 protocol PBlob;
+include protocol PSendStream;
+include IPCStream;
 include ProtocolTypes;
 
 using struct mozilla::void_t
   from "ipc/IPCMessageUtils.h";
 
 using struct mozilla::SerializedStructuredCloneBuffer
   from "ipc/IPCMessageUtils.h";
 
@@ -38,17 +40,17 @@ struct ClonedMessageData
 };
 
 union BlobData
 {
   // For remote blobs.
   nsID;
 
   // For memory-backed blobs.
-  uint8_t[];
+  IPCStream;
 
   // For multiplex blobs.
   BlobData[];
 };
 
 union OptionalBlobData
 {
   BlobData;
--- a/dom/ipc/PBlob.ipdl
+++ b/dom/ipc/PBlob.ipdl
@@ -2,16 +2,17 @@
  * 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 protocol PBackground;
 include protocol PBlobStream;
 include protocol PContent;
 include protocol PContentBridge;
 include protocol PFileDescriptorSet;
+include protocol PSendStream;
 
 include BlobTypes;
 include DOMTypes;
 include InputStreamParams;
 
 namespace mozilla {
 namespace dom {