diff options
author | Daniel Baumann <daniel.baumann@progress-linux.org> | 2024-04-07 17:32:43 +0000 |
---|---|---|
committer | Daniel Baumann <daniel.baumann@progress-linux.org> | 2024-04-07 17:32:43 +0000 |
commit | 6bf0a5cb5034a7e684dcc3500e841785237ce2dd (patch) | |
tree | a68f146d7fa01f0134297619fbe7e33db084e0aa /js/src/wasm/WasmMemory.h | |
parent | Initial commit. (diff) | |
download | thunderbird-upstream.tar.xz thunderbird-upstream.zip |
Adding upstream version 1:115.7.0.upstream/1%115.7.0upstream
Signed-off-by: Daniel Baumann <daniel.baumann@progress-linux.org>
Diffstat (limited to 'js/src/wasm/WasmMemory.h')
-rw-r--r-- | js/src/wasm/WasmMemory.h | 226 |
1 files changed, 226 insertions, 0 deletions
diff --git a/js/src/wasm/WasmMemory.h b/js/src/wasm/WasmMemory.h new file mode 100644 index 0000000000..ea2c61aa38 --- /dev/null +++ b/js/src/wasm/WasmMemory.h @@ -0,0 +1,226 @@ +/* -*- Mode: C++; tab-width: 8; indent-tabs-mode: nil; c-basic-offset: 2 -*- + * vim: set ts=8 sts=2 et sw=2 tw=80: + * + * Copyright 2021 Mozilla Foundation + * + * Licensed under the Apache License, Version 2.0 (the "License"); + * you may not use this file except in compliance with the License. + * You may obtain a copy of the License at + * + * http://www.apache.org/licenses/LICENSE-2.0 + * + * Unless required by applicable law or agreed to in writing, software + * distributed under the License is distributed on an "AS IS" BASIS, + * WITHOUT WARRANTIES OR CONDITIONS OF ANY KIND, either express or implied. + * See the License for the specific language governing permissions and + * limitations under the License. + */ + +#ifndef wasm_memory_h +#define wasm_memory_h + +#include "mozilla/CheckedInt.h" +#include "mozilla/Maybe.h" + +#include <stdint.h> + +#include "js/Value.h" +#include "vm/NativeObject.h" +#include "wasm/WasmConstants.h" +#include "wasm/WasmValType.h" + +namespace js { +namespace wasm { + +// Limits are parameterized by an IndexType which is used to index the +// underlying resource (either a Memory or a Table). Tables are restricted to +// I32, while memories may use I64 when memory64 is enabled. + +enum class IndexType : uint8_t { I32, I64 }; + +inline ValType ToValType(IndexType it) { + return it == IndexType::I64 ? ValType::I64 : ValType::I32; +} + +extern bool ToIndexType(JSContext* cx, HandleValue value, IndexType* indexType); + +extern const char* ToString(IndexType indexType); + +// Pages is a typed unit representing a multiple of wasm::PageSize. 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. +// +// Modules may specify pages up to 2^48 inclusive and so Pages is 64-bit on all +// platforms. +// +// We represent byte lengths using the native word size, as it is assumed that +// consumers of this API will only need byte lengths once it is time to +// allocate memory, at which point the pages will be checked against the +// implementation limits `MaxMemoryPages()` and will then be guaranteed to +// fit in a native word. +struct Pages { + private: + // Pages are specified by limit fields, which in general may be up to 2^48, + // so we must use uint64_t here. + uint64_t value_; + + public: + constexpr Pages() : value_(0) {} + constexpr explicit Pages(uint64_t value) : value_(value) {} + + // Get the wrapped page value. Only use this if you must, prefer to use or + // add new APIs to Page. + uint64_t value() const { return value_; } + + // 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 % PageSize == 0); + return Pages(byteLength / PageSize); + } + + // 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 *= PageSize; + return length.isValid(); + } + + // Converts from pages to byte length in the native word size. Users must + // check for overflow, or be assured else-how that overflow cannot happen. + size_t byteLength() const { + mozilla::CheckedInt<size_t> length(value_); + length *= PageSize; + return length.value(); + } + + // Increment this pages by delta and return whether the resulting value + // did not overflow. If there is no overflow, then this is set to the + // resulting value. + bool checkedIncrement(Pages delta) { + mozilla::CheckedInt<uint64_t> newValue = value_; + newValue += delta.value_; + if (!newValue.isValid()) { + return false; + } + value_ = newValue.value(); + return true; + } + + // Implement pass-through comparison operators so that Pages can be compared. + + bool operator==(Pages other) const { return value_ == other.value_; } + bool operator!=(Pages other) const { return value_ != other.value_; } + bool operator<=(Pages other) const { return value_ <= other.value_; } + bool operator<(Pages other) const { return value_ < other.value_; } + bool operator>=(Pages other) const { return value_ >= other.value_; } + bool operator>(Pages other) const { return value_ > other.value_; } +}; + +// The largest number of pages the application can request. +extern Pages MaxMemoryPages(IndexType t); + +// The byte value of MaxMemoryPages(t). +static inline size_t MaxMemoryBytes(IndexType t) { + return MaxMemoryPages(t).byteLength(); +} + +// A value at least as large as MaxMemoryBytes(t) representing the largest valid +// bounds check limit on the system. (It can be larger than MaxMemoryBytes() +// because bounds check limits are rounded up to fit formal requirements on some +// platforms. Also see ComputeMappedSize().) +extern size_t MaxMemoryBoundsCheckLimit(IndexType t); + +static inline uint64_t MaxMemoryLimitField(IndexType indexType) { + return indexType == IndexType::I32 ? MaxMemory32LimitField + : MaxMemory64LimitField; +} + +// Compute the 'clamped' maximum size of a memory. See +// 'WASM Linear Memory structure' in ArrayBufferObject.cpp for background. +extern Pages ClampedMaxPages(IndexType t, Pages initialPages, + const mozilla::Maybe<Pages>& sourceMaxPages, + bool useHugeMemory); + +// For a given WebAssembly/asm.js 'clamped' max pages, return the number of +// bytes to map which will necessarily be a multiple of the system page size and +// greater than clampedMaxPages in bytes. See "Wasm Linear Memory Structure" in +// vm/ArrayBufferObject.cpp. +extern size_t ComputeMappedSize(Pages clampedMaxPages); + +extern size_t GetMaxOffsetGuardLimit(bool hugeMemory); + +// Return whether the given immediate satisfies the constraints of the platform. +extern bool IsValidBoundsCheckImmediate(uint32_t i); + +// Return whether the given immediate is valid on arm. +extern bool IsValidARMImmediate(uint32_t i); + +// Return the next higher valid immediate that satisfies the constraints of the +// platform. +extern uint64_t RoundUpToNextValidBoundsCheckImmediate(uint64_t i); + +// Return the next higher valid immediate for arm. +extern uint64_t RoundUpToNextValidARMImmediate(uint64_t i); + +#ifdef WASM_SUPPORTS_HUGE_MEMORY +// On WASM_SUPPORTS_HUGE_MEMORY platforms, every asm.js or WebAssembly 32-bit +// memory unconditionally allocates a huge region of virtual memory of size +// wasm::HugeMappedSize. This allows all memory resizing to work without +// reallocation and provides enough guard space for most offsets to be folded +// into memory accesses. See "Linear memory addresses and bounds checking" in +// wasm/WasmMemory.cpp for more information. + +// Reserve 4GiB to support any i32 index. +static const uint64_t HugeIndexRange = uint64_t(UINT32_MAX) + 1; +// Reserve 32MiB to support most offset immediates. Any immediate that is over +// this will require a bounds check to be emitted. 32MiB was chosen to +// generously cover the max offset immediate, 20MiB, found in a corpus of wasm +// modules. +static const uint64_t HugeOffsetGuardLimit = 1 << 25; +// Reserve a wasm page (64KiB) to support slop on unaligned accesses. +static const uint64_t HugeUnalignedGuardPage = PageSize; + +// Compute the total memory reservation. +static const uint64_t HugeMappedSize = + HugeIndexRange + HugeOffsetGuardLimit + HugeUnalignedGuardPage; + +// 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 % PageSize == 0); + +#endif + +// The size of the guard page for non huge-memories. +static const size_t GuardSize = PageSize; + +// 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. +// for a structure fields operations. +static const size_t NullPtrGuardSize = 4096; + +// Check if a range of wasm memory is within bounds, specified as byte offset +// and length (using 32-bit indices). Omits one check by converting from +// uint32_t to uint64_t, at which point overflow cannot occur. +static inline bool MemoryBoundsCheck(uint32_t offset, uint32_t len, + size_t memLen) { + uint64_t offsetLimit = uint64_t(offset) + uint64_t(len); + return offsetLimit <= memLen; +} + +// Check if a range of wasm memory is within bounds, specified as byte offset +// and length (using 64-bit indices). +static inline bool MemoryBoundsCheck(uint64_t offset, uint64_t len, + size_t memLen) { + uint64_t offsetLimit = offset + len; + bool didOverflow = offsetLimit < offset; + bool tooLong = memLen < offsetLimit; + return !didOverflow && !tooLong; +} + +} // namespace wasm +} // namespace js + +#endif // wasm_memory_h |