Bug 1424732 - When creating a wasm buffer, fully initialize the buffer object before exposing it to the rest of the JS engine (in particularly, to memory-accounting code). r=lth
authorJeff Walden <jwalden@mit.edu>
Fri, 15 Dec 2017 15:57:52 -0600
changeset 454185 e1f7574c4a92738748c10baa49cdf36942cae489
parent 454180 36a8327139530a95003d431f3f43e7c3f6f9c3ee
child 454186 d01ca5c5f3d06aef7c40b59335302de83bfcdb62
push id1648
push usermtabara@mozilla.com
push dateThu, 01 Mar 2018 12:45:47 +0000
treeherdermozilla-release@cbb9688c2eeb [default view] [failures only]
perfherder[talos] [build metrics] [platform microbench] (compared to previous push)
reviewerslth
bugs1424732
milestone59.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 1424732 - When creating a wasm buffer, fully initialize the buffer object before exposing it to the rest of the JS engine (in particularly, to memory-accounting code). r=lth
js/src/vm/ArrayBufferObject.cpp
js/src/vm/ArrayBufferObject.h
js/src/vm/SharedArrayObject.cpp
js/src/vm/SharedArrayObject.h
--- a/js/src/vm/ArrayBufferObject.cpp
+++ b/js/src/vm/ArrayBufferObject.cpp
@@ -782,33 +782,21 @@ WasmArrayRawBuffer::Release(void* mem)
 
 WasmArrayRawBuffer*
 ArrayBufferObject::BufferContents::wasmBuffer() const
 {
     MOZ_RELEASE_ASSERT(kind_ == WASM);
     return (WasmArrayRawBuffer*)(data_ - sizeof(WasmArrayRawBuffer));
 }
 
