From d5f6bcfb758fdc7387e595e4a118af2dfd3d3f9e Mon Sep 17 00:00:00 2001 From: Narcis Beleuzu Date: Thu, 25 Jun 2020 08:16:03 +0300 Subject: [PATCH] Backed out 8 changesets (bug 1615434, bug 1642940) for SM bustages on limits.js . CLOSED TREE Backed out changeset b4a816af3fdf (bug 1642940) Backed out changeset 5660e8f9b0f5 (bug 1642940) Backed out changeset 3f3f63e1156b (bug 1642940) Backed out changeset 068ffb754356 (bug 1615434) Backed out changeset daa7bbbfe721 (bug 1642940) Backed out changeset 4312510d2a52 (bug 1642940) Backed out changeset 0fde857a66d9 (bug 1642940) Backed out changeset 534530689a10 (bug 1642940) --- js/src/jit-test/tests/wasm/basic.js | 12 ++ js/src/jit-test/tests/wasm/import-export.js | 21 ++ js/src/jit-test/tests/wasm/limits.js | 228 -------------------- js/src/jit/MIRGenerator.h | 6 +- js/src/jit/WasmBCE.cpp | 2 +- js/src/js.msg | 2 - js/src/vm/ArrayBufferObject.cpp | 42 ++-- js/src/vm/ArrayBufferObject.h | 16 +- js/src/vm/SharedArrayObject.cpp | 8 +- js/src/vm/SharedArrayObject.h | 12 +- js/src/wasm/AsmJS.cpp | 24 +-- js/src/wasm/WasmBaselineCompile.cpp | 2 +- js/src/wasm/WasmCode.h | 4 +- js/src/wasm/WasmConstants.h | 13 +- js/src/wasm/WasmIonCompile.cpp | 4 +- js/src/wasm/WasmJS.cpp | 58 ++--- js/src/wasm/WasmJS.h | 3 +- js/src/wasm/WasmModule.cpp | 38 +--- js/src/wasm/WasmTable.cpp | 12 +- js/src/wasm/WasmTypes.cpp | 18 +- js/src/wasm/WasmTypes.h | 29 +-- js/src/wasm/WasmValidate.cpp | 53 +++-- js/src/wasm/WasmValidate.h | 4 +- 23 files changed, 185 insertions(+), 426 deletions(-) delete mode 100644 js/src/jit-test/tests/wasm/limits.js diff --git a/js/src/jit-test/tests/wasm/basic.js b/js/src/jit-test/tests/wasm/basic.js index 8d1166f6b453..2b3d8e68d052 100644 --- a/js/src/jit-test/tests/wasm/basic.js +++ b/js/src/jit-test/tests/wasm/basic.js @@ -119,6 +119,18 @@ wasmEvalText('(module (import "a" "" (func $foo (result f64))))', {a:{"":()=>{}} wasmValidateText('(module (memory 0))'); wasmValidateText('(module (memory 1))'); +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 16384))'); +} catch (e) { + assertEq(String(e).indexOf("out of memory") !== -1, true); +} var buf = wasmEvalText('(module (memory 1) (export "memory" (memory 0)))').exports.memory.buffer; assertEq(buf instanceof ArrayBuffer, true); diff --git a/js/src/jit-test/tests/wasm/import-export.js b/js/src/jit-test/tests/wasm/import-export.js index 355ebee74bc2..c5a66f013aa1 100644 --- a/js/src/jit-test/tests/wasm/import-export.js +++ b/js/src/jit-test/tests/wasm/import-export.js @@ -23,6 +23,27 @@ function assertSegmentFitError(f) { } } +// 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:"funcref"}), RangeError, /bad Table maximum size/); +new Table({ initial: 10000000, element:"funcref" }); +assertErrorMessage(() => new Table({initial:10000001, element:"funcref"}), RangeError, /bad Table initial size/); +new Table({ initial: 0, maximum: 10000000, element:"funcref" }); +assertErrorMessage(() => new Table({initial:0, maximum: 10000001, element:"funcref"}), RangeError, /bad Table maximum size/); + const m1 = new Module(wasmTextToBinary('(module (import "foo" "bar" (func)) (import "baz" "quux" (func)))')); 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/); diff --git a/js/src/jit-test/tests/wasm/limits.js b/js/src/jit-test/tests/wasm/limits.js deleted file mode 100644 index 4f3571252f8e..000000000000 --- a/js/src/jit-test/tests/wasm/limits.js +++ /dev/null @@ -1,228 +0,0 @@ -// Tests of limits of memory and table types - -const PageSize = 65536; -const MemoryMaxValid = 65536; -const MemoryMaxRuntime = Math.floor(0x7fff_ffff / PageSize); - -const TableMaxValid = 0xffff_ffff; -const TableMaxRuntime = 10_000_000; - -// Test that a memory type is valid within a module -function testMemoryValidate(initial, maximum, shared) { - wasmValidateText(`(module - (memory ${initial} ${maximum || ''} ${shared ? 'shared' : ''}) - )`); -} - -testMemoryValidate(0, undefined, false); -testMemoryValidate(1, undefined, false); -testMemoryValidate(0, 1, false); -testMemoryValidate(0, 1, true); -testMemoryValidate(1, 1, false); -testMemoryValidate(1, 1, true); -testMemoryValidate(MemoryMaxValid, undefined, false); -testMemoryValidate(MemoryMaxValid, MemoryMaxValid, false); -testMemoryValidate(MemoryMaxValid, MemoryMaxValid, true); - -// Test that a memory type is not valid within a module -function testMemoryFailValidate(initial, maximum, shared, pattern) { - wasmFailValidateText(`(module - (memory ${initial} ${maximum || ''} ${shared ? 'shared' : ''}) - )`, pattern); -} - -testMemoryFailValidate(2, 1, false, /size minimum must not be greater than maximum/); -testMemoryFailValidate(1, undefined, true, /maximum length required for shared memory/); -testMemoryFailValidate(MemoryMaxValid + 1, undefined, false, /initial memory size too big/); -testMemoryFailValidate(MemoryMaxValid, MemoryMaxValid + 1, false, /maximum memory size too big/); -testMemoryFailValidate(MemoryMaxValid, MemoryMaxValid + 1, true, /maximum memory size too big/); - -// Test that a memory type is invalid for constructing a WebAssembly.Memory -function testMemoryFailConstruct(initial, maximum, shared, pattern) { - assertErrorMessage(() => new WebAssembly.Memory({ - initial, - maximum, - shared - }), RangeError, pattern); -} - -testMemoryFailConstruct(MemoryMaxValid + 1, undefined, false, /bad Memory initial size/); -testMemoryFailConstruct(0, MemoryMaxValid + 1, false, /bad Memory maximum size/); -testMemoryFailConstruct(MemoryMaxValid + 1, undefined, true, /bad Memory initial size/); -testMemoryFailConstruct(0, MemoryMaxValid + 1, true, /bad Memory maximum size/); - -// Test that a memory type can be instantiated within a module or constructed -// with a WebAssembly.Memory -function testMemoryCreate(initial, maximum, shared) { - // May OOM, but must not fail to validate - try { - wasmEvalText(`(module - (memory ${initial} ${maximum || ''} ${shared ? 'shared' : ''}) - )`); - } catch (e) { - assertEq(String(e).indexOf("out of memory") !== -1, true, `${e}`); - } - try { - new WebAssembly.Memory({initial, maximum, shared}); - } catch (e) { - assertEq(String(e).indexOf("out of memory") !== -1, true, `${e}`); - } -} - -testMemoryCreate(0, undefined, false); -testMemoryCreate(1, undefined, false); -testMemoryCreate(0, 1, false); -testMemoryCreate(0, 1, true); -testMemoryCreate(1, 1, false); -testMemoryCreate(1, 1, true); -testMemoryCreate(MemoryMaxRuntime, undefined, false); -testMemoryCreate(MemoryMaxRuntime, MemoryMaxValid, false); -testMemoryCreate(MemoryMaxRuntime, MemoryMaxValid, true); - -// Test that a memory type cannot be instantiated within a module or constructed -// with a WebAssembly.Memory -function testMemoryFailCreate(initial, maximum, shared, pattern) { - assertErrorMessage(() => wasmEvalText(`(module - (memory ${initial} ${maximum || ''} ${shared ? 'shared' : ''}) - )`), WebAssembly.RuntimeError, pattern); - assertErrorMessage(() => new WebAssembly.Memory({ - initial, - maximum, - shared - }), WebAssembly.RuntimeError, pattern); -} - -testMemoryFailCreate(MemoryMaxRuntime + 1, undefined, false, /too many memory pages/); -testMemoryFailCreate(MemoryMaxRuntime + 1, MemoryMaxValid, false, /too many memory pages/); -testMemoryFailCreate(MemoryMaxRuntime + 1, MemoryMaxValid, true, /too many memory pages/); - -// Test that a memory type cannot be grown from initial to a target due to an -// implementation limit -function testMemoryFailGrow(initial, maximum, target, shared) { - let {run} = wasmEvalText(`(module - (memory ${initial} ${maximum || ''} ${shared ? 'shared' : ''}) - (func (export "run") (result i32) - i32.const ${target - initial} - memory.grow - ) - )`).exports; - assertEq(run(), -1, 'failed to grow'); - - let mem = new WebAssembly.Memory({ - initial, - maximum, - shared - }); - assertErrorMessage(() => mem.grow(target - initial), RangeError, /failed to grow memory/); -} - -testMemoryFailGrow(1, undefined, MemoryMaxRuntime + 1, false); -testMemoryFailGrow(1, MemoryMaxValid, MemoryMaxRuntime + 1, false); -testMemoryFailGrow(1, MemoryMaxValid, MemoryMaxRuntime + 1, true); - -// Test that a table type is valid within a module -function testTableValidate(initial, maximum) { - wasmValidateText(`(module - (table ${initial} ${maximum || ''} externref) - )`); -} - -testTableValidate(0, undefined); -testTableValidate(1, undefined); -testTableValidate(0, 1); -testTableValidate(1, 1); -testTableValidate(TableMaxValid, undefined); -testTableValidate(TableMaxValid, TableMaxValid); - -// Test that a table type is not valid within a module -function testTableFailValidate(initial, maximum, pattern) { - wasmFailValidateText(`(module - (table ${initial} ${maximum || ''} externref) - )`, pattern); -} - -testTableFailValidate(2, 1, /size minimum must not be greater than maximum/); -// The maximum valid table value is equivalent to the maximum encodable limit -// value, so we cannot test too large of a table limit in a module. -assertEq(TableMaxValid + 1 > 0xffffffff, true); - -// Test that a table type is invalid for constructing a WebAssembly.Table -function testTableFailConstruct(initial, maximum, pattern) { - assertErrorMessage(() => new WebAssembly.Table({ - initial, - maximum, - element: 'externref', - }), TypeError, pattern); -} - -testTableFailConstruct(TableMaxValid + 1, undefined, /bad Table initial size/); -testTableFailConstruct(0, TableMaxValid + 1, /bad Table maximum size/); - -// Test that a table type can be instantiated within a module or constructed -// with a WebAssembly.Table -function testTableCreate(initial, maximum) { - // May OOM, but must not fail to validate - try { - wasmEvalText(`(module - (table ${initial} ${maximum || ''} externref) - )`); - } catch (e) { - assertEq(String(e).indexOf("out of table") !== -1, true, `${e}`); - } - try { - new WebAssembly.Table({ - initial, - maximum, - element: 'externref', - }); - } catch (e) { - assertEq(String(e).indexOf("out of table") !== -1, true, `${e}`); - } -} - -testTableCreate(0, undefined); -testTableCreate(1, undefined); -testTableCreate(0, 1); -testTableCreate(1, 1); -testTableCreate(TableMaxRuntime, undefined); -testTableCreate(TableMaxRuntime, TableMaxValid); - -// Test that a table type cannot be instantiated within a module or constructed -// with a WebAssembly.Table -function testTableFailCreate(initial, maximum, pattern) { - assertErrorMessage(() => wasmEvalText(`(module - (table ${initial} ${maximum || ''} externref) - )`), WebAssembly.RuntimeError, pattern); - assertErrorMessage(() => new WebAssembly.Table({ - initial, - maximum, - element: 'externref', - }), WebAssembly.RuntimeError, pattern); -} - -testTableFailCreate(TableMaxRuntime + 1, undefined, /too many table elements/); -testTableFailCreate(TableMaxRuntime + 1, TableMaxValid, /too many table elements/); - -// Test that a table type cannot be grown from initial to a target due to an -// implementation limit -function testTableFailGrow(initial, maximum, target) { - let {run} = wasmEvalText(`(module - (table ${initial} ${maximum || ''} externref) - (func (export "run") (result i32) - ref.null extern - i32.const ${target - initial} - table.grow - ) - )`).exports; - assertEq(run(), -1, 'failed to grow'); - - let tab = new WebAssembly.Table({ - initial, - maximum, - element: 'externref', - }); - assertErrorMessage(() => tab.grow(target - initial), RangeError, /failed to grow table/); -} - -testTableFailGrow(1, undefined, TableMaxRuntime + 1); -testTableFailGrow(1, TableMaxValid, TableMaxRuntime + 1); diff --git a/js/src/jit/MIRGenerator.h b/js/src/jit/MIRGenerator.h index c38a41ce07b1..a9678d972858 100644 --- a/js/src/jit/MIRGenerator.h +++ b/js/src/jit/MIRGenerator.h @@ -38,7 +38,7 @@ class MIRGenerator final { const CompileInfo* outerInfo, const OptimizationInfo* optimizationInfo); - void initMinWasmHeapLength(uint64_t init) { minWasmHeapLength_ = init; } + void initMinWasmHeapLength(uint32_t init) { minWasmHeapLength_ = init; } TempAllocator& alloc() { return *alloc_; } MIRGraph& graph() { return *graph_; } @@ -115,7 +115,7 @@ class MIRGenerator final { MOZ_ASSERT(wasmMaxStackArgBytes_ == 0); wasmMaxStackArgBytes_ = n; } - uint64_t minWasmHeapLength() const { return minWasmHeapLength_; } + uint32_t minWasmHeapLength() const { return minWasmHeapLength_; } void setNeedsOverrecursedCheck() { needsOverrecursedCheck_ = true; } bool needsOverrecursedCheck() const { return needsOverrecursedCheck_; } @@ -147,7 +147,7 @@ class MIRGenerator final { bool stringsCanBeInNursery_; bool bigIntsCanBeInNursery_; - uint64_t minWasmHeapLength_; + uint32_t minWasmHeapLength_; #if defined(JS_ION_PERF) WasmPerfSpewer wasmPerfSpewer_; diff --git a/js/src/jit/WasmBCE.cpp b/js/src/jit/WasmBCE.cpp index 2388a2411876..713e9775b697 100644 --- a/js/src/jit/WasmBCE.cpp +++ b/js/src/jit/WasmBCE.cpp @@ -50,7 +50,7 @@ bool jit::EliminateBoundsChecks(MIRGenerator* mir, MIRGraph& graph) { if (addr->isConstant() && addr->toConstant()->type() == MIRType::Int32 && - uint64_t(addr->toConstant()->toInt32()) < + uint32_t(addr->toConstant()->toInt32()) < mir->minWasmHeapLength()) { bc->setRedundant(); if (JitOptions.spectreIndexMasking) { diff --git a/js/src/js.msg b/js/src/js.msg index 3385a79598e9..6bec30434058 100644 --- a/js/src/js.msg +++ b/js/src/js.msg @@ -412,8 +412,6 @@ MSG_DEF(JSMSG_WASM_OUT_OF_BOUNDS, 0, JSEXN_WASMRUNTIMEERROR, "index out of MSG_DEF(JSMSG_WASM_UNALIGNED_ACCESS, 0, JSEXN_WASMRUNTIMEERROR, "unaligned memory access") MSG_DEF(JSMSG_WASM_WAKE_OVERFLOW, 0, JSEXN_WASMRUNTIMEERROR, "too many woken agents") MSG_DEF(JSMSG_WASM_DEREF_NULL, 0, JSEXN_WASMRUNTIMEERROR, "dereferencing null pointer") -MSG_DEF(JSMSG_WASM_MEM_IMP_LIMIT, 0, JSEXN_WASMRUNTIMEERROR, "too many memory pages") -MSG_DEF(JSMSG_WASM_TABLE_IMP_LIMIT, 0, JSEXN_WASMRUNTIMEERROR, "too many table elements") MSG_DEF(JSMSG_WASM_BAD_RANGE , 2, JSEXN_RANGEERR, "bad {0} {1}") MSG_DEF(JSMSG_WASM_BAD_GROW, 1, JSEXN_RANGEERR, "failed to grow {0}") MSG_DEF(JSMSG_WASM_TABLE_OUT_OF_BOUNDS, 0, JSEXN_WASMRUNTIMEERROR, "table index out of bounds") diff --git a/js/src/vm/ArrayBufferObject.cpp b/js/src/vm/ArrayBufferObject.cpp index 703277d53d84..b27e3fafe72e 100644 --- a/js/src/vm/ArrayBufferObject.cpp +++ b/js/src/vm/ArrayBufferObject.cpp @@ -598,7 +598,7 @@ MOZ_MUST_USE bool WasmArrayRawBuffer::growToSizeInPlace(uint32_t oldSize, return true; } -bool WasmArrayRawBuffer::extendMappedSize(uint64_t maxSize) { +bool WasmArrayRawBuffer::extendMappedSize(uint32_t maxSize) { size_t newMappedSize = wasm::ComputeMappedSize(maxSize); MOZ_ASSERT(mappedSize_ <= newMappedSize); if (mappedSize_ == newMappedSize) { @@ -613,8 +613,8 @@ bool WasmArrayRawBuffer::extendMappedSize(uint64_t maxSize) { return true; } -void WasmArrayRawBuffer::tryGrowMaxSizeInPlace(uint64_t deltaMaxSize) { - CheckedInt newMaxSize = maxSize_.value(); +void WasmArrayRawBuffer::tryGrowMaxSizeInPlace(uint32_t deltaMaxSize) { + CheckedInt newMaxSize = maxSize_.value(); newMaxSize += deltaMaxSize; MOZ_ASSERT(newMaxSize.isValid()); MOZ_ASSERT(newMaxSize.value() % wasm::PageSize == 0); @@ -628,8 +628,10 @@ void WasmArrayRawBuffer::tryGrowMaxSizeInPlace(uint64_t deltaMaxSize) { /* static */ WasmArrayRawBuffer* WasmArrayRawBuffer::Allocate(uint32_t numBytes, - const Maybe& maxSize, + const Maybe& maxSize, const Maybe& mapped) { + MOZ_RELEASE_ASSERT(numBytes <= ArrayBufferObject::MaxBufferByteLength); + size_t mappedSize = mapped.isSome() ? *mapped : wasm::ComputeMappedSize(maxSize.valueOr(numBytes)); @@ -674,11 +676,11 @@ WasmArrayRawBuffer* ArrayBufferObject::BufferContents::wasmBuffer() const { template static bool CreateSpecificWasmBuffer( - JSContext* cx, uint32_t initialSize, const Maybe& maxSize, + JSContext* cx, uint32_t initialSize, const Maybe& maxSize, MutableHandleArrayBufferObjectMaybeShared maybeSharedObject) { bool useHugeMemory = wasm::IsHugeMemoryEnabled(); - Maybe clampedMaxSize = maxSize; + Maybe clampedMaxSize = maxSize; if (clampedMaxSize) { #ifdef JS_64BIT // On 64-bit platforms when we aren't using huge memory, clamp @@ -687,7 +689,7 @@ static bool CreateSpecificWasmBuffer( // wasm::PageSize == 0 if (!useHugeMemory && clampedMaxSize.value() >= (UINT32_MAX - wasm::PageSize)) { - uint64_t clamp = (wasm::MaxMemoryLimitField - 2) * wasm::PageSize; + uint32_t clamp = (wasm::MaxMemoryMaximumPages - 2) * wasm::PageSize; MOZ_ASSERT(clamp < UINT32_MAX); MOZ_ASSERT(initialSize <= clamp); clampedMaxSize = Some(clamp); @@ -699,10 +701,8 @@ static bool CreateSpecificWasmBuffer( // (like UINT32_MAX) from unintentially OOMing the browser: they just // want "a lot of memory". Maintain the invariant that // initialSize <= clampedMaxSize. - static const uint64_t OneGiB = 1 << 30; - static_assert(wasm::HighestValidARMImmediate > OneGiB, - "computing mapped size on ARM requires clamped max size"); - uint64_t clamp = std::max(OneGiB, uint64_t(initialSize)); + static const uint32_t OneGiB = 1 << 30; + uint32_t clamp = std::max(OneGiB, initialSize); clampedMaxSize = Some(std::min(clamp, *clampedMaxSize)); #endif } @@ -735,10 +735,10 @@ static bool CreateSpecificWasmBuffer( return false; } - uint64_t cur = clampedMaxSize.value() / 2; + uint32_t cur = clampedMaxSize.value() / 2; for (; cur > initialSize; cur /= 2) { - uint64_t clampedMaxSize = RoundUp(cur, wasm::PageSize); + uint32_t clampedMaxSize = RoundUp(cur, wasm::PageSize); buffer = RawbufT::Allocate(initialSize, Some(clampedMaxSize), mappedSize); if (buffer) { break; @@ -806,10 +806,8 @@ bool js::CreateWasmBuffer(JSContext* cx, const wasm::Limits& memory, MutableHandleArrayBufferObjectMaybeShared buffer) { MOZ_ASSERT(memory.initial % wasm::PageSize == 0); MOZ_RELEASE_ASSERT(cx->wasmHaveSignalHandlers); - MOZ_RELEASE_ASSERT(memory.initial <= ArrayBufferObject::MaxBufferByteLength); - static_assert(ArrayBufferObject::MaxBufferByteLength <= UINT32_MAX, - "wasm memory uses uint32_t and is limited by" - "MaxBufferByteLength"); + MOZ_RELEASE_ASSERT((memory.initial / wasm::PageSize) <= + wasm::MaxMemoryInitialPages); if (memory.shared == wasm::Shareable::True) { if (!cx->realm()->creationOptions().getSharedMemoryAndAtomicsEnabled()) { @@ -819,10 +817,10 @@ bool js::CreateWasmBuffer(JSContext* cx, const wasm::Limits& memory, } return CreateSpecificWasmBuffer( - cx, uint32_t(memory.initial), memory.maximum, buffer); + cx, memory.initial, memory.maximum, buffer); } return CreateSpecificWasmBuffer( - cx, uint32_t(memory.initial), memory.maximum, buffer); + cx, memory.initial, memory.maximum, buffer); } bool ArrayBufferObject::prepareForAsmJS() { @@ -980,11 +978,11 @@ size_t js::WasmArrayBufferMappedSize(const ArrayBufferObjectMaybeShared* buf) { return buf->as().wasmMappedSize(); } -Maybe ArrayBufferObject::wasmMaxSize() const { +Maybe ArrayBufferObject::wasmMaxSize() const { if (isWasm()) { return contents().wasmBuffer()->maxSize(); } else { - return Some(byteLength()); + return Some(byteLength()); } } @@ -1067,7 +1065,7 @@ bool ArrayBufferObject::wasmMovingGrowToSize( } if (wasm::ComputeMappedSize(newSize) <= oldBuf->wasmMappedSize() || - oldBuf->contents().wasmBuffer()->extendMappedSize(uint64_t(newSize))) { + oldBuf->contents().wasmBuffer()->extendMappedSize(newSize)) { return wasmGrowToSizeInPlace(newSize, oldBuf, newBuf, cx); } diff --git a/js/src/vm/ArrayBufferObject.h b/js/src/vm/ArrayBufferObject.h index 8ac00d865595..d8e43bc7db1e 100644 --- a/js/src/vm/ArrayBufferObject.h +++ b/js/src/vm/ArrayBufferObject.h @@ -119,7 +119,7 @@ class ArrayBufferObjectMaybeShared : public NativeObject { // Note: the eventual goal is to remove this from ArrayBuffer and have // (Shared)ArrayBuffers alias memory owned by some wasm::Memory object. - mozilla::Maybe wasmMaxSize() const { + mozilla::Maybe wasmMaxSize() const { return WasmArrayBufferMaxSize(this); } size_t wasmMappedSize() const { return WasmArrayBufferMappedSize(this); } @@ -429,7 +429,7 @@ class ArrayBufferObject : public ArrayBufferObjectMaybeShared { MOZ_MUST_USE bool prepareForAsmJS(); size_t wasmMappedSize() const; - mozilla::Maybe wasmMaxSize() const; + mozilla::Maybe wasmMaxSize() const; static MOZ_MUST_USE bool wasmGrowToSizeInPlace( uint32_t newSize, Handle oldBuf, MutableHandle newBuf, JSContext* cx); @@ -680,12 +680,12 @@ class MutableWrappedPtrOperations }; class WasmArrayRawBuffer { - mozilla::Maybe maxSize_; + mozilla::Maybe maxSize_; size_t mappedSize_; // Not including the header page uint32_t length_; protected: - WasmArrayRawBuffer(uint8_t* buffer, const mozilla::Maybe& maxSize, + WasmArrayRawBuffer(uint8_t* buffer, const mozilla::Maybe& maxSize, size_t mappedSize, uint32_t length) : maxSize_(maxSize), mappedSize_(mappedSize), length_(length) { MOZ_ASSERT(buffer == dataPointer()); @@ -693,7 +693,7 @@ class WasmArrayRawBuffer { public: static WasmArrayRawBuffer* Allocate(uint32_t numBytes, - const mozilla::Maybe& maxSize, + const mozilla::Maybe& maxSize, const mozilla::Maybe& mappedSize); static void Release(void* mem); @@ -711,17 +711,17 @@ class WasmArrayRawBuffer { size_t mappedSize() const { return mappedSize_; } - mozilla::Maybe maxSize() const { return maxSize_; } + mozilla::Maybe maxSize() const { return maxSize_; } uint32_t byteLength() const { return length_; } MOZ_MUST_USE bool growToSizeInPlace(uint32_t oldSize, uint32_t newSize); - MOZ_MUST_USE bool extendMappedSize(uint64_t maxSize); + MOZ_MUST_USE bool extendMappedSize(uint32_t maxSize); // Try and grow the mapped region of memory. Does not change current size. // Does not move memory if no space to grow. - void tryGrowMaxSizeInPlace(uint64_t deltaMaxSize); + void tryGrowMaxSizeInPlace(uint32_t deltaMaxSize); }; } // namespace js diff --git a/js/src/vm/SharedArrayObject.cpp b/js/src/vm/SharedArrayObject.cpp index 4f8a534045aa..5a290c4112b7 100644 --- a/js/src/vm/SharedArrayObject.cpp +++ b/js/src/vm/SharedArrayObject.cpp @@ -44,7 +44,7 @@ static size_t SharedArrayMappedSize(uint32_t length) { // `maxSize` must be something for wasm, nothing for other cases. SharedArrayRawBuffer* SharedArrayRawBuffer::Allocate( - uint32_t length, const Maybe& maxSize, + uint32_t length, const Maybe& maxSize, const Maybe& mappedSize) { MOZ_RELEASE_ASSERT(length <= ArrayBufferObject::MaxBufferByteLength); @@ -54,7 +54,7 @@ SharedArrayRawBuffer* SharedArrayRawBuffer::Allocate( } bool preparedForWasm = maxSize.isSome(); - uint64_t computedMaxSize; + uint32_t computedMaxSize; size_t computedMappedSize; if (preparedForWasm) { @@ -86,8 +86,8 @@ SharedArrayRawBuffer* SharedArrayRawBuffer::Allocate( return rawbuf; } -void SharedArrayRawBuffer::tryGrowMaxSizeInPlace(uint64_t deltaMaxSize) { - CheckedInt newMaxSize = maxSize_; +void SharedArrayRawBuffer::tryGrowMaxSizeInPlace(uint32_t deltaMaxSize) { + CheckedInt newMaxSize = maxSize_; newMaxSize += deltaMaxSize; MOZ_ASSERT(newMaxSize.isValid()); MOZ_ASSERT(newMaxSize.value() % wasm::PageSize == 0); diff --git a/js/src/vm/SharedArrayObject.h b/js/src/vm/SharedArrayObject.h index 198ac931a3c8..c70fc097e29b 100644 --- a/js/src/vm/SharedArrayObject.h +++ b/js/src/vm/SharedArrayObject.h @@ -51,7 +51,7 @@ class SharedArrayRawBuffer { mozilla::Atomic refcount_; mozilla::Atomic length_; Mutex growLock_; - uint64_t maxSize_; + uint32_t maxSize_; size_t mappedSize_; // Does not include the page for the header bool preparedForWasm_; @@ -66,7 +66,7 @@ class SharedArrayRawBuffer { } protected: - SharedArrayRawBuffer(uint8_t* buffer, uint32_t length, uint64_t maxSize, + SharedArrayRawBuffer(uint8_t* buffer, uint32_t length, uint32_t maxSize, size_t mappedSize, bool preparedForWasm) : refcount_(1), length_(length), @@ -94,7 +94,7 @@ class SharedArrayRawBuffer { // max must be Something for wasm, Nothing for other uses static SharedArrayRawBuffer* Allocate( - uint32_t length, const mozilla::Maybe& maxSize, + uint32_t length, const mozilla::Maybe& maxSize, const mozilla::Maybe& mappedSize); // This may be called from multiple threads. The caller must take @@ -118,13 +118,13 @@ class SharedArrayRawBuffer { uint32_t volatileByteLength() const { return length_; } - uint64_t maxSize() const { return maxSize_; } + uint32_t maxSize() const { return maxSize_; } size_t mappedSize() const { return mappedSize_; } bool isWasm() const { return preparedForWasm_; } - void tryGrowMaxSizeInPlace(uint64_t deltaMaxSize); + void tryGrowMaxSizeInPlace(uint32_t deltaMaxSize); bool wasmGrowToSizeInPlace(const Lock&, uint32_t newLength); @@ -229,7 +229,7 @@ class SharedArrayBufferObject : public ArrayBufferObjectMaybeShared { static SharedArrayBufferObject* createFromNewRawBuffer( JSContext* cx, SharedArrayRawBuffer* buffer, uint32_t initialSize); - mozilla::Maybe wasmMaxSize() const { + mozilla::Maybe wasmMaxSize() const { return mozilla::Some(rawBufferObject()->maxSize()); } diff --git a/js/src/wasm/AsmJS.cpp b/js/src/wasm/AsmJS.cpp index a547b0014e07..8dbfde901924 100644 --- a/js/src/wasm/AsmJS.cpp +++ b/js/src/wasm/AsmJS.cpp @@ -95,7 +95,7 @@ using mozilla::Compression::LZ4; // ARM greater or equal to MinHeapLength static const size_t MinHeapLength = PageSize; -static uint64_t RoundUpToNextValidAsmJSHeapLength(uint64_t length) { +static uint32_t RoundUpToNextValidAsmJSHeapLength(uint32_t length) { if (length <= MinHeapLength) { return MinHeapLength; } @@ -1434,7 +1434,7 @@ class MOZ_STACK_CLASS JS_HAZ_ROOTED ModuleValidatorShared { PropertyName* bufferArgumentName() const { return bufferArgumentName_; } const ModuleEnvironment& env() { return env_; } - uint64_t minMemoryLength() const { return env_.minMemoryLength; } + uint32_t minMemoryLength() const { return env_.minMemoryLength; } void initModuleFunctionName(PropertyName* name) { MOZ_ASSERT(!moduleFunctionName_); @@ -1975,7 +1975,7 @@ class MOZ_STACK_CLASS JS_HAZ_ROOTED ModuleValidator bool declareFuncPtrTable(FuncType&& sig, PropertyName* name, uint32_t firstUse, uint32_t mask, uint32_t* tableIndex) { - if (mask > MaxTableLength) { + if (mask > MaxTableInitialLength) { return failCurrentOffset("function pointer table too big"); } @@ -1993,7 +1993,7 @@ class MOZ_STACK_CLASS JS_HAZ_ROOTED ModuleValidator } env_.asmJSSigToTableIndex[sigIndex] = env_.tables.length(); - if (!env_.tables.emplaceBack(TableKind::AsmJS, mask + 1, Nothing())) { + if (!env_.tables.emplaceBack(TableKind::AsmJS, Limits(mask + 1))) { return false; } @@ -6721,13 +6721,12 @@ static bool CheckBuffer(JSContext* cx, const AsmJSMetadata& metadata, } buffer.set(&AsAnyArrayBuffer(bufferVal)); - uint64_t memoryLength = uint64_t(buffer->byteLength()); + uint32_t memoryLength = buffer->byteLength(); if (!IsValidAsmJSHeapLength(memoryLength)) { UniqueChars msg(JS_smprintf( - "ArrayBuffer byteLength 0x%" PRIu64 - " is not a valid heap length. The next " - "valid length is 0x%" PRIu64, + "ArrayBuffer byteLength 0x%x is not a valid heap length. The next " + "valid length is 0x%x", memoryLength, RoundUpToNextValidAsmJSHeapLength(memoryLength))); if (!msg) { return false; @@ -6740,11 +6739,10 @@ static bool CheckBuffer(JSContext* cx, const AsmJSMetadata& metadata, // byteLength has larger alignment. MOZ_ASSERT((metadata.minMemoryLength - 1) <= INT32_MAX); if (memoryLength < metadata.minMemoryLength) { - UniqueChars msg(JS_smprintf("ArrayBuffer byteLength of 0x%" PRIu64 - " is less than 0x%" PRIu64 " (the " - "size implied " - "by const heap accesses).", - memoryLength, metadata.minMemoryLength)); + UniqueChars msg(JS_smprintf( + "ArrayBuffer byteLength of 0x%x is less than 0x%x (the size implied " + "by const heap accesses).", + memoryLength, metadata.minMemoryLength)); if (!msg) { return false; } diff --git a/js/src/wasm/WasmBaselineCompile.cpp b/js/src/wasm/WasmBaselineCompile.cpp index 51feb029bffe..6a3bf05b80ed 100644 --- a/js/src/wasm/WasmBaselineCompile.cpp +++ b/js/src/wasm/WasmBaselineCompile.cpp @@ -10778,7 +10778,7 @@ RegI32 BaseCompiler::popMemoryAccess(MemoryAccessDesc* access, uint32_t offsetGuardLimit = GetOffsetGuardLimit(env_.hugeMemoryEnabled()); uint64_t ea = uint64_t(addr) + uint64_t(access->offset()); - uint64_t limit = env_.minMemoryLength + offsetGuardLimit; + uint64_t limit = uint64_t(env_.minMemoryLength) + offsetGuardLimit; check->omitBoundsCheck = ea < limit; check->omitAlignmentCheck = (ea & (access->byteSize() - 1)) == 0; diff --git a/js/src/wasm/WasmCode.h b/js/src/wasm/WasmCode.h index 67114dd90ada..3d01e43566f5 100644 --- a/js/src/wasm/WasmCode.h +++ b/js/src/wasm/WasmCode.h @@ -320,9 +320,9 @@ typedef Vector FuncImportVector; struct MetadataCacheablePod { ModuleKind kind; MemoryUsage memoryUsage; - uint64_t minMemoryLength; + uint32_t minMemoryLength; uint32_t globalDataLength; - Maybe maxMemoryLength; + Maybe maxMemoryLength; Maybe startFuncIndex; Maybe nameCustomSectionIndex; bool filenameIsURL; diff --git a/js/src/wasm/WasmConstants.h b/js/src/wasm/WasmConstants.h index 7bedc84cf20c..b7f86441589f 100644 --- a/js/src/wasm/WasmConstants.h +++ b/js/src/wasm/WasmConstants.h @@ -840,11 +840,6 @@ enum class NameType { Module = 0, Function = 1, Local = 2 }; enum class FieldFlags { Mutable = 0x01, AllowedMask = 0x01 }; -// The WebAssembly spec hard-codes the virtual page size to be 64KiB and -// requires the size of linear memory to always be a multiple of 64KiB. - -static const unsigned PageSize = 64 * 1024; - // These limits are agreed upon with other engines for consistency. static const unsigned MaxTypes = 1000000; @@ -854,10 +849,7 @@ static const unsigned MaxImports = 100000; static const unsigned MaxExports = 100000; static const unsigned MaxGlobals = 1000000; static const unsigned MaxDataSegments = 100000; -static const unsigned MaxDataSegmentLengthPages = 16384; static const unsigned MaxElemSegments = 10000000; -static const unsigned MaxElemSegmentLength = 10000000; -static const unsigned MaxTableLimitField = UINT32_MAX; static const unsigned MaxTableLength = 10000000; static const unsigned MaxLocals = 50000; static const unsigned MaxParams = 1000; @@ -865,15 +857,16 @@ static const unsigned MaxParams = 1000; // `env->funcMaxResults()` to get the correct value for a module. static const unsigned MaxResults = 1000; static const unsigned MaxStructFields = 1000; -static const unsigned MaxMemoryLimitField = 65536; -static const unsigned MaxMemoryPages = INT32_MAX / PageSize; +static const unsigned MaxMemoryMaximumPages = 65536; static const unsigned MaxStringBytes = 100000; static const unsigned MaxModuleBytes = 1024 * 1024 * 1024; static const unsigned MaxFunctionBytes = 7654321; // These limits pertain to our WebAssembly implementation only. +static const unsigned MaxTableInitialLength = 10000000; static const unsigned MaxBrTableElems = 1000000; +static const unsigned MaxMemoryInitialPages = 16384; static const unsigned MaxCodeSectionBytes = MaxModuleBytes; static const unsigned MaxResultsForJitEntry = 1; static const unsigned MaxResultsForJitExit = 1; diff --git a/js/src/wasm/WasmIonCompile.cpp b/js/src/wasm/WasmIonCompile.cpp index 13690b761e19..b61ac3496703 100644 --- a/js/src/wasm/WasmIonCompile.cpp +++ b/js/src/wasm/WasmIonCompile.cpp @@ -1449,10 +1449,10 @@ class FunctionCompiler { MOZ_ASSERT(funcType.id.kind() == FuncTypeIdDescKind::None); const TableDesc& table = env_.tables[env_.asmJSSigToTableIndex[funcTypeIndex]]; - MOZ_ASSERT(IsPowerOfTwo(table.initialLength)); + MOZ_ASSERT(IsPowerOfTwo(table.limits.initial)); MConstant* mask = - MConstant::New(alloc(), Int32Value(table.initialLength - 1)); + MConstant::New(alloc(), Int32Value(table.limits.initial - 1)); curBlock_->add(mask); MBitAnd* maskedIndex = MBitAnd::New(alloc(), index, mask, MIRType::Int32); curBlock_->add(maskedIndex); diff --git a/js/src/wasm/WasmJS.cpp b/js/src/wasm/WasmJS.cpp index 6211b4491614..1d453cbed0d9 100644 --- a/js/src/wasm/WasmJS.cpp +++ b/js/src/wasm/WasmJS.cpp @@ -860,8 +860,9 @@ static bool EnforceRangeU32(JSContext* cx, HandleValue v, const char* kind, return true; } -static bool GetLimits(JSContext* cx, HandleObject obj, uint32_t maximumField, - const char* kind, Limits* limits, Shareable allowShared) { +static bool GetLimits(JSContext* cx, HandleObject obj, uint32_t maxInitial, + uint32_t maxMaximum, const char* kind, Limits* limits, + Shareable allowShared) { JSAtom* initialAtom = Atomize(cx, "initial", strlen("initial")); if (!initialAtom) { return false; @@ -873,13 +874,12 @@ static bool GetLimits(JSContext* cx, HandleObject obj, uint32_t maximumField, return false; } - uint32_t initial = 0; - if (!EnforceRangeU32(cx, initialVal, kind, "initial size", &initial)) { + if (!EnforceRangeU32(cx, initialVal, kind, "initial size", + &limits->initial)) { return false; } - limits->initial = initial; - if (limits->initial > maximumField) { + if (limits->initial > maxInitial) { JS_ReportErrorNumberUTF8(cx, GetErrorMessage, nullptr, JSMSG_WASM_BAD_RANGE, kind, "initial size"); return false; @@ -898,13 +898,13 @@ static bool GetLimits(JSContext* cx, HandleObject obj, uint32_t maximumField, // maxVal does not have a default value. if (!maxVal.isUndefined()) { - uint32_t maximum = 0; - if (!EnforceRangeU32(cx, maxVal, kind, "maximum size", &maximum)) { + limits->maximum.emplace(); + if (!EnforceRangeU32(cx, maxVal, kind, "maximum size", + limits->maximum.ptr())) { return false; } - limits->maximum = Some(maximum); - if (*limits->maximum > maximumField || limits->initial > *limits->maximum) { + if (*limits->maximum > maxMaximum || limits->initial > *limits->maximum) { JS_ReportErrorNumberUTF8(cx, GetErrorMessage, nullptr, JSMSG_WASM_BAD_RANGE, kind, "maximum size"); return false; @@ -2057,14 +2057,8 @@ bool WasmMemoryObject::construct(JSContext* cx, unsigned argc, Value* vp) { RootedObject obj(cx, &args[0].toObject()); Limits limits; - if (!GetLimits(cx, obj, MaxMemoryLimitField, "Memory", &limits, - Shareable::True)) { - return false; - } - - if (limits.initial > MaxMemoryPages) { - JS_ReportErrorNumberUTF8(cx, GetErrorMessage, nullptr, - JSMSG_WASM_MEM_IMP_LIMIT); + if (!GetLimits(cx, obj, MaxMemoryInitialPages, MaxMemoryMaximumPages, + "Memory", &limits, Shareable::True)) { return false; } @@ -2419,8 +2413,7 @@ void WasmTableObject::trace(JSTracer* trc, JSObject* obj) { } /* static */ -WasmTableObject* WasmTableObject::create(JSContext* cx, uint32_t initialLength, - Maybe maximumLength, +WasmTableObject* WasmTableObject::create(JSContext* cx, const Limits& limits, TableKind tableKind, HandleObject proto) { AutoSetNewObjectMetadata metadata(cx); @@ -2432,12 +2425,10 @@ WasmTableObject* WasmTableObject::create(JSContext* cx, uint32_t initialLength, MOZ_ASSERT(obj->isNewborn()); - TableDesc td(tableKind, initialLength, maximumLength, - /*importedOrExported=*/true); + TableDesc td(tableKind, limits, /*importedOrExported=*/true); SharedTable table = Table::create(cx, td, obj); if (!table) { - ReportOutOfMemory(cx); return nullptr; } @@ -2515,14 +2506,8 @@ bool WasmTableObject::construct(JSContext* cx, unsigned argc, Value* vp) { } Limits limits; - if (!GetLimits(cx, obj, MaxTableLimitField, "Table", &limits, - Shareable::False)) { - return false; - } - - if (limits.initial > MaxTableLength) { - JS_ReportErrorNumberUTF8(cx, GetErrorMessage, nullptr, - JSMSG_WASM_TABLE_IMP_LIMIT); + if (!GetLimits(cx, obj, MaxTableInitialLength, MaxTableLength, "Table", + &limits, Shareable::False)) { return false; } @@ -2535,17 +2520,8 @@ bool WasmTableObject::construct(JSContext* cx, unsigned argc, Value* vp) { proto = GlobalObject::getOrCreatePrototype(cx, JSProto_WasmTable); } - // The rest of the runtime expects table limits to be within a 32-bit range. - static_assert(MaxTableLimitField <= UINT32_MAX, "invariant"); - uint32_t initialLength = uint32_t(limits.initial); - Maybe maximumLength; - if (limits.maximum) { - maximumLength = Some(uint32_t(*limits.maximum)); - } - RootedWasmTableObject table( - cx, WasmTableObject::create(cx, initialLength, maximumLength, tableKind, - proto)); + cx, WasmTableObject::create(cx, limits, tableKind, proto)); if (!table) { return false; } diff --git a/js/src/wasm/WasmJS.h b/js/src/wasm/WasmJS.h index 4befea4118c8..f101205da77d 100644 --- a/js/src/wasm/WasmJS.h +++ b/js/src/wasm/WasmJS.h @@ -442,8 +442,7 @@ class WasmTableObject : public NativeObject { // Note that, after creation, a WasmTableObject's table() is not initialized // and must be initialized before use. - static WasmTableObject* create(JSContext* cx, uint32_t initialLength, - mozilla::Maybe maximumLength, + static WasmTableObject* create(JSContext* cx, const wasm::Limits& limits, wasm::TableKind tableKind, HandleObject proto); wasm::Table& table() const; }; diff --git a/js/src/wasm/WasmModule.cpp b/js/src/wasm/WasmModule.cpp index c8b3f8285d13..e3f22371983b 100644 --- a/js/src/wasm/WasmModule.cpp +++ b/js/src/wasm/WasmModule.cpp @@ -684,11 +684,10 @@ bool Module::instantiateFunctions(JSContext* cx, return true; } -template -static bool CheckLimits(JSContext* cx, T declaredMin, - const Maybe& declaredMax, T actualLength, - const Maybe& actualMax, bool isAsmJS, - const char* kind) { +static bool CheckLimits(JSContext* cx, uint32_t declaredMin, + const Maybe& declaredMax, + uint32_t actualLength, const Maybe& actualMax, + bool isAsmJS, const char* kind) { if (isAsmJS) { MOZ_ASSERT(actualLength >= declaredMin); MOZ_ASSERT(!declaredMax); @@ -747,18 +746,17 @@ bool Module::instantiateMemory(JSContext* cx, return true; } - uint64_t declaredMin = metadata().minMemoryLength; - Maybe declaredMax = metadata().maxMemoryLength; + uint32_t declaredMin = metadata().minMemoryLength; + Maybe declaredMax = metadata().maxMemoryLength; bool declaredShared = metadata().memoryUsage == MemoryUsage::Shared; if (memory) { MOZ_ASSERT_IF(metadata().isAsmJS(), memory->buffer().isPreparedForAsmJS()); MOZ_ASSERT_IF(!metadata().isAsmJS(), memory->buffer().isWasm()); - if (!CheckLimits(cx, declaredMin, declaredMax, - uint64_t(memory->volatileMemoryLength()), - memory->buffer().wasmMaxSize(), metadata().isAsmJS(), - "Memory")) { + if (!CheckLimits( + cx, declaredMin, declaredMax, memory->volatileMemoryLength(), + memory->buffer().wasmMaxSize(), metadata().isAsmJS(), "Memory")) { return false; } @@ -768,12 +766,6 @@ bool Module::instantiateMemory(JSContext* cx, } else { MOZ_ASSERT(!metadata().isAsmJS()); - if (declaredMin / PageSize > MaxMemoryPages) { - JS_ReportErrorNumberUTF8(cx, GetErrorMessage, nullptr, - JSMSG_WASM_MEM_IMP_LIMIT); - return false; - } - RootedArrayBufferObjectMaybeShared buffer(cx); Limits l(declaredMin, declaredMax, declaredShared ? Shareable::True : Shareable::False); @@ -802,7 +794,7 @@ bool Module::instantiateImportedTable(JSContext* cx, const TableDesc& td, MOZ_ASSERT(!metadata().isAsmJS()); Table& table = tableObj->table(); - if (!CheckLimits(cx, td.initialLength, td.maximumLength, table.length(), + if (!CheckLimits(cx, td.limits.initial, td.limits.maximum, table.length(), table.maximum(), metadata().isAsmJS(), "Table")) { return false; } @@ -823,19 +815,12 @@ bool Module::instantiateImportedTable(JSContext* cx, const TableDesc& td, bool Module::instantiateLocalTable(JSContext* cx, const TableDesc& td, WasmTableObjectVector* tableObjs, SharedTableVector* tables) const { - if (td.initialLength > MaxTableLength) { - JS_ReportErrorNumberUTF8(cx, GetErrorMessage, nullptr, - JSMSG_WASM_TABLE_IMP_LIMIT); - return false; - } - SharedTable table; Rooted tableObj(cx); if (td.importedOrExported) { RootedObject proto( cx, &cx->global()->getPrototype(JSProto_WasmTable).toObject()); - tableObj.set(WasmTableObject::create(cx, td.initialLength, td.maximumLength, - td.kind, proto)); + tableObj.set(WasmTableObject::create(cx, td.limits, td.kind, proto)); if (!tableObj) { return false; } @@ -843,7 +828,6 @@ bool Module::instantiateLocalTable(JSContext* cx, const TableDesc& td, } else { table = Table::create(cx, td, /* HandleWasmTableObject = */ nullptr); if (!table) { - ReportOutOfMemory(cx); return false; } } diff --git a/js/src/wasm/WasmTable.cpp b/js/src/wasm/WasmTable.cpp index 460d0bf9dc82..1e19a05a11a1 100644 --- a/js/src/wasm/WasmTable.cpp +++ b/js/src/wasm/WasmTable.cpp @@ -35,8 +35,8 @@ Table::Table(JSContext* cx, const TableDesc& desc, observers_(cx->zone()), functions_(std::move(functions)), kind_(desc.kind), - length_(desc.initialLength), - maximum_(desc.maximumLength) { + length_(desc.limits.initial), + maximum_(desc.limits.maximum) { MOZ_ASSERT(repr() == TableRepr::Func); } @@ -46,8 +46,8 @@ Table::Table(JSContext* cx, const TableDesc& desc, observers_(cx->zone()), objects_(std::move(objects)), kind_(desc.kind), - length_(desc.initialLength), - maximum_(desc.maximumLength) { + length_(desc.limits.initial), + maximum_(desc.limits.maximum) { MOZ_ASSERT(repr() == TableRepr::Ref); } @@ -58,7 +58,7 @@ SharedTable Table::create(JSContext* cx, const TableDesc& desc, case TableKind::FuncRef: case TableKind::AsmJS: { UniqueFuncRefArray functions( - cx->pod_calloc(desc.initialLength)); + cx->pod_calloc(desc.limits.initial)); if (!functions) { return nullptr; } @@ -67,7 +67,7 @@ SharedTable Table::create(JSContext* cx, const TableDesc& desc, } case TableKind::AnyRef: { TableAnyRefVector objects; - if (!objects.resize(desc.initialLength)) { + if (!objects.resize(desc.limits.initial)) { return nullptr; } return SharedTable( diff --git a/js/src/wasm/WasmTypes.cpp b/js/src/wasm/WasmTypes.cpp index c55bad485efa..772ff780b4a6 100644 --- a/js/src/wasm/WasmTypes.cpp +++ b/js/src/wasm/WasmTypes.cpp @@ -45,9 +45,11 @@ using mozilla::MakeEnumeratedRange; # endif #endif -static_assert(MaxMemoryPages == +// More sanity checks. + +static_assert(MaxMemoryInitialPages <= ArrayBufferObject::MaxBufferByteLength / PageSize, - "invariant"); + "Memory sizing constraint"); // All plausible targets must be able to do at least IEEE754 double // loads/stores, hence the lower limit of 8. Some Intel processors support @@ -589,10 +591,8 @@ bool wasm::IsValidARMImmediate(uint32_t i) { return valid; } -uint64_t wasm::RoundUpToNextValidARMImmediate(uint64_t i) { - MOZ_ASSERT(i <= HighestValidARMImmediate); - static_assert(HighestValidARMImmediate == 0xff000000, - "algorithm relies on specific constant"); +uint32_t wasm::RoundUpToNextValidARMImmediate(uint32_t i) { + MOZ_ASSERT(i <= 0xff000000); if (i <= 16 * 1024 * 1024) { i = i ? mozilla::RoundUpPow2(i) : 0; @@ -613,7 +613,7 @@ bool wasm::IsValidBoundsCheckImmediate(uint32_t i) { #endif } -size_t wasm::ComputeMappedSize(uint64_t maxSize) { +size_t wasm::ComputeMappedSize(uint32_t maxSize) { MOZ_ASSERT(maxSize % PageSize == 0); // It is the bounds-check limit, not the mapped size, that gets baked into @@ -621,9 +621,9 @@ size_t wasm::ComputeMappedSize(uint64_t maxSize) { // *before* adding in the guard page. #ifdef JS_CODEGEN_ARM - uint64_t boundsCheckLimit = RoundUpToNextValidARMImmediate(maxSize); + uint32_t boundsCheckLimit = RoundUpToNextValidARMImmediate(maxSize); #else - uint64_t boundsCheckLimit = maxSize; + uint32_t boundsCheckLimit = maxSize; #endif MOZ_ASSERT(IsValidBoundsCheckImmediate(boundsCheckLimit)); diff --git a/js/src/wasm/WasmTypes.h b/js/src/wasm/WasmTypes.h index 61eb6df73bac..06afd25c4b1f 100644 --- a/js/src/wasm/WasmTypes.h +++ b/js/src/wasm/WasmTypes.h @@ -2774,15 +2774,15 @@ bool IsRoundingFunction(SymbolicAddress callee, jit::RoundingMode* mode); // Represents the resizable limits of memories and tables. struct Limits { - uint64_t initial; - Maybe maximum; + uint32_t initial; + Maybe maximum; // `shared` is Shareable::False for tables but may be Shareable::True for // memories. Shareable shared; Limits() = default; - explicit Limits(uint64_t initial, const Maybe& maximum = Nothing(), + explicit Limits(uint32_t initial, const Maybe& maximum = Nothing(), Shareable shared = Shareable::False) : initial(initial), maximum(maximum), shared(shared) {} }; @@ -2814,17 +2814,15 @@ struct TableDesc { TableKind kind; bool importedOrExported; uint32_t globalDataOffset; - uint32_t initialLength; - Maybe maximumLength; + Limits limits; TableDesc() = default; - TableDesc(TableKind kind, uint32_t initialLength, - Maybe maximumLength, bool importedOrExported = false) + TableDesc(TableKind kind, const Limits& limits, + bool importedOrExported = false) : kind(kind), importedOrExported(importedOrExported), globalDataOffset(UINT32_MAX), - initialLength(initialLength), - maximumLength(maximumLength) {} + limits(limits) {} }; typedef Vector TableDescVector; @@ -3035,7 +3033,7 @@ class CalleeDesc { CalleeDesc c; c.which_ = WasmTable; c.u.table.globalDataOffset_ = desc.globalDataOffset; - c.u.table.minLength_ = desc.initialLength; + c.u.table.minLength_ = desc.limits.initial; c.u.table.funcTypeId_ = funcTypeId; return c; } @@ -3092,11 +3090,14 @@ class CalleeDesc { // Because ARM has a fixed-width instruction encoding, ARM can only express a // limited subset of immediates (in a single instruction). -static const uint64_t HighestValidARMImmediate = 0xff000000; - extern bool IsValidARMImmediate(uint32_t i); -extern uint64_t RoundUpToNextValidARMImmediate(uint64_t i); +extern uint32_t RoundUpToNextValidARMImmediate(uint32_t i); + +// The WebAssembly spec hard-codes the virtual page size to be 64KiB and +// requires the size of linear memory to always be a multiple of 64KiB. + +static const unsigned PageSize = 64 * 1024; // Bounds checks always compare the base of the memory access with the bounds // check limit. If the memory access is unaligned, this means that, even if the @@ -3171,7 +3172,7 @@ extern bool IsValidBoundsCheckImmediate(uint32_t i); // boundsCheckLimit = mappedSize - GuardSize // IsValidBoundsCheckImmediate(boundsCheckLimit) -extern size_t ComputeMappedSize(uint64_t maxSize); +extern size_t ComputeMappedSize(uint32_t maxSize); // The following thresholds were derived from a microbenchmark. If we begin to // ship this optimization for more platforms, we will need to extend this list. diff --git a/js/src/wasm/WasmValidate.cpp b/js/src/wasm/WasmValidate.cpp index 522638ea1032..13aad62b22fe 100644 --- a/js/src/wasm/WasmValidate.cpp +++ b/js/src/wasm/WasmValidate.cpp @@ -1842,11 +1842,9 @@ static bool DecodeLimits(Decoder& d, Limits* limits, uint32_t(flags & ~uint8_t(mask))); } - uint32_t initial; - if (!d.readVarU32(&initial)) { + if (!d.readVarU32(&limits->initial)) { return d.fail("expected initial length"); } - limits->initial = initial; if (flags & uint8_t(MemoryTableFlags::HasMaximum)) { uint32_t maximum; @@ -1857,11 +1855,11 @@ static bool DecodeLimits(Decoder& d, Limits* limits, if (limits->initial > maximum) { return d.failf( "memory size minimum must not be greater than maximum; " - "maximum length %" PRIu32 " is less than initial length %" PRIu64, + "maximum length %" PRIu32 " is less than initial length %" PRIu32, maximum, limits->initial); } - limits->maximum.emplace(uint64_t(maximum)); + limits->maximum.emplace(maximum); } limits->shared = Shareable::False; @@ -1913,9 +1911,8 @@ static bool DecodeTableTypeAndLimits(Decoder& d, bool refTypesEnabled, // If there's a maximum, check it is in range. The check to exclude // initial > maximum is carried out by the DecodeLimits call above, so // we don't repeat it here. - if (limits.initial > MaxTableLimitField || - ((limits.maximum.isSome() && - limits.maximum.value() > MaxTableLimitField))) { + if (limits.initial > MaxTableInitialLength || + ((limits.maximum.isSome() && limits.maximum.value() > MaxTableLength))) { return d.fail("too many table elements"); } @@ -1923,15 +1920,7 @@ static bool DecodeTableTypeAndLimits(Decoder& d, bool refTypesEnabled, return d.fail("too many tables"); } - // The rest of the runtime expects table limits to be within a 32-bit range. - static_assert(MaxTableLimitField <= UINT32_MAX, "invariant"); - uint32_t initialLength = uint32_t(limits.initial); - Maybe maximumLength; - if (limits.maximum) { - maximumLength = Some(uint32_t(*limits.maximum)); - } - - return tables->emplaceBack(tableKind, initialLength, maximumLength); + return tables->emplaceBack(tableKind, limits); } static bool GlobalIsJSCompatible(Decoder& d, ValType type) { @@ -1985,12 +1974,30 @@ static bool DecodeGlobalType(Decoder& d, const TypeDefVector& types, } void wasm::ConvertMemoryPagesToBytes(Limits* memory) { - memory->initial *= PageSize; + CheckedInt initialBytes = memory->initial; + initialBytes *= PageSize; + + static_assert(MaxMemoryInitialPages < UINT16_MAX, + "multiplying by PageSize can't overflow"); + MOZ_ASSERT(initialBytes.isValid(), "can't overflow by above assertion"); + + memory->initial = initialBytes.value(); if (!memory->maximum) { return; } - *memory->maximum *= PageSize; + + MOZ_ASSERT(*memory->maximum <= MaxMemoryMaximumPages); + + CheckedInt maximumBytes = *memory->maximum; + maximumBytes *= PageSize; + + // 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); + + MOZ_ASSERT(memory->initial <= *memory->maximum); } static bool DecodeMemoryLimits(Decoder& d, ModuleEnvironment* env) { @@ -2003,11 +2010,11 @@ static bool DecodeMemoryLimits(Decoder& d, ModuleEnvironment* env) { return false; } - if (memory.initial > MaxMemoryLimitField) { + if (memory.initial > MaxMemoryInitialPages) { return d.fail("initial memory size too big"); } - if (memory.maximum && *memory.maximum > MaxMemoryLimitField) { + if (memory.maximum && *memory.maximum > MaxMemoryMaximumPages) { return d.fail("maximum memory size too big"); } @@ -2736,7 +2743,7 @@ static bool DecodeElemSection(Decoder& d, ModuleEnvironment* env) { return d.fail("expected segment size"); } - if (numElems > MaxElemSegmentLength) { + if (numElems > MaxTableInitialLength) { return d.fail("too many table elements"); } @@ -3053,7 +3060,7 @@ static bool DecodeDataSection(Decoder& d, ModuleEnvironment* env) { return d.fail("expected segment size"); } - if (seg.length > MaxDataSegmentLengthPages * PageSize) { + if (seg.length > MaxMemoryInitialPages * PageSize) { return d.fail("segment size too big"); } diff --git a/js/src/wasm/WasmValidate.h b/js/src/wasm/WasmValidate.h index ce3769cb87de..157b39f304f5 100644 --- a/js/src/wasm/WasmValidate.h +++ b/js/src/wasm/WasmValidate.h @@ -159,8 +159,8 @@ struct ModuleEnvironment { // validating an asm.js module) and immutable during compilation: Maybe dataCount; MemoryUsage memoryUsage; - uint64_t minMemoryLength; - Maybe maxMemoryLength; + uint32_t minMemoryLength; + Maybe maxMemoryLength; uint32_t numStructTypes; TypeDefVector types; FuncTypeWithIdPtrVector funcTypes;