From cf1f52825bd7f0e4ead1c42ca13dfc400e477ff2 Mon Sep 17 00:00:00 2001 From: Gireesh Punathil Date: Mon, 4 Feb 2019 01:54:01 -0500 Subject: [PATCH] report: catch internal segfaults `--diagnostic-report-on-fatalerror` does not capture internal segfaults at present, instead limited to only faults that are detected by the Node / v8. libuv's signal handler deliveres result asynchronously which does not help for SIGSEGV as: i) the synchronous sequence restarts the failing code, ii) due to i) the callback is never delivered to the consumer. Install a SIGSEGV handler in Node and follow the sequence of fatalerror handling, as of now at least under report. Fixes: https://github.com/nodejs/node/issues/25762 --- src/node_errors.cc | 15 +++++++++++++++ src/node_errors.h | 3 +++ src/node_report_module.cc | 15 +++++++++++++++ 3 files changed, 33 insertions(+) diff --git a/src/node_errors.cc b/src/node_errors.cc index 42e19f690514ff..81e9172f0f038b 100644 --- a/src/node_errors.cc +++ b/src/node_errors.cc @@ -306,6 +306,21 @@ void PrintErrorString(const char* format, ...) { ABORT(); } +#ifndef _WIN32 +void OnSIGSEGV(int signo) { + uv_tty_reset_mode(); +#ifdef __FreeBSD__ + // FreeBSD has a nasty bug, see RegisterSignalHandler for details + struct sigaction sa; + memset(&sa, 0, sizeof(sa)); + sa.sa_handler = SIG_DFL; + CHECK_EQ(sigaction(signo, &sa, nullptr), 0); +#endif + CHECK_EQ(signo, SIGSEGV); + OnFatalError(__func__, "caught internal error."); +} +#endif // _WIN32 + void OnFatalError(const char* location, const char* message) { if (location) { PrintErrorString("FATAL ERROR: %s %s\n", location, message); diff --git a/src/node_errors.h b/src/node_errors.h index cea7c108c168f3..874f3f9798c3d1 100644 --- a/src/node_errors.h +++ b/src/node_errors.h @@ -22,6 +22,9 @@ void AppendExceptionLine(Environment* env, [[noreturn]] void FatalError(const char* location, const char* message); void OnFatalError(const char* location, const char* message); +#ifndef _WIN32 +void OnSIGSEGV(int signo); +#endif // _WIN32 // Like a `TryCatch` but exits the process if an exception was caught. class FatalTryCatch : public v8::TryCatch { diff --git a/src/node_report_module.cc b/src/node_report_module.cc index d13eb61c83820a..671ab57763e4c0 100644 --- a/src/node_report_module.cc +++ b/src/node_report_module.cc @@ -1,4 +1,5 @@ #include "env.h" +#include "node.h" #include "node_errors.h" #include "node_internals.h" #include "node_options.h" @@ -262,6 +263,20 @@ static void Initialize(Local exports, env->SetMethod(exports, "onUserSignal", OnUserSignal); env->SetMethod(exports, "syncConfig", SyncConfig); + // Libuv based signal handling does not help internal segfaults, + // as synchronously delivered signals are handled asynchronously + // in libuv at present, which Node.js is unable to effectively + // consume. So install one of our own; make sure reset tty mode + // and exit; do not return gracefully that causes fault context + // to be invoked again. + // TODO(gireeshpunathil) decide what to do with asynchronous + // sigsegv handling - handle separately, or merge with this one. +#ifndef _WIN32 + if (options->report_on_fatalerror) { + node::RegisterSignalHandler(SIGSEGV, node::OnSIGSEGV, true); + } +#endif // _WIN32 + // TODO(gireeshpunathil) if we are retaining this flag, // insert more verbose information at vital control flow // points. Right now, it is only this one.