Bug 1368844: Check WebAssembly.{Memory,Table} sizes against internal limits; r=luke
authorBenjamin Bouvier <benj@benj.me>
Wed, 31 May 2017 17:23:12 +0200
changeset 410119 27a355cf603699b458a0509bfa5719ae3cbd82f3
parent 410116 5f944d9ffbe0080d9a1b7410224570c857a82993
child 410120 db0bd0d1b8fdd78de2132c4f005e6005ea7a39ce
push id7391
push usermtabara@mozilla.com
push dateMon, 12 Jun 2017 13:08:53 +0000
treeherdermozilla-beta@2191d7f87e2e [default view] [failures only]
perfherder[talos] [build metrics] [platform microbench] (compared to previous push)
reviewersluke
bugs1368844
milestone55.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 1368844: Check WebAssembly.{Memory,Table} sizes against internal limits; r=luke MozReview-Commit-ID: GZSVjza1RfL
js/src/jit-test/tests/wasm/basic.js
js/src/jit-test/tests/wasm/import-export.js
js/src/jit-test/tests/wasm/memory.js
js/src/vm/ArrayBufferObject.cpp
js/src/wasm/WasmJS.cpp
--- a/js/src/jit-test/tests/wasm/basic.js
+++ b/js/src/jit-test/tests/wasm/basic.js
@@ -126,24 +126,27 @@ wasmEvalText('(module (import "a" "" (re
 wasmEvalText('(module (import "a" "" (result f64)))', {a:{"":()=>{}}});
 wasmEvalText('(module (import $foo "a" "" (result f64)))', {a:{"":()=>{}}});
 
 // ----------------------------------------------------------------------------
 // memory
 
 wasmValidateText('(module (memory 0))');
 wasmValidateText('(module (memory 1))');
-wasmFailValidateText('(module (memory 65536))', /initial memory size too big/);
+wasmValidateText('(module (memory 16384))');
+wasmFailValidateText('(module (memory 16385))', /initial memory size too big/);
+
+wasmEvalText('(module (memory 0 65536))')
+wasmFailValidateText('(module (memory 0 65537))', /maximum memory size too big/);
 
 // May OOM, but must not crash:
 try {
-    wasmEvalText('(module (memory 65535))');
+    wasmEvalText('(module (memory 16384))');
 } catch (e) {
-    assertEq(String(e).indexOf("out of memory") != -1 ||
-             String(e).indexOf("memory size too big") != -1, true);
+    assertEq(String(e).indexOf("out of memory") !== -1, true);
 }
 
 var buf = wasmEvalText('(module (memory 1) (export "memory" memory))').exports.memory.buffer;
 assertEq(buf instanceof ArrayBuffer, true);
 assertEq(buf.byteLength, 65536);
 
 var obj = wasmEvalText('(module (memory 1) (func (result i32) (i32.const 42)) (func (nop)) (export "memory" memory) (export "b" 0) (export "c" 1))').exports;
 assertEq(obj.memory.buffer instanceof ArrayBuffer, true);
--- a/js/src/jit-test/tests/wasm/import-export.js
+++ b/js/src/jit-test/tests/wasm/import-export.js
@@ -14,18 +14,37 @@ const mem3Page = new Memory({initial:3})
 const mem3PageMax3 = new Memory({initial:3, maximum: 3});
 const mem4Page = new Memory({initial:4});
 const mem4PageMax4 = new Memory({initial:4, maximum: 4});
 const tab1Elem = new Table({initial:1, element:"anyfunc"});
 const tab2Elem = new Table({initial:2, element:"anyfunc"});
 const tab3Elem = new Table({initial:3, element:"anyfunc"});
 const tab4Elem = new Table({initial:4, element:"anyfunc"});
 
+// Memory size consistency and internal limits.
 assertErrorMessage(() => new Memory({initial:2, maximum:1}), RangeError, /bad Memory maximum size/);
 
+try {
+    new Memory({initial:16384});
+} catch(e) {
+    assertEq(String(e).indexOf("out of memory") !== -1, true);
+}
+
+assertErrorMessage(() => new Memory({initial: 16385}), RangeError, /bad Memory initial size/);
+
+new Memory({initial: 0, maximum: 65536});
+assertErrorMessage(() => new Memory({initial: 0, maximum: 65537}), RangeError, /bad Memory maximum size/);
+
+// Table size consistency and internal limits.
+assertErrorMessage(() => new Table({initial:2, maximum:1, element:"anyfunc"}), RangeError, /bad Table maximum size/);
+new Table({ initial: 10000000, element:"anyfunc" });
+assertErrorMessage(() => new Table({initial:10000001, element:"anyfunc"}), RangeError, /bad Table initial size/);
+new Table({ initial: 0, maximum: 2**32 - 1, element:"anyfunc" });
+assertErrorMessage(() => new Table({initial:0, maximum: 2**32, element:"anyfunc"}), RangeError, /bad Table maximum size/);
+
 const m1 = new Module(wasmTextToBinary('(module (import "foo" "bar") (import "baz" "quux"))'));
 assertErrorMessage(() => new Instance(m1), TypeError, /second argument must be an object/);
 assertErrorMessage(() => new Instance(m1, {foo:null}), TypeError, /import object field 'foo' is not an Object/);
 assertErrorMessage(() => new Instance(m1, {foo:{bar:{}}}), LinkError, /import object field 'bar' is not a Function/);
 assertErrorMessage(() => new Instance(m1, {foo:{bar:()=>{}}, baz:null}), TypeError, /import object field 'baz' is not an Object/);
 assertErrorMessage(() => new Instance(m1, {foo:{bar:()=>{}}, baz:{}}), LinkError, /import object field 'quux' is not a Function/);
 assertEq(new Instance(m1, {foo:{bar:()=>{}}, baz:{quux:()=>{}}}) instanceof Instance, true);
 
--- a/js/src/jit-test/tests/wasm/memory.js
+++ b/js/src/jit-test/tests/wasm/memory.js
@@ -269,20 +269,16 @@ for (var foldOffsets = 0; foldOffsets <=
     wasmFailValidateText('(module (memory 1) (func (f64.store offset=0 (i32.const 0) (f32.const 0))))', mismatchError("f32", "f64"));
 
     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))')
