| FazBrowse GitHub Viewer | Trending | | Home |
| Tools: [Download Repo ZIP] [Original HTTPS Page] |
1 parent c6043fe commit fb85d38
9 files changed
| Original file line number | Diff line number | Diff line change | |
|---|---|---|---|
@@ -565,6 +565,45 @@ const vm = require('node:vm'); | |||
| 565 | 565 | vm.measureMemory(); | |
| 566 | 566 | ``` | |
| 567 | 567 | ||
| 568 | + ### `--disable-wasm-trap-handler` | ||
| 569 | + | ||
| 570 | + <!-- YAML | ||
| 571 | + added: REPLACEME | ||
| 572 | + --> | ||
| 573 | + | ||
| 574 | + By default, Node.js enables trap-handler-based WebAssembly bound | ||
| 575 | + checks. As a result, V8 does not need to insert inline bound checks | ||
| 576 | + int the code compiled from WebAssembly which may speedup WebAssembly | ||
| 577 | + execution significantly, but this optimization requires allocating | ||
| 578 | + a big virtual memory cage (currently 10GB). If the Node.js process | ||
| 579 | + does not have access to a large enough virtual memory address space | ||
| 580 | + due to system configurations or hardware limitations, users won't | ||
| 581 | + be able to run any WebAssembly that involves allocation in this | ||
| 582 | + virtual memory cage and will see an out-of-memory error. | ||
| 583 | + | ||
| 584 | + ```console | ||
| 585 | + $ ulimit -v 5000000 | ||
| 586 | + $ node -p "new WebAssembly.Memory({ initial: 10, maximum: 100 });" | ||
| 587 | + [eval]:1 | ||
| 588 | + new WebAssembly.Memory({ initial: 10, maximum: 100 }); | ||
| 589 | + ^ | ||
| 590 | + | ||
| 591 | + RangeError: WebAssembly.Memory(): could not allocate memory | ||
| 592 | + at [eval]:1:1 | ||
| 593 | + at runScriptInThisContext (node:internal/vm:209:10) | ||
| 594 | + at node:internal/process/execution:118:14 | ||
| 595 | + at [eval]-wrapper:6:24 | ||
| 596 | + at runScript (node:internal/process/execution:101:62) | ||
| 597 | + at evalScript (node:internal/process/execution:136:3) | ||
| 598 | + at node:internal/main/eval_string:49:3 | ||
| 599 | + | ||
| 600 | + ``` | ||
| 601 | + | ||
| 602 | + `--disable-wasm-trap-handler` disables this optimization so that | ||
| 603 | + users can at least run WebAssembly (with less optimal performance) | ||
| 604 | + when the virtual memory address space available to their Node.js | ||
| 605 | + process is lower than what the V8 WebAssembly memory cage needs. | ||
| 606 | + | ||
| 568 | 607 | ### `--disable-proto=mode` | |
| 569 | 608 | ||
| 570 | 609 | <!-- YAML | |
@@ -2634,6 +2673,7 @@ one is included in the list below. | |||
| 2634 | 2673 | * `--diagnostic-dir` | |
| 2635 | 2674 | * `--disable-proto` | |
| 2636 | 2675 | * `--disable-warning` | |
| 2676 | + * `--disable-wasm-trap-handler` | ||
| 2637 | 2677 | * `--dns-result-order` | |
| 2638 | 2678 | * `--enable-fips` | |
| 2639 | 2679 | * `--enable-network-family-autoselection` | |
| Original file line number | Diff line number | Diff line change | |
|---|---|---|---|
@@ -142,6 +142,11 @@ is `delete`, the property will be removed entirely. If | |||
| 142 | 142 | is `throw`, accesses to the property will throw an exception with the code | |
| 143 | 143 | `ERR_PROTO_ACCESS`. | |
| 144 | 144 | . | |
| 145 | + .It Fl -disable-wasm-trap-handler Ns = Ns Ar mode | ||
| 146 | + Disable trap-handler-based WebAssembly bound checks and fall back to | ||
| 147 | + inline bound checks so that WebAssembly can be run with limited virtual | ||
| 148 | + memory. | ||
| 149 | + . | ||
| 145 | 150 | .It Fl -disallow-code-generation-from-strings | |
| 146 | 151 | Make built-in language features like `eval` and `new Function` that generate | |
| 147 | 152 | code from strings throw an exception instead. This does not affect the Node.js | |
| Original file line number | Diff line number | Diff line change | |
|---|---|---|---|
@@ -425,6 +425,7 @@ MaybeLocal<Value> StartExecution(Environment* env, StartExecutionCallback cb) { | |||
| 425 | 425 | typedef void (*sigaction_cb)(int signo, siginfo_t* info, void* ucontext); | |
| 426 | 426 | #endif | |
| 427 | 427 | #if NODE_USE_V8_WASM_TRAP_HANDLER | |
| 428 | + static std::atomic<bool> is_wasm_trap_handler_configured{false}; | ||
| 428 | 429 | #if defined(_WIN32) | |
| 429 | 430 | static LONG WINAPI TrapWebAssemblyOrContinue(EXCEPTION_POINTERS* exception) { | |
| 430 | 431 | if (v8::TryHandleWebAssemblyTrapWindows(exception)) { | |
@@ -470,15 +471,17 @@ void RegisterSignalHandler(int signal, | |||
| 470 | 471 | bool reset_handler) { | |
| 471 | 472 | CHECK_NOT_NULL(handler); | |
| 472 | 473 | #if NODE_USE_V8_WASM_TRAP_HANDLER | |
| 473 | - if (signal == SIGSEGV) { | ||
| 474 | + // Stash the user-registered handlers for TrapWebAssemblyOrContinue | ||
| 475 | + // to call out to when the signal is not coming from a WASM OOM. | ||
| 476 | + if (signal == SIGSEGV && is_wasm_trap_handler_configured.load()) { | ||
| 474 | 477 | CHECK(previous_sigsegv_action.is_lock_free()); | |
| 475 | 478 | CHECK(!reset_handler); | |
| 476 | 479 | previous_sigsegv_action.store(handler); | |
| 477 | 480 | return; | |
| 478 | 481 | } | |
| 479 | - // TODO(align behavior between macos and other in next major version) | ||
| 482 | + // TODO(align behavior between macos and other in next major version) | ||
| 480 | 483 | #if defined(__APPLE__) | |
| 481 | - if (signal == SIGBUS) { | ||
| 484 | + if (signal == SIGBUS && is_wasm_trap_handler_configured.load()) { | ||
| 482 | 485 | CHECK(previous_sigbus_action.is_lock_free()); | |
| 483 | 486 | CHECK(!reset_handler); | |
| 484 | 487 | previous_sigbus_action.store(handler); | |
@@ -630,25 +633,6 @@ static void PlatformInit(ProcessInitializationFlags::Flags flags) { | |||
| 630 | 633 | if (!(flags & ProcessInitializationFlags::kNoDefaultSignalHandling)) { | |
| 631 | 634 | RegisterSignalHandler(SIGINT, SignalExit, true); | |
| 632 | 635 | RegisterSignalHandler(SIGTERM, SignalExit, true); | |
| 633 | - | ||
| 634 | - #if NODE_USE_V8_WASM_TRAP_HANDLER | ||
| 635 | - // Tell V8 to disable emitting WebAssembly | ||
| 636 | - // memory bounds checks. This means that we have | ||
| 637 | - // to catch the SIGSEGV/SIGBUS in TrapWebAssemblyOrContinue | ||
| 638 | - // and pass the signal context to V8. | ||
| 639 | - { | ||
| 640 | - struct sigaction sa; | ||
| 641 | - memset(&sa, 0, sizeof(sa)); | ||
| 642 | - sa.sa_sigaction = TrapWebAssemblyOrContinue; | ||
| 643 | - sa.sa_flags = SA_SIGINFO; | ||
| 644 | - CHECK_EQ(sigaction(SIGSEGV, &sa, nullptr), 0); | ||
| 645 | - // TODO(align behavior between macos and other in next major version) | ||
| 646 | - #if defined(__APPLE__) | ||
| 647 | - CHECK_EQ(sigaction(SIGBUS, &sa, nullptr), 0); | ||
| 648 | - #endif | ||
| 649 | - } | ||
| 650 | - V8::EnableWebAssemblyTrapHandler(false); | ||
| 651 | - #endif // NODE_USE_V8_WASM_TRAP_HANDLER | ||
| 652 | 636 | } | |
| 653 | 637 | ||
| 654 | 638 | if (!(flags & ProcessInitializationFlags::kNoAdjustResourceLimits)) { | |
@@ -675,14 +659,6 @@ static void PlatformInit(ProcessInitializationFlags::Flags flags) { | |||
| 675 | 659 | } | |
| 676 | 660 | #endif // __POSIX__ | |
| 677 | 661 | #ifdef _WIN32 | |
| 678 | - #ifdef NODE_USE_V8_WASM_TRAP_HANDLER | ||
| 679 | - { | ||
| 680 | - constexpr ULONG first = TRUE; | ||
| 681 | - per_process::old_vectored_exception_handler = | ||
| 682 | - AddVectoredExceptionHandler(first, TrapWebAssemblyOrContinue); | ||
| 683 | - } | ||
| 684 | - V8::EnableWebAssemblyTrapHandler(false); | ||
| 685 | - #endif // NODE_USE_V8_WASM_TRAP_HANDLER | ||
| 686 | 662 | if (!(flags & ProcessInitializationFlags::kNoStdioInitialization)) { | |
| 687 | 663 | for (int fd = 0; fd <= 2; ++fd) { | |
| 688 | 664 | auto handle = reinterpret_cast<HANDLE>(_get_osfhandle(fd)); | |
@@ -1225,6 +1201,37 @@ InitializeOncePerProcessInternal(const std::vector<std::string>& args, | |||
| 1225 | 1201 | cppgc::InitializeProcess(allocator); | |
| 1226 | 1202 | } | |
| 1227 | 1203 | ||
| 1204 | + #if NODE_USE_V8_WASM_TRAP_HANDLER | ||
| 1205 | + bool use_wasm_trap_handler = | ||
| 1206 | + !per_process::cli_options->disable_wasm_trap_handler; | ||
| 1207 | + if (!(flags & ProcessInitializationFlags::kNoDefaultSignalHandling) && | ||
| 1208 | + use_wasm_trap_handler) { | ||
| 1209 | + #if defined(_WIN32) | ||
| 1210 | + constexpr ULONG first = TRUE; | ||
| 1211 | + per_process::old_vectored_exception_handler = | ||
| 1212 | + AddVectoredExceptionHandler(first, TrapWebAssemblyOrContinue); | ||
| 1213 | + #else | ||
| 1214 | + // Tell V8 to disable emitting WebAssembly | ||
| 1215 | + // memory bounds checks. This means that we have | ||
| 1216 | + // to catch the SIGSEGV/SIGBUS in TrapWebAssemblyOrContinue | ||
| 1217 | + // and pass the signal context to V8. | ||
| 1218 | + { | ||
| 1219 | + struct sigaction sa; | ||
| 1220 | + memset(&sa, 0, sizeof(sa)); | ||
| 1221 | + sa.sa_sigaction = TrapWebAssemblyOrContinue; | ||
| 1222 | + sa.sa_flags = SA_SIGINFO; | ||
| 1223 | + CHECK_EQ(sigaction(SIGSEGV, &sa, nullptr), 0); | ||
| 1224 | + // TODO(align behavior between macos and other in next major version) | ||
| 1225 | + #if defined(__APPLE__) | ||
| 1226 | + CHECK_EQ(sigaction(SIGBUS, &sa, nullptr), 0); | ||
| 1227 | + #endif | ||
| 1228 | + } | ||
| 1229 | + #endif // defined(_WIN32) | ||
| 1230 | + is_wasm_trap_handler_configured.store(true); | ||
| 1231 | + V8::EnableWebAssemblyTrapHandler(false); | ||
| 1232 | + } | ||
| 1233 | + #endif // NODE_USE_V8_WASM_TRAP_HANDLER | ||
| 1234 | + | ||
| 1228 | 1235 | performance::performance_v8_start = PERFORMANCE_NOW(); | |
| 1229 | 1236 | per_process::v8_initialized = true; | |
| 1230 | 1237 | ||
@@ -1254,7 +1261,7 @@ void TearDownOncePerProcess() { | |||
| 1254 | 1261 | } | |
| 1255 | 1262 | ||
| 1256 | 1263 | #if NODE_USE_V8_WASM_TRAP_HANDLER && defined(_WIN32) | |
| 1257 | - if (!(flags & ProcessInitializationFlags::kNoDefaultSignalHandling)) { | ||
| 1264 | + if (is_wasm_trap_handler_configured.load()) { | ||
| 1258 | 1265 | RemoveVectoredExceptionHandler(per_process::old_vectored_exception_handler); | |
| 1259 | 1266 | } | |
| 1260 | 1267 | #endif | |
| Original file line number | Diff line number | Diff line change | |
|---|---|---|---|
@@ -1066,6 +1066,13 @@ PerProcessOptionsParser::PerProcessOptionsParser( | |||
| 1066 | 1066 | AddOption("--run", | |
| 1067 | 1067 | "Run a script specified in package.json", | |
| 1068 | 1068 | &PerProcessOptions::run); | |
| 1069 | + AddOption( | ||
| 1070 | + "--disable-wasm-trap-handler", | ||
| 1071 | + "Disable trap-handler-based WebAssembly bound checks. V8 will insert " | ||
| 1072 | + "inline bound checks when compiling WebAssembly which may slow down " | ||
| 1073 | + "performance.", | ||
| 1074 | + &PerProcessOptions::disable_wasm_trap_handler, | ||
| 1075 | + kAllowedInEnvvar); | ||
| 1069 | 1076 | } | |
| 1070 | 1077 | ||
| 1071 | 1078 | inline std::string RemoveBrackets(const std::string& host) { | |
| Original file line number | Diff line number | Diff line change | |
|---|---|---|---|
@@ -306,6 +306,8 @@ class PerProcessOptions : public Options { | |||
| 306 | 306 | bool openssl_shared_config = false; | |
| 307 | 307 | #endif | |
| 308 | 308 | ||
| 309 | + bool disable_wasm_trap_handler = false; | ||
| 310 | + | ||
| 309 | 311 | // Per-process because reports can be triggered outside a known V8 context. | |
| 310 | 312 | bool report_on_fatalerror = false; | |
| 311 | 313 | bool report_compact = false; | |
| Original file line number | Diff line number | Diff line change | |
|---|---|---|---|
@@ -167,3 +167,15 @@ def ListTests(self, current_path, path, arch, mode): | |||
| 167 | 167 | for tst in result: | |
| 168 | 168 | tst.disable_core_files = True | |
| 169 | 169 | return result | |
| 170 | + | ||
| 171 | + class WasmAllocationTestConfiguration(SimpleTestConfiguration): | ||
| 172 | + def __init__(self, context, root, section, additional=None): | ||
| 173 | + super(WasmAllocationTestConfiguration, self).__init__(context, root, section, | ||
| 174 | + additional) | ||
| 175 | + | ||
| 176 | + def ListTests(self, current_path, path, arch, mode): | ||
| 177 | + result = super(WasmAllocationTestConfiguration, self).ListTests( | ||
| 178 | + current_path, path, arch, mode) | ||
| 179 | + for tst in result: | ||
| 180 | + tst.max_virtual_memory = 5 * 1024 * 1024 * 1024 # 5GB | ||
| 181 | + return result | ||
| Original file line number | Diff line number | Diff line change | |
|---|---|---|---|
@@ -0,0 +1,7 @@ | |||
| 1 | + // Flags: --disable-wasm-trap-handler | ||
| 2 | + // Test that with limited virtual memory space, --disable-wasm-trap-handler | ||
| 3 | + // allows WASM to at least run with inline bound checks. | ||
| 4 | + 'use strict'; | ||
| 5 | + | ||
| 6 | + require('../common'); | ||
| 7 | + new WebAssembly.Memory({ initial: 10, maximum: 100 }); | ||
| Original file line number | Diff line number | Diff line change | |
|---|---|---|---|
@@ -0,0 +1,6 @@ | |||
| 1 | + import sys, os | ||
| 2 | + sys.path.append(os.path.join(os.path.dirname(__file__), '..')) | ||
| 3 | + import testpy | ||
| 4 | + | ||
| 5 | + def GetConfiguration(context, root): | ||
| 6 | + return testpy.WasmAllocationTestConfiguration(context, root, 'wasm-allocation') | ||
| Original file line number | Diff line number | Diff line change | |
|---|---|---|---|
@@ -0,0 +1,10 @@ | |||
| 1 | + prefix wasm-allocation | ||
| 2 | + | ||
| 3 | + # To mark a test as flaky, list the test name in the appropriate section | ||
| 4 | + # below, without ".js", followed by ": PASS,FLAKY". Example: | ||
| 5 | + # sample-test : PASS,FLAKY | ||
| 6 | + | ||
| 7 | + [true] # This section applies to all platforms | ||
| 8 | + | ||
| 9 | + [$system!=linux || $asan==on] | ||
| 10 | + test-wasm-allocation: SKIP | ||
| Back | FazBrowse Home | New Git URL |
0 commit comments