Commit ee4fe896 authored by Eric Holk's avatar Eric Holk Committed by Commit Bot

[wasm] trap handlers: fall back on old signal handler

This is primarily needed to test D8 under ASan. ASan installs a signal handler
early in the process startup to show stack traces from crashes. We need to make
sure that if V8 does not handle a signal then the existing handler gets a
chance.

This change only applies when using V8's default signal handler. When
integrating with the embedder's signal handler the behavior is unchanged.

Bug: chromium:771948
Change-Id: Ifd560acf9700ec5f714f009530258fa92c83cabe
Reviewed-on: https://chromium-review.googlesource.com/705823Reviewed-by: 's avatarDeepti Gandluri <gdeepti@chromium.org>
Commit-Queue: Eric Holk <eholk@chromium.org>
Cr-Commit-Position: refs/heads/master@{#48429}
parent 982fed2b
......@@ -160,18 +160,14 @@ void HandleSignal(int signum, siginfo_t* info, void* context) {
if (!TryHandleSignal(signum, info, uc)) {
// Since V8 didn't handle this signal, we want to re-raise the same signal.
// For kernel-generated SEGV signals, we do this by restoring the default
// For kernel-generated SEGV signals, we do this by restoring the original
// SEGV handler and then returning. The fault will happen again and the
// usual SEGV handling will happen.
//
// We handle user-generated signals by calling raise() instead. This is for
// completeness. We should never actually see one of these, but just in
// case, we do the right thing.
struct sigaction action;
action.sa_handler = SIG_DFL;
sigemptyset(&action.sa_mask);
action.sa_flags = 0;
sigaction(signum, &action, nullptr);
RestoreOriginalSignalHandler();
if (!IsKernelGeneratedSignal(info)) {
raise(signum);
}
......
......@@ -248,6 +248,8 @@ void ReleaseHandlerData(int index) {
bool RegisterDefaultSignalHandler() {
#if V8_TRAP_HANDLER_SUPPORTED
CHECK(!g_is_default_signal_handler_registered);
struct sigaction action;
action.sa_sigaction = HandleSignal;
action.sa_flags = SA_SIGINFO;
......@@ -255,10 +257,11 @@ bool RegisterDefaultSignalHandler() {
// {sigaction} installs a new custom segfault handler. On success, it returns
// 0. If we get a nonzero value, we report an error to the caller by returning
// false.
if (sigaction(SIGSEGV, &action, nullptr) != 0) {
if (sigaction(SIGSEGV, &action, &g_old_handler) != 0) {
return false;
}
g_is_default_signal_handler_registered = true;
return true;
#else
return false;
......
......@@ -26,7 +26,22 @@ namespace trap_handler {
// We declare this as int rather than bool as a workaround for a glibc bug, in
// which the dynamic loader cannot handle executables whose TLS area is only
// 1 byte in size; see https://sourceware.org/bugzilla/show_bug.cgi?id=14898.
THREAD_LOCAL int g_thread_in_wasm_code = false;
THREAD_LOCAL int g_thread_in_wasm_code;
#if V8_TRAP_HANDLER_SUPPORTED
// When using the default signal handler, we save the old one to restore in case
// V8 chooses not to handle the signal.
struct sigaction g_old_handler;
bool g_is_default_signal_handler_registered;
#endif
void RestoreOriginalSignalHandler() {
#if V8_TRAP_HANDLER_SUPPORTED
if (sigaction(SIGSEGV, &g_old_handler, nullptr) == 0) {
g_is_default_signal_handler_registered = false;
}
#endif
}
static_assert(sizeof(g_thread_in_wasm_code) > 1,
"sizeof(thread_local_var) must be > 1, see "
......
......@@ -68,6 +68,13 @@ extern std::atomic_size_t gRecoveredTrapCount;
// unchanged.
bool TryFindLandingPad(uintptr_t fault_addr, uintptr_t* landing_pad);
#if V8_TRAP_HANDLER_SUPPORTED
// When using the default signal handler, we save the old one to restore in case
// V8 chooses not to handle the signal.
extern struct sigaction g_old_handler;
extern bool g_is_default_signal_handler_registered;
#endif
} // namespace trap_handler
} // namespace internal
} // namespace v8
......
......@@ -87,6 +87,7 @@ inline void ClearThreadInWasm() {
}
bool RegisterDefaultSignalHandler();
void RestoreOriginalSignalHandler();
#if V8_OS_LINUX
bool TryHandleSignal(int signum, siginfo_t* info, ucontext_t* context);
......
......@@ -182,6 +182,7 @@ v8_source_set("unittests_sources") {
"wasm/loop-assignment-analysis-unittest.cc",
"wasm/module-decoder-unittest.cc",
"wasm/streaming-decoder-unittest.cc",
"wasm/trap-handler-unittest.cc",
"wasm/wasm-heap-unittest.cc",
"wasm/wasm-macro-gen-unittest.cc",
"wasm/wasm-module-builder-unittest.cc",
......
......@@ -160,6 +160,7 @@
'wasm/loop-assignment-analysis-unittest.cc',
'wasm/module-decoder-unittest.cc',
'wasm/streaming-decoder-unittest.cc',
'wasm/trap-handler-unittest.cc',
'wasm/wasm-macro-gen-unittest.cc',
'wasm/wasm-module-builder-unittest.cc',
'wasm/wasm-opcodes-unittest.cc',
......
// Copyright 2017 the V8 project authors. All rights reserved.
// Use of this source code is governed by a BSD-style license that can be
// found in the LICENSE file.
#include "src/trap-handler/trap-handler.h"
#include "include/v8.h"
#include "testing/gtest/include/gtest/gtest.h"
#if V8_OS_POSIX
#include <setjmp.h>
#include <signal.h>
#endif
namespace {
void CrashOnPurpose() { *reinterpret_cast<volatile int*>(42); }
#if V8_OS_POSIX
// When using V8::RegisterDefaultSignalHandler, we save the old one to fall back
// on if V8 doesn't handle the signal. This allows tools like ASan to register a
// handler early on during the process startup and still generate stack traces
// on failures.
class SignalHandlerFallbackTest : public ::testing::Test {
protected:
virtual void SetUp() {
struct sigaction action;
action.sa_sigaction = SignalHandler;
sigemptyset(&action.sa_mask);
action.sa_flags = SA_SIGINFO;
sigaction(SIGSEGV, &action, &old_segv_action_);
sigaction(SIGBUS, &action, &old_bus_action_);
}
virtual void TearDown() {
// be a good citizen and restore the old signal handler.
sigaction(SIGSEGV, &old_segv_action_, nullptr);
sigaction(SIGBUS, &old_bus_action_, nullptr);
}
static sigjmp_buf continuation_;
private:
static void SignalHandler(int signal, siginfo_t* info, void*) {
siglongjmp(continuation_, 1);
}
struct sigaction old_segv_action_;
struct sigaction old_bus_action_; // We get SIGBUS on Mac sometimes.
};
sigjmp_buf SignalHandlerFallbackTest::continuation_;
TEST_F(SignalHandlerFallbackTest, DoTest) {
const int save_sigs = 1;
if (!sigsetjmp(continuation_, save_sigs)) {
v8::V8::RegisterDefaultSignalHandler();
CrashOnPurpose();
FAIL();
} else {
// Our signal handler ran.
v8::internal::trap_handler::RestoreOriginalSignalHandler();
SUCCEED();
return;
}
FAIL();
}
#endif
} // namespace
Markdown is supported
0% or
You are about to add 0 people to the discussion. Proceed with caution.
Finish editing this message first!
Please register or to comment