-    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")
               (func (param i32) (local i32 i32 i32 i32 f32 f64) (result i32)
--- a/js/src/vm/ArrayBufferObject.cpp
+++ b/js/src/vm/ArrayBufferObject.cpp
@@ -705,16 +705,17 @@ ArrayBufferObject::BufferContents::wasmB
 #define ROUND_UP(v, a) ((v) % (a) == 0 ? (v) : v + a - ((v) % (a)))
 
 /* static */ ArrayBufferObject*
 ArrayBufferObject::createForWasm(JSContext* cx, uint32_t initialSize,
                                  const Maybe<uint32_t>& maybeMaxSize)
 {
     MOZ_ASSERT(initialSize % wasm::PageSize == 0);
     MOZ_RELEASE_ASSERT(wasm::HaveSignalHandlers());
+    MOZ_RELEASE_ASSERT((initialSize / wasm::PageSize) <= wasm::MaxMemoryInitialPages);
 
     // Prevent applications specifying a large max (like UINT32_MAX) from
     // unintentially OOMing the browser on 32-bit: they just want "a lot of
     // memory". Maintain the invariant that initialSize <= maxSize.
     Maybe<uint32_t> maxSize = maybeMaxSize;
     if (sizeof(void*) == 4 && maybeMaxSize) {
         static const uint32_t OneGiB = 1 << 30;
         uint32_t clamp = Max(OneGiB, initialSize);
--- a/js/src/wasm/WasmJS.cpp
+++ b/js/src/wasm/WasmJS.cpp
@@ -418,55 +418,54 @@ static bool
 ToNonWrappingUint32(JSContext* cx, HandleValue v, uint32_t max, const char* kind, const char* noun,
                     uint32_t* u32)
 {
     double dbl;
     if (!ToInteger(cx, v, &dbl))
         return false;
 
     if (dbl < 0 || dbl > max) {
-        JS_ReportErrorNumberASCII(cx, GetErrorMessage, nullptr, JSMSG_WASM_BAD_UINT32,
-                                  kind, noun);
+        JS_ReportErrorNumberASCII(cx, GetErrorMessage, nullptr, JSMSG_WASM_BAD_UINT32, kind, noun);
         return false;
     }
 
     *u32 = uint32_t(dbl);
     MOZ_ASSERT(double(*u32) == dbl);
     return true;
 }
 
 static bool
-GetLimits(JSContext* cx, HandleObject obj, uint32_t max, const char* kind,
-          Limits* limits)
+GetLimits(JSContext* cx, HandleObject obj, uint32_t maxInitial, uint32_t maxMaximum,
+          const char* kind, Limits* limits)
 {
     JSAtom* initialAtom = Atomize(cx, "initial", strlen("initial"));
     if (!initialAtom)
         return false;
     RootedId initialId(cx, AtomToId(initialAtom));
 
     RootedValue initialVal(cx);
     if (!GetProperty(cx, obj, obj, initialId, &initialVal))
         return false;
 
-    if (!ToNonWrappingUint32(cx, initialVal, max, kind, "initial size", &limits->initial))
+    if (!ToNonWrappingUint32(cx, initialVal, maxInitial, kind, "initial size", &limits->initial))
         return false;
 
     JSAtom* maximumAtom = Atomize(cx, "maximum", strlen("maximum"));
     if (!maximumAtom)
         return false;
     RootedId maximumId(cx, AtomToId(maximumAtom));
 
     bool found;
     if (HasProperty(cx, obj, maximumId, &found) && found) {
         RootedValue maxVal(cx);
         if (!GetProperty(cx, obj, obj, maximumId, &maxVal))
             return false;
 
         limits->maximum.emplace();
-        if (!ToNonWrappingUint32(cx, maxVal, max, kind, "maximum size", limits->maximum.ptr()))
+        if (!ToNonWrappingUint32(cx, maxVal, maxMaximum, kind, "maximum size", limits->maximum.ptr()))
             return false;
 
         if (limits->initial > *limits->maximum) {
             JS_ReportErrorNumberASCII(cx, GetErrorMessage, nullptr, JSMSG_WASM_BAD_UINT32,
                                       kind, "maximum size");
             return false;
         }
     }
@@ -1338,17 +1337,17 @@ WasmMemoryObject::construct(JSContext* c
 
     if (!args.get(0).isObject()) {
         JS_ReportErrorNumberASCII(cx, GetErrorMessage, nullptr, JSMSG_WASM_BAD_DESC_ARG, "memory");
         return false;
     }
 
     RootedObject obj(cx, &args[0].toObject());
     Limits limits;
-    if (!GetLimits(cx, obj, UINT32_MAX / PageSize, "Memory", &limits))
+    if (!GetLimits(cx, obj, MaxMemoryInitialPages, MaxMemoryMaximumPages, "Memory", &limits))
         return false;
 
     limits.initial *= PageSize;
     if (limits.maximum)
         limits.maximum = Some(*limits.maximum * PageSize);
 
     RootedArrayBufferObject buffer(cx,
         ArrayBufferObject::createForWasm(cx, limits.initial, limits.maximum));
@@ -1649,17 +1648,17 @@ WasmTableObject::construct(JSContext* cx
         return false;
 
     if (!StringEqualsAscii(elementStr, "anyfunc")) {
         JS_ReportErrorNumberASCII(cx, GetErrorMessage, nullptr, JSMSG_WASM_BAD_ELEMENT);
         return false;
     }
 
     Limits limits;
-    if (!GetLimits(cx, obj, UINT32_MAX, "Table", &limits))
+    if (!GetLimits(cx, obj, MaxTableInitialLength, UINT32_MAX, "Table", &limits))
         return false;
 
     RootedWasmTableObject table(cx, WasmTableObject::create(cx, limits));
     if (!table)
         return false;
 
     args.rval().setObject(*table);
     return true;