Skip to content

Commit

Permalink
src: enable V8's WASM trap handlers
Browse files Browse the repository at this point in the history
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: #14927

PR-URL: #27246
Reviewed-By: Ben Noordhuis <[email protected]>
Reviewed-By: Joyee Cheung <[email protected]>
Reviewed-By: Anna Henningsen <[email protected]>
  • Loading branch information
devsnek authored and sam-github committed May 16, 2019
1 parent cca375f commit f206193
Show file tree
Hide file tree
Showing 6 changed files with 76 additions and 11 deletions.
2 changes: 1 addition & 1 deletion src/inspector_agent.cc
Original file line number Diff line number Diff line change
Expand Up @@ -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);
}

Expand Down
58 changes: 55 additions & 3 deletions src/node.cc
Original file line number Diff line number Diff line change
Expand Up @@ -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
Expand Down Expand Up @@ -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
Expand All @@ -188,6 +200,7 @@ void SignalExit(int signo) {
#endif
raise(signo);
}
#endif // __POSIX__

MaybeLocal<Value> ExecuteBootstrapper(Environment* env,
const char* id,
Expand Down Expand Up @@ -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.
Expand Down Expand Up @@ -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) {
Expand Down
15 changes: 15 additions & 0 deletions src/node.h
Original file line number Diff line number Diff line change
Expand Up @@ -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))

Expand Down Expand Up @@ -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_
5 changes: 1 addition & 4 deletions src/node_internals.h
Original file line number Diff line number Diff line change
Expand Up @@ -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();
Expand Down
5 changes: 3 additions & 2 deletions src/node_watchdog.cc
Original file line number Diff line number Diff line change
Expand Up @@ -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_);
}

Expand Down
2 changes: 1 addition & 1 deletion src/node_watchdog.h
Original file line number Diff line number Diff line change
Expand Up @@ -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);
Expand Down

0 comments on commit f206193

Please sign in to comment.