Bug 1302682 - Crash [@ __memcpy_sse2_unaligned] with TypedArray. r=jonco, a=gchang
authorSander Mathijs van Veen <sander@leaningtech.com>
Thu, 13 Oct 2016 10:52:13 -0400
changeset 358474 74ddf7de7dc9f95eb63b140e7cb318a41584c91a
parent 358473 1ec527fde116a5e705db035f379afdb45529fbb0
child 358475 a730703766df1e901496524d8baff44e3ead1697
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)
reviewersjonco, gchang
bugs1302682
milestone51.0a2
Bug 1302682 - Crash [@ __memcpy_sse2_unaligned] with TypedArray. r=jonco, a=gchang
js/src/gc/Marking.cpp
js/src/jit-test/tests/basic/bug1302682.js
js/src/jsobj.cpp
js/src/vm/TypedArrayObject.cpp
--- a/js/src/gc/Marking.cpp
+++ b/js/src/gc/Marking.cpp
@@ -2492,18 +2492,34 @@ js::TenuringTracer::moveObjectToTenured(
      * Arrays do not necessarily have the same AllocKind between src and dst.
      * We deal with this by copying elements manually, possibly re-inlining
      * them if there is adequate room inline in dst.
      *
      * For Arrays we're reducing tenuredSize to the smaller srcSize
      * because moveElementsToTenured() accounts for all Array elements,
      * even if they are inlined.
      */
-    if (src->is<ArrayObject>())
+    if (src->is<ArrayObject>()) {
         tenuredSize = srcSize = sizeof(NativeObject);
+    } else if (src->is<TypedArrayObject>()) {
+        TypedArrayObject* tarray = &src->as<TypedArrayObject>();
+        // Typed arrays with inline data do not necessarily have the same
+        // AllocKind between src and dst. The nursery does not allocate an
+        // inline data buffer that has the same size as the slow path will do.
+        // In the slow path, the Typed Array Object stores the inline data
+        // in the allocated space that fits the AllocKind. In the fast path,
+        // the nursery will allocate another buffer that is directly behind the
+        // minimal JSObject. That buffer size plus the JSObject size is not
+        // necessarily as large as the slow path's AllocKind size.
+        if (tarray->hasInlineElements()) {
+            AllocKind srcKind = GetGCObjectKind(TypedArrayObject::FIXED_DATA_START);
+            size_t headerSize = Arena::thingSize(srcKind);
+            srcSize = headerSize + tarray->byteLength();
+        }
+    }
 
     // Copy the Cell contents.
     js_memcpy(dst, src, srcSize);
 
     // Move any hash code attached to the object.
     src->zone()->transferUniqueId(dst, src);
 
     // Move the slots and elements, if we need to.
new file mode 100644
--- /dev/null
+++ b/js/src/jit-test/tests/basic/bug1302682.js
@@ -0,0 +1,6 @@
+for (var i = 0; i < 30000; i++) {
+    var a = inIon() ? 7 : 300;
+    var buf = new Uint8ClampedArray(a);
+    (function() {}) * this;
+    try {} catch (e) {}
+}
--- a/js/src/jsobj.cpp
+++ b/js/src/jsobj.cpp
@@ -3665,19 +3665,19 @@ JSObject::allocKindForTenure(const js::N
         return as<JSFunction>().getAllocKind();
 
     /*
      * Typed arrays in the nursery may have a lazily allocated buffer, make
      * sure there is room for the array's fixed data when moving the array.
      */
     if (is<TypedArrayObject>() && !as<TypedArrayObject>().hasBuffer()) {
         size_t nbytes = as<TypedArrayObject>().byteLength();
-        if (nbytes >= TypedArrayObject::INLINE_BUFFER_LIMIT)
-            return GetGCObjectKind(getClass());
-        return GetBackgroundAllocKind(TypedArrayObject::AllocKindForLazyBuffer(nbytes));
+        if (as<TypedArrayObject>().hasInlineElements())
+            return GetBackgroundAllocKind(TypedArrayObject::AllocKindForLazyBuffer(nbytes));
+        return GetGCObjectKind(getClass());
     }
 
     // Proxies that are CrossCompartmentWrappers may be nursery allocated.
     if (IsProxy(this))
         return as<ProxyObject>().allocKindForTenure();
 
     // Unboxed plain objects are sized according to the data they store.
     if (is<UnboxedPlainObject>()) {
--- a/js/src/vm/TypedArrayObject.cpp
+++ b/js/src/vm/TypedArrayObject.cpp
@@ -205,19 +205,22 @@ TypedArrayObject::objectMovedDuringMinor
         nbytes = oldObj->length() * sizeof(T); \
         break;
 JS_FOR_EACH_TYPED_ARRAY(OBJECT_MOVED_TYPED_ARRAY)
 #undef OBJECT_MOVED_TYPED_ARRAY
       default:
         MOZ_CRASH("Unsupported TypedArray type");
     }
 
-    if (dataOffset() + nbytes <= GetGCKindBytes(newAllocKind)) {
+    size_t headerSize = dataOffset() + sizeof(HeapSlot);
+    if (headerSize + nbytes <= GetGCKindBytes(newAllocKind)) {
+        MOZ_ASSERT(oldObj->hasInlineElements());
         newObj->setInlineElements();
     } else {
+        MOZ_ASSERT(!oldObj->hasInlineElements());
         AutoEnterOOMUnsafeRegion oomUnsafe;
         nbytes = JS_ROUNDUP(nbytes, sizeof(Value));
         void* data = newObj->zone()->pod_malloc<uint8_t>(nbytes);
         if (!data)
             oomUnsafe.crash("Failed to allocate typed array elements while tenuring.");
         MOZ_ASSERT(!nursery.isInside(data));
         newObj->initPrivate(data);
     }
@@ -230,17 +233,18 @@ JS_FOR_EACH_TYPED_ARRAY(OBJECT_MOVED_TYP
                                       /* direct = */nbytes >= sizeof(uintptr_t));
 
     return newObj->hasInlineElements() ? 0 : nbytes;
 }
 
 bool
 TypedArrayObject::hasInlineElements() const
 {
-    return elements() == this->fixedData(TypedArrayObject::FIXED_DATA_START);
+    return elements() == this->fixedData(TypedArrayObject::FIXED_DATA_START) &&
+        byteLength() <= TypedArrayObject::INLINE_BUFFER_LIMIT;
 }
 
 void
 TypedArrayObject::setInlineElements()
 {
     char* dataSlot = reinterpret_cast<char*>(this) + this->dataOffset();
     *reinterpret_cast<void**>(dataSlot) = this->fixedData(TypedArrayObject::FIXED_DATA_START);
 }
@@ -578,37 +582,42 @@ class TypedArrayObjectTemplate : public 
             return nullptr;
         if (script && !ObjectGroup::setAllocationSiteObjectGroup(cx, script, pc, tmp,
                                                                  newKind == SingletonObject))
         {
             return nullptr;
         }
 
         TypedArrayObject* tarray = &tmp->as<TypedArrayObject>();
+        initTypedArraySlots(cx, tarray, len);
+
         // Template objects do not need memory for its elements, since there
-        // won't be any elements to store. Therefore, we set the pointer to the
-        // inline data and avoid allocating memory that will never be used.
-        void* buf = tarray->fixedData(FIXED_DATA_START);
-        initTypedArraySlots(cx, tarray, len, buf, allocKind);
+        // won't be any elements to store. Therefore, we set the pointer to
+        // nullptr and avoid allocating memory that will never be used.
+        tarray->initPrivate(nullptr);
 
         return tarray;
     }
 
     static void
-    initTypedArraySlots(JSContext* cx, TypedArrayObject* tarray, int32_t len,
-                        void* buf, AllocKind allocKind)
+    initTypedArraySlots(JSContext* cx, TypedArrayObject* tarray, int32_t len)
     {
         MOZ_ASSERT(len >= 0);
         tarray->setFixedSlot(TypedArrayObject::BUFFER_SLOT, NullValue());
         tarray->setFixedSlot(TypedArrayObject::LENGTH_SLOT, Int32Value(AssertedCast<int32_t>(len)));
         tarray->setFixedSlot(TypedArrayObject::BYTEOFFSET_SLOT, Int32Value(0));
 
         // Verify that the private slot is at the expected place.
         MOZ_ASSERT(tarray->numFixedSlots() == TypedArrayObject::DATA_SLOT);
-
+    }
+
+    static void
+    initTypedArrayData(JSContext* cx, TypedArrayObject* tarray, int32_t len,
+                       void* buf, AllocKind allocKind)
+    {
         if (buf) {
 #ifdef DEBUG
             Nursery& nursery = cx->runtime()->gc.nursery;
             MOZ_ASSERT_IF(!nursery.isInside(buf) && !tarray->hasInlineElements(),
                           tarray->isTenured());
 #endif
             tarray->initPrivate(buf);
         } else {
@@ -659,17 +668,18 @@ class TypedArrayObjectTemplate : public 
             memset(buf, 0, nbytes);
          }
 
         RootedObject tmp(cx, NewObjectWithGroup<TypedArrayObject>(cx, group, allocKind, newKind));
         if (!tmp)
             return nullptr;
 
         TypedArrayObject* obj = &tmp->as<TypedArrayObject>();
-        initTypedArraySlots(cx, obj, len, buf.forget(), allocKind);
+        initTypedArraySlots(cx, obj, len);
+        initTypedArrayData(cx, obj, len, buf.forget(), allocKind);
 
         return obj;
     }
 
     /*
      * new [Type]Array(length)
      * new [Type]Array(otherTypedArray)
      * new [Type]Array(JSArray)