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 <info@bnoordhuis.nl>
Reviewed-By: Joyee Cheung <joyeec9h3@gmail.com>
Reviewed-By: Anna Henningsen <anna@addaleax.net>
This commit is contained in:
Gus Caplan 2019-04-15 16:09:51 -05:00 committed by Sam Roberts
parent cca375f4af
commit f2061930c8
6 changed files with 76 additions and 11 deletions

View File

@ -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);
}

View File

@ -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 <atomic>
#include "v8-wasm-trap-handler-posix.h"
#endif // NODE_USE_V8_WASM_TRAP_HANDLER
// ========== global C headers ==========
#include <fcntl.h> // _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<Value> 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<void (*)(int signo, siginfo_t* info, void* ucontext)>
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) {

View File

@ -66,6 +66,10 @@
#include <memory>
#ifdef __POSIX__
#include <signal.h>
#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_

View File

@ -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();

View File

@ -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_);
}

View File

@ -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);