Bug 1386787 - Throw for short structured clone reads. r=kanru, a=gchang
authorSteve Fink <sfink@mozilla.com>
Wed, 09 Aug 2017 18:34:40 -0700
changeset 660471 234a1abc97dd5c39c5ba8fecdee115fcdfbd4520
parent 660470 2b4f305aed0addd8ebc65ba0b4667db9a81754b2
child 660472 76c905a48281455d562ef5bc32f6b028196bf478
push id78424
push userkikuo@mozilla.com
push dateThu, 07 Sep 2017 03:53:36 +0000
reviewerskanru, gchang
bugs1386787
milestone56.0
Bug 1386787 - Throw for short structured clone reads. r=kanru, a=gchang
js/src/vm/StructuredClone.cpp
--- a/js/src/vm/StructuredClone.cpp
+++ b/js/src/vm/StructuredClone.cpp
@@ -221,16 +221,20 @@ struct BufferIterator {
         *reinterpret_cast<T*>(mIter.Data()) = data;
     }
 
     T peek() const {
         MOZ_ASSERT(mIter.HasRoomFor(sizeof(T)));
         return *reinterpret_cast<T*>(mIter.Data());
     }
 
+    bool canPeek() const {
+        return mIter.HasRoomFor(sizeof(T));
+    }
+
     BufferList& mBuffer;
     typename BufferList::IterImpl mIter;
 };
 
 SharedArrayRawBufferRefs&
 SharedArrayRawBufferRefs::operator=(SharedArrayRawBufferRefs&& other)
 {
     takeOwnership(Move(other));
@@ -634,23 +638,25 @@ static void
 DiscardTransferables(mozilla::BufferList<AllocPolicy>& buffer,
                      const JSStructuredCloneCallbacks* cb, void* cbClosure)
 {
     auto point = BufferIterator<uint64_t, AllocPolicy>(buffer);
     if (point.done())
         return; // Empty buffer
 
     uint32_t tag, data;
+    MOZ_RELEASE_ASSERT(point.canPeek());
     SCInput::getPair(point.peek(), &tag, &data);
     point.next();
 
     if (tag == SCTAG_HEADER) {
         if (point.done())
             return;
 
+        MOZ_RELEASE_ASSERT(point.canPeek());
         SCInput::getPair(point.peek(), &tag, &data);
         point.next();
     }
 
     if (tag != SCTAG_TRANSFER_MAP_HEADER)
         return;
 
     if (TransferableMapHeader(data) == SCTAG_TM_TRANSFERRED)
@@ -660,30 +666,30 @@ DiscardTransferables(mozilla::BufferList
     JS::AutoSuppressGCAnalysis nogc;
 
     if (point.done())
         return;
 
     uint64_t numTransferables = NativeEndian::swapFromLittleEndian(point.peek());
     point.next();
     while (numTransferables--) {
-        if (point.done())
+        if (!point.canPeek())
             return;
 
         uint32_t ownership;
         SCInput::getPair(point.peek(), &tag, &ownership);
         point.next();
         MOZ_ASSERT(tag >= SCTAG_TRANSFER_MAP_PENDING_ENTRY);
-        if (point.done())
+        if (!point.canPeek())
             return;
 
         void* content;
         SCInput::getPtr(point.peek(), &content);
         point.next();
-        if (point.done())
+        if (!point.canPeek())
             return;
 
         uint64_t extraData = NativeEndian::swapFromLittleEndian(point.peek());
         point.next();
 
         if (ownership < JS::SCTAG_TMO_FIRST_OWNED)
             continue;
 
@@ -722,29 +728,29 @@ SCInput::SCInput(JSContext* cx, JSStruct
     static_assert(JSStructuredCloneData::kSegmentAlignment % 8 == 0,
                   "structured clone buffer reads should be aligned");
     MOZ_ASSERT(data.Size() % 8 == 0);
 }
 
 bool
 SCInput::read(uint64_t* p)
 {
-    if (point.done()) {
+    if (!point.canPeek()) {
         *p = 0;  /* initialize to shut GCC up */
         return reportTruncated();
     }
     *p = NativeEndian::swapFromLittleEndian(point.peek());
     point.next();
     return true;
 }
 
 bool
 SCInput::readNativeEndian(uint64_t* p)
 {
-    if (point.done()) {
+    if (!point.canPeek()) {
         *p = 0;  /* initialize to shut GCC up */
         return reportTruncated();
     }
     *p = point.peek();
     point.next();
     return true;
 }
 
@@ -758,17 +764,17 @@ SCInput::readPair(uint32_t* tagp, uint32
         *datap = uint32_t(u);
     }
     return ok;
 }
 
 bool
 SCInput::get(uint64_t* p)
 {
-    if (point.done())
+    if (!point.canPeek())
         return reportTruncated();
     *p = NativeEndian::swapFromLittleEndian(point.peek());
     return true;
 }
 
 bool
 SCInput::getPair(uint32_t* tagp, uint32_t* datap)
 {
@@ -1586,20 +1592,23 @@ JSStructuredCloneWriter::transferOwnersh
 {
     if (transferableObjects.empty())
         return true;
 
     // Walk along the transferables and the transfer map at the same time,
     // grabbing out pointers from the transferables and stuffing them into the
     // transfer map.
     auto point = out.iter();
+    MOZ_RELEASE_ASSERT(point.canPeek());
     MOZ_ASSERT(uint32_t(NativeEndian::swapFromLittleEndian(point.peek()) >> 32) == SCTAG_HEADER);
     point++;
+    MOZ_RELEASE_ASSERT(point.canPeek());
     MOZ_ASSERT(uint32_t(NativeEndian::swapFromLittleEndian(point.peek()) >> 32) == SCTAG_TRANSFER_MAP_HEADER);
     point++;
+    MOZ_RELEASE_ASSERT(point.canPeek());
     MOZ_ASSERT(NativeEndian::swapFromLittleEndian(point.peek()) == transferableObjects.count());
     point++;
 
     JSContext* cx = context();
     RootedObject obj(cx);
     for (auto tr = transferableObjects.all(); !tr.empty(); tr.popFront()) {
         obj = tr.front();