GV

GeezerVison

The World Wide Web, restored to 800 pixels of glory.

Home|About|Gaming cabinet|Archives|How it works|Starter sites

On this site

  • Sign in
  • ActionsCI/CD and automation
  • CodespacesCloud development environments
  • All Features
  • Documentation
  • Training
  • Certifications
  • Build with Copilot
  • Customize Copilot
  • Integrations
  • Trending repos
  • GitHub Sponsors
  • Developer articles
  • Enterprises

This view

  • Original page
  • New address

GeezerVison is showing github.com — cli: allow running wasm in limited vmem with --disable-wasm-trap-handler · nodejs/node@fb85d38

File tree

Expand file treeCollapse file tree
Open diff view settings
Filter options
  • doc
    • api
      • cli.md
    • node.1
  • src
    • node.cc
    • node_options.cc
    • node_options.h
  • test
    • testpy
      • __init__.py
    • wasm-allocation
      • test-wasm-allocation.js
      • testcfg.py
      • wasm-allocation.status
Expand file treeCollapse file tree
Open diff view settings
Collapse file

‎doc/api/cli.md‎

Copy file name to clipboardExpand all lines: doc/api/cli.md
+40Lines changed: 40 additions & 0 deletions
  • Display the source diff
  • Display the rich diff
Original file line numberDiff line numberDiff line change
@@ -565,6 +565,45 @@ const vm = require('node:vm');
565565
vm.measureMemory();
566566
```
567567

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+
568607
### `--disable-proto=mode`
569608

570609
<!-- YAML
@@ -2634,6 +2673,7 @@ one is included in the list below.
26342673
* `--diagnostic-dir`
26352674
* `--disable-proto`
26362675
* `--disable-warning`
2676+
* `--disable-wasm-trap-handler`
26372677
* `--dns-result-order`
26382678
* `--enable-fips`
26392679
* `--enable-network-family-autoselection`
Collapse file

‎doc/node.1‎

Copy file name to clipboardExpand all lines: doc/node.1
+5Lines changed: 5 additions & 0 deletions
Original file line numberDiff line numberDiff line change
@@ -142,6 +142,11 @@ is `delete`, the property will be removed entirely. If
142142
is `throw`, accesses to the property will throw an exception with the code
143143
`ERR_PROTO_ACCESS`.
144144
.
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+
.
145150
.It Fl -disallow-code-generation-from-strings
146151
Make built-in language features like `eval` and `new Function` that generate
147152
code from strings throw an exception instead. This does not affect the Node.js
Collapse file

‎src/node.cc‎

Copy file name to clipboardExpand all lines: src/node.cc
+38-31Lines changed: 38 additions & 31 deletions
Original file line numberDiff line numberDiff line change
@@ -425,6 +425,7 @@ MaybeLocal<Value> StartExecution(Environment* env, StartExecutionCallback cb) {
425425
typedef void (*sigaction_cb)(int signo, siginfo_t* info, void* ucontext);
426426
#endif
427427
#if NODE_USE_V8_WASM_TRAP_HANDLER
428+
static std::atomic<bool> is_wasm_trap_handler_configured{false};
428429
#if defined(_WIN32)
429430
static LONG WINAPI TrapWebAssemblyOrContinue(EXCEPTION_POINTERS* exception) {
430431
if (v8::TryHandleWebAssemblyTrapWindows(exception)) {
@@ -470,15 +471,17 @@ void RegisterSignalHandler(int signal,
470471
bool reset_handler) {
471472
CHECK_NOT_NULL(handler);
472473
#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()) {
474477
CHECK(previous_sigsegv_action.is_lock_free());
475478
CHECK(!reset_handler);
476479
previous_sigsegv_action.store(handler);
477480
return;
478481
}
479-
// TODO(align behavior between macos and other in next major version)
482+
// TODO(align behavior between macos and other in next major version)
480483
#if defined(__APPLE__)
481-
if (signal == SIGBUS) {
484+
if (signal == SIGBUS && is_wasm_trap_handler_configured.load()) {
482485
CHECK(previous_sigbus_action.is_lock_free());
483486
CHECK(!reset_handler);
484487
previous_sigbus_action.store(handler);
@@ -630,25 +633,6 @@ static void PlatformInit(ProcessInitializationFlags::Flags flags) {
630633
if (!(flags & ProcessInitializationFlags::kNoDefaultSignalHandling)) {
631634
RegisterSignalHandler(SIGINT, SignalExit, true);
632635
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
652636
}
653637

654638
if (!(flags & ProcessInitializationFlags::kNoAdjustResourceLimits)) {
@@ -675,14 +659,6 @@ static void PlatformInit(ProcessInitializationFlags::Flags flags) {
675659
}
676660
#endif // __POSIX__
677661
#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
686662
if (!(flags & ProcessInitializationFlags::kNoStdioInitialization)) {
687663
for (int fd = 0; fd <= 2; ++fd) {
688664
auto handle = reinterpret_cast<HANDLE>(_get_osfhandle(fd));
@@ -1225,6 +1201,37 @@ InitializeOncePerProcessInternal(const std::vector<std::string>& args,
12251201
cppgc::InitializeProcess(allocator);
12261202
}
12271203

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+
12281235
performance::performance_v8_start = PERFORMANCE_NOW();
12291236
per_process::v8_initialized = true;
12301237

@@ -1254,7 +1261,7 @@ void TearDownOncePerProcess() {
12541261
}
12551262

12561263
#if NODE_USE_V8_WASM_TRAP_HANDLER && defined(_WIN32)
1257-
if (!(flags & ProcessInitializationFlags::kNoDefaultSignalHandling)) {
1264+
if (is_wasm_trap_handler_configured.load()) {
12581265
RemoveVectoredExceptionHandler(per_process::old_vectored_exception_handler);
12591266
}
12601267
#endif
Collapse file

‎src/node_options.cc‎

Copy file name to clipboardExpand all lines: src/node_options.cc
+7Lines changed: 7 additions & 0 deletions
Original file line numberDiff line numberDiff line change
@@ -1066,6 +1066,13 @@ PerProcessOptionsParser::PerProcessOptionsParser(
10661066
AddOption("--run",
10671067
"Run a script specified in package.json",
10681068
&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);
10691076
}
10701077

10711078
inline std::string RemoveBrackets(const std::string& host) {
Collapse file

‎src/node_options.h‎

Copy file name to clipboardExpand all lines: src/node_options.h
+2Lines changed: 2 additions & 0 deletions
Original file line numberDiff line numberDiff line change
@@ -306,6 +306,8 @@ class PerProcessOptions : public Options {
306306
bool openssl_shared_config = false;
307307
#endif
308308

309+
bool disable_wasm_trap_handler = false;
310+
309311
// Per-process because reports can be triggered outside a known V8 context.
310312
bool report_on_fatalerror = false;
311313
bool report_compact = false;
Collapse file

‎test/testpy/__init__.py‎

Copy file name to clipboardExpand all lines: test/testpy/__init__.py
+12Lines changed: 12 additions & 0 deletions
Original file line numberDiff line numberDiff line change
@@ -167,3 +167,15 @@ def ListTests(self, current_path, path, arch, mode):
167167
for tst in result:
168168
tst.disable_core_files = True
169169
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
Collapse file

‎test/wasm-allocation/test-wasm-allocation.js‎

Copy file name to clipboard
+7Lines changed: 7 additions & 0 deletions
Original file line numberDiff line numberDiff 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 });
Collapse file

‎test/wasm-allocation/testcfg.py‎

Copy file name to clipboard
+6Lines changed: 6 additions & 0 deletions
Original file line numberDiff line numberDiff 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')
Collapse file

‎test/wasm-allocation/wasm-allocation.status‎

Copy file name to clipboard
+10Lines changed: 10 additions & 0 deletions
Original file line numberDiff line numberDiff 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

0 commit comments

Comments
0 (0)
You are visitor 01402503 — GeezerVison does not store the pages you look up.
Best viewed with Netscape Navigator 4.7 or Internet Explorer 6 at 800×600. No cookies required.