tor-browser

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

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

Bug 1977854 - Wasm custom page sizes part 4: add import check for page size r=rhunt,bvisness

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

Diffstat:
Mjs/public/friend/ErrorNumbers.msg | 1+
Ajs/src/jit-test/tests/wasm/custom-page-sizes/js-api.js | 78++++++++++++++++++++++++++++++++++++++++++++++++++++++++++++++++++++++++++++++
Mjs/src/wasm/WasmJS.cpp | 7+++++++
Mjs/src/wasm/WasmJS.h | 1+
Mjs/src/wasm/WasmModule.cpp | 21+++++++++++++++++++++
5 files changed, 108 insertions(+), 0 deletions(-)

diff --git a/js/public/friend/ErrorNumbers.msg b/js/public/friend/ErrorNumbers.msg @@ -439,6 +439,7 @@ MSG_DEF(JSMSG_WASM_BAD_TAG_SIG, 2, JSEXN_WASMLINKERROR, "imported tag '{0 MSG_DEF(JSMSG_WASM_BAD_IMP_ADDRESS, 2, JSEXN_WASMLINKERROR, "imported {0} with incompatible address type {1}") MSG_DEF(JSMSG_WASM_BAD_IMP_SIZE, 1, JSEXN_WASMLINKERROR, "imported {0} with incompatible size") MSG_DEF(JSMSG_WASM_BAD_IMP_MAX, 1, JSEXN_WASMLINKERROR, "imported {0} with incompatible maximum size") +MSG_DEF(JSMSG_WASM_BAD_IMP_PAGE_SIZE, 0, JSEXN_WASMLINKERROR, "imported memory with incompatible page size") MSG_DEF(JSMSG_WASM_IMP_SHARED_REQD, 0, JSEXN_WASMLINKERROR, "imported unshared memory but shared required") MSG_DEF(JSMSG_WASM_IMP_SHARED_BANNED, 0, JSEXN_WASMLINKERROR, "imported shared memory but unshared required") MSG_DEF(JSMSG_WASM_NO_SHMEM_LINK, 0, JSEXN_WASMLINKERROR, "shared memory is disabled") diff --git a/js/src/jit-test/tests/wasm/custom-page-sizes/js-api.js b/js/src/jit-test/tests/wasm/custom-page-sizes/js-api.js @@ -0,0 +1,78 @@ +// |jit-test| skip-if: !wasmCustomPageSizesEnabled() + +load(libdir + "wasm-binary.js"); + +{ + let instance = wasmEvalText(`(module + (memory (export "mem") 0 1 (pagesize 1)) + ) + `); + + assertErrorMessage( + () => wasmEvalText(`(module + (memory (import "m" "mem") 0 1 (pagesize 65536)) + ) + `, { m: { mem: instance.exports.mem } }), + WebAssembly.LinkError, + /imported memory with incompatible page size/ + ) +} + +// Test against explicit page size in the binary encoding. +{ + let instance = wasmEvalBinary( + moduleWithSections([ {name:memoryId, body:[0x01, 0x08, 0x00, 0x10]}, + exportSection([ {name: "mem", memIndex: 0} ]) ]) + ); + + assertErrorMessage( + () => wasmEvalText(`(module + (memory (import "m" "mem") 0 1 (pagesize 1)) + ) + `, { m: { mem: instance.exports.mem } }), + WebAssembly.LinkError, + /imported memory with incompatible page size/ + ) +} + +// Test against default page size in the binary encoding. +{ + let instance = wasmEvalBinary( + moduleWithSections([ {name:memoryId, body:[0x01, 0x01, 0x00, 0x01]}, + exportSection([ {name: "mem", memIndex: 0} ]) ]) + ); + + assertErrorMessage( + () => wasmEvalText(`(module + (memory (import "m" "mem") 0 1 (pagesize 1)) + ) + `, { m: { mem: instance.exports.mem } }), + WebAssembly.LinkError, + /imported memory with incompatible page size/ + ) +} + +// Valid cases +{ + let instance = wasmEvalText(`(module + (memory (export "mem") 0 1 (pagesize 1)) + ) + `); + + wasmEvalText(`(module + (memory (import "m" "mem") 0 1 (pagesize 1)) + ) + `, { m: { mem: instance.exports.mem } }); +} + +{ + let instance = wasmEvalText(`(module + (memory (export "mem") 0 1) + ) + `); + + wasmEvalText(`(module + (memory (import "m" "mem") 0 1 (pagesize 65536)) + ) + `, { m: { mem: instance.exports.mem } }); +} diff --git a/js/src/wasm/WasmJS.cpp b/js/src/wasm/WasmJS.cpp @@ -2626,6 +2626,13 @@ size_t WasmMemoryObject::boundsCheckLimit() const { return limit; } +wasm::PageSize WasmMemoryObject::pageSize() const { + if (isShared()) { + return sharedArrayRawBuffer()->wasmPageSize(); + } + return buffer().wasmPageSize(); +} + bool WasmMemoryObject::addMovingGrowObserver(JSContext* cx, WasmInstanceObject* instance) { MOZ_ASSERT(movingGrowable()); diff --git a/js/src/wasm/WasmJS.h b/js/src/wasm/WasmJS.h @@ -319,6 +319,7 @@ class WasmMemoryObject : public NativeObject { bool isHuge() const; bool movingGrowable() const; size_t boundsCheckLimit() const; + wasm::PageSize pageSize() const; // If isShared() is true then obtain the underlying buffer object. WasmSharedArrayRawBuffer* sharedArrayRawBuffer() const; diff --git a/js/src/wasm/WasmModule.cpp b/js/src/wasm/WasmModule.cpp @@ -494,6 +494,19 @@ static bool CheckSharing(JSContext* cx, bool declaredShared, bool isShared) { return true; } +#ifdef ENABLE_WASM_CUSTOM_PAGE_SIZES +static bool CheckPageSize(JSContext* cx, PageSize declaredPageSize, + PageSize actualPageSize) { + if (declaredPageSize != actualPageSize) { + JS_ReportErrorNumberUTF8(cx, GetErrorMessage, nullptr, + JSMSG_WASM_BAD_IMP_PAGE_SIZE); + return false; + } + + return true; +} +#endif + // asm.js module instantiation supplies its own buffer, but for wasm, create and // initialize the buffer if one is requested. Either way, the buffer is wrapped // in a WebAssembly.Memory object which is what the Instance stores. @@ -518,6 +531,14 @@ bool Module::instantiateMemories( return false; } +#ifdef ENABLE_WASM_CUSTOM_PAGE_SIZES + // Page size needs to be checked first because comparisons between + // incompatible page sizes are invalid in CheckLimits. + if (!CheckPageSize(cx, desc.pageSize(), memory->pageSize())) { + return false; + } +#endif + if (!CheckLimits(cx, desc.initialPages(), desc.maximumPages(), /* defaultMax */ MaxMemoryPages(desc.addressType(), desc.pageSize()),