From 1628d99f7b21b6920ac8bf197b2acd38021f1df9 Mon Sep 17 00:00:00 2001 From: Eric Holk Date: Mon, 19 Jun 2017 10:06:28 -0700 Subject: Add first chance exception handler API This change adds the option for Breakpad hosts to register a callback that gets the first chance to handle an exception. The handler will return true if it handled the exception and false otherwise. The primary use case is V8's trap-based bounds checking support for WebAssembly. Bug: Change-Id: I5aa5b87d1229f1cef905a00404fa2027ee86be56 Reviewed-on: https://chromium-review.googlesource.com/509994 Reviewed-by: Mark Mentovai --- src/client/linux/handler/exception_handler.cc | 17 ++++++++++++++++ src/client/linux/handler/exception_handler.h | 4 ++++ .../linux/handler/exception_handler_unittest.cc | 23 ++++++++++++++++++++++ 3 files changed, 44 insertions(+) (limited to 'src/client/linux') diff --git a/src/client/linux/handler/exception_handler.cc b/src/client/linux/handler/exception_handler.cc index e9ec2bad..586d84e9 100644 --- a/src/client/linux/handler/exception_handler.cc +++ b/src/client/linux/handler/exception_handler.cc @@ -218,6 +218,7 @@ pthread_mutex_t g_handler_stack_mutex_ = PTHREAD_MUTEX_INITIALIZER; // time can use |g_crash_context_|. ExceptionHandler::CrashContext g_crash_context_; +FirstChanceHandler g_first_chance_handler_ = nullptr; } // namespace // Runs before crashing: normal context. @@ -331,6 +332,18 @@ void ExceptionHandler::RestoreHandlersLocked() { // Runs on the crashing thread. // static void ExceptionHandler::SignalHandler(int sig, siginfo_t* info, void* uc) { + + // Give the first chance handler a chance to recover from this signal + // + // This is primarily used by V8. V8 uses guard regions to guarantee memory + // safety in WebAssembly. This means some signals might be expected if they + // originate from Wasm code while accessing the guard region. We give V8 the + // chance to handle and recover from these signals first. + if (g_first_chance_handler_ != nullptr && + g_first_chance_handler_(sig, info, uc)) { + return; + } + // All the exception signals are blocked at this point. pthread_mutex_lock(&g_handler_stack_mutex_); @@ -782,4 +795,8 @@ bool ExceptionHandler::WriteMinidumpForChild(pid_t child, return callback ? callback(descriptor, callback_context, true) : true; } +void SetFirstChanceExceptionHandler(FirstChanceHandler callback) { + g_first_chance_handler_ = callback; +} + } // namespace google_breakpad diff --git a/src/client/linux/handler/exception_handler.h b/src/client/linux/handler/exception_handler.h index 591c3108..daba57e0 100644 --- a/src/client/linux/handler/exception_handler.h +++ b/src/client/linux/handler/exception_handler.h @@ -273,6 +273,10 @@ class ExceptionHandler { AppMemoryList app_memory_list_; }; + +typedef bool (*FirstChanceHandler)(int, void*, void*); +void SetFirstChanceExceptionHandler(FirstChanceHandler callback); + } // namespace google_breakpad #endif // CLIENT_LINUX_HANDLER_EXCEPTION_HANDLER_H_ diff --git a/src/client/linux/handler/exception_handler_unittest.cc b/src/client/linux/handler/exception_handler_unittest.cc index 17d84cf7..cede40bd 100644 --- a/src/client/linux/handler/exception_handler_unittest.cc +++ b/src/client/linux/handler/exception_handler_unittest.cc @@ -1177,3 +1177,26 @@ TEST(ExceptionHandlerTest, WriteMinidumpForChild) { close(fds[1]); unlink(minidump_filename.c_str()); } + +namespace { +const int kSimpleFirstChanceReturnStatus = 42; +bool SimpleFirstChanceHandler(int, void*, void*) { + exit(kSimpleFirstChanceReturnStatus); +} +} + +TEST(ExceptionHandlerTest, FirstChanceHandlerRuns) { + AutoTempDir temp_dir; + + const pid_t child = fork(); + if (child == 0) { + ExceptionHandler handler( + MinidumpDescriptor(temp_dir.path()), NULL, NULL, NULL, true, -1); + google_breakpad::SetFirstChanceExceptionHandler(SimpleFirstChanceHandler); + DoNullPointerDereference(); + } + int status; + ASSERT_NE(HANDLE_EINTR(waitpid(child, &status, 0)), -1); + ASSERT_TRUE(WIFEXITED(status)); + ASSERT_EQ(kSimpleFirstChanceReturnStatus, WEXITSTATUS(status)); +} -- cgit v1.2.1