From 99493b07d400ecaf813433a128c60ec90a3c68fc Mon Sep 17 00:00:00 2001 From: legendecas Date: Tue, 11 May 2021 00:45:24 +0800 Subject: [PATCH] src: fix fatal errors when a current isolate not exist napi_fatal_error and node watchdog trigger fatal error but rather running on a thread that hold no current isolate. PR-URL: https://github.com/nodejs/node/pull/38624 Backport-PR-URL: https://github.com/nodejs/node/pull/39515 Reviewed-By: Michael Dawson --- src/node_errors.cc | 6 ++++++ test/node-api/test_fatal/test.js | 1 + test/node-api/test_fatal/test2.js | 1 + test/node-api/test_fatal/test_fatal.c | 21 +++++++++++++++++++++ test/node-api/test_fatal/test_threads.js | 20 ++++++++++++++++++++ 5 files changed, 49 insertions(+) create mode 100644 test/node-api/test_fatal/test_threads.js diff --git a/src/node_errors.cc b/src/node_errors.cc index 43e9705ec89800..786e4153f2c13f 100644 --- a/src/node_errors.cc +++ b/src/node_errors.cc @@ -424,6 +424,12 @@ void OnFatalError(const char* location, const char* message) { } Isolate* isolate = Isolate::GetCurrent(); + // TODO(legendecas): investigate failures on triggering node-report with + // nullptr isolates. + if (isolate == nullptr) { + fflush(stderr); + ABORT(); + } Environment* env = Environment::GetCurrent(isolate); bool report_on_fatalerror; { diff --git a/test/node-api/test_fatal/test.js b/test/node-api/test_fatal/test.js index 7ff9a395635dce..c4af828ff154d1 100644 --- a/test/node-api/test_fatal/test.js +++ b/test/node-api/test_fatal/test.js @@ -16,3 +16,4 @@ const p = child_process.spawnSync( assert.ifError(p.error); assert.ok(p.stderr.toString().includes( 'FATAL ERROR: test_fatal::Test fatal message')); +assert.ok(p.status === 134 || p.signal === 'SIGABRT'); diff --git a/test/node-api/test_fatal/test2.js b/test/node-api/test_fatal/test2.js index b9bde8f13016cc..6449f098458d76 100644 --- a/test/node-api/test_fatal/test2.js +++ b/test/node-api/test_fatal/test2.js @@ -16,3 +16,4 @@ const p = child_process.spawnSync( assert.ifError(p.error); assert.ok(p.stderr.toString().includes( 'FATAL ERROR: test_fatal::Test fatal message')); +assert.ok(p.status === 134 || p.signal === 'SIGABRT'); diff --git a/test/node-api/test_fatal/test_fatal.c b/test/node-api/test_fatal/test_fatal.c index 70bb458ef20e4e..1b4bdd180e5dfa 100644 --- a/test/node-api/test_fatal/test_fatal.c +++ b/test/node-api/test_fatal/test_fatal.c @@ -1,12 +1,32 @@ +// For the purpose of this test we use libuv's threading library. When deciding +// on a threading library for a new project it bears remembering that in the +// future libuv may introduce API changes which may render it non-ABI-stable, +// which, in turn, may affect the ABI stability of the project despite its use +// of N-API. +#include #include #include "../../js-native-api/common.h" +static uv_thread_t uv_thread; + +static void work_thread(void* data) { + napi_fatal_error("work_thread", NAPI_AUTO_LENGTH, + "foobar", NAPI_AUTO_LENGTH); +} + static napi_value Test(napi_env env, napi_callback_info info) { napi_fatal_error("test_fatal::Test", NAPI_AUTO_LENGTH, "fatal message", NAPI_AUTO_LENGTH); return NULL; } +static napi_value TestThread(napi_env env, napi_callback_info info) { + NAPI_ASSERT(env, + (uv_thread_create(&uv_thread, work_thread, NULL) == 0), + "Thread creation"); + return NULL; +} + static napi_value TestStringLength(napi_env env, napi_callback_info info) { napi_fatal_error("test_fatal::TestStringLength", 16, "fatal message", 13); return NULL; @@ -16,6 +36,7 @@ static napi_value Init(napi_env env, napi_value exports) { napi_property_descriptor properties[] = { DECLARE_NAPI_PROPERTY("Test", Test), DECLARE_NAPI_PROPERTY("TestStringLength", TestStringLength), + DECLARE_NAPI_PROPERTY("TestThread", TestThread), }; NAPI_CALL(env, napi_define_properties( diff --git a/test/node-api/test_fatal/test_threads.js b/test/node-api/test_fatal/test_threads.js new file mode 100644 index 00000000000000..fd56f60cbe832f --- /dev/null +++ b/test/node-api/test_fatal/test_threads.js @@ -0,0 +1,20 @@ +'use strict'; +const common = require('../../common'); +const assert = require('assert'); +const child_process = require('child_process'); +const test_fatal = require(`./build/${common.buildType}/test_fatal`); + +// Test in a child process because the test code will trigger a fatal error +// that crashes the process. +if (process.argv[2] === 'child') { + test_fatal.TestThread(); + // Busy loop to allow the work thread to abort. + while (true) {} +} + +const p = child_process.spawnSync( + process.execPath, [ __filename, 'child' ]); +assert.ifError(p.error); +assert.ok(p.stderr.toString().includes( + 'FATAL ERROR: work_thread foobar')); +assert.ok(p.status === 134 || p.signal === 'SIGABRT');