tor-browser

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

commit e5f94075691d21bf86086589e65fb7b8e6ac7c0d
parent 8823c6f33424ae335f5bacb800a22bd6ce5ee656
Author: Asumu Takikawa <asumu@igalia.com>
Date:   Mon,  8 Dec 2025 16:02:51 +0000

Bug 1977854 - Add PageSize enum type, use it to define StandardPageSizeBytes r=rhunt,bvisness

StandardPageSize is renamed to StandardPageSizeBytes to avoid confusion with
PageSize::Standard.

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

Diffstat:
Mjs/src/vm/ArrayBufferObject.cpp | 18+++++++++---------
Mjs/src/vm/SharedArrayObject.cpp | 22++++++++++++----------
Mjs/src/wasm/AsmJS.cpp | 6+++---
Mjs/src/wasm/WasmBCMemory.cpp | 2+-
Mjs/src/wasm/WasmConstants.h | 7+------
Mjs/src/wasm/WasmFeatures.cpp | 2+-
Mjs/src/wasm/WasmInstance.cpp | 4++--
Mjs/src/wasm/WasmJS.cpp | 6+++---
Mjs/src/wasm/WasmMemory.cpp | 12+++++++-----
Mjs/src/wasm/WasmMemory.h | 33+++++++++++++++++++++++----------
Mjs/src/wasm/WasmModuleTypes.h | 8++++----
Mjs/src/wasm/WasmValidate.cpp | 2+-
12 files changed, 67 insertions(+), 55 deletions(-)

