Bug 1342045: Allow wasm::Memory of size 2**32; r=luke
authorBenjamin Bouvier <benj@benj.me>
Thu, 23 Feb 2017 16:11:00 +0100
changeset 373799 3453a55b30531155572f0128b01159ba37caf6ec
parent 373798 339878f975ccfaf5037d622ceb28a57292ccf857
child 373800 d5a8190c6f7b94fa125a5277046eb38d2423d5c0
push id10863
push userjlorenzo@mozilla.com
push dateMon, 06 Mar 2017 23:02:23 +0000
treeherdermozilla-aurora@0931190cd725 [default view] [failures only]
perfherder[talos] [build metrics] [platform microbench] (compared to previous push)
reviewersluke
bugs1342045
milestone54.0a1
Bug 1342045: Allow wasm::Memory of size 2**32; r=luke MozReview-Commit-ID: EVzM6yr0UHf
js/src/jit-test/tests/wasm/memory.js
js/src/vm/ArrayBufferObject.cpp
js/src/wasm/WasmBinaryConstants.h
js/src/wasm/WasmValidate.cpp
--- a/js/src/jit-test/tests/wasm/memory.js
+++ b/js/src/jit-test/tests/wasm/memory.js
@@ -270,17 +270,18 @@ for (var foldOffsets = 0; foldOffsets <=
 
     wasmFailValidateText('(module (memory 1) (func (f32.store offset=0 (i32.const 0) (i32.const 0))))', mismatchError("i32", "f32"));
     wasmFailValidateText('(module (memory 1) (func (f32.store offset=0 (i32.const 0) (f64.const 0))))', mismatchError("f64", "f32"));
 
     wasmFailValidateText('(module (memory 1) (func (i32.store offset=0 (i32.const 0) (f32.const 0))))', mismatchError("f32", "i32"));
     wasmFailValidateText('(module (memory 1) (func (i32.store offset=0 (i32.const 0) (f64.const 0))))', mismatchError("f64", "i32"));
 
     wasmEvalText('(module (memory 0 65535))')
-    wasmFailValidateText('(module (memory 0 65536))', /maximum memory size too big/);
+    wasmEvalText('(module (memory 0 65536))')
+    wasmFailValidateText('(module (memory 0 65537))', /maximum memory size too big/);
 
     // Test high charge of registers
     function testRegisters() {
         assertEq(wasmEvalText(
             `(module
               (memory 1)
               (data (i32.const 0) "\\00\\01\\02\\03\\04\\05\\06\\07\\08\\09\\0a\\0b\\0c\\0d\\0e\\0f")
               (data (i32.const 16) "\\f0\\f1\\f2\\f3\\f4\\f5\\f6\\f7\\f8\\f9\\fa\\fb\\fc\\fd\\fe\\ff")
--- a/js/src/vm/ArrayBufferObject.cpp
+++ b/js/src/vm/ArrayBufferObject.cpp
@@ -940,17 +940,19 @@ ArrayBufferObject::wasmGrowToSizeInPlace
                                          MutableHandleArrayBufferObject newBuf,
                                          JSContext* cx)
 {
     // On failure, do not throw and ensure that the original buffer is
     // unmodified and valid. After WasmArrayRawBuffer::growToSizeInPlace(), the
     // wasm-visible length of the buffer has been increased so it must be the
     // last fallible operation.
 
-    // byteLength can be at most INT32_MAX.
+    // byteLength can be at most INT32_MAX. Note: if this hard limit changes,
+    // update the clamping behavior in wasm::DecodeMemoryLimits and remove this
+    // comment as well as the one in wasmMovingGrowToSize.
     if (newSize > INT32_MAX)
         return false;
 
     newBuf.set(ArrayBufferObject::createEmpty(cx));
     if (!newBuf) {
         cx->clearPendingException();
         return false;
     }
@@ -971,16 +973,17 @@ ArrayBufferObject::wasmMovingGrowToSize(
                                         HandleArrayBufferObject oldBuf,
                                         MutableHandleArrayBufferObject newBuf,
                                         JSContext* cx)
 {
     // On failure, do not throw and ensure that the original buffer is
     // unmodified and valid.
 
     // byteLength can be at most INT32_MAX.
+    // See comment in wasmGrowToSizeInPlace about wasm::DecodeMemoryLimits.
     if (newSize > INT32_MAX)
         return false;
 
     if (newSize <= oldBuf->wasmBoundsCheckLimit() ||
         oldBuf->contents().wasmBuffer()->extendMappedSize(newSize))
     {
         return wasmGrowToSizeInPlace(newSize, oldBuf, newBuf, cx);
     }
--- a/js/src/wasm/WasmBinaryConstants.h
+++ b/js/src/wasm/WasmBinaryConstants.h
@@ -449,17 +449,18 @@ static const unsigned MaxExports        
 static const unsigned MaxGlobals             =  1000000;
 static const unsigned MaxDataSegments        =   100000;
 static const unsigned MaxElemSegments        = 10000000;
 static const unsigned MaxTableInitialLength  = 10000000;
 static const unsigned MaxStringBytes         =   100000;
 static const unsigned MaxLocals              =    50000;
 static const unsigned MaxParams              =     1000;
 static const unsigned MaxBrTableElems        =  1000000;
-static const unsigned MaxMemoryInitialBytes  = 1024 * 1024 * 1024;
+static const unsigned MaxMemoryInitialPages  = 16384;
+static const unsigned MaxMemoryMaximumPages  = 65536;
 static const unsigned MaxModuleBytes         = 1024 * 1024 * 1024;
 static const unsigned MaxFunctionBytes       =         128 * 1024;
 
 // To be able to assign function indices during compilation while the number of
 // imports is still unknown, asm.js sets a maximum number of imports so it can
 // immediately start handing out function indices starting at the maximum + 1.
 // this means that there is a "hole" between the last import and the first
 // definition, but that's fine.
--- a/js/src/wasm/WasmValidate.cpp
+++ b/js/src/wasm/WasmValidate.cpp
@@ -824,30 +824,34 @@ DecodeMemoryLimits(Decoder& d, ModuleEnv
 {
     if (env->usesMemory())
         return d.fail("already have default memory");
 
     Limits memory;
     if (!DecodeLimits(d, &memory))
         return false;
 
+    if (memory.initial > MaxMemoryInitialPages)
+        return d.fail("initial memory size too big");
+
     CheckedInt<uint32_t> initialBytes = memory.initial;
     initialBytes *= PageSize;
-    if (!initialBytes.isValid() || initialBytes.value() > MaxMemoryInitialBytes)
-        return d.fail("initial memory size too big");
-
+    MOZ_ASSERT(initialBytes.isValid());
     memory.initial = initialBytes.value();
 
     if (memory.maximum) {
+        if (*memory.maximum > MaxMemoryMaximumPages)
+            return d.fail("maximum memory size too big");
+
         CheckedInt<uint32_t> maximumBytes = *memory.maximum;
         maximumBytes *= PageSize;
-        if (!maximumBytes.isValid())
-            return d.fail("maximum memory size too big");
 
-        memory.maximum = Some(maximumBytes.value());
+        // Clamp the maximum memory value to UINT32_MAX; it's not semantically
+        // visible since growing will fail for values greater than INT32_MAX.
+        memory.maximum = Some(maximumBytes.isValid() ? maximumBytes.value() : UINT32_MAX);
     }
 
     env->memoryUsage = MemoryUsage::Unshared;
     env->minMemoryLength = memory.initial;
     env->maxMemoryLength = memory.maximum;
     return true;
 }
 
@@ -1457,17 +1461,17 @@ DecodeDataSection(Decoder& d, ModuleEnvi
 
         DataSegment seg;
         if (!DecodeInitializerExpression(d, env->globals, ValType::I32, &seg.offset))
             return false;
 
         if (!d.readVarU32(&seg.length))
             return d.fail("expected segment size");
 
-        if (seg.length > MaxMemoryInitialBytes)
+        if (seg.length > MaxMemoryInitialPages * PageSize)
             return d.fail("segment size too big");
 
         seg.bytecodeOffset = d.currentOffset();
 
         if (!d.readBytes(seg.length))
             return d.fail("data segment shorter than declared");
 
         if (!env->dataSegments.append(seg))