Bug 1583251 - P3 - Check if it is okay to allow shared memory while deserializing; r=nika,lth
authorTom Tung <ttung@mozilla.com>
Wed, 23 Oct 2019 07:20:18 +0000
changeset 498676 8900fd9c1c09a2e4e33caf82a336f718f402345f
parent 498675 51bb06ee4062fe6356268096ff3f9ec3efe47618
child 498677 481515dd4b9d31d5ae7d934935e5262093a60a2a
push id98599
push userttung@mozilla.com
push dateWed, 23 Oct 2019 07:22:53 +0000
treeherderautoland@058946a415aa [default view] [failures only]
perfherder[talos] [build metrics] [platform microbench] (compared to previous push)
reviewersnika, lth
bugs1583251
milestone72.0a1
first release with
nightly linux32
nightly linux64
nightly mac
nightly win32
nightly win64
last release without
nightly linux32
nightly linux64
nightly mac
nightly win32
nightly win64
Bug 1583251 - P3 - Check if it is okay to allow shared memory while deserializing; r=nika,lth Differential Revision: https://phabricator.services.mozilla.com/D48349
dom/base/PostMessageEvent.cpp
dom/base/StructuredCloneHolder.cpp
dom/base/StructuredCloneHolder.h
dom/indexedDB/IDBObjectStore.cpp
js/public/StructuredClone.h
js/src/builtin/TestingFunctions.cpp
js/src/fuzz-tests/testStructuredCloneReader.cpp
js/src/jsapi-tests/testMappedArrayBuffer.cpp
js/src/vm/StructuredClone.cpp
--- a/dom/base/PostMessageEvent.cpp
+++ b/dom/base/PostMessageEvent.cpp
@@ -157,28 +157,27 @@ PostMessageEvent::Run() {
     }
   }
 
   IgnoredErrorResult rv;
   JS::Rooted<JS::Value> messageData(cx);
   nsCOMPtr<mozilla::dom::EventTarget> eventTarget =
       do_QueryObject(targetWindow);
 
