From 6591826e99b5b9f92b69e7735a25542575d2edc5 Mon Sep 17 00:00:00 2001 From: Congcong Cai Date: Mon, 12 Jun 2023 16:50:21 +0200 Subject: [PATCH] src: handle wasm out of bound in osx will raise SIGBUS correctly fix: #46559 OSX will raise both SIGBUS and SIGSEGV when out of bound memory visit, This commit set sigaction in OSX for two signals to handle this. PR-URL: https://github.com/nodejs/node/pull/46561 Fixes: https://github.com/nodejs/node/issues/46559 Reviewed-By: Anna Henningsen Reviewed-By: James M Snell Reviewed-By: Darshan Sen Reviewed-By: Minwoo Jung Reviewed-By: Michael Dawson --- src/node.cc | 24 +++++++++++++++++- test/fixtures/out-of-bound.wasm | Bin 0 -> 58 bytes test/fixtures/out-of-bound.wat | 16 ++++++++++++ .../parallel/test-wasm-memory-out-of-bound.js | 12 +++++++++ 4 files changed, 51 insertions(+), 1 deletion(-) create mode 100644 test/fixtures/out-of-bound.wasm create mode 100644 test/fixtures/out-of-bound.wat create mode 100644 test/parallel/test-wasm-memory-out-of-bound.js diff --git a/src/node.cc b/src/node.cc index 7881742ac91b59..f483e59dd155a8 100644 --- a/src/node.cc +++ b/src/node.cc @@ -362,10 +362,19 @@ static LONG TrapWebAssemblyOrContinue(EXCEPTION_POINTERS* exception) { } #else static std::atomic previous_sigsegv_action; +// TODO(align behavior between macos and other in next major version) +#if defined(__APPLE__) +static std::atomic previous_sigbus_action; +#endif // __APPLE__ void TrapWebAssemblyOrContinue(int signo, siginfo_t* info, void* ucontext) { if (!v8::TryHandleWebAssemblyTrapPosix(signo, info, ucontext)) { +#if defined(__APPLE__) + sigaction_cb prev = signo == SIGBUS ? previous_sigbus_action.load() + : previous_sigsegv_action.load(); +#else sigaction_cb prev = previous_sigsegv_action.load(); +#endif // __APPLE__ if (prev != nullptr) { prev(signo, info, ucontext); } else { @@ -395,6 +404,15 @@ void RegisterSignalHandler(int signal, previous_sigsegv_action.store(handler); return; } +// TODO(align behavior between macos and other in next major version) +#if defined(__APPLE__) + if (signal == SIGBUS) { + CHECK(previous_sigbus_action.is_lock_free()); + CHECK(!reset_handler); + previous_sigbus_action.store(handler); + return; + } +#endif // __APPLE__ #endif // NODE_USE_V8_WASM_TRAP_HANDLER struct sigaction sa; memset(&sa, 0, sizeof(sa)); @@ -551,7 +569,7 @@ static void PlatformInit(ProcessInitializationFlags::Flags flags) { #else // Tell V8 to disable emitting WebAssembly // memory bounds checks. This means that we have - // to catch the SIGSEGV in TrapWebAssemblyOrContinue + // to catch the SIGSEGV/SIGBUS in TrapWebAssemblyOrContinue // and pass the signal context to V8. { struct sigaction sa; @@ -559,6 +577,10 @@ static void PlatformInit(ProcessInitializationFlags::Flags flags) { sa.sa_sigaction = TrapWebAssemblyOrContinue; sa.sa_flags = SA_SIGINFO; CHECK_EQ(sigaction(SIGSEGV, &sa, nullptr), 0); +// TODO(align behavior between macos and other in next major version) +#if defined(__APPLE__) + CHECK_EQ(sigaction(SIGBUS, &sa, nullptr), 0); +#endif } #endif // defined(_WIN32) V8::EnableWebAssemblyTrapHandler(false); diff --git a/test/fixtures/out-of-bound.wasm b/test/fixtures/out-of-bound.wasm new file mode 100644 index 0000000000000000000000000000000000000000..a95761b0f514101b7db446f034699064927513e0 GIT binary patch literal 58 zcmZQbEY4+QU|?WmVN76PU}j=uU}a`xU}WcFWQ#8@Nh~U1VBivE6lSnza6G_}_none (func)) + (memory $0 1) + (export "_start" (func $_start)) + (func $_start + memory.size + i32.const 64 + i32.mul + i32.const 1024 + i32.mul + i32.const 3 + i32.sub + i32.load + drop + ) +) diff --git a/test/parallel/test-wasm-memory-out-of-bound.js b/test/parallel/test-wasm-memory-out-of-bound.js new file mode 100644 index 00000000000000..56ac96beabd33e --- /dev/null +++ b/test/parallel/test-wasm-memory-out-of-bound.js @@ -0,0 +1,12 @@ +'use strict'; + +const common = require('../common'); +const assert = require('assert'); +const fixtures = require('../common/fixtures'); + +const buffer = fixtures.readSync('out-of-bound.wasm'); +WebAssembly.instantiate(buffer, {}).then(common.mustCall((results) => { + assert.throws(() => { + results.instance.exports._start(); + }, WebAssembly.RuntimeError('memory access out of bounds')); +}));