diff --git a/js/src/asmjs/Wasm.cpp b/js/src/asmjs/Wasm.cpp index d73e6a52d86..ddcfd35fa4a 100644 --- a/js/src/asmjs/Wasm.cpp +++ b/js/src/asmjs/Wasm.cpp @@ -18,6 +18,8 @@ #include "asmjs/Wasm.h" +#include "mozilla/CheckedInt.h" + #include "jsprf.h" #include "asmjs/WasmGenerator.h" @@ -30,6 +32,7 @@ using namespace js; using namespace js::wasm; +using mozilla::CheckedInt; using mozilla::IsNaN; typedef Handle HandleWasmModule; @@ -890,28 +893,29 @@ DecodeMemorySection(JSContext* cx, Decoder& d, ModuleGenerator& mg, if (sectionStart == Decoder::NotStarted) return true; - - if (!d.readCStringIf(InitialLabel)) - return Fail(cx, d, "expected memory section initial field"); - - uint32_t initialHeapSize; - if (!d.readVarU32(&initialHeapSize)) + uint32_t initialSizePages; + if (!d.readVarU32(&initialSizePages)) return Fail(cx, d, "expected initial memory size"); - if (initialHeapSize < PageSize || initialHeapSize % PageSize != 0) - return Fail(cx, d, "initial memory size not a multiple of 0x10000"); - - if (initialHeapSize > INT32_MAX) + CheckedInt initialSize = initialSizePages; + initialSize *= PageSize; + if (!initialSize.isValid()) return Fail(cx, d, "initial memory size too big"); - if (!d.readCStringIf(EndLabel)) - return Fail(cx, d, "expected end field of memory section"); + uint32_t maxSizePages; + if (!d.readVarU32(&maxSizePages)) + return Fail(cx, d, "expected initial memory size"); + + CheckedInt maxSize = maxSizePages; + maxSize *= PageSize; + if (!maxSize.isValid()) + return Fail(cx, d, "initial memory size too big"); if (!d.finishSection(sectionStart)) return Fail(cx, d, "memory section byte size mismatch"); bool signalsForOOB = CompileArgs(cx).useSignalHandlersForOOB; - heap.set(ArrayBufferObject::createForWasm(cx, initialHeapSize, signalsForOOB)); + heap.set(ArrayBufferObject::createForWasm(cx, initialSize.value(), signalsForOOB)); if (!heap) return false; diff --git a/js/src/asmjs/WasmBinary.h b/js/src/asmjs/WasmBinary.h index 516e3521cd7..9fb944ce2b7 100644 --- a/js/src/asmjs/WasmBinary.h +++ b/js/src/asmjs/WasmBinary.h @@ -54,7 +54,6 @@ static const char ExportLabel[] = "export"; static const char FuncLabel[] = "func"; static const char DataLabel[] = "data"; static const char SegmentLabel[] = "segment"; -static const char InitialLabel[] = "initial"; static const char EndLabel[] = ""; enum class Expr : uint16_t diff --git a/js/src/asmjs/WasmText.cpp b/js/src/asmjs/WasmText.cpp index 7215acc1c5b..79e1733fcfd 100644 --- a/js/src/asmjs/WasmText.cpp +++ b/js/src/asmjs/WasmText.cpp @@ -562,14 +562,18 @@ typedef WasmAstVector WasmAstSegmentVector; class WasmAstMemory : public WasmAstNode { uint32_t initialSize_; + Maybe maxSize_; WasmAstSegmentVector segments_; public: - explicit WasmAstMemory(uint32_t initialSize, WasmAstSegmentVector&& segments) + explicit WasmAstMemory(uint32_t initialSize, Maybe maxSize, + WasmAstSegmentVector&& segments) : initialSize_(initialSize), + maxSize_(maxSize), segments_(Move(segments)) {} uint32_t initialSize() const { return initialSize_; } + const Maybe& maxSize() const { return maxSize_; } const WasmAstSegmentVector& segments() const { return segments_; } }; @@ -2897,6 +2901,11 @@ ParseMemory(WasmParseContext& c) if (!c.ts.match(WasmToken::Index, &initialSize, c.error)) return nullptr; + Maybe maxSize; + WasmToken token; + if (c.ts.getIf(WasmToken::Index, &token)) + maxSize.emplace(token.index()); + WasmAstSegmentVector segments(c.lifo); while (c.ts.getIf(WasmToken::OpenParen)) { WasmAstSegment* segment = ParseSegment(c); @@ -2906,7 +2915,7 @@ ParseMemory(WasmParseContext& c) return nullptr; } - return new(c.lifo) WasmAstMemory(initialSize.index(), Move(segments)); + return new(c.lifo) WasmAstMemory(initialSize.index(), maxSize, Move(segments)); } static WasmAstImport* @@ -3814,13 +3823,11 @@ EncodeMemorySection(Encoder& e, WasmAstModule& module) WasmAstMemory& memory = *module.maybeMemory(); - if (!e.writeCString(InitialLabel)) - return false; - if (!e.writeVarU32(memory.initialSize())) return false; - if (!e.writeCString(EndLabel)) + uint32_t maxSize = memory.maxSize() ? *memory.maxSize() : memory.initialSize(); + if (!e.writeVarU32(maxSize)) return false; e.finishSection(offset); diff --git a/js/src/jit-test/tests/wasm/basic-memory.js b/js/src/jit-test/tests/wasm/basic-memory.js index 68ae89e5ce3..86728071c18 100644 --- a/js/src/jit-test/tests/wasm/basic-memory.js +++ b/js/src/jit-test/tests/wasm/basic-memory.js @@ -6,7 +6,7 @@ if (!wasmIsSupported()) function testLoad(type, ext, base, offset, align, expect) { assertEq(wasmEvalText( '(module' + - ' (memory 0x10000' + + ' (memory 1' + ' (segment 0 "\\00\\01\\02\\03\\04\\05\\06\\07\\08\\09\\0a\\0b\\0c\\0d\\0e\\0f")' + ' (segment 16 "\\f0\\f1\\f2\\f3\\f4\\f5\\f6\\f7\\f8\\f9\\fa\\fb\\fc\\fd\\fe\\ff")' + ' )' + @@ -23,7 +23,7 @@ function testLoad(type, ext, base, offset, align, expect) { function testStore(type, ext, base, offset, align, value) { assertEq(wasmEvalText( '(module' + - ' (memory 0x10000' + + ' (memory 1' + ' (segment 0 "\\00\\01\\02\\03\\04\\05\\06\\07\\08\\09\\0a\\0b\\0c\\0d\\0e\\0f")' + ' (segment 16 "\\f0\\f1\\f2\\f3\\f4\\f5\\f6\\f7\\f8\\f9\\fa\\fb\\fc\\fd\\fe\\ff")' + ' )' + @@ -41,7 +41,7 @@ function testStore(type, ext, base, offset, align, value) { function testLoadError(type, ext, base, offset, align, errorMsg) { assertErrorMessage(() => wasmEvalText( '(module' + - ' (memory 0x10000' + + ' (memory 1' + ' (segment 0 "\\00\\01\\02\\03\\04\\05\\06\\07\\08\\09\\0a\\0b\\0c\\0d\\0e\\0f")' + ' (segment 16 "\\f0\\f1\\f2\\f3\\f4\\f5\\f6\\f7\\f8\\f9\\fa\\fb\\fc\\fd\\fe\\ff")' + ' )' + @@ -58,7 +58,7 @@ function testLoadError(type, ext, base, offset, align, errorMsg) { function testStoreError(type, ext, base, offset, align, errorMsg) { assertErrorMessage(() => wasmEvalText( '(module' + - ' (memory 0x10000' + + ' (memory 1' + ' (segment 0 "\\00\\01\\02\\03\\04\\05\\06\\07\\08\\09\\0a\\0b\\0c\\0d\\0e\\0f")' + ' (segment 16 "\\f0\\f1\\f2\\f3\\f4\\f5\\f6\\f7\\f8\\f9\\fa\\fb\\fc\\fd\\fe\\ff")' + ' )' + @@ -100,7 +100,7 @@ testLoad('i32', '16_u', 16, 0, 0, 0xf1f0); // When these tests fail, uncomment the load/store tests below. function testLoadNYI(ext) { - assertErrorMessage(() => wasmEvalText(`(module (memory 0x10000) (func (i64.load${ext} (i32.const 0))))`), TypeError, /NYI/); + assertErrorMessage(() => wasmEvalText(`(module (memory 1) (func (i64.load${ext} (i32.const 0))))`), TypeError, /NYI/); } testLoadNYI(''); testLoadNYI('8_s'); @@ -122,7 +122,7 @@ testStore('i32', '', 0, 0, 0, -0x3f3e2c2c); //testStore('i32', '', 1, 1, 4, 0xc0c1d3d4); // TODO: offset NYI function testStoreNYI(ext) { - assertErrorMessage(() => wasmEvalText(`(module (memory 0x10000) (func (i64.store${ext} (i32.const 0) (i32.const 0))))`), TypeError, /NYI/); + assertErrorMessage(() => wasmEvalText(`(module (memory 1) (func (i64.store${ext} (i32.const 0) (i32.const 0))))`), TypeError, /NYI/); } testStoreNYI(''); testStoreNYI('8'); @@ -151,11 +151,11 @@ testStore('i32', '16', 0, 0, 0, 0x2345); testLoadError('i32', '', 0, 0, 3, /memory access alignment must be a power of two/); testStoreError('i32', '', 0, 0, 3, /memory access alignment must be a power of two/); -assertErrorMessage(() => wasmEvalText('(module (memory 0x10000) (func (f64.store offset=0 (i32.const 0) (i32.const 0))))'), TypeError, mismatchError("i32", "f64")); -assertErrorMessage(() => wasmEvalText('(module (memory 0x10000) (func (f64.store offset=0 (i32.const 0) (f32.const 0))))'), TypeError, mismatchError("f32", "f64")); +assertErrorMessage(() => wasmEvalText('(module (memory 1) (func (f64.store offset=0 (i32.const 0) (i32.const 0))))'), TypeError, mismatchError("i32", "f64")); +assertErrorMessage(() => wasmEvalText('(module (memory 1) (func (f64.store offset=0 (i32.const 0) (f32.const 0))))'), TypeError, mismatchError("f32", "f64")); -assertErrorMessage(() => wasmEvalText('(module (memory 0x10000) (func (f32.store offset=0 (i32.const 0) (i32.const 0))))'), TypeError, mismatchError("i32", "f32")); -assertErrorMessage(() => wasmEvalText('(module (memory 0x10000) (func (f32.store offset=0 (i32.const 0) (f64.const 0))))'), TypeError, mismatchError("f64", "f32")); +assertErrorMessage(() => wasmEvalText('(module (memory 1) (func (f32.store offset=0 (i32.const 0) (i32.const 0))))'), TypeError, mismatchError("i32", "f32")); +assertErrorMessage(() => wasmEvalText('(module (memory 1) (func (f32.store offset=0 (i32.const 0) (f64.const 0))))'), TypeError, mismatchError("f64", "f32")); -assertErrorMessage(() => wasmEvalText('(module (memory 0x10000) (func (i32.store offset=0 (i32.const 0) (f32.const 0))))'), TypeError, mismatchError("f32", "i32")); -assertErrorMessage(() => wasmEvalText('(module (memory 0x10000) (func (i32.store offset=0 (i32.const 0) (f64.const 0))))'), TypeError, mismatchError("f64", "i32")); +assertErrorMessage(() => wasmEvalText('(module (memory 1) (func (i32.store offset=0 (i32.const 0) (f32.const 0))))'), TypeError, mismatchError("f32", "i32")); +assertErrorMessage(() => wasmEvalText('(module (memory 1) (func (i32.store offset=0 (i32.const 0) (f64.const 0))))'), TypeError, mismatchError("f64", "i32")); diff --git a/js/src/jit-test/tests/wasm/basic.js b/js/src/jit-test/tests/wasm/basic.js index 98dd0bee7fe..5883b336761 100644 --- a/js/src/jit-test/tests/wasm/basic.js +++ b/js/src/jit-test/tests/wasm/basic.js @@ -132,34 +132,32 @@ wasmEvalText('(module (import $foo "a" "" (result f64)))', {a: ()=> {}}); // ---------------------------------------------------------------------------- // memory -wasmEvalText('(module (memory 65536))'); -wasmEvalText('(module (memory 131072))'); -assertErrorMessage(() => wasmEvalText('(module (memory 0))'), TypeError, /not a multiple of 0x10000/); -assertErrorMessage(() => wasmEvalText('(module (memory 1))'), TypeError, /not a multiple of 0x10000/); -assertErrorMessage(() => wasmEvalText('(module (memory 65535))'), TypeError, /not a multiple of 0x10000/); -assertErrorMessage(() => wasmEvalText('(module (memory 131071))'), TypeError, /not a multiple of 0x10000/); -assertErrorMessage(() => wasmEvalText('(module (memory 2147483648))'), TypeError, /initial memory size too big/); +wasmEvalText('(module (memory 0))'); +wasmEvalText('(module (memory 1))'); +assertErrorMessage(() => wasmEvalText('(module (memory 65536))'), TypeError, /initial memory size too big/); +assertErrorMessage(() => wasmEvalText('(module (memory 32768))'), TypeError, /initial memory size too big/); // May OOM, but must not crash: try { - wasmEvalText('(module (memory 2147418112))'); + wasmEvalText('(module (memory 32767))'); } catch (e) { + print(e); assertEq(String(e).indexOf("out of memory") != -1, true); } assertErrorMessage(() => wasmEvalText('(module (export "" memory))'), TypeError, /no memory section/); -var buf = wasmEvalText('(module (memory 65536) (export "" memory))'); +var buf = wasmEvalText('(module (memory 1) (export "" memory))'); assertEq(buf instanceof ArrayBuffer, true); assertEq(buf.byteLength, 65536); -assertErrorMessage(() => wasmEvalText('(module (memory 65536) (export "a" memory) (export "a" memory))'), TypeError, /duplicate export/); -assertErrorMessage(() => wasmEvalText('(module (memory 65536) (func) (export "a" memory) (export "a" 0))'), TypeError, /duplicate export/); -var {a, b} = wasmEvalText('(module (memory 65536) (export "a" memory) (export "b" memory))'); +assertErrorMessage(() => wasmEvalText('(module (memory 1) (export "a" memory) (export "a" memory))'), TypeError, /duplicate export/); +assertErrorMessage(() => wasmEvalText('(module (memory 1) (func) (export "a" memory) (export "a" 0))'), TypeError, /duplicate export/); +var {a, b} = wasmEvalText('(module (memory 1) (export "a" memory) (export "b" memory))'); assertEq(a instanceof ArrayBuffer, true); assertEq(a, b); -var obj = wasmEvalText('(module (memory 65536) (func (result i32) (i32.const 42)) (func (nop)) (export "a" memory) (export "b" 0) (export "c" 1))'); +var obj = wasmEvalText('(module (memory 1) (func (result i32) (i32.const 42)) (func (nop)) (export "a" memory) (export "b" 0) (export "c" 1))'); assertEq(obj.a instanceof ArrayBuffer, true); assertEq(obj.b instanceof Function, true); assertEq(obj.c instanceof Function, true); @@ -167,7 +165,7 @@ assertEq(obj.a.byteLength, 65536); assertEq(obj.b(), 42); assertEq(obj.c(), undefined); -var obj = wasmEvalText('(module (memory 65536) (func (result i32) (i32.const 42)) (export "" memory) (export "a" 0) (export "b" 0))'); +var obj = wasmEvalText('(module (memory 1) (func (result i32) (i32.const 42)) (export "" memory) (export "a" 0) (export "b" 0))'); assertEq(obj instanceof ArrayBuffer, true); assertEq(obj.a instanceof Function, true); assertEq(obj.b instanceof Function, true); @@ -175,26 +173,26 @@ assertEq(obj.a, obj.b); assertEq(obj.byteLength, 65536); assertEq(obj.a(), 42); -var buf = wasmEvalText('(module (memory 65536 (segment 0 "")) (export "" memory))'); +var buf = wasmEvalText('(module (memory 1 (segment 0 "")) (export "" memory))'); assertEq(new Uint8Array(buf)[0], 0); -var buf = wasmEvalText('(module (memory 65536 (segment 65536 "")) (export "" memory))'); +var buf = wasmEvalText('(module (memory 1 (segment 65536 "")) (export "" memory))'); assertEq(new Uint8Array(buf)[0], 0); -var buf = wasmEvalText('(module (memory 65536 (segment 0 "a")) (export "" memory))'); +var buf = wasmEvalText('(module (memory 1 (segment 0 "a")) (export "" memory))'); assertEq(new Uint8Array(buf)[0], 'a'.charCodeAt(0)); -var buf = wasmEvalText('(module (memory 65536 (segment 0 "a") (segment 2 "b")) (export "" memory))'); +var buf = wasmEvalText('(module (memory 1 (segment 0 "a") (segment 2 "b")) (export "" memory))'); assertEq(new Uint8Array(buf)[0], 'a'.charCodeAt(0)); assertEq(new Uint8Array(buf)[1], 0); assertEq(new Uint8Array(buf)[2], 'b'.charCodeAt(0)); -var buf = wasmEvalText('(module (memory 65536 (segment 65535 "c")) (export "" memory))'); +var buf = wasmEvalText('(module (memory 1 (segment 65535 "c")) (export "" memory))'); assertEq(new Uint8Array(buf)[0], 0); assertEq(new Uint8Array(buf)[65535], 'c'.charCodeAt(0)); -assertErrorMessage(() => wasmEvalText('(module (memory 65536 (segment 65536 "a")) (export "" memory))'), TypeError, /data segment does not fit/); -assertErrorMessage(() => wasmEvalText('(module (memory 65536 (segment 65535 "ab")) (export "" memory))'), TypeError, /data segment does not fit/); +assertErrorMessage(() => wasmEvalText('(module (memory 1 (segment 65536 "a")) (export "" memory))'), TypeError, /data segment does not fit/); +assertErrorMessage(() => wasmEvalText('(module (memory 1 (segment 65535 "ab")) (export "" memory))'), TypeError, /data segment does not fit/); // ---------------------------------------------------------------------------- // locals