From f2061930c83cb579410a7f26bed726d6568575e3 Mon Sep 17 00:00:00 2001 From: Gus Caplan Date: Mon, 15 Apr 2019 16:09:51 -0500 Subject: [PATCH] src: enable V8's WASM trap handlers This uses SIGSEGV handlers to catch WASM out of bound (OOB) memory accesses instead of inserting OOB checks inline, resulting in a 25%-30% speed increase. Note that installing a custom SIGSEGV handler will break this, resulting in potentially scary behaviour. Refs: https://github.com/nodejs/node/issues/14927 PR-URL: https://github.com/nodejs/node/pull/27246 Reviewed-By: Ben Noordhuis Reviewed-By: Joyee Cheung Reviewed-By: Anna Henningsen --- src/inspector_agent.cc | 2 +- src/node.cc | 58 +++++++++++++++++++++++++++++++++++++++--- src/node.h | 15 +++++++++++ src/node_internals.h | 5 +--- src/node_watchdog.cc | 5 ++-- src/node_watchdog.h | 2 +- 6 files changed, 76 insertions(+), 11 deletions(-) diff --git a/src/inspector_agent.cc b/src/inspector_agent.cc index d8b5d01a285834..d551c3a7d7324c 100644 --- a/src/inspector_agent.cc +++ b/src/inspector_agent.cc @@ -88,7 +88,7 @@ void StartIoInterrupt(Isolate* isolate, void* agent) { #ifdef __POSIX__ -static void StartIoThreadWakeup(int signo) { +static void StartIoThreadWakeup(int signo, siginfo_t* info, void* ucontext) { uv_sem_post(&start_io_thread_semaphore); } diff --git a/src/node.cc b/src/node.cc index 953465e04ac9e4..7dccd7ce576828 100644 --- a/src/node.cc +++ b/src/node.cc @@ -70,6 +70,17 @@ #include "node_report.h" #endif +#if defined(__APPLE__) || defined(__linux__) +#define NODE_USE_V8_WASM_TRAP_HANDLER 1 +#else +#define NODE_USE_V8_WASM_TRAP_HANDLER 0 +#endif + +#if NODE_USE_V8_WASM_TRAP_HANDLER +#include +#include "v8-wasm-trap-handler-posix.h" +#endif // NODE_USE_V8_WASM_TRAP_HANDLER + // ========== global C headers ========== #include // _O_RDWR @@ -177,7 +188,8 @@ void WaitForInspectorDisconnect(Environment* env) { #endif } -void SignalExit(int signo) { +#ifdef __POSIX__ +void SignalExit(int signo, siginfo_t* info, void* ucontext) { uv_tty_reset_mode(); #ifdef __FreeBSD__ // FreeBSD has a nasty bug, see RegisterSignalHandler for details @@ -188,6 +200,7 @@ void SignalExit(int signo) { #endif raise(signo); } +#endif // __POSIX__ MaybeLocal ExecuteBootstrapper(Environment* env, const char* id, @@ -434,14 +447,39 @@ void LoadEnvironment(Environment* env) { USE(StartMainThreadExecution(env)); } +#if NODE_USE_V8_WASM_TRAP_HANDLER +static std::atomic + previous_sigsegv_action; + +void TrapWebAssemblyOrContinue(int signo, siginfo_t* info, void* ucontext) { + if (!v8::TryHandleWebAssemblyTrapPosix(signo, info, ucontext)) { + auto prev = previous_sigsegv_action.load(); + if (prev != nullptr) { + prev(signo, info, ucontext); + } else { + uv_tty_reset_mode(); + raise(signo); + } + } +} +#endif // NODE_USE_V8_WASM_TRAP_HANDLER #ifdef __POSIX__ void RegisterSignalHandler(int signal, - void (*handler)(int signal), + void (*handler)(int signal, + siginfo_t* info, + void* ucontext), bool reset_handler) { +#if NODE_USE_V8_WASM_TRAP_HANDLER + if (signal == SIGSEGV) { + CHECK(previous_sigsegv_action.is_lock_free()); + previous_sigsegv_action.store(handler); + return; + } +#endif // NODE_USE_V8_WASM_TRAP_HANDLER struct sigaction sa; memset(&sa, 0, sizeof(sa)); - sa.sa_handler = handler; + sa.sa_sigaction = handler; #ifndef __FreeBSD__ // FreeBSD has a nasty bug with SA_RESETHAND reseting the SA_SIGINFO, that is // in turn set for a libthr wrapper. This leads to a crash. @@ -499,6 +537,20 @@ inline void PlatformInit() { RegisterSignalHandler(SIGINT, SignalExit, true); RegisterSignalHandler(SIGTERM, SignalExit, true); +#if NODE_USE_V8_WASM_TRAP_HANDLER + // Tell V8 to disable emitting WebAssembly + // memory bounds checks. This means that we have + // to catch the SIGSEGV in TrapWebAssemblyOrContinue + // and pass the signal context to V8. + { + struct sigaction sa; + memset(&sa, 0, sizeof(sa)); + sa.sa_sigaction = TrapWebAssemblyOrContinue; + CHECK_EQ(sigaction(SIGSEGV, &sa, nullptr), 0); + } + V8::EnableWebAssemblyTrapHandler(false); +#endif // NODE_USE_V8_WASM_TRAP_HANDLER + // Raise the open file descriptor limit. struct rlimit lim; if (getrlimit(RLIMIT_NOFILE, &lim) == 0 && lim.rlim_cur != lim.rlim_max) { diff --git a/src/node.h b/src/node.h index 5af0655a84b808..73a399828522ca 100644 --- a/src/node.h +++ b/src/node.h @@ -66,6 +66,10 @@ #include +#ifdef __POSIX__ +#include +#endif // __POSIX__ + #define NODE_MAKE_VERSION(major, minor, patch) \ ((major) * 0x1000 + (minor) * 0x100 + (patch)) @@ -816,6 +820,17 @@ class NODE_EXTERN AsyncResource { async_context async_context_; }; +#ifdef __POSIX__ +// Register a signal handler without interrupting +// any handlers that node itself needs. +NODE_EXTERN +void RegisterSignalHandler(int signal, + void (*handler)(int signal, + siginfo_t* info, + void* ucontext), + bool reset_handler = false); +#endif // __POSIX__ + } // namespace node #endif // SRC_NODE_H_ diff --git a/src/node_internals.h b/src/node_internals.h index 91cc0efd508d8c..cb039f297b190b 100644 --- a/src/node_internals.h +++ b/src/node_internals.h @@ -91,11 +91,8 @@ void PrintCaughtException(v8::Isolate* isolate, const v8::TryCatch& try_catch); void WaitForInspectorDisconnect(Environment* env); -void SignalExit(int signo); #ifdef __POSIX__ -void RegisterSignalHandler(int signal, - void (*handler)(int signal), - bool reset_handler = false); +void SignalExit(int signal, siginfo_t* info, void* ucontext); #endif std::string GetHumanReadableProcessName(); diff --git a/src/node_watchdog.cc b/src/node_watchdog.cc index 7c62aafa82257b..28df2fb44983e1 100644 --- a/src/node_watchdog.cc +++ b/src/node_watchdog.cc @@ -127,8 +127,9 @@ void* SigintWatchdogHelper::RunSigintWatchdog(void* arg) { return nullptr; } - -void SigintWatchdogHelper::HandleSignal(int signum) { +void SigintWatchdogHelper::HandleSignal(int signum, + siginfo_t* info, + void* ucontext) { uv_sem_post(&instance.sem_); } diff --git a/src/node_watchdog.h b/src/node_watchdog.h index 9c56b90e9ee431..36c2df110908b0 100644 --- a/src/node_watchdog.h +++ b/src/node_watchdog.h @@ -99,7 +99,7 @@ class SigintWatchdogHelper { bool stopping_; static void* RunSigintWatchdog(void* arg); - static void HandleSignal(int signum); + static void HandleSignal(int signum, siginfo_t* info, void* ucontext); #else bool watchdog_disabled_; static BOOL WINAPI WinCtrlCHandlerRoutine(DWORD dwCtrlType);