-  // XXX cloneDataPolicy will be used in P3
   JS::CloneDataPolicy cloneDataPolicy;
   MOZ_DIAGNOSTIC_ASSERT(targetWindow);
   if (mCallerAgentClusterId.isSome() &&
       targetWindow->CanShareMemory(mCallerAgentClusterId.ref())) {
     cloneDataPolicy.allowSharedMemory();
   }
 
   StructuredCloneHolder* holder;
   if (mHolder.constructed<StructuredCloneHolder>()) {
     mHolder.ref<StructuredCloneHolder>().Read(targetWindow->AsGlobal(), cx,
-                                              &messageData, rv);
+                                              &messageData, cloneDataPolicy, rv);
     holder = &mHolder.ref<StructuredCloneHolder>();
   } else {
     MOZ_ASSERT(mHolder.constructed<ipc::StructuredCloneData>());
     mHolder.ref<ipc::StructuredCloneData>().Read(cx, &messageData, rv);
     holder = &mHolder.ref<ipc::StructuredCloneData>();
   }
   if (NS_WARN_IF(rv.Failed())) {
     DispatchError(cx, targetWindow, eventTarget);
--- a/dom/base/StructuredCloneHolder.cpp
+++ b/dom/base/StructuredCloneHolder.cpp
@@ -185,21 +185,27 @@ bool StructuredCloneHolderBase::Write(JS
     return false;
   }
 
   return true;
 }
 
 bool StructuredCloneHolderBase::Read(JSContext* aCx,
                                      JS::MutableHandle<JS::Value> aValue) {
+  return Read(aCx, aValue, JS::CloneDataPolicy());
+}
+
+bool StructuredCloneHolderBase::Read(JSContext* aCx,
+                                     JS::MutableHandle<JS::Value> aValue,
+                                     JS::CloneDataPolicy aCloneDataPolicy) {
   MOZ_ASSERT(mBuffer, "Read() without Write() is not allowed.");
   MOZ_ASSERT(!mClearCalled, "This method cannot be called after Clear.");
 
-  bool ok =
-      mBuffer->read(aCx, aValue, &StructuredCloneHolder::sCallbacks, this);
+  bool ok = mBuffer->read(aCx, aValue, aCloneDataPolicy,
+                          &StructuredCloneHolder::sCallbacks, this);
   return ok;
 }
 
 bool StructuredCloneHolderBase::CustomReadTransferHandler(
     JSContext* aCx, JSStructuredCloneReader* aReader, uint32_t aTag,
     void* aContent, uint64_t aExtraData,
     JS::MutableHandleObject aReturnObject) {
   MOZ_CRASH("Nothing to read.");
@@ -264,25 +270,32 @@ void StructuredCloneHolder::Write(JSCont
     aRv.Throw(NS_ERROR_DOM_DATA_CLONE_ERR);
     return;
   }
 }
 
 void StructuredCloneHolder::Read(nsIGlobalObject* aGlobal, JSContext* aCx,
                                  JS::MutableHandle<JS::Value> aValue,
                                  ErrorResult& aRv) {
+  return Read(aGlobal, aCx, aValue, JS::CloneDataPolicy(), aRv);
+}
+
+void StructuredCloneHolder::Read(nsIGlobalObject* aGlobal, JSContext* aCx,
+                                 JS::MutableHandle<JS::Value> aValue,
+                                 JS::CloneDataPolicy aCloneDataPolicy,
+                                 ErrorResult& aRv) {
   MOZ_ASSERT_IF(
       mStructuredCloneScope == StructuredCloneScope::SameProcessSameThread,
       mCreationEventTarget->IsOnCurrentThread());
   MOZ_ASSERT(aGlobal);
 
   mozilla::AutoRestore<nsIGlobalObject*> guard(mGlobal);
   mGlobal = aGlobal;
 
-  if (!StructuredCloneHolderBase::Read(aCx, aValue)) {
+  if (!StructuredCloneHolderBase::Read(aCx, aValue, aCloneDataPolicy)) {
     JS_ClearPendingException(aCx);
     aRv.Throw(NS_ERROR_DOM_DATA_CLONE_ERR);
     return;
   }
 
   // If we are tranferring something, we cannot call 'Read()' more than once.
   if (mSupportsTransferring) {
     mBlobImplArray.Clear();
@@ -313,18 +326,18 @@ void StructuredCloneHolder::ReadFromBuff
       mCreationEventTarget->IsOnCurrentThread());
 
   MOZ_ASSERT(!mBuffer, "ReadFromBuffer() must be called without a Write().");
 
   mozilla::AutoRestore<nsIGlobalObject*> guard(mGlobal);
   mGlobal = aGlobal;
 
   if (!JS_ReadStructuredClone(aCx, aBuffer, aAlgorithmVersion,
-                              mStructuredCloneScope, aValue, &sCallbacks,
-                              this)) {
+                              mStructuredCloneScope, aValue,
+                              JS::CloneDataPolicy(), &sCallbacks, this)) {
     JS_ClearPendingException(aCx);
     aRv.Throw(NS_ERROR_DOM_DATA_CLONE_ERR);
   }
 }
 
 /* static */
 JSObject* StructuredCloneHolder::ReadFullySerializableObjects(
     JSContext* aCx, JSStructuredCloneReader* aReader, uint32_t aTag) {
--- a/dom/base/StructuredCloneHolder.h
+++ b/dom/base/StructuredCloneHolder.h
@@ -99,16 +99,20 @@ class StructuredCloneHolderBase {
   bool Write(JSContext* aCx, JS::Handle<JS::Value> aValue,
              JS::Handle<JS::Value> aTransfer,
              JS::CloneDataPolicy aCloneDataPolicy);
 
   // If Write() has been called, this method retrieves data and stores it into
   // aValue.
   bool Read(JSContext* aCx, JS::MutableHandle<JS::Value> aValue);
 
+  // Like Read() but it supports handling of clone policy.
+  bool Read(JSContext* aCx, JS::MutableHandle<JS::Value> aValue,
+            JS::CloneDataPolicy aCloneDataPolicy);
+
   bool HasData() const { return !!mBuffer; }
 
   JSStructuredCloneData& BufferData() const {
     MOZ_ASSERT(mBuffer, "Write() has never been called.");
     return mBuffer->data();
   }
 
   size_t SizeOfExcludingThis(mozilla::MallocSizeOf aMallocSizeOf) {
@@ -160,16 +164,20 @@ class StructuredCloneHolder : public Str
 
   void Write(JSContext* aCx, JS::Handle<JS::Value> aValue,
              JS::Handle<JS::Value> aTransfer,
              JS::CloneDataPolicy aCloneDataPolicy, ErrorResult& aRv);
 
   void Read(nsIGlobalObject* aGlobal, JSContext* aCx,
             JS::MutableHandle<JS::Value> aValue, ErrorResult& aRv);
 
+  void Read(nsIGlobalObject* aGlobal, JSContext* aCx,
+            JS::MutableHandle<JS::Value> aValue,
+            JS::CloneDataPolicy aCloneDataPolicy, ErrorResult& aRv);
+
   // Call this method to know if this object is keeping some DOM object alive.
   bool HasClonedDOMObjects() const {
     return !mBlobImplArray.IsEmpty() || !mWasmModuleArray.IsEmpty() ||
            !mClonedSurfaces.IsEmpty() || !mInputStreamArray.IsEmpty();
   }
 
   nsTArray<RefPtr<BlobImpl>>& BlobImpls() {
     MOZ_ASSERT(mSupportsCloning,
--- a/dom/indexedDB/IDBObjectStore.cpp
+++ b/dom/indexedDB/IDBObjectStore.cpp
@@ -1065,17 +1065,17 @@ bool IDBObjectStore::DeserializeValue(JS
       nullptr,
       nullptr};
 
   // FIXME: Consider to use StructuredCloneHolder here and in other
   //        deserializing methods.
   if (!JS_ReadStructuredClone(
           aCx, aCloneReadInfo.mData, JS_STRUCTURED_CLONE_VERSION,
           JS::StructuredCloneScope::DifferentProcessForIndexedDB, aValue,
-          &callbacks, &aCloneReadInfo)) {
+          JS::CloneDataPolicy(), &callbacks, &aCloneReadInfo)) {
     return false;
   }
 
   return true;
 }
 
 namespace {
 
@@ -1224,17 +1224,17 @@ class DeserializeIndexValueHelper final 
         nullptr,
         nullptr,
         nullptr,
         nullptr};
 
     if (!JS_ReadStructuredClone(
             aCx, mCloneReadInfo.mData, JS_STRUCTURED_CLONE_VERSION,
             JS::StructuredCloneScope::DifferentProcessForIndexedDB, aValue,
-            &callbacks, &mCloneReadInfo)) {
+            JS::CloneDataPolicy(), &callbacks, &mCloneReadInfo)) {
       return NS_ERROR_DOM_DATA_CLONE_ERR;
     }
 
     return NS_OK;
   }
 
   void OperationCompleted(nsresult aStatus) {
     mStatus = aStatus;
@@ -1330,17 +1330,17 @@ class DeserializeUpgradeValueHelper fina
         nullptr,
         nullptr,
         nullptr,
         nullptr};
 
     if (!JS_ReadStructuredClone(
             aCx, mCloneReadInfo.mData, JS_STRUCTURED_CLONE_VERSION,
             JS::StructuredCloneScope::DifferentProcessForIndexedDB, aValue,
-            &callbacks, &mCloneReadInfo)) {
+            JS::CloneDataPolicy(), &callbacks, &mCloneReadInfo)) {
       return NS_ERROR_DOM_DATA_CLONE_ERR;
     }
 
     return NS_OK;
   }
 
   void PopulateFileIds(nsAString& aFileIds) {
     for (uint32_t count = mCloneReadInfo.mFiles.Length(), index = 0;
--- a/js/public/StructuredClone.h
+++ b/js/public/StructuredClone.h
@@ -538,16 +538,17 @@ class MOZ_NON_MEMMOVABLE JS_PUBLIC_API J
 /**
  * Implements StructuredDeserialize and StructuredDeserializeWithTransfer.
  *
  * Note: If `data` contains transferable objects, it can be read only once.
  */
 JS_PUBLIC_API bool JS_ReadStructuredClone(
     JSContext* cx, JSStructuredCloneData& data, uint32_t version,
     JS::StructuredCloneScope scope, JS::MutableHandleValue vp,
+    JS::CloneDataPolicy cloneDataPolicy,
     const JSStructuredCloneCallbacks* optionalCallbacks, void* closure);
 
 /**
  * Implements StructuredSerialize, StructuredSerializeForStorage, and
  * StructuredSerializeWithTransfer.
  *
  * Note: If the scope is DifferentProcess then the cloneDataPolicy must deny
  * shared-memory objects, or an error will be signaled if a shared memory object
@@ -627,16 +628,17 @@ class JS_PUBLIC_API JSAutoStructuredClon
    * an external container, though note that you will need to use adopt() to
    * properly release that data eventually.
    */
   void abandon() {
     data_.ownTransferables_ = OwnTransferablePolicy::IgnoreTransferablesIfAny;
   }
 
   bool read(JSContext* cx, JS::MutableHandleValue vp,
+            JS::CloneDataPolicy cloneDataPolicy = JS::CloneDataPolicy(),
             const JSStructuredCloneCallbacks* optionalCallbacks = nullptr,
             void* closure = nullptr);
 
   bool write(JSContext* cx, JS::HandleValue v,
              const JSStructuredCloneCallbacks* optionalCallbacks = nullptr,
              void* closure = nullptr);
 
   bool write(JSContext* cx, JS::HandleValue v, JS::HandleValue transferable,
--- a/js/src/builtin/TestingFunctions.cpp
+++ b/js/src/builtin/TestingFunctions.cpp
@@ -3493,17 +3493,18 @@ static bool Deserialize(JSContext* cx, u
 
   bool hasTransferable;
   if (!JS_StructuredCloneHasTransferables(*obj->data(), &hasTransferable)) {
     return false;
   }
 
   RootedValue deserialized(cx);
   if (!JS_ReadStructuredClone(cx, *obj->data(), JS_STRUCTURED_CLONE_VERSION,
-                              scope, &deserialized, nullptr, nullptr)) {
+                              scope, &deserialized, JS::CloneDataPolicy(),
+                              nullptr, nullptr)) {
     return false;
   }
   args.rval().set(deserialized);
 
   // Consume any clone buffer with transferables; throw an error if it is
   // deserialized again.
   if (hasTransferable) {
     obj->discard();
--- a/js/src/fuzz-tests/testStructuredCloneReader.cpp
+++ b/js/src/fuzz-tests/testStructuredCloneReader.cpp
@@ -49,33 +49,33 @@ static int testStructuredCloneReaderFuzz
     return 0;
   }
   char padding[kSegmentAlignment] = {0};
   if (!clonebuf->AppendBytes(padding, buf_size - size)) {
     ReportOutOfMemory(gCx);
     return 0;
   }
 
+  JS::CloneDataPolicy policy;
   RootedValue deserialized(gCx);
   if (!JS_ReadStructuredClone(gCx, *clonebuf, JS_STRUCTURED_CLONE_VERSION,
-                              scope, &deserialized, nullptr, nullptr)) {
+                              scope, &deserialized, policy, nullptr, nullptr)) {
     return 0;
   }
 
   /* If we succeeded in deserializing, we should try to reserialize the data.
      This has two main advantages:
 
      1) It tests parts of the serializer as well.
      2) The deserialized data is actually used, making it more likely to detect
         further memory-related problems.
 
      Tests show that this also doesn't cause a serious performance penalty.
   */
   mozilla::Maybe<JSAutoStructuredCloneBuffer> clonebufOut;
-  JS::CloneDataPolicy policy;
 
   clonebufOut.emplace(scope, nullptr, nullptr);
   if (!clonebufOut->write(gCx, deserialized, UndefinedHandleValue, policy)) {
     return 0;
   }
 
   return 0;
 }
--- a/js/src/jsapi-tests/testMappedArrayBuffer.cpp
+++ b/js/src/jsapi-tests/testMappedArrayBuffer.cpp
@@ -132,17 +132,17 @@ bool TestDetachObject() {
 bool TestCloneObject() {
   JS::RootedObject obj1(cx, CreateNewObject(8, 12));
   CHECK(obj1);
   JSAutoStructuredCloneBuffer cloned_buffer(
       JS::StructuredCloneScope::SameProcessSameThread, nullptr, nullptr);
   JS::RootedValue v1(cx, JS::ObjectValue(*obj1));
   CHECK(cloned_buffer.write(cx, v1, nullptr, nullptr));
   JS::RootedValue v2(cx);
-  CHECK(cloned_buffer.read(cx, &v2, nullptr, nullptr));
+  CHECK(cloned_buffer.read(cx, &v2, JS::CloneDataPolicy(), nullptr, nullptr));
   JS::RootedObject obj2(cx, v2.toObjectOrNull());
   CHECK(VerifyObject(obj2, 8, 12, false));
 
   return true;
 }
 
 bool TestStealContents() {
   JS::RootedObject obj(cx, CreateNewObject(8, 12));
@@ -168,20 +168,20 @@ bool TestTransferObject() {
 
   JS::RootedObject obj(
       cx, JS_NewArrayObject(cx, JS::HandleValueArray::subarray(argv, 0, 1)));
   CHECK(obj);
   JS::RootedValue transferable(cx, JS::ObjectValue(*obj));
 
   JSAutoStructuredCloneBuffer cloned_buffer(
       JS::StructuredCloneScope::SameProcessSameThread, nullptr, nullptr);
-  CHECK(cloned_buffer.write(cx, v1, transferable, JS::CloneDataPolicy(),
-                            nullptr, nullptr));
+  JS::CloneDataPolicy policy;
+  CHECK(cloned_buffer.write(cx, v1, transferable, policy, nullptr, nullptr));
   JS::RootedValue v2(cx);
-  CHECK(cloned_buffer.read(cx, &v2, nullptr, nullptr));
+  CHECK(cloned_buffer.read(cx, &v2, policy, nullptr, nullptr));
   JS::RootedObject obj2(cx, v2.toObjectOrNull());
   CHECK(VerifyObject(obj2, 8, 12, true));
   CHECK(JS::IsDetachedArrayBufferObject(obj1));
 
   return true;
 }
 
 static void GC(JSContext* cx) {
--- a/js/src/vm/StructuredClone.cpp
+++ b/js/src/vm/StructuredClone.cpp
@@ -381,20 +381,22 @@ class SCInput {
   BufferIterator point;
 };
 
 }  // namespace js
 
 struct JSStructuredCloneReader {
  public:
   explicit JSStructuredCloneReader(SCInput& in, JS::StructuredCloneScope scope,
+                                   JS::CloneDataPolicy cloneDataPolicy,
                                    const JSStructuredCloneCallbacks* cb,
                                    void* cbClosure)
       : in(in),
         allowedScope(scope),
+        cloneDataPolicy(cloneDataPolicy),
         objs(in.context()),
         allObjs(in.context()),
         callbacks(cb),
         closure(cbClosure) {}
 
   SCInput& input() { return in; }
   bool read(MutableHandleValue vp);
 
@@ -425,16 +427,18 @@ struct JSStructuredCloneReader {
   SCInput& in;
 
   // The widest scope that the caller will accept, where
   // SameProcessSameThread is the widest (it can store anything it wants) and
   // DifferentProcess is the narrowest (it cannot contain pointers and must
   // be valid cross-process.)
   JS::StructuredCloneScope allowedScope;
 
+  const JS::CloneDataPolicy cloneDataPolicy;
+
   // Stack of objects with properties remaining to be read.
   RootedValueVector objs;
 
   // Array of all objects read during this deserialization, for resolving
   // backreferences.
   //
   // For backreferences to work correctly, objects must be added to this
   // array in exactly the order expected by the version of the Writer that
@@ -632,20 +636,21 @@ bool WriteStructuredClone(JSContext* cx,
     return false;
   }
   w.extractBuffer(bufp);
   return true;
 }
 
 bool ReadStructuredClone(JSContext* cx, JSStructuredCloneData& data,
                          JS::StructuredCloneScope scope, MutableHandleValue vp,
+                         JS::CloneDataPolicy cloneDataPolicy,
                          const JSStructuredCloneCallbacks* cb,
                          void* cbClosure) {
   SCInput in(cx, data);
-  JSStructuredCloneReader r(in, scope, cb, cbClosure);
+  JSStructuredCloneReader r(in, scope, cloneDataPolicy, cb, cbClosure);
   return r.read(vp);
 }
 
 static bool StructuredCloneHasTransferObjects(
     const JSStructuredCloneData& data) {
   if (data.Size() < sizeof(uint64_t)) {
     return false;
   }
@@ -2206,16 +2211,22 @@ bool JSStructuredCloneReader::readArrayB
   }
   vp.setObject(*obj);
   ArrayBufferObject& buffer = obj->as<ArrayBufferObject>();
   MOZ_ASSERT(buffer.byteLength() == nbytes);
   return in.readArray(buffer.dataPointer(), nbytes);
 }
 
 bool JSStructuredCloneReader::readSharedArrayBuffer(MutableHandleValue vp) {
+  if (!cloneDataPolicy.isSharedArrayBufferAllowed()) {
+    JS_ReportErrorNumberASCII(context(), GetErrorMessage, nullptr,
+                              JSMSG_SC_NOT_CLONABLE, "SharedArrayBuffer");
+    return false;
+  }
+
   uint32_t byteLength;
   if (!in.readBytes(&byteLength, sizeof(byteLength))) {
     return in.reportTruncated();
   }
 
   intptr_t p;
   if (!in.readBytes(&p, sizeof(p))) {
     return in.reportTruncated();
@@ -2252,24 +2263,29 @@ bool JSStructuredCloneReader::readShared
   }
 
   vp.setObject(*obj);
   return true;
 }
 
 bool JSStructuredCloneReader::readSharedWasmMemory(uint32_t nbytes,
                                                    MutableHandleValue vp) {
+  JSContext* cx = context();
   if (nbytes != 0) {
-    JS_ReportErrorNumberASCII(context(), GetErrorMessage, nullptr,
+    JS_ReportErrorNumberASCII(cx, GetErrorMessage, nullptr,
                               JSMSG_SC_BAD_SERIALIZED_DATA,
                               "invalid shared wasm memory tag");
     return false;
   }
 
-  JSContext* cx = context();
+  if (!cloneDataPolicy.isSharedArrayBufferAllowed()) {
+    JS_ReportErrorNumberASCII(cx, GetErrorMessage, nullptr,
+                              JSMSG_SC_NOT_CLONABLE, "WebAssembly.Memory");
+    return false;
+  }
 
   // Read the SharedArrayBuffer object.
   RootedValue payload(cx);
   if (!startRead(&payload)) {
     return false;
   }
   if (!payload.isObject() ||
       !payload.toObject().is<SharedArrayBufferObject>()) {
@@ -3003,27 +3019,29 @@ bool JSStructuredCloneReader::read(Mutab
   return true;
 }
 
 using namespace js;
 
 JS_PUBLIC_API bool JS_ReadStructuredClone(
     JSContext* cx, JSStructuredCloneData& buf, uint32_t version,
     JS::StructuredCloneScope scope, MutableHandleValue vp,
+    JS::CloneDataPolicy cloneDataPolicy,
     const JSStructuredCloneCallbacks* optionalCallbacks, void* closure) {
   AssertHeapIsIdle();
   CHECK_THREAD(cx);
 
   if (version > JS_STRUCTURED_CLONE_VERSION) {
     JS_ReportErrorNumberASCII(cx, GetErrorMessage, nullptr,
                               JSMSG_SC_BAD_CLONE_VERSION);
     return false;
   }
   const JSStructuredCloneCallbacks* callbacks = optionalCallbacks;
-  return ReadStructuredClone(cx, buf, scope, vp, callbacks, closure);
+  return ReadStructuredClone(cx, buf, scope, vp, cloneDataPolicy, callbacks,
+                             closure);
 }
 
 JS_PUBLIC_API bool JS_WriteStructuredClone(
     JSContext* cx, HandleValue value, JSStructuredCloneData* bufp,
     JS::StructuredCloneScope scope, JS::CloneDataPolicy cloneDataPolicy,
     const JSStructuredCloneCallbacks* optionalCallbacks, void* closure,
     HandleValue transferable) {
   AssertHeapIsIdle();
@@ -3077,17 +3095,17 @@ JS_PUBLIC_API bool JS_StructuredClone(
       }
     } else {
       if (!buf.write(cx, value, callbacks, closure)) {
         return false;
       }
     }
   }
 
-  return buf.read(cx, vp, callbacks, closure);
+  return buf.read(cx, vp, JS::CloneDataPolicy(), callbacks, closure);
 }
 
 JSAutoStructuredCloneBuffer::JSAutoStructuredCloneBuffer(
     JSAutoStructuredCloneBuffer&& other)
     : scope_(other.scope()), data_(other.scope()) {
   data_.ownTransferables_ = other.data_.ownTransferables_;
   other.steal(&data_, &version_, &data_.callbacks_, &data_.closure_);
 }
@@ -3134,21 +3152,21 @@ void JSAutoStructuredCloneBuffer::steal(
   }
   *data = std::move(data_);
 
   version_ = 0;
   data_.setCallbacks(nullptr, nullptr, OwnTransferablePolicy::NoTransferables);
 }
 
 bool JSAutoStructuredCloneBuffer::read(
-    JSContext* cx, MutableHandleValue vp,
+    JSContext* cx, MutableHandleValue vp, JS::CloneDataPolicy cloneDataPolicy,
     const JSStructuredCloneCallbacks* optionalCallbacks, void* closure) {
   MOZ_ASSERT(cx);
   return !!JS_ReadStructuredClone(cx, data_, version_, scope_, vp,
-                                  optionalCallbacks, closure);
+                                  cloneDataPolicy, optionalCallbacks, closure);
 }
 
 bool JSAutoStructuredCloneBuffer::write(
     JSContext* cx, HandleValue value,
     const JSStructuredCloneCallbacks* optionalCallbacks, void* closure) {
   HandleValue transferable = UndefinedHandleValue;
   return write(cx, value, transferable, JS::CloneDataPolicy(),
                optionalCallbacks, closure);