tor-browser

The Tor Browser
git clone https://git.dasho.dev/tor-browser.git
Log | Files | Refs | README | LICENSE

commit 7950242da0f65154aeeed52e1a18866ce56ef730
parent a548794c3bff22010945cd0a4f80fc05c9d689ba
Author: Asumu Takikawa <asumu@igalia.com>
Date:   Mon,  8 Dec 2025 16:02:53 +0000

Bug 1977854 - Wasm custom page sizes part 3: initial support for tiny pages in Baseline r=bvisness

Supporting tiny page sizes requires a number of changes to Wasm memory
allocation and bounds checks:

  * Memories with tiny page size are allocated to the closest system page
    size.
  * Bounds have granularity smaller than the page size, so guard pages
    cannot be used, so offsets must always be checked.
  * Similarly, access size must be accounted for. We handle this by
    storing a bounds check limit for each access size.
  * Tiny page sizes disable huge memory, and also prevent bounds
    check elimination.

Tiny pages are unsupported in Ion in this commit.

Differential Revision: https://phabricator.services.mozilla.com/D267331

Diffstat:
Ajs/src/jit-test/tests/wasm/custom-page-sizes/runtime.js | 504+++++++++++++++++++++++++++++++++++++++++++++++++++++++++++++++++++++++++++++++
Mjs/src/jit/shared/Assembler-shared.cpp | 3++-
Mjs/src/vm/ArrayBufferObject.cpp | 41+++++++++++++++++++++++++++++++++++------
Mjs/src/vm/ArrayBufferObject.h | 2+-
Mjs/src/vm/SharedArrayObject.cpp | 2+-
Mjs/src/wasm/WasmBCClass-inl.h | 38++++++++++++++++++++++++++++++++++----
Mjs/src/wasm/WasmBCClass.h | 19++++++++++---------
Mjs/src/wasm/WasmBCMemory.cpp | 64++++++++++++++++++++++++++++++++++++++++++++--------------------
Mjs/src/wasm/WasmInstance.cpp | 18++++++++++++++++--
Mjs/src/wasm/WasmInstanceData.h | 10++++++++++
Mjs/src/wasm/WasmIonCompile.cpp | 6++++--
Mjs/src/wasm/WasmJS.cpp | 15++++++++++++++-
Mjs/src/wasm/WasmMemory.cpp | 43++++++++++++++++++++++++++++++++++++++-----
Mjs/src/wasm/WasmMemory.h | 2+-
Mjs/src/wasm/WasmMetadata.h | 3++-
Mjs/src/wasm/WasmModule.cpp | 28++++++++++++++++++++--------
Mjs/src/wasm/WasmModuleTypes.h | 2+-
Mjs/src/wasm/WasmProcess.cpp | 6+++---
Mjs/src/wasm/WasmProcess.h | 2+-
19 files changed, 741 insertions(+), 67 deletions(-)