diff --git a/js/src/vm/ArrayBufferObject.cpp b/js/src/vm/ArrayBufferObject.cpp @@ -555,7 +555,7 @@ bool ArrayBufferObject::maxByteLengthGetterImpl(JSContext* cx, uint64_t sourceMaxBytes = sourceMaxPages.byteLength64(); MOZ_ASSERT(sourceMaxBytes <= - wasm::StandardPageSize * wasm::MaxMemory64PagesValidation); + wasm::StandardPageSizeBytes * wasm::MaxMemory64PagesValidation); args.rval().setNumber(double(sourceMaxBytes)); return true; @@ -806,7 +806,7 @@ bool ArrayBufferObject::resizeImpl(JSContext* cx, const CallArgs& args) { if (obj->isWasm()) { // Special case for resizing of Wasm buffers. - if (newByteLength % wasm::StandardPageSize != 0) { + if (newByteLength % wasm::StandardPageSizeBytes != 0) { JS_ReportErrorNumberASCII(cx, GetErrorMessage, nullptr, JSMSG_WASM_ARRAYBUFFER_PAGE_MULTIPLE); return false; @@ -1541,7 +1541,7 @@ void ResizableArrayBufferObject::resize(size_t newByteLength) { MOZ_ASSERT(newSize <= mappedSize()); size_t delta = newSize - oldSize; - MOZ_ASSERT(delta % wasm::StandardPageSize == 0); + MOZ_ASSERT(delta % wasm::StandardPageSizeBytes == 0); uint8_t* dataEnd = dataPointer() + oldSize; MOZ_ASSERT(uintptr_t(dataEnd) % gc::SystemPageSize() == 0); @@ -1560,8 +1560,8 @@ void WasmArrayRawBuffer::discard(size_t byteOffset, size_t byteLen) { // The caller is responsible for ensuring these conditions are met; see this // function's comment in ArrayBufferObject.h. - MOZ_ASSERT(byteOffset % wasm::StandardPageSize == 0); - MOZ_ASSERT(byteLen % wasm::StandardPageSize == 0); + MOZ_ASSERT(byteOffset % wasm::StandardPageSizeBytes == 0); + MOZ_ASSERT(byteLen % wasm::StandardPageSizeBytes == 0); MOZ_ASSERT(wasm::MemoryBoundsCheck(uint64_t(byteOffset), uint64_t(byteLen), byteLength())); @@ -1788,7 +1788,7 @@ ArrayBufferObjectMaybeShared* js::CreateWasmBuffer( } bool ArrayBufferObject::prepareForAsmJS() { - MOZ_ASSERT(byteLength() % wasm::StandardPageSize == 0, + MOZ_ASSERT(byteLength() % wasm::StandardPageSizeBytes == 0, "prior size checking should have guaranteed page-size multiple"); MOZ_ASSERT(byteLength() > 0, "prior size checking should have excluded empty buffers"); @@ -1807,9 +1807,9 @@ bool ArrayBufferObject::prepareForAsmJS() { return true; case INLINE_DATA: - static_assert( - wasm::StandardPageSize > FixedLengthArrayBufferObject::MaxInlineBytes, - "inline data must be too small to be a page size multiple"); + static_assert(wasm::StandardPageSizeBytes > + FixedLengthArrayBufferObject::MaxInlineBytes, + "inline data must be too small to be a page size multiple"); MOZ_ASSERT_UNREACHABLE( "inline-data buffers should be implicitly excluded by size checks"); return false; diff --git a/js/src/vm/SharedArrayObject.cpp b/js/src/vm/SharedArrayObject.cpp @@ -156,7 +156,7 @@ bool WasmSharedArrayRawBuffer::wasmGrowToPagesInPlace(const Lock&, } size_t delta = newLength - length_; - MOZ_ASSERT(delta % wasm::StandardPageSize == 0); + MOZ_ASSERT(delta % wasm::StandardPageSizeBytes == 0); uint8_t* dataEnd = dataPointerShared().unwrap(/* for resize */) + length_; MOZ_ASSERT(uintptr_t(dataEnd) % gc::SystemPageSize() == 0); @@ -178,8 +178,8 @@ void WasmSharedArrayRawBuffer::discard(size_t byteOffset, size_t byteLen) { // The caller is responsible for ensuring these conditions are met; see this // function's comment in SharedArrayObject.h. - MOZ_ASSERT(byteOffset % wasm::StandardPageSize == 0); - MOZ_ASSERT(byteLen % wasm::StandardPageSize == 0); + MOZ_ASSERT(byteOffset % wasm::StandardPageSizeBytes == 0); + MOZ_ASSERT(byteLen % wasm::StandardPageSizeBytes == 0); MOZ_ASSERT(wasm::MemoryBoundsCheck(uint64_t(byteOffset), uint64_t(byteLen), volatileByteLength())); @@ -213,12 +213,14 @@ void WasmSharedArrayRawBuffer::discard(size_t byteOffset, size_t byteLen) { // disastrous when discarding already-discarded memory. To mitigate this, we // discard a chunk of memory at a time - this comes at a small performance // cost from syscalls and potentially less-optimal memsets. - size_t numPages = byteLen / wasm::StandardPageSize; + size_t numPages = byteLen / wasm::StandardPageSizeBytes; for (size_t i = 0; i < numPages; i++) { - AtomicOperations::memsetSafeWhenRacy(addr + (i * wasm::StandardPageSize), 0, - wasm::StandardPageSize); - DebugOnly<bool> result = VirtualUnlock( - addr.unwrap() + (i * wasm::StandardPageSize), wasm::StandardPageSize); + AtomicOperations::memsetSafeWhenRacy( + addr + (i * wasm::StandardPageSizeBytes), 0, + wasm::StandardPageSizeBytes); + DebugOnly<bool> result = + VirtualUnlock(addr.unwrap() + (i * wasm::StandardPageSizeBytes), + wasm::StandardPageSizeBytes); MOZ_ASSERT(!result); // this always "fails" when unlocking unlocked // memory...which is the only case we care about } @@ -339,7 +341,7 @@ bool SharedArrayBufferObject::maxByteLengthGetterImpl(JSContext* cx, uint64_t sourceMaxBytes = sourceMaxPages.byteLength64(); MOZ_ASSERT(sourceMaxBytes <= - wasm::StandardPageSize * wasm::MaxMemory64PagesValidation); + wasm::StandardPageSizeBytes * wasm::MaxMemory64PagesValidation); args.rval().setNumber(double(sourceMaxBytes)); return true; @@ -408,7 +410,7 @@ bool SharedArrayBufferObject::growImpl(JSContext* cx, const CallArgs& args) { if (buffer->isWasm()) { // Special case for resizing of Wasm buffers. - if (newByteLength % wasm::StandardPageSize != 0) { + if (newByteLength % wasm::StandardPageSizeBytes != 0) { JS_ReportErrorNumberASCII(cx, GetErrorMessage, nullptr, JSMSG_WASM_ARRAYBUFFER_PAGE_MULTIPLE); return false; diff --git a/js/src/wasm/AsmJS.cpp b/js/src/wasm/AsmJS.cpp @@ -116,7 +116,7 @@ enum class MemoryUsage { None = false, Unshared = 1, Shared = 2 }; // The asm.js valid heap lengths are precisely the WASM valid heap lengths for // ARM greater or equal to MinHeapLength -static const size_t MinHeapLength = StandardPageSize; +static const size_t MinHeapLength = StandardPageSizeBytes; // An asm.js heap can in principle be up to INT32_MAX bytes but requirements // on the format restrict it further to the largest pseudo-ARM-immediate. // See IsValidAsmJSHeapLength(). @@ -134,7 +134,7 @@ static const uint64_t HighestValidARMImmediate = 0xff000000; static bool IsValidARMImmediate(uint32_t i) { bool valid = (IsPowerOfTwo(i) || (i & 0x00ffffff) == 0); - MOZ_ASSERT_IF(valid, i % StandardPageSize == 0); + MOZ_ASSERT_IF(valid, i % StandardPageSizeBytes == 0); return valid; } @@ -1113,7 +1113,7 @@ class MOZ_STACK_CLASS ModuleValidatorShared { uint64_t minLength; uint64_t minPages() const { - return DivideRoundingUp(minLength, StandardPageSize); + return DivideRoundingUp(minLength, StandardPageSizeBytes); } Memory() = default; diff --git a/js/src/wasm/WasmBCMemory.cpp b/js/src/wasm/WasmBCMemory.cpp @@ -147,7 +147,7 @@ RegI32 BaseCompiler::popConstMemoryAccess<RegI32>(MemoryAccessDesc* access, // Validation ensures that the offset is in 32-bit range, and the calculation // of the limit cannot overflow due to our choice of HugeOffsetGuardLimit. #ifdef WASM_SUPPORTS_HUGE_MEMORY - static_assert(MaxMemory32PagesValidation * StandardPageSize <= + static_assert(MaxMemory32PagesValidation * StandardPageSizeBytes <= UINT64_MAX - HugeOffsetGuardLimit); #endif uint64_t ea = uint64_t(addr) + uint64_t(access->offset32()); diff --git a/js/src/wasm/WasmConstants.h b/js/src/wasm/WasmConstants.h @@ -1133,7 +1133,7 @@ enum class FieldWideningOp { None, Signed, Unsigned }; // 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 StandardPageSize = 64 * 1024; +enum class PageSize { Standard = 16 }; // These limits are agreed upon with other engines for consistency. @@ -1164,11 +1164,6 @@ static const unsigned MaxModuleBytes = 1024 * 1024 * 1024; static const unsigned MaxFunctionBytes = 7654321; static const unsigned MaxArrayNewFixedElements = 10000; -// By spec, see -// https://github.com/WebAssembly/spec/issues/1895#issuecomment-2895078022 -static_assert((StandardPageSize * MaxMemory64PagesValidation) <= - (uint64_t(1) << 53) - 1); - // Maximum payload size, in bytes, of a gc-proposal Array. Puts it fairly // close to 2^31 without exposing us to potential danger at the signed-i32 // wraparound boundary. Note that gc-proposal Struct sizes are limited by diff --git a/js/src/wasm/WasmFeatures.cpp b/js/src/wasm/WasmFeatures.cpp @@ -251,7 +251,7 @@ bool wasm::HasPlatformSupport() { return false; } - if (gc::SystemPageSize() > wasm::StandardPageSize) { + if (gc::SystemPageSize() > wasm::StandardPageSizeBytes) { return false; } diff --git a/js/src/wasm/WasmInstance.cpp b/js/src/wasm/WasmInstance.cpp @@ -1250,8 +1250,8 @@ static bool WasmDiscardCheck(Instance* instance, I byteOffset, I byteLen, size_t memLen, bool shared) { JSContext* cx = instance->cx(); - if (byteOffset % wasm::StandardPageSize != 0 || - byteLen % wasm::StandardPageSize != 0) { + if (byteOffset % wasm::StandardPageSizeBytes != 0 || + byteLen % wasm::StandardPageSizeBytes != 0) { ReportTrapError(cx, JSMSG_WASM_UNALIGNED_ACCESS); return false; } diff --git a/js/src/wasm/WasmJS.cpp b/js/src/wasm/WasmJS.cpp @@ -2336,8 +2336,8 @@ bool WasmMemoryObject::discardImpl(JSContext* cx, const CallArgs& args) { return false; } - if (byteOffset % wasm::StandardPageSize != 0 || - byteLen % wasm::StandardPageSize != 0) { + if (byteOffset % wasm::StandardPageSizeBytes != 0 || + byteLen % wasm::StandardPageSizeBytes != 0) { JS_ReportErrorNumberUTF8(cx, GetErrorMessage, nullptr, JSMSG_WASM_UNALIGNED_ACCESS); return false; @@ -2598,7 +2598,7 @@ size_t WasmMemoryObject::boundsCheckLimit() const { // max field. MOZ_ASSERT(mappedSize < UINT32_MAX); #endif - MOZ_ASSERT(mappedSize % wasm::StandardPageSize == 0); + MOZ_ASSERT(mappedSize % wasm::StandardPageSizeBytes == 0); MOZ_ASSERT(mappedSize >= wasm::GuardSize); size_t limit = mappedSize - wasm::GuardSize; MOZ_ASSERT(limit <= MaxMemoryBoundsCheckLimit(addressType())); diff --git a/js/src/wasm/WasmMemory.cpp b/js/src/wasm/WasmMemory.cpp @@ -291,7 +291,8 @@ static_assert(HugeOffsetGuardLimit < UINT32_MAX, // where `offset < OffsetGuardLimit` as well as the overflow from unaligned // accesses, as described above for MaxMemoryAccessSize. -static const size_t OffsetGuardLimit = StandardPageSize - MaxMemoryAccessSize; +static const size_t OffsetGuardLimit = + StandardPageSizeBytes - MaxMemoryAccessSize; static_assert(MaxMemoryAccessSize < GuardSize, "Guard page handles partial out-of-bounds"); @@ -317,15 +318,15 @@ wasm::Pages wasm::MaxMemoryPages(AddressType t) { MOZ_ASSERT_IF(t == AddressType::I64, !IsHugeMemoryEnabled(t)); size_t desired = MaxMemoryPagesValidation(t); constexpr size_t actual = - ArrayBufferObject::ByteLengthLimit / StandardPageSize; + ArrayBufferObject::ByteLengthLimit / StandardPageSizeBytes; return wasm::Pages(std::min(desired, actual)); #else // On 32-bit systems, the heap limit must be representable in the nonnegative // range of an int32_t, which means the maximum heap size as observed by wasm // code is one wasm page less than 2GB. static_assert(ArrayBufferObject::ByteLengthLimit >= - INT32_MAX / StandardPageSize); - return wasm::Pages(INT32_MAX / StandardPageSize); + INT32_MAX / StandardPageSizeBytes); + return wasm::Pages(INT32_MAX / StandardPageSizeBytes); #endif } @@ -351,7 +352,8 @@ Pages wasm::ClampedMaxPages(AddressType t, Pages initialPages, // "a lot of memory". Maintain the invariant that initialPages <= // clampedMaxPages. static const uint64_t OneGib = 1 << 30; - static const Pages OneGibPages = Pages(OneGib / wasm::StandardPageSize); + static const Pages OneGibPages = + Pages(OneGib / wasm::StandardPageSizeBytes); Pages clampedPages = std::max(OneGibPages, initialPages); clampedMaxPages = std::min(clampedPages, clampedMaxPages); diff --git a/js/src/wasm/WasmMemory.h b/js/src/wasm/WasmMemory.h @@ -53,8 +53,21 @@ extern bool ToAddressType(JSContext* cx, HandleValue value, extern const char* ToString(AddressType addressType); -// Pages is a typed unit representing a multiple of wasm::StandardPageSize. We -// generally use pages as the unit of length when representing linear memory +static constexpr unsigned PageSizeInBytes(PageSize sz) { + return 1U << static_cast<uint8_t>(sz); +} + +static constexpr unsigned StandardPageSizeBytes = + PageSizeInBytes(PageSize::Standard); +static_assert(StandardPageSizeBytes == 64 * 1024); + +// By spec, see +// https://github.com/WebAssembly/spec/issues/1895#issuecomment-2895078022 +static_assert((StandardPageSizeBytes * MaxMemory64PagesValidation) <= + (uint64_t(1) << 53) - 1); + +// Pages is a typed unit representing a multiple of wasm::StandardPageSizeBytes. +// We generally use pages as the unit of length when representing linear memory // lengths so as to avoid overflow when the specified initial or maximum pages // would overflow the native word size. // @@ -83,15 +96,15 @@ struct Pages { // Converts from a byte length to pages, assuming that the length is an // exact multiple of the page size. static Pages fromByteLengthExact(size_t byteLength) { - MOZ_ASSERT(byteLength % StandardPageSize == 0); - return Pages(byteLength / StandardPageSize); + MOZ_ASSERT(byteLength % StandardPageSizeBytes == 0); + return Pages(byteLength / StandardPageSizeBytes); } // Return whether the page length may overflow when converted to a byte // length in the native word size. bool hasByteLength() const { mozilla::CheckedInt<size_t> length(value_); - length *= StandardPageSize; + length *= StandardPageSizeBytes; return length.isValid(); } @@ -99,14 +112,14 @@ struct Pages { // check for overflow, or be assured else-how that overflow cannot happen. size_t byteLength() const { mozilla::CheckedInt<size_t> length(value_); - length *= StandardPageSize; + length *= StandardPageSizeBytes; return length.value(); } // Return the byteLength for a 64-bits memory. uint64_t byteLength64() const { mozilla::CheckedInt<uint64_t> length(value_); - length *= StandardPageSize; + length *= StandardPageSizeBytes; return length.value(); } @@ -183,7 +196,7 @@ static const uint64_t HugeIndexRange = uint64_t(UINT32_MAX) + 1; // modules. static const uint64_t HugeOffsetGuardLimit = 1 << 25; // Reserve a wasm page (64KiB) to support slop on unaligned accesses. -static const uint64_t HugeUnalignedGuardPage = StandardPageSize; +static const uint64_t HugeUnalignedGuardPage = StandardPageSizeBytes; // Compute the total memory reservation. static const uint64_t HugeMappedSize = @@ -191,12 +204,12 @@ static const uint64_t HugeMappedSize = // Try to keep the memory reservation aligned to the wasm page size. This // ensures that it's aligned to the system page size. -static_assert(HugeMappedSize % StandardPageSize == 0); +static_assert(HugeMappedSize % StandardPageSizeBytes == 0); #endif // The size of the guard page for non huge-memories. -static const size_t GuardSize = StandardPageSize; +static const size_t GuardSize = StandardPageSizeBytes; // The size of the guard page that included NULL pointer. Reserve a smallest // range for typical hardware, to catch near NULL pointer accesses, e.g. diff --git a/js/src/wasm/WasmModuleTypes.h b/js/src/wasm/WasmModuleTypes.h @@ -829,7 +829,7 @@ struct MemoryDesc { // for "WASM Linear Memory structure". bool boundsCheckLimitIsAlways32Bits() const { return limits.maximum.isSome() && - limits.maximum.value() < (0x100000000 / StandardPageSize); + limits.maximum.value() < (0x100000000 / StandardPageSizeBytes); } AddressType addressType() const { return limits.addressType; } @@ -846,8 +846,8 @@ struct MemoryDesc { uint64_t initialLength() const { // See static_assert after MemoryDesc for why this is safe for memory32. MOZ_ASSERT_IF(addressType() == AddressType::I64, - limits.initial <= UINT64_MAX / StandardPageSize); - return limits.initial * StandardPageSize; + limits.initial <= UINT64_MAX / StandardPageSizeBytes); + return limits.initial * StandardPageSizeBytes; } MemoryDesc() = default; @@ -861,7 +861,7 @@ using MemoryDescVector = Vector<MemoryDesc, 1, SystemAllocPolicy>; // We never need to worry about overflow with a Memory32 field when // using a uint64_t. -static_assert(MaxMemory32PagesValidation <= UINT64_MAX / StandardPageSize); +static_assert(MaxMemory32PagesValidation <= UINT64_MAX / StandardPageSizeBytes); struct TableDesc { Limits limits; diff --git a/js/src/wasm/WasmValidate.cpp b/js/src/wasm/WasmValidate.cpp @@ -4094,7 +4094,7 @@ static bool DecodeDataSection(Decoder& d, CodeMetadata* codeMeta, return d.fail("expected segment size"); } - if (segRange.length > MaxDataSegmentLengthPages * StandardPageSize) { + if (segRange.length > MaxDataSegmentLengthPages * StandardPageSizeBytes) { return d.fail("segment size too big"); }