diff --git a/deps/v8/include/v8-initialization.h b/deps/v8/include/v8-initialization.h index 46a21a02cbcdd6..82c6985d04d7a4 100644 --- a/deps/v8/include/v8-initialization.h +++ b/deps/v8/include/v8-initialization.h @@ -253,6 +253,13 @@ class V8_EXPORT V8 { static size_t GetSandboxReservationSizeInBytes(); #endif // V8_ENABLE_SANDBOX + /** + * Enabling the WebAssembly trap handler requires the process to be able to + * access enough virtual address space. Returns true if the + * system requirements are met at the time of the call. + */ + static bool CanEnableWebAssemblyTrapHandler(); + /** * Activate trap-based bounds checking for WebAssembly. * diff --git a/deps/v8/src/api/api.cc b/deps/v8/src/api/api.cc index 5a879e9ff5d9e8..c009ef214e2691 100644 --- a/deps/v8/src/api/api.cc +++ b/deps/v8/src/api/api.cc @@ -183,6 +183,14 @@ #include "src/diagnostics/etw-jit-win.h" #endif // V8_ENABLE_ETW_STACK_WALKING +#if V8_OS_WIN +#define WIN32_LEAN_AND_MEAN +#include +#include +#else +#include +#include +#endif namespace v8 { i::ExternalPointerTag ToExternalPointerTag(v8::EmbedderDataTypeTag api_tag) { @@ -6277,6 +6285,9 @@ bool v8::V8::Initialize(const int build_config) { bool TryHandleWebAssemblyTrapPosix(int sig_code, siginfo_t* info, void* context) { #if V8_ENABLE_WEBASSEMBLY && V8_TRAP_HANDLER_SUPPORTED + if (!i::trap_handler::IsTrapHandlerEnabled()) { + return false; + } return i::trap_handler::TryHandleSignal(sig_code, info, context); #else return false; @@ -6287,6 +6298,9 @@ bool TryHandleWebAssemblyTrapPosix(int sig_code, siginfo_t* info, #if V8_OS_WIN bool TryHandleWebAssemblyTrapWindows(EXCEPTION_POINTERS* exception) { #if V8_ENABLE_WEBASSEMBLY && V8_TRAP_HANDLER_SUPPORTED + if (!i::trap_handler::IsTrapHandlerEnabled()) { + return false; + } return i::trap_handler::TryHandleWasmTrap(exception); #else return false; @@ -6294,6 +6308,52 @@ bool TryHandleWebAssemblyTrapWindows(EXCEPTION_POINTERS* exception) { } #endif +static uint64_t GetAddressSpaceSize() { +#if V8_OS_WIN + SYSTEM_INFO si{}; + GetSystemInfo(&si); + + auto lo = reinterpret_cast(si.lpMinimumApplicationAddress); + auto hi = reinterpret_cast(si.lpMaximumApplicationAddress); + + // The range is inclusive, so add 1. + return static_cast(hi - lo) + 1ULL; +#else + struct rlimit lim; + if (getrlimit(RLIMIT_AS, &lim) == 0 && lim.rlim_cur != RLIM_INFINITY) { + return static_cast(lim.rlim_cur); + } + // Either RLIM_INFINITY or getrlimit failed — treat as "unlimited". + return std::numeric_limits::max(); +#endif +} + +bool V8::CanEnableWebAssemblyTrapHandler() { +#if V8_ENABLE_WEBASSEMBLY && V8_TRAP_HANDLER_SUPPORTED + uint64_t virtual_memory_available = GetAddressSpaceSize(); + if (virtual_memory_available == 0) { + // There's no limit, assume trap handler can be enabled. + return true; + } + bool has_guard_regions = true; // Assume guard regions are enabled. + // Check if it can reserve memory for an allocation as low as 1 byte. + size_t byte_capacity = 1; + size_t memory32_size = i::BackingStore::GetWasmReservationSize( + has_guard_regions, byte_capacity, false); + uint64_t required_size = static_cast(memory32_size); + if (i::v8_flags.wasm_memory64_trap_handling) { + uint64_t memory64_size = + static_cast(i::BackingStore::GetWasmReservationSize( + has_guard_regions, byte_capacity, true)); + required_size = std::max(memory64_size, required_size); + } + + return virtual_memory_available >= required_size; +#else + return false; +#endif +} + bool V8::EnableWebAssemblyTrapHandler(bool use_v8_signal_handler) { #if V8_ENABLE_WEBASSEMBLY return i::trap_handler::EnableTrapHandler(use_v8_signal_handler); diff --git a/deps/v8/src/objects/backing-store.cc b/deps/v8/src/objects/backing-store.cc index 3292ef26da3469..f8dbc46646ebc5 100644 --- a/deps/v8/src/objects/backing-store.cc +++ b/deps/v8/src/objects/backing-store.cc @@ -51,8 +51,23 @@ enum class AllocationStatus { kOtherFailure // Failed for an unknown reason }; -size_t GetReservationSize(bool has_guard_regions, size_t byte_capacity, - bool is_wasm_memory64) { +base::AddressRegion GetReservedRegion(bool has_guard_regions, + bool is_wasm_memory64, void* buffer_start, + size_t byte_capacity) { + return base::AddressRegion( + reinterpret_cast
(buffer_start), + BackingStore::GetWasmReservationSize(has_guard_regions, byte_capacity, is_wasm_memory64)); +} + +void RecordStatus(Isolate* isolate, AllocationStatus status) { + isolate->counters()->wasm_memory_allocation_result()->AddSample( + static_cast(status)); +} + +} // namespace + +size_t BackingStore::GetWasmReservationSize(bool has_guard_regions, size_t byte_capacity, + bool is_wasm_memory64) { #if V8_TARGET_ARCH_64_BIT && V8_ENABLE_WEBASSEMBLY DCHECK_IMPLIES(is_wasm_memory64 && has_guard_regions, v8_flags.wasm_memory64_trap_handling); @@ -73,21 +88,6 @@ size_t GetReservationSize(bool has_guard_regions, size_t byte_capacity, return byte_capacity; } -base::AddressRegion GetReservedRegion(bool has_guard_regions, - bool is_wasm_memory64, void* buffer_start, - size_t byte_capacity) { - return base::AddressRegion( - reinterpret_cast
(buffer_start), - GetReservationSize(has_guard_regions, byte_capacity, is_wasm_memory64)); -} - -void RecordStatus(Isolate* isolate, AllocationStatus status) { - isolate->counters()->wasm_memory_allocation_result()->AddSample( - static_cast(status)); -} - -} // namespace - // The backing store for a Wasm shared memory remembers all the isolates // with which it has been shared. struct SharedWasmMemoryData { @@ -168,7 +168,7 @@ BackingStore::~BackingStore() { #if V8_ENABLE_WEBASSEMBLY if (is_wasm_memory()) { - size_t reservation_size = GetReservationSize( + size_t reservation_size = GetWasmReservationSize( has_guard_regions(), byte_capacity_, is_wasm_memory64()); TRACE_BS( "BSw:free bs=%p mem=%p (length=%zu, capacity=%zu, reservation=%zu)\n", @@ -325,7 +325,7 @@ std::unique_ptr BackingStore::TryAllocateAndPartiallyCommitMemory( size_t byte_capacity = maximum_pages * page_size; size_t reservation_size = - GetReservationSize(has_guard_regions, byte_capacity, is_wasm_memory64); + GetWasmReservationSize(has_guard_regions, byte_capacity, is_wasm_memory64); //-------------------------------------------------------------------------- // Allocate pages (inaccessible by default). diff --git a/deps/v8/src/objects/backing-store.h b/deps/v8/src/objects/backing-store.h index 70882e9bdeafce..aab2a3c7764bfe 100644 --- a/deps/v8/src/objects/backing-store.h +++ b/deps/v8/src/objects/backing-store.h @@ -183,6 +183,9 @@ class V8_EXPORT_PRIVATE BackingStore : public BackingStoreBase { uint32_t id() const { return id_; } + // Return the size of the reservation needed for a wasm backing store. + static size_t GetWasmReservationSize(bool has_guard_regions, size_t byte_capacity, + bool is_wasm_memory64); private: friend class GlobalBackingStoreRegistry; diff --git a/src/node.cc b/src/node.cc index a18660d388be9d..624b22c2160035 100644 --- a/src/node.cc +++ b/src/node.cc @@ -1258,10 +1258,10 @@ InitializeOncePerProcessInternal(const std::vector& args, } #if NODE_USE_V8_WASM_TRAP_HANDLER - bool use_wasm_trap_handler = - !per_process::cli_options->disable_wasm_trap_handler; + bool wasm_trap_handler_disabled = + per_process::cli_options->disable_wasm_trap_handler; if (!(flags & ProcessInitializationFlags::kNoDefaultSignalHandling) && - use_wasm_trap_handler) { + !wasm_trap_handler_disabled && V8::CanEnableWebAssemblyTrapHandler()) { #if defined(_WIN32) constexpr ULONG first = TRUE; per_process::old_vectored_exception_handler = diff --git a/test/wasm-allocation/test-wasm-allocation-works-by-default.js b/test/wasm-allocation/test-wasm-allocation-works-by-default.js new file mode 100644 index 00000000000000..1244ac8b0329c6 --- /dev/null +++ b/test/wasm-allocation/test-wasm-allocation-works-by-default.js @@ -0,0 +1,6 @@ +// Test that with limited virtual memory space, WASM can still allocate memory +// even without --disable-wasm-trap-handler. +'use strict'; + +require('../common'); +new WebAssembly.Memory({ initial: 10, maximum: 100 });