-void
-ArrayBufferObject::initializeRawBuffer(JSContext* cx, WasmArrayRawBuffer* wasmBuf, uint32_t initialSize)
-{
-    auto contents = BufferContents::create<WASM>(wasmBuf->dataPointer());
-    initialize(initialSize, contents, OwnsData);
-    cx->updateMallocCounter(wasmBuf->mappedSize());
-}
-
 template<typename ObjT, typename RawbufT>
 static bool
 CreateBuffer(JSContext* cx, uint32_t initialSize, const Maybe<uint32_t>& maxSize,
              MutableHandleArrayBufferObjectMaybeShared maybeSharedObject)
 {
-    Rooted<ObjT*> object(cx, ObjT::createEmpty(cx));
-    if (!object)
-        return false;
-
 #define ROUND_UP(v, a) ((v) % (a) == 0 ? (v) : v + a - ((v) % (a)))
 
     RawbufT* buffer = RawbufT::Allocate(initialSize, maxSize);
     if (!buffer) {
 #ifdef  WASM_HUGE_MEMORY
         ReportOutOfMemory(cx);
         return false;
 #else
@@ -835,19 +823,23 @@ CreateBuffer(JSContext* cx, uint32_t ini
         // Try to grow our chunk as much as possible.
         for (size_t d = cur / 2; d >= wasm::PageSize; d /= 2)
             buffer->tryGrowMaxSizeInPlace(ROUND_UP(d, wasm::PageSize));
 #endif
     }
 
 #undef ROUND_UP
 
-    object->initializeRawBuffer(cx, buffer, initialSize);
+    // ObjT::createFromNewRawBuffer assumes ownership of |buffer| even in case
+    // of failure.
+    ObjT* object = ObjT::createFromNewRawBuffer(cx, buffer, initialSize);
+    if (!object)
+        return false;
 
-    maybeSharedObject.set(object.get());
+    maybeSharedObject.set(object);
     return true;
 }
 
 bool
 js::CreateWasmBuffer(JSContext* cx, const wasm::Limits& memory,
                      MutableHandleArrayBufferObjectMaybeShared buffer)
 {
     MOZ_ASSERT(memory.initial % wasm::PageSize == 0);
@@ -1234,17 +1226,44 @@ ArrayBufferObject::create(JSContext* cx,
 ArrayBufferObject*
 ArrayBufferObject::createEmpty(JSContext* cx)
 {
     AutoSetNewObjectMetadata metadata(cx);
     ArrayBufferObject* obj = NewObjectWithClassProto<ArrayBufferObject>(cx, nullptr);
     if (!obj)
         return nullptr;
 
-    obj->initEmpty();
+    obj->setByteLength(0);
+    obj->setFlags(0);
+    obj->setFirstView(nullptr);
+    obj->setDataPointer(BufferContents::createPlain(nullptr), DoesntOwnData);
+
+    return obj;
+}
+
+ArrayBufferObject*
+ArrayBufferObject::createFromNewRawBuffer(JSContext* cx, WasmArrayRawBuffer* buffer,
+                                          uint32_t initialSize)
+{
+    AutoSetNewObjectMetadata metadata(cx);
+    ArrayBufferObject* obj = NewObjectWithClassProto<ArrayBufferObject>(cx, nullptr);
+    if (!obj) {
+        WasmArrayRawBuffer::Release(buffer->dataPointer());
+        return nullptr;
+    }
+
+    obj->setByteLength(initialSize);
+    obj->setFlags(0);
+    obj->setFirstView(nullptr);
+
+    auto contents = BufferContents::create<WASM>(buffer->dataPointer());
+    obj->setDataPointer(contents, OwnsData);
+
+    cx->updateMallocCounter(buffer->mappedSize());
+
     return obj;
 }
 
 /* static */ ArrayBufferObject::BufferContents
 ArrayBufferObject::externalizeContents(JSContext* cx, Handle<ArrayBufferObject*> buffer,
                                        bool hasStealableContents)
 {
     MOZ_ASSERT(buffer->isPlain(), "Only support doing this on plain ABOs");
--- a/js/src/vm/ArrayBufferObject.h
+++ b/js/src/vm/ArrayBufferObject.h
@@ -288,16 +288,22 @@ class ArrayBufferObject : public ArrayBu
     static ArrayBufferObject* create(JSContext* cx, uint32_t nbytes,
                                      HandleObject proto = nullptr,
                                      NewObjectKind newKind = GenericObject);
 
     // Create an ArrayBufferObject that is safely finalizable and can later be
     // initialize()d to become a real, content-visible ArrayBufferObject.
     static ArrayBufferObject* createEmpty(JSContext* cx);
 
+    // Create an ArrayBufferObject using the provided buffer and size.  Assumes
+    // ownership of |buffer| even in case of failure, i.e. on failure |buffer|
+    // is deallocated.
+    static ArrayBufferObject*
+    createFromNewRawBuffer(JSContext* cx, WasmArrayRawBuffer* buffer, uint32_t initialSize);
+
     static void copyData(Handle<ArrayBufferObject*> toBuffer, uint32_t toIndex,
                          Handle<ArrayBufferObject*> fromBuffer, uint32_t fromIndex,
                          uint32_t count);
 
     static void trace(JSTracer* trc, JSObject* obj);
     static size_t objectMoved(JSObject* obj, JSObject* old);
 
     static BufferContents externalizeContents(JSContext* cx,
@@ -366,17 +372,16 @@ class ArrayBufferObject : public ArrayBu
     bool isWasm() const { return bufferKind() == WASM; }
     bool isMapped() const { return bufferKind() == MAPPED; }
     bool isDetached() const { return flags() & DETACHED; }
     bool isPreparedForAsmJS() const { return flags() & FOR_ASMJS; }
 
     // WebAssembly support:
     static MOZ_MUST_USE bool prepareForAsmJS(JSContext* cx, Handle<ArrayBufferObject*> buffer,
                                              bool needGuard);
-    void initializeRawBuffer(JSContext* cx, WasmArrayRawBuffer* buffer, uint32_t byteLength);
     size_t wasmMappedSize() const;
     mozilla::Maybe<uint32_t> wasmMaxSize() const;
     static MOZ_MUST_USE bool wasmGrowToSizeInPlace(uint32_t newSize,
                                                    Handle<ArrayBufferObject*> oldBuf,
                                                    MutableHandle<ArrayBufferObject*> newBuf,
                                                    JSContext* cx);
 #ifndef WASM_HUGE_MEMORY
     static MOZ_MUST_USE bool wasmMovingGrowToSize(uint32_t newSize,
@@ -424,25 +429,16 @@ class ArrayBufferObject : public ArrayBu
     void setIsPreparedForAsmJS() { setFlags(flags() | FOR_ASMJS); }
 
     void initialize(size_t byteLength, BufferContents contents, OwnsState ownsState) {
         setByteLength(byteLength);
         setFlags(0);
         setFirstView(nullptr);
         setDataPointer(contents, ownsState);
     }
-
-    // Note: initialize() may be called after initEmpty(); initEmpty() must
-    // only initialize the ArrayBufferObject to a safe, finalizable state.
-    void initEmpty() {
-        setByteLength(0);
-        setFlags(0);
-        setFirstView(nullptr);
-        setDataPointer(BufferContents::createPlain(nullptr), DoesntOwnData);
-    }
 };
 
 typedef Rooted<ArrayBufferObject*> RootedArrayBufferObject;
 typedef Handle<ArrayBufferObject*> HandleArrayBufferObject;
 typedef MutableHandle<ArrayBufferObject*> MutableHandleArrayBufferObject;
 
 bool CreateWasmBuffer(JSContext* cx, const wasm::Limits& memory,
                       MutableHandleArrayBufferObjectMaybeShared buffer);
--- a/js/src/vm/SharedArrayObject.cpp
+++ b/js/src/vm/SharedArrayObject.cpp
@@ -352,43 +352,33 @@ SharedArrayBufferObject::copyData(Handle
     MOZ_ASSERT(fromBuffer->byteLength() >= fromIndex + count);
 
     jit::AtomicOperations::memcpySafeWhenRacy(toBuffer->dataPointerShared() + toIndex,
                                               fromBuffer->dataPointerShared() + fromIndex,
                                               count);
 }
 
 SharedArrayBufferObject*
-SharedArrayBufferObject::createEmpty(JSContext* cx)
+SharedArrayBufferObject::createFromNewRawBuffer(JSContext* cx, SharedArrayRawBuffer* buffer,
+                                                uint32_t initialSize)
 {
     MOZ_ASSERT(cx->compartment()->creationOptions().getSharedMemoryAndAtomicsEnabled());
 
     AutoSetNewObjectMetadata metadata(cx);
-    Rooted<SharedArrayBufferObject*> obj(cx,
-        NewObjectWithClassProto<SharedArrayBufferObject>(cx, nullptr));
-    if (!obj)
+    SharedArrayBufferObject* obj = NewObjectWithClassProto<SharedArrayBufferObject>(cx, nullptr);
+    if (!obj) {
+        buffer->dropReference();
         return nullptr;
+    }
 
-    MOZ_ASSERT(obj->getClass() == &class_);
-
-    obj->setReservedSlot(RAWBUF_SLOT, UndefinedValue());
-    obj->setReservedSlot(LENGTH_SLOT, UndefinedValue());
+    obj->acceptRawBuffer(buffer, initialSize);
 
     return obj;
 }
 
-void
-SharedArrayBufferObject::initializeRawBuffer(JSContext* cx, SharedArrayRawBuffer* buffer, uint32_t length)
-{
-    MOZ_ASSERT(getReservedSlot(RAWBUF_SLOT).isUndefined());
-    MOZ_ASSERT(getReservedSlot(LENGTH_SLOT).isUndefined());
-
-    acceptRawBuffer(buffer, length);
-}
-
 static JSObject*
 CreateSharedArrayBufferPrototype(JSContext* cx, JSProtoKey key)
 {
     return GlobalObject::createBlankPrototype(cx, cx->global(),
                                               &SharedArrayBufferObject::protoClass_);
 }
 
 static const ClassOps SharedArrayBufferObjectClassOps = {
--- a/js/src/vm/SharedArrayObject.h
+++ b/js/src/vm/SharedArrayObject.h
@@ -246,22 +246,21 @@ class SharedArrayBufferObject : public A
         return rawBufferObject()->isWasm();
     }
     SharedMem<uint8_t*> dataPointerShared() const {
         return rawBufferObject()->dataPointerShared();
     }
 
     // WebAssembly support:
 
-    // Create a SharedArrayBufferObject without an attached SARB.  This is a
-    // valid object only if a SARB is then attached with initializeRawBuffer().
-    static SharedArrayBufferObject* createEmpty(JSContext* cx);
-
-    // Install the buffer if the object was created by createEmpty().
-    void initializeRawBuffer(JSContext* cx, SharedArrayRawBuffer* buffer, uint32_t length);
+    // Create a SharedArrayBufferObject using the provided buffer and size.
+    // Assumes ownership of a reference to |buffer| even in case of failure,
+    // i.e. on failure |buffer->dropReference()| is performed.
+    static SharedArrayBufferObject*
+    createFromNewRawBuffer(JSContext* cx, SharedArrayRawBuffer* buffer, uint32_t initialSize);
 
     mozilla::Maybe<uint32_t> wasmMaxSize() const {
         return mozilla::Some(rawBufferObject()->maxSize());
     }
 
     size_t wasmMappedSize() const {
         return rawBufferObject()->mappedSize();
     }