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:
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");
}