diff --git a/js/src/jit-test/tests/wasm/custom-page-sizes/runtime.js b/js/src/jit-test/tests/wasm/custom-page-sizes/runtime.js @@ -0,0 +1,504 @@ +// |jit-test| skip-if: !wasmCustomPageSizesEnabled() + +// Test access to an index from a local. +{ + let instance = wasmEvalText(`(module + (memory i32 10 10 (pagesize 1)) + (func (export "f") (param i32) (result i32) + (i32.store (local.get 0) (i32.const 42)) + (i32.load (local.get 0)) + ) + ) + `); + + assertEq(instance.exports.f(5), 42); +} + +// Test constant memory access. +{ + let instance = wasmEvalText(`(module + (memory i32 10 10 (pagesize 1)) + (func (export "f") (result i32) + (i32.store (i32.const 5) (i32.const 42)) + (i32.load (i32.const 5)) + ) + ) + `); + + assertEq(instance.exports.f(), 42); +} + +// Test bounds checks with various offsets. +{ + let instance = wasmEvalText(`(module + (memory i32 10 10 (pagesize 1)) + (func (export "f") (param i32) + (i32.store8 offset=1 (local.get 0) (i32.const 42)) + ) + ) + `); + + instance.exports.f(1); + instance.exports.f(8); + assertErrorMessage(() => instance.exports.f(9), WebAssembly.RuntimeError, "index out of bounds"); + assertErrorMessage(() => instance.exports.f(10), WebAssembly.RuntimeError, "index out of bounds"); +} + +{ + let instance = wasmEvalText(`(module + (memory i32 20 20 (pagesize 1)) + (func (export "f") (param i32) + (i32.store8 offset=17 (local.get 0) (i32.const 42)) + ) + ) + `); + + instance.exports.f(0); + instance.exports.f(2); + assertErrorMessage(() => instance.exports.f(3), WebAssembly.RuntimeError, "index out of bounds"); + assertErrorMessage(() => instance.exports.f(10), WebAssembly.RuntimeError, "index out of bounds"); + assertErrorMessage(() => instance.exports.f(19), WebAssembly.RuntimeError, "index out of bounds"); +} + +{ + let instance = wasmEvalText(`(module + (memory i32 20 20 (pagesize 1)) + (func (export "f") (param i32) + (i32.store offset=2 (local.get 0) (i32.const 42)) + ) + ) + `); + + instance.exports.f(0); + instance.exports.f(14); + assertErrorMessage(() => instance.exports.f(15), WebAssembly.RuntimeError, "index out of bounds"); + assertErrorMessage(() => instance.exports.f(19), WebAssembly.RuntimeError, "index out of bounds"); +} + +// Test bounds checks with various access sizes. +{ + let instance = wasmEvalText(`(module + (memory i32 10 10 (pagesize 1)) + (func (export "f") (param i32) + (i32.store8 (local.get 0) (i32.const 42)) + ) + ) + `); + + instance.exports.f(1); + instance.exports.f(3); + instance.exports.f(9); + assertErrorMessage(() => instance.exports.f(10), WebAssembly.RuntimeError, "index out of bounds"); +} + +{ + let instance = wasmEvalText(`(module + (memory i32 10 10 (pagesize 1)) + (func (export "f") (param i32) + (i32.store16 (local.get 0) (i32.const 42)) + ) + ) + `); + + instance.exports.f(1); + instance.exports.f(3); + assertErrorMessage(() => instance.exports.f(9), WebAssembly.RuntimeError, "index out of bounds"); + assertErrorMessage(() => instance.exports.f(10), WebAssembly.RuntimeError, "index out of bounds"); +} + +{ + let instance = wasmEvalText(`(module + (memory i32 10 10 (pagesize 1)) + (func (export "f") (param i32) + (i32.store (local.get 0) (i32.const 42)) + ) + ) + `); + + instance.exports.f(1); + instance.exports.f(6); + assertErrorMessage(() => instance.exports.f(7), WebAssembly.RuntimeError, "index out of bounds"); + assertErrorMessage(() => instance.exports.f(8), WebAssembly.RuntimeError, "index out of bounds"); + assertErrorMessage(() => instance.exports.f(9), WebAssembly.RuntimeError, "index out of bounds"); + assertErrorMessage(() => instance.exports.f(10), WebAssembly.RuntimeError, "index out of bounds"); +} + +{ + let instance = wasmEvalText(`(module + (memory i32 10 10 (pagesize 1)) + (func (export "f") (param i32) + (i64.store (local.get 0) (i64.const 42)) + ) + ) + `); + + instance.exports.f(1); + instance.exports.f(2); + assertErrorMessage(() => instance.exports.f(3), WebAssembly.RuntimeError, "index out of bounds"); + assertErrorMessage(() => instance.exports.f(4), WebAssembly.RuntimeError, "index out of bounds"); + assertErrorMessage(() => instance.exports.f(5), WebAssembly.RuntimeError, "index out of bounds"); + assertErrorMessage(() => instance.exports.f(6), WebAssembly.RuntimeError, "index out of bounds"); + assertErrorMessage(() => instance.exports.f(7), WebAssembly.RuntimeError, "index out of bounds"); + assertErrorMessage(() => instance.exports.f(8), WebAssembly.RuntimeError, "index out of bounds"); + assertErrorMessage(() => instance.exports.f(9), WebAssembly.RuntimeError, "index out of bounds"); + assertErrorMessage(() => instance.exports.f(10), WebAssembly.RuntimeError, "index out of bounds"); +} + +{ + let instance = wasmEvalText(`(module + (memory i32 20 20 (pagesize 1)) + (func (export "f") (param i32) (result i32) + (i32x4.extract_lane 0 (v128.load (local.get 0))) + ) + ) + `); + + instance.exports.f(1); + instance.exports.f(4); + assertErrorMessage(() => instance.exports.f(5), WebAssembly.RuntimeError, "index out of bounds"); + assertErrorMessage(() => instance.exports.f(10), WebAssembly.RuntimeError, "index out of bounds"); + assertErrorMessage(() => instance.exports.f(15), WebAssembly.RuntimeError, "index out of bounds"); + assertErrorMessage(() => instance.exports.f(20), WebAssembly.RuntimeError, "index out of bounds"); +} + +{ + let instance = wasmEvalText(`(module + (memory i32 0 0 (pagesize 1)) + (func (export "f") (param i32) + (i64.store8 (local.get 0) (i64.const 42)) + ) + ) + `); + + assertErrorMessage(() => instance.exports.f(0), WebAssembly.RuntimeError, "index out of bounds"); + assertErrorMessage(() => instance.exports.f(1), WebAssembly.RuntimeError, "index out of bounds"); +} + +{ + let instance = wasmEvalText(`(module + (memory i32 0 0 (pagesize 1)) + (func (export "f") (param i32) + (i64.store16 (local.get 0) (i64.const 42)) + ) + ) + `); + + assertErrorMessage(() => instance.exports.f(0), WebAssembly.RuntimeError, "index out of bounds"); + assertErrorMessage(() => instance.exports.f(1), WebAssembly.RuntimeError, "index out of bounds"); +} + +{ + let instance = wasmEvalText(`(module + (memory i32 0 0 (pagesize 1)) + (func (export "f") (param i32) + (i32.store (local.get 0) (i32.const 42)) + ) + ) + `); + + assertErrorMessage(() => instance.exports.f(0), WebAssembly.RuntimeError, "index out of bounds"); + assertErrorMessage(() => instance.exports.f(1), WebAssembly.RuntimeError, "index out of bounds"); +} + +{ + let instance = wasmEvalText(`(module + (memory i32 0 0 (pagesize 1)) + (func (export "f") (param i32) + (i64.store (local.get 0) (i64.const 42)) + ) + ) + `); + + assertErrorMessage(() => instance.exports.f(0), WebAssembly.RuntimeError, "index out of bounds"); + assertErrorMessage(() => instance.exports.f(1), WebAssembly.RuntimeError, "index out of bounds"); +} + +{ + let instance = wasmEvalText(`(module + (memory i64 0 0 (pagesize 1)) + (func (export "f") (param i64) (result i32) + (i32x4.extract_lane 0 (v128.load (local.get 0))) + ) + ) + `); + + assertErrorMessage(() => instance.exports.f(0n), WebAssembly.RuntimeError, "index out of bounds"); + assertErrorMessage(() => instance.exports.f(1n), WebAssembly.RuntimeError, "index out of bounds"); + assertErrorMessage(() => instance.exports.f(2n), WebAssembly.RuntimeError, "index out of bounds"); +} + +{ + let instance = wasmEvalText(`(module + (memory i32 1 1 (pagesize 1)) + (func (export "f") (param i32) + (i64.store16 (local.get 0) (i64.const 42)) + ) + ) + `); + + assertErrorMessage(() => instance.exports.f(0), WebAssembly.RuntimeError, "index out of bounds"); + assertErrorMessage(() => instance.exports.f(1), WebAssembly.RuntimeError, "index out of bounds"); + assertErrorMessage(() => instance.exports.f(2), WebAssembly.RuntimeError, "index out of bounds"); +} + +{ + let instance = wasmEvalText(`(module + (memory i32 2 2 (pagesize 1)) + (func (export "f") (param i32) + (i64.store16 (local.get 0) (i64.const 42)) + ) + ) + `); + + instance.exports.f(0); + assertErrorMessage(() => instance.exports.f(1), WebAssembly.RuntimeError, "index out of bounds"); + assertErrorMessage(() => instance.exports.f(2), WebAssembly.RuntimeError, "index out of bounds"); +} + +{ + let instance = wasmEvalText(`(module + (memory i32 4 4 (pagesize 1)) + (func (export "f") (param i32) + (i64.store32 (local.get 0) (i64.const 42)) + ) + ) + `); + + instance.exports.f(0); + assertErrorMessage(() => instance.exports.f(1), WebAssembly.RuntimeError, "index out of bounds"); + assertErrorMessage(() => instance.exports.f(2), WebAssembly.RuntimeError, "index out of bounds"); +} + +{ + let instance = wasmEvalText(`(module + (memory i32 8 8 (pagesize 1)) + (func (export "f") (param i32) + (i64.store (local.get 0) (i64.const 42)) + ) + ) + `); + + instance.exports.f(0); + assertErrorMessage(() => instance.exports.f(1), WebAssembly.RuntimeError, "index out of bounds"); + assertErrorMessage(() => instance.exports.f(2), WebAssembly.RuntimeError, "index out of bounds"); +} + +{ + let instance = wasmEvalText(`(module + (memory i64 16 16 (pagesize 1)) + (func (export "f") (param i64) (result i32) + (i32x4.extract_lane 0 (v128.load (local.get 0))) + ) + ) + `); + + instance.exports.f(0n); + assertErrorMessage(() => instance.exports.f(1n), WebAssembly.RuntimeError, "index out of bounds"); + assertErrorMessage(() => instance.exports.f(2n), WebAssembly.RuntimeError, "index out of bounds"); +} + +// Test ARM-related cases +{ + let instance = wasmEvalText(`(module + (memory i32 69648 69648 (pagesize 1)) + (func (export "f") (param i32) + (i32.store8 (local.get 0) (i32.const 42)) + ) + ) + `); + + instance.exports.f(0) + instance.exports.f(69647) + assertErrorMessage(() => instance.exports.f(69648), WebAssembly.RuntimeError, "index out of bounds"); + assertErrorMessage(() => instance.exports.f(69649), WebAssembly.RuntimeError, "index out of bounds"); +} + +{ + let instance = wasmEvalText(`(module + (memory i64 69648 69648 (pagesize 1)) + (func (export "f") (param i64) + (i32.store8 (local.get 0) (i32.const 42)) + ) + ) + `); + + instance.exports.f(0n) + instance.exports.f(69647n) + assertErrorMessage(() => instance.exports.f(69648n), WebAssembly.RuntimeError, "index out of bounds"); + assertErrorMessage(() => instance.exports.f(69649n), WebAssembly.RuntimeError, "index out of bounds"); +} + +// Test bounds checking under 4GB. +{ + let instance = wasmEvalText(`(module + (memory i64 5000 10000 (pagesize 1)) + (func (export "f") + (i32.store (i64.const 11000) (i32.const 42)) + ) + ) + `); + + assertErrorMessage(() => instance.exports.f(), WebAssembly.RuntimeError, "index out of bounds"); +} + +{ + let instance = wasmEvalText(`(module + (memory i64 5000 (pagesize 1)) + (func (export "f") + (i32.store (i64.const 11000) (i32.const 42)) + ) + ) + `); + + assertErrorMessage(() => instance.exports.f(), WebAssembly.RuntimeError, "index out of bounds"); +} + +{ + let instance = wasmEvalText(`(module + (memory i64 5000 128000 (pagesize 1)) + (func (export "f") + (i32.store (i64.const 64000) (i32.const 42)) + ) + ) + `); + + assertErrorMessage(() => instance.exports.f(), WebAssembly.RuntimeError, "index out of bounds"); +} + +{ + let instance = wasmEvalText(`(module + (memory i64 5000 10000 (pagesize 1)) + (func (export "f") + (i32.store (i64.const 6000) (i32.const 42)) + ) + ) + `); + + assertErrorMessage(() => instance.exports.f(), WebAssembly.RuntimeError, "index out of bounds"); +} + +{ + let instance = wasmEvalText(`(module + (memory i32 5000 10000 (pagesize 1)) + (func (export "f") + (i32.store (i32.const 11000) (i32.const 42)) + ) + ) + `); + + assertErrorMessage(() => instance.exports.f(), WebAssembly.RuntimeError, "index out of bounds"); +} + +{ + let instance = wasmEvalText(`(module + (memory i32 5000 128000 (pagesize 1)) + (func (export "f") + (i32.store (i32.const 64000) (i32.const 42)) + ) + ) + `); + + assertErrorMessage(() => instance.exports.f(), WebAssembly.RuntimeError, "index out of bounds"); +} + +{ + let instance = wasmEvalText(`(module + (memory i32 5000 10000 (pagesize 1)) + (func (export "f") + (i32.store (i32.const 6000) (i32.const 42)) + ) + ) + `); + + assertErrorMessage(() => instance.exports.f(), WebAssembly.RuntimeError, "index out of bounds"); +} + +if (getBuildConfiguration("x86")) { + assertErrorMessage( + () => wasmEvalText(`(module + (memory i32 4294967295 4294967295 (pagesize 1)) + (func (export "f") (param i32) + (i32.store8 (local.get 0) (i32.const 42)) + ) + ) + `), + WebAssembly.RuntimeError, + "too many memory pages" + ); +} else { + { + let instance = wasmEvalText(`(module + (memory i32 4294967295 4294967295 (pagesize 1)) + (func (export "f") (param i32) + (i32.store8 (local.get 0) (i32.const 42)) + ) + ) + `); + + instance.exports.f(1); + instance.exports.f(3000); + instance.exports.f(4294967292); + instance.exports.f(4294967293); + instance.exports.f(4294967294); + assertErrorMessage(() => instance.exports.f(4294967295), WebAssembly.RuntimeError, "index out of bounds"); + } + + { + let instance = wasmEvalText(`(module + (memory i32 4294967295 4294967295 (pagesize 1)) + (func (export "f") (param i32) + (i32.store16 (local.get 0) (i32.const 42)) + ) + ) + `); + + instance.exports.f(1); + instance.exports.f(3000); + instance.exports.f(4294967292); + instance.exports.f(4294967293); + assertErrorMessage(() => instance.exports.f(4294967294), WebAssembly.RuntimeError, "index out of bounds"); + assertErrorMessage(() => instance.exports.f(4294967295), WebAssembly.RuntimeError, "index out of bounds"); + } + + { + let instance = wasmEvalText(`(module + (memory i32 4294967295 (pagesize 1)) + (func (export "f") (param i32) + (i32.store16 (local.get 0) (i32.const 42)) + ) + ) + `); + + instance.exports.f(1); + instance.exports.f(3000); + instance.exports.f(4294967292); + instance.exports.f(4294967293); + assertErrorMessage(() => instance.exports.f(4294967294), WebAssembly.RuntimeError, "index out of bounds"); + assertErrorMessage(() => instance.exports.f(4294967295), WebAssembly.RuntimeError, "index out of bounds"); + } +} + +// Ensure bounds checking above 4GB works as expected. +if (getBuildConfiguration("x86")) { + assertErrorMessage( + () => wasmEvalText(`(module + (memory i64 4294967299 4294967299 (pagesize 1)) + (func (export "f") + (i32.store (i64.const 4294967300) (i32.const 42)) + ) + ) + `), + WebAssembly.RuntimeError, + "too many memory pages" + ); +} else { + let instance = wasmEvalText(`(module + (memory i64 4294967299 4294967299 (pagesize 1)) + (func (export "f") + (i32.store (i64.const 4294967300) (i32.const 42)) + ) + ) + `); + + assertErrorMessage(() => instance.exports.f(), WebAssembly.RuntimeError, "index out of bounds"); +} diff --git a/js/src/jit/shared/Assembler-shared.cpp b/js/src/jit/shared/Assembler-shared.cpp @@ -17,7 +17,8 @@ namespace wasm { #ifdef DEBUG void MemoryAccessDesc::assertOffsetInGuardPages() const { - MOZ_ASSERT(offset_ < (uint64_t)GetMaxOffsetGuardLimit(hugeMemory_)); + MOZ_ASSERT(offset_ < (uint64_t)GetMaxOffsetGuardLimit( + hugeMemory_, wasm::PageSize::Standard)); } #endif diff --git a/js/src/vm/ArrayBufferObject.cpp b/js/src/vm/ArrayBufferObject.cpp @@ -172,8 +172,8 @@ uint64_t js::WasmReservedBytes() { return wasmReservedBytes; } return true; } -void* js::MapBufferMemory(wasm::AddressType t, size_t mappedSize, - size_t initialCommittedSize) { +void* js::MapBufferMemory(wasm::AddressType t, wasm::PageSize pageSize, + size_t mappedSize, size_t initialCommittedSize) { MOZ_ASSERT(mappedSize % gc::SystemPageSize() == 0); MOZ_ASSERT(initialCommittedSize % gc::SystemPageSize() == 0); MOZ_ASSERT(initialCommittedSize <= mappedSize); @@ -1622,20 +1622,37 @@ WasmArrayRawBuffer* WasmArrayRawBuffer::AllocateWasm( // Use an override mapped size, or else compute the mapped size from // initialMappedPages. +#ifdef ENABLE_WASM_CUSTOM_PAGE_SIZES + MOZ_ASSERT_IF(pageSize == wasm::PageSize::Tiny, !mapped.isSome()); +#endif size_t mappedSize = mapped.isSome() ? *mapped : wasm::ComputeMappedSize(initialMappedPages); MOZ_RELEASE_ASSERT(mappedSize <= SIZE_MAX - gc::SystemPageSize()); MOZ_RELEASE_ASSERT(numBytes <= SIZE_MAX - gc::SystemPageSize()); MOZ_RELEASE_ASSERT(initialPages <= clampedMaxPages); - MOZ_ASSERT(numBytes % gc::SystemPageSize() == 0); + // With custom page sizes, the wasm-visible byte length may not fall along + // system page boundaries. + MOZ_ASSERT_IF(pageSize == wasm::PageSize::Standard, + numBytes % gc::SystemPageSize() == 0); MOZ_ASSERT(mappedSize % gc::SystemPageSize() == 0); uint64_t mappedSizeWithHeader = mappedSize + gc::SystemPageSize(); +#ifndef ENABLE_WASM_CUSTOM_PAGE_SIZES uint64_t numBytesWithHeader = numBytes + gc::SystemPageSize(); +#else + // For tiny page size, the mapped size and the committed size are the + // same since we do not have a slop area or guard page. + uint64_t numBytesWithHeader = pageSize == wasm::PageSize::Tiny + ? mappedSizeWithHeader + : (numBytes + gc::SystemPageSize()); +#endif - void* data = MapBufferMemory(addressType, (size_t)mappedSizeWithHeader, - (size_t)numBytesWithHeader); + MOZ_ASSERT(numBytesWithHeader % gc::SystemPageSize() == 0); + + void* data = + MapBufferMemory(addressType, pageSize, (size_t)mappedSizeWithHeader, + (size_t)numBytesWithHeader); if (!data) { return nullptr; } @@ -1656,7 +1673,15 @@ void WasmArrayRawBuffer::Release(void* mem) { MOZ_RELEASE_ASSERT(header->mappedSize() <= SIZE_MAX - gc::SystemPageSize()); size_t mappedSizeWithHeader = header->mappedSize() + gc::SystemPageSize(); +#ifndef ENABLE_WASM_CUSTOM_PAGE_SIZES size_t committedSize = header->byteLength() + gc::SystemPageSize(); +#else + // See above for numBytesWithHeader in AllocateWasm. + size_t committedSize = header->pageSize() == wasm::PageSize::Tiny + ? mappedSizeWithHeader + : (header->byteLength() + gc::SystemPageSize()); +#endif + MOZ_ASSERT(committedSize % gc::SystemPageSize() == 0); static_assert(std::is_trivially_destructible_v<WasmArrayRawBuffer>, "no need to call the destructor"); @@ -1673,7 +1698,8 @@ WasmArrayRawBuffer* ArrayBufferObject::BufferContents::wasmBuffer() const { template <typename ObjT, typename RawbufT> static ArrayBufferObjectMaybeShared* CreateSpecificWasmBuffer( JSContext* cx, const wasm::MemoryDesc& memory) { - bool useHugeMemory = wasm::IsHugeMemoryEnabled(memory.addressType()); + bool useHugeMemory = + wasm::IsHugeMemoryEnabled(memory.addressType(), memory.pageSize()); wasm::PageSize pageSize = memory.pageSize(); Pages initialPages = memory.initialPages(); Maybe<Pages> sourceMaxPages = memory.maximumPages(); @@ -1797,7 +1823,9 @@ ArrayBufferObjectMaybeShared* js::CreateWasmBuffer( memory.initialPages() <= wasm::MaxMemoryPages(memory.addressType(), memory.pageSize())); MOZ_RELEASE_ASSERT(cx->wasm().haveSignalHandlers); +#ifndef ENABLE_WASM_CUSTOM_PAGE_SIZES MOZ_ASSERT(memory.pageSize() == wasm::PageSize::Standard); +#endif if (memory.isShared()) { if (!cx->realm()->creationOptions().getSharedMemoryAndAtomicsEnabled()) { @@ -2180,6 +2208,7 @@ ArrayBufferObject* ArrayBufferObject::wasmMovingGrowToPages( Pages clampedMaxPages = wasm::ClampedMaxPages(t, newPages, Nothing(), /* hugeMemory */ false); + MOZ_ASSERT(newPages.pageSize() == oldBuf->wasmPageSize()); WasmArrayRawBuffer* newRawBuf = WasmArrayRawBuffer::AllocateWasm( oldBuf->wasmAddressType(), oldBuf->wasmPageSize(), newPages, clampedMaxPages, Nothing(), Nothing()); diff --git a/js/src/vm/ArrayBufferObject.h b/js/src/vm/ArrayBufferObject.h @@ -39,7 +39,7 @@ struct MemoryDesc; // of size `initialCommittedSize`. Both arguments denote bytes and must be // multiples of the page size, with `initialCommittedSize` <= `mappedSize`. // Returns nullptr on failure. -void* MapBufferMemory(wasm::AddressType, size_t mappedSize, +void* MapBufferMemory(wasm::AddressType, wasm::PageSize, size_t mappedSize, size_t initialCommittedSize); // Commit additional memory in an existing mapping. `dataEnd` must be the diff --git a/js/src/vm/SharedArrayObject.cpp b/js/src/vm/SharedArrayObject.cpp @@ -124,7 +124,7 @@ WasmSharedArrayRawBuffer* WasmSharedArrayRawBuffer::AllocateWasm( uint64_t mappedSizeWithHeader = computedMappedSize + gc::SystemPageSize(); uint64_t accessibleSizeWithHeader = accessibleSize + gc::SystemPageSize(); - void* p = MapBufferMemory(addressType, mappedSizeWithHeader, + void* p = MapBufferMemory(addressType, pageSize, mappedSizeWithHeader, accessibleSizeWithHeader); if (!p) { return nullptr; diff --git a/js/src/wasm/WasmBCClass-inl.h b/js/src/wasm/WasmBCClass-inl.h @@ -72,13 +72,43 @@ uint32_t BaseCompiler::instanceOffsetOfMemoryBase(uint32_t memoryIndex) const { } uint32_t BaseCompiler::instanceOffsetOfBoundsCheckLimit( - uint32_t memoryIndex) const { - if (memoryIndex == 0) { + uint32_t memoryIndex, unsigned byteSize) const { + bool hasCustomPageSize = false; +#ifdef ENABLE_WASM_CUSTOM_PAGE_SIZES + hasCustomPageSize = + codeMeta_.memories[memoryIndex].pageSize() != PageSize::Standard; +#endif + + if (memoryIndex == 0 && !hasCustomPageSize) { return Instance::offsetOfMemory0BoundsCheckLimit(); } + uintptr_t boundsCheckOffset; +#ifndef ENABLE_WASM_CUSTOM_PAGE_SIZES + boundsCheckOffset = offsetof(MemoryInstanceData, boundsCheckLimit); +#else + switch (byteSize) { + case 1: + boundsCheckOffset = offsetof(MemoryInstanceData, boundsCheckLimit); + break; + case 2: + boundsCheckOffset = offsetof(MemoryInstanceData, boundsCheckLimit16); + break; + case 4: + boundsCheckOffset = offsetof(MemoryInstanceData, boundsCheckLimit32); + break; + case 8: + boundsCheckOffset = offsetof(MemoryInstanceData, boundsCheckLimit64); + break; + case 16: + boundsCheckOffset = offsetof(MemoryInstanceData, boundsCheckLimit128); + break; + default: + MOZ_CRASH("invalid byte size for memory access"); + break; + } +#endif return Instance::offsetInData( - codeMeta_.offsetOfMemoryInstanceData(memoryIndex) + - offsetof(MemoryInstanceData, boundsCheckLimit)); + codeMeta_.offsetOfMemoryInstanceData(memoryIndex) + boundsCheckOffset); } // The results parameter for BaseCompiler::emitCallArgs is used for diff --git a/js/src/wasm/WasmBCClass.h b/js/src/wasm/WasmBCClass.h @@ -395,7 +395,8 @@ struct BaseCompiler final { inline bool isMem64(uint32_t memoryIndex) const; inline bool hugeMemoryEnabled(uint32_t memoryIndex) const; inline uint32_t instanceOffsetOfMemoryBase(uint32_t memoryIndex) const; - inline uint32_t instanceOffsetOfBoundsCheckLimit(uint32_t memoryIndex) const; + inline uint32_t instanceOffsetOfBoundsCheckLimit(uint32_t memoryIndex, + unsigned byteSize) const; // The casts are used by some of the ScratchRegister implementations. operator MacroAssembler&() const { return masm; } @@ -1236,17 +1237,17 @@ struct BaseCompiler final { void branchAddNoOverflow(uint64_t offset, RegI32 ptr, Label* ok); void branchTestLowZero(RegI32 ptr, Imm32 mask, Label* ok); - void boundsCheck4GBOrLargerAccess(uint32_t memoryIndex, RegPtr instance, - RegI32 ptr, Label* ok); - void boundsCheckBelow4GBAccess(uint32_t memoryIndex, RegPtr instance, - RegI32 ptr, Label* ok); + void boundsCheck4GBOrLargerAccess(uint32_t memoryIndex, unsigned byteSize, + RegPtr instance, RegI32 ptr, Label* ok); + void boundsCheckBelow4GBAccess(uint32_t memoryIndex, unsigned byteSize, + RegPtr instance, RegI32 ptr, Label* ok); void branchAddNoOverflow(uint64_t offset, RegI64 ptr, Label* ok); void branchTestLowZero(RegI64 ptr, Imm32 mask, Label* ok); - void boundsCheck4GBOrLargerAccess(uint32_t memoryIndex, RegPtr instance, - RegI64 ptr, Label* ok); - void boundsCheckBelow4GBAccess(uint32_t memoryIndex, RegPtr instance, - RegI64 ptr, Label* ok); + void boundsCheck4GBOrLargerAccess(uint32_t memoryIndex, unsigned byteSize, + RegPtr instance, RegI64 ptr, Label* ok); + void boundsCheckBelow4GBAccess(uint32_t memoryIndex, unsigned byteSize, + RegPtr instance, RegI64 ptr, Label* ok); // Some consumers depend on the returned Address not incorporating instance, // as instance may be the scratch register. diff --git a/js/src/wasm/WasmBCMemory.cpp b/js/src/wasm/WasmBCMemory.cpp @@ -102,8 +102,14 @@ void BaseCompiler::bceCheckLocal(MemoryAccessDesc* access, AccessCheck* check, return; } - uint64_t offsetGuardLimit = - GetMaxOffsetGuardLimit(codeMeta_.hugeMemoryEnabled(0)); +#ifdef ENABLE_WASM_CUSTOM_PAGE_SIZES + if (codeMeta_.memories[0].pageSize() != PageSize::Standard) { + return; + } +#endif + + uint64_t offsetGuardLimit = GetMaxOffsetGuardLimit( + codeMeta_.hugeMemoryEnabled(0), codeMeta_.memories[0].pageSize()); if ((bceSafe_ & (BCESet(1) << local)) && access->offset64() < offsetGuardLimit) { @@ -142,7 +148,8 @@ RegI32 BaseCompiler::popConstMemoryAccess<RegI32>(MemoryAccessDesc* access, uint32_t addr = addrTemp; uint64_t offsetGuardLimit = GetMaxOffsetGuardLimit( - codeMeta_.hugeMemoryEnabled(access->memoryIndex())); + codeMeta_.hugeMemoryEnabled(access->memoryIndex()), + codeMeta_.memories[access->memoryIndex()].pageSize()); // Validation ensures that the offset is in 32-bit range, and the calculation // of the limit cannot overflow due to our choice of HugeOffsetGuardLimit. @@ -179,7 +186,8 @@ RegI64 BaseCompiler::popConstMemoryAccess<RegI64>(MemoryAccessDesc* access, uint64_t addr = addrTemp; uint64_t offsetGuardLimit = GetMaxOffsetGuardLimit( - codeMeta_.hugeMemoryEnabled(access->memoryIndex())); + codeMeta_.hugeMemoryEnabled(access->memoryIndex()), + codeMeta_.memories[access->memoryIndex()].pageSize()); mozilla::CheckedUint64 ea(addr); ea += access->offset64(); @@ -290,6 +298,7 @@ void BaseCompiler::branchTestLowZero(RegI64 ptr, Imm32 mask, Label* ok) { } void BaseCompiler::boundsCheck4GBOrLargerAccess(uint32_t memoryIndex, + unsigned byteSize, RegPtr instance, RegI32 ptr, Label* ok) { #ifdef JS_64BIT @@ -309,7 +318,7 @@ void BaseCompiler::boundsCheck4GBOrLargerAccess(uint32_t memoryIndex, masm.move32To64ZeroExtend(ptr, ptr64); # endif - boundsCheck4GBOrLargerAccess(memoryIndex, instance, ptr64, ok); + boundsCheck4GBOrLargerAccess(memoryIndex, byteSize, instance, ptr64, ok); // Restore the value to the canonical form for a 32-bit value in a // 64-bit register and/or the appropriate form for further use in the @@ -326,31 +335,34 @@ void BaseCompiler::boundsCheck4GBOrLargerAccess(uint32_t memoryIndex, } void BaseCompiler::boundsCheckBelow4GBAccess(uint32_t memoryIndex, - RegPtr instance, RegI32 ptr, - Label* ok) { + unsigned byteSize, RegPtr instance, + RegI32 ptr, Label* ok) { // If the memory's max size is known to be smaller than 64K pages exactly, // we can use a 32-bit check and avoid extension and wrapping. - masm.wasmBoundsCheck32( - Assembler::Below, ptr, - Address(instance, instanceOffsetOfBoundsCheckLimit(memoryIndex)), ok); + masm.wasmBoundsCheck32(Assembler::Below, ptr, + Address(instance, instanceOffsetOfBoundsCheckLimit( + memoryIndex, byteSize)), + ok); } void BaseCompiler::boundsCheck4GBOrLargerAccess(uint32_t memoryIndex, + unsigned byteSize, RegPtr instance, RegI64 ptr, Label* ok) { // Any Spectre mitigation will appear to update the ptr64 register. - masm.wasmBoundsCheck64( - Assembler::Below, ptr, - Address(instance, instanceOffsetOfBoundsCheckLimit(memoryIndex)), ok); + masm.wasmBoundsCheck64(Assembler::Below, ptr, + Address(instance, instanceOffsetOfBoundsCheckLimit( + memoryIndex, byteSize)), + ok); } void BaseCompiler::boundsCheckBelow4GBAccess(uint32_t memoryIndex, - RegPtr instance, RegI64 ptr, - Label* ok) { + unsigned byteSize, RegPtr instance, + RegI64 ptr, Label* ok) { // The bounds check limit is valid to 64 bits, so there's no sense in doing // anything complicated here. There may be optimization paths here in the // future and they may differ on 32-bit and 64-bit. - boundsCheck4GBOrLargerAccess(memoryIndex, instance, ptr, ok); + boundsCheck4GBOrLargerAccess(memoryIndex, byteSize, instance, ptr, ok); } // Make sure the ptr could be used as an index register. @@ -371,11 +383,14 @@ template <typename RegAddressType> void BaseCompiler::prepareMemoryAccess(MemoryAccessDesc* access, AccessCheck* check, RegPtr instance, RegAddressType ptr) { +#ifndef ENABLE_WASM_CUSTOM_PAGE_SIZES MOZ_ASSERT(codeMeta_.memories[access->memoryIndex()].pageSize() == PageSize::Standard); +#endif uint64_t offsetGuardLimit = GetMaxOffsetGuardLimit( - codeMeta_.hugeMemoryEnabled(access->memoryIndex())); + codeMeta_.hugeMemoryEnabled(access->memoryIndex()), + codeMeta_.memories[access->memoryIndex()].pageSize()); // Fold offset if necessary for further computations. if (access->offset64() >= offsetGuardLimit || @@ -417,6 +432,12 @@ void BaseCompiler::prepareMemoryAccess(MemoryAccessDesc* access, // Bounds check if required. +#ifdef ENABLE_WASM_CUSTOM_PAGE_SIZES + MOZ_ASSERT_IF(codeMeta_.memories[access->memoryIndex()].pageSize() != + PageSize::Standard, + !codeMeta_.hugeMemoryEnabled(access->memoryIndex())); +#endif + if (!codeMeta_.hugeMemoryEnabled(access->memoryIndex()) && !check->omitBoundsCheck) { Label ok; @@ -428,12 +449,15 @@ void BaseCompiler::prepareMemoryAccess(MemoryAccessDesc* access, MaxMemoryBytes(codeMeta_.memories[access->memoryIndex()].addressType(), codeMeta_.memories[access->memoryIndex()].pageSize()) >= 0x100000000) { - boundsCheck4GBOrLargerAccess(access->memoryIndex(), instance, ptr, &ok); + boundsCheck4GBOrLargerAccess(access->memoryIndex(), access->byteSize(), + instance, ptr, &ok); } else { - boundsCheckBelow4GBAccess(access->memoryIndex(), instance, ptr, &ok); + boundsCheckBelow4GBAccess(access->memoryIndex(), access->byteSize(), + instance, ptr, &ok); } #else - boundsCheckBelow4GBAccess(access->memoryIndex(), instance, ptr, &ok); + boundsCheckBelow4GBAccess(access->memoryIndex(), access->byteSize(), + instance, ptr, &ok); #endif trap(Trap::OutOfBounds); masm.bind(&ok); diff --git a/js/src/wasm/WasmInstance.cpp b/js/src/wasm/WasmInstance.cpp @@ -614,8 +614,10 @@ static int32_t PerformWake(Instance* instance, PtrT byteOffset, int32_t count, Pages pages = instance->memory(memoryIndex)->volatilePages(); #ifdef JS_64BIT // Ensure that the memory size is no more than 4GiB. - MOZ_ASSERT(pages <= Pages::fromPageCount(MaxMemory32StandardPagesValidation, - pages.pageSize())); + MOZ_ASSERT(pages <= + Pages::fromPageCount( + MaxMemoryPagesValidation(AddressType::I32, pages.pageSize()), + pages.pageSize())); #endif return uint32_t(pages.pageCount()); } @@ -2682,6 +2684,12 @@ bool Instance::init(JSContext* cx, const JSObjectVector& funcImports, MOZ_ASSERT(limit <= UINT32_MAX); #endif data.boundsCheckLimit = limit; +#ifdef ENABLE_WASM_CUSTOM_PAGE_SIZES + data.boundsCheckLimit16 = limit > 1 ? limit - 1 : 0; + data.boundsCheckLimit32 = limit > 3 ? limit - 3 : 0; + data.boundsCheckLimit64 = limit > 7 ? limit - 7 : 0; + data.boundsCheckLimit128 = limit > 15 ? limit - 15 : 0; +#endif data.isShared = md.isShared(); // Add observer if our memory base may grow @@ -4047,6 +4055,12 @@ void Instance::onMovingGrowMemory(const WasmMemoryObject* memory) { MOZ_ASSERT(limit <= UINT32_MAX); #endif md.boundsCheckLimit = limit; +#ifdef ENABLE_WASM_CUSTOM_PAGE_SIZES + md.boundsCheckLimit16 = limit > 1 ? limit - 1 : 0; + md.boundsCheckLimit32 = limit > 3 ? limit - 3 : 0; + md.boundsCheckLimit64 = limit > 7 ? limit - 7 : 0; + md.boundsCheckLimit128 = limit > 15 ? limit - 15 : 0; +#endif if (i == 0) { memory0Base_ = md.base; diff --git a/js/src/wasm/WasmInstanceData.h b/js/src/wasm/WasmInstanceData.h @@ -157,6 +157,16 @@ struct MemoryInstanceData { // See "Linear memory addresses and bounds checking" in WasmMemory.cpp. uintptr_t boundsCheckLimit; + // The default boundsCheckLimit is used for standard page sizes and also 8-bit + // memory accesses on custom page sizes. These other limits are only used for + // accesses on memories with custom page sizes. +#ifdef ENABLE_WASM_CUSTOM_PAGE_SIZES + uintptr_t boundsCheckLimit16; + uintptr_t boundsCheckLimit32; + uintptr_t boundsCheckLimit64; + uintptr_t boundsCheckLimit128; +#endif + // Whether this memory is shared or not. bool isShared; }; diff --git a/js/src/wasm/WasmIonCompile.cpp b/js/src/wasm/WasmIonCompile.cpp @@ -1544,7 +1544,8 @@ class FunctionCompiler { // by both explicit bounds checking and bounds check elimination. void foldConstantPointer(MemoryAccessDesc* access, MDefinition** base) { uint64_t offsetGuardLimit = GetMaxOffsetGuardLimit( - codeMeta().hugeMemoryEnabled(access->memoryIndex())); + codeMeta().hugeMemoryEnabled(access->memoryIndex()), + codeMeta().memories[access->memoryIndex()].pageSize()); if ((*base)->isConstant()) { uint64_t basePtr = 0; @@ -1569,7 +1570,8 @@ class FunctionCompiler { void maybeComputeEffectiveAddress(MemoryAccessDesc* access, MDefinition** base, bool mustAddOffset) { uint64_t offsetGuardLimit = GetMaxOffsetGuardLimit( - codeMeta().hugeMemoryEnabled(access->memoryIndex())); + codeMeta().hugeMemoryEnabled(access->memoryIndex()), + codeMeta().memories[access->memoryIndex()].pageSize()); if (access->offset64() >= offsetGuardLimit || access->offset64() > UINT32_MAX || mustAddOffset || diff --git a/js/src/wasm/WasmJS.cpp b/js/src/wasm/WasmJS.cpp @@ -2206,7 +2206,8 @@ bool WasmMemoryObject::construct(JSContext* cx, unsigned argc, Value* vp) { Rooted<WasmMemoryObject*> memoryObj( cx, WasmMemoryObject::create( - cx, buffer, IsHugeMemoryEnabled(limits.addressType), proto)); + cx, buffer, + IsHugeMemoryEnabled(limits.addressType, limits.pageSize), proto)); if (!memoryObj) { return false; } @@ -2597,6 +2598,17 @@ size_t WasmMemoryObject::boundsCheckLimit() const { if (!buffer().isWasm() || isHuge()) { return buffer().byteLength(); } +#ifdef ENABLE_WASM_CUSTOM_PAGE_SIZES + // For tiny page sizes, we need to use the actual byte length as the bounds + // check as we cannot rely on virtual memory for accesses between the byte + // length and the mapped size. + if (buffer().wasmPageSize() == wasm::PageSize::Tiny) { + size_t limit = buffer().byteLength(); + MOZ_ASSERT(limit <= MaxMemoryBoundsCheckLimit(addressType(), + buffer().wasmPageSize())); + return limit; + } +#endif size_t mappedSize = buffer().wasmMappedSize(); #if !defined(JS_64BIT) // See clamping performed in CreateSpecificWasmBuffer(). On 32-bit systems @@ -2605,6 +2617,7 @@ size_t WasmMemoryObject::boundsCheckLimit() const { // max field. MOZ_ASSERT(mappedSize < UINT32_MAX); #endif + MOZ_ASSERT(buffer().wasmPageSize() == wasm::PageSize::Standard); MOZ_ASSERT(mappedSize % wasm::StandardPageSizeBytes == 0); MOZ_ASSERT(mappedSize >= wasm::GuardSize); size_t limit = mappedSize - wasm::GuardSize; diff --git a/js/src/wasm/WasmMemory.cpp b/js/src/wasm/WasmMemory.cpp @@ -299,11 +299,16 @@ static_assert(MaxMemoryAccessSize < GuardSize, static_assert(OffsetGuardLimit < UINT32_MAX, "checking for overflow against OffsetGuardLimit is enough."); -uint64_t wasm::GetMaxOffsetGuardLimit(bool hugeMemory) { +uint64_t wasm::GetMaxOffsetGuardLimit(bool hugeMemory, PageSize sz) { +#ifndef ENABLE_WASM_CUSTOM_PAGE_SIZES + MOZ_ASSERT(sz == PageSize::Standard); +#endif + + uint64_t guardLimit = sz == PageSize::Standard ? OffsetGuardLimit : 0; #ifdef WASM_SUPPORTS_HUGE_MEMORY - return hugeMemory ? HugeOffsetGuardLimit : OffsetGuardLimit; + return hugeMemory ? HugeOffsetGuardLimit : guardLimit; #else - return OffsetGuardLimit; + return guardLimit; #endif } @@ -315,7 +320,7 @@ static_assert(MaxInlineMemoryFillLength < MinOffsetGuardLimit, "precondition"); wasm::Pages wasm::MaxMemoryPages(AddressType t, PageSize pageSize) { #ifdef JS_64BIT - MOZ_ASSERT_IF(t == AddressType::I64, !IsHugeMemoryEnabled(t)); + MOZ_ASSERT_IF(t == AddressType::I64, !IsHugeMemoryEnabled(t, pageSize)); size_t desired = MaxMemoryPagesValidation(t, pageSize); size_t actual = ArrayBufferObject::ByteLengthLimit / PageSizeInBytes(pageSize); @@ -380,9 +385,37 @@ size_t wasm::ComputeMappedSize(wasm::Pages clampedMaxPages) { // implementation limits. size_t maxSize = clampedMaxPages.byteLength(); + // For tiny page sizes, round up the mapped size to a multiple of the + // system page size after clamping. +#ifdef ENABLE_WASM_CUSTOM_PAGE_SIZES + if (clampedMaxPages.pageSize() == wasm::PageSize::Tiny) { + mozilla::CheckedInt<size_t> length(maxSize); + + if (length.value() % gc::SystemPageSize() != 0) { + length += ComputeByteAlignment(length.value(), gc::SystemPageSize()); + // This should be valid because of previous clamping. + MOZ_RELEASE_ASSERT(length.isValid()); + MOZ_ASSERT(length.value() % gc::SystemPageSize() == 0); + maxSize = length.value(); + } + + MOZ_ASSERT(maxSize <= clampedMaxPages.byteLength() + GuardSize); + } +#endif + MOZ_ASSERT(maxSize % gc::SystemPageSize() == 0); MOZ_ASSERT(GuardSize % gc::SystemPageSize() == 0); - maxSize += GuardSize; + if (clampedMaxPages.pageSize() == PageSize::Standard) { + maxSize += GuardSize; + } else { +#ifdef ENABLE_WASM_CUSTOM_PAGE_SIZES + // In the case of a tiny page, we omit the guard page size + // because we can't use guard pages for tiny page bounds checks. + MOZ_ASSERT(clampedMaxPages.pageSize() == PageSize::Tiny); +#else + MOZ_CRASH(); +#endif + } return maxSize; } diff --git a/js/src/wasm/WasmMemory.h b/js/src/wasm/WasmMemory.h @@ -215,7 +215,7 @@ extern Pages ClampedMaxPages(AddressType t, Pages initialPages, // vm/ArrayBufferObject.cpp. extern size_t ComputeMappedSize(Pages clampedMaxPages); -extern uint64_t GetMaxOffsetGuardLimit(bool hugeMemory); +extern uint64_t GetMaxOffsetGuardLimit(bool hugeMemory, PageSize sz); // Return the next higher valid immediate that satisfies the constraints of the // platform. diff --git a/js/src/wasm/WasmMetadata.h b/js/src/wasm/WasmMetadata.h @@ -180,7 +180,8 @@ struct CodeMetadata : public ShareableBase<CodeMetadata> { bool hugeMemoryEnabled(uint32_t memoryIndex) const { return !isAsmJS() && memoryIndex < memories.length() && - IsHugeMemoryEnabled(memories[memoryIndex].addressType()); + IsHugeMemoryEnabled(memories[memoryIndex].addressType(), + memories[memoryIndex].pageSize()); } bool usesSharedMemory(uint32_t memoryIndex) const { return memoryIndex < memories.length() && memories[memoryIndex].isShared(); diff --git a/js/src/wasm/WasmModule.cpp b/js/src/wasm/WasmModule.cpp @@ -263,10 +263,20 @@ bool wasm::GetOptimizedEncodingBuildId(JS::BuildIdCharVector* buildId) { buildId->infallibleAppend(')'); buildId->infallibleAppend('m'); - buildId->infallibleAppend(wasm::IsHugeMemoryEnabled(AddressType::I32) ? '+' - : '-'); - buildId->infallibleAppend(wasm::IsHugeMemoryEnabled(AddressType::I64) ? '+' - : '-'); + buildId->infallibleAppend( + wasm::IsHugeMemoryEnabled(AddressType::I32, PageSize::Standard) ? '+' + : '-'); + buildId->infallibleAppend( + wasm::IsHugeMemoryEnabled(AddressType::I64, PageSize::Standard) ? '+' + : '-'); + + // We don't expect huge memory to be supported if custom page sizes are used. +#ifdef ENABLE_WASM_CUSTOM_PAGE_SIZES + MOZ_RELEASE_ASSERT( + !wasm::IsHugeMemoryEnabled(AddressType::I32, PageSize::Tiny)); + MOZ_RELEASE_ASSERT( + !wasm::IsHugeMemoryEnabled(AddressType::I64, PageSize::Tiny)); +#endif return true; } @@ -538,15 +548,17 @@ bool Module::instantiateMemories( RootedObject proto(cx, &cx->global()->getPrototype(JSProto_WasmMemory)); memory = WasmMemoryObject::create( - cx, buffer, IsHugeMemoryEnabled(desc.addressType()), proto); + cx, buffer, IsHugeMemoryEnabled(desc.addressType(), desc.pageSize()), + proto); if (!memory) { return false; } } - MOZ_RELEASE_ASSERT(codeMeta().isAsmJS() || - memory->isHuge() == - IsHugeMemoryEnabled(desc.addressType())); + MOZ_RELEASE_ASSERT( + codeMeta().isAsmJS() || + memory->isHuge() == + IsHugeMemoryEnabled(desc.addressType(), desc.pageSize())); if (!memoryObjs.get().append(memory)) { ReportOutOfMemory(cx); diff --git a/js/src/wasm/WasmModuleTypes.h b/js/src/wasm/WasmModuleTypes.h @@ -833,7 +833,7 @@ struct MemoryDesc { // for "WASM Linear Memory structure". bool boundsCheckLimitIsAlways32Bits() const { return limits.maximum.isSome() && - limits.maximum.value() < (0x100000000 / StandardPageSizeBytes); + limits.maximum.value() < (0x100000000 / PageSizeInBytes(pageSize())); } AddressType addressType() const { return limits.addressType; } diff --git a/js/src/wasm/WasmProcess.cpp b/js/src/wasm/WasmProcess.cpp @@ -133,9 +133,9 @@ static const size_t MinVirtualMemoryLimitForHugeMemory = static bool sHugeMemoryEnabled32 = false; -bool wasm::IsHugeMemoryEnabled(wasm::AddressType t) { - if (t == AddressType::I64) { - // No support for huge memory with 64-bit memories +bool wasm::IsHugeMemoryEnabled(wasm::AddressType t, wasm::PageSize sz) { + if (t == AddressType::I64 || sz != wasm::PageSize::Standard) { + // No support for huge memory with 64-bit memories or custom page sizes. return false; } return sHugeMemoryEnabled32; diff --git a/js/src/wasm/WasmProcess.h b/js/src/wasm/WasmProcess.h @@ -63,7 +63,7 @@ void UnregisterCodeBlock(const CodeBlock* cs); // is not precise enough to tell whether a particular memory uses huge memory, // there are additional conditions for that. -bool IsHugeMemoryEnabled(AddressType t); +bool IsHugeMemoryEnabled(AddressType t, PageSize sz); // Called once before/after the last VM execution which could execute or compile // wasm.