From 56f756a00e687a0cc704e8fdb7ed0663d57f0e18 Mon Sep 17 00:00:00 2001 From: legendecas Date: Tue, 6 Apr 2021 11:49:38 +0800 Subject: [PATCH 1/3] lib: properly processing JavaScript exceptions on async_hooks fatal error JavaScript exceptions could be arbitrary values. --- lib/internal/async_hooks.js | 13 ++++- test/parallel/test-async-hooks-fatal-error.js | 52 +++++++++++++++++++ 2 files changed, 63 insertions(+), 2 deletions(-) create mode 100644 test/parallel/test-async-hooks-fatal-error.js diff --git a/lib/internal/async_hooks.js b/lib/internal/async_hooks.js index 43ba749cd69946..db05386ff4f521 100644 --- a/lib/internal/async_hooks.js +++ b/lib/internal/async_hooks.js @@ -100,6 +100,9 @@ const { const { async_id_symbol, trigger_async_id_symbol } = internalBinding('symbols'); +// Lazy load of internal/util/inspect; +let inspect; + // Used in AsyncHook and AsyncResource. const init_symbol = Symbol('init'); const before_symbol = Symbol('before'); @@ -156,12 +159,18 @@ function executionAsyncResource() { return lookupPublicResource(resource); } +function inspectExceptionValue(e) { + inspect ??= require('internal/util/inspect').inspect; + const o = { message: inspect(e) }; + return o; +} + // Used to fatally abort the process if a callback throws. function fatalError(e) { - if (typeof e.stack === 'string') { + if (typeof e?.stack === 'string') { process._rawDebug(e.stack); } else { - const o = { message: e }; + const o = inspectExceptionValue(e); ErrorCaptureStackTrace(o, fatalError); process._rawDebug(o.stack); } diff --git a/test/parallel/test-async-hooks-fatal-error.js b/test/parallel/test-async-hooks-fatal-error.js new file mode 100644 index 00000000000000..0000b0036d807a --- /dev/null +++ b/test/parallel/test-async-hooks-fatal-error.js @@ -0,0 +1,52 @@ +'use strict'; +require('../common'); +const assert = require('assert'); +const childProcess = require('child_process'); + +if (process.argv[2] === 'child') { + child(process.argv[3], process.argv[4]); +} else { + main(); +} + +function child(type, valueType) { + const { createHook } = require('async_hooks'); + const fs = require('fs'); + + createHook({ + [type]() { + if (valueType === 'symbol') { + throw Symbol('foo'); + } + // eslint-disable-next-line no-throw-literal + throw null; + } + }).enable(); + + // Trigger `promiseResolve`. + new Promise((resolve) => resolve()) + // Trigger `after`/`destroy`. + .then(() => fs.promises.readFile(__filename, 'utf8')) + // Make process exit with code 0 if no error caught. + .then(() => process.exit(0)); +} + +function main() { + const types = [ 'init', 'before', 'after', 'destroy', 'promiseResolve' ]; + const valueTypes = [ + [ 'null', 'Error: null' ], + [ 'symbol', 'Error: Symbol(foo)' ], + ]; + for (const type of types) { + for (const [valueType, expect] of valueTypes) { + const cp = childProcess.spawnSync( + process.execPath, + [ __filename, 'child', type, valueType ], + { + encoding: 'utf8', + }); + assert.strictEqual(cp.status, 1, type); + assert.strictEqual(cp.stderr.trim().split('\n')[0], expect, type); + } + } +} From a9af38da3a3d6afb58fd28647f06494030259eab Mon Sep 17 00:00:00 2001 From: legendecas Date: Wed, 7 Apr 2021 16:13:14 +0800 Subject: [PATCH 2/3] fixup! lib: properly processing JavaScript exceptions on async_hooks fatal error --- test/parallel/test-async-hooks-fatal-error.js | 3 ++- 1 file changed, 2 insertions(+), 1 deletion(-) diff --git a/test/parallel/test-async-hooks-fatal-error.js b/test/parallel/test-async-hooks-fatal-error.js index 0000b0036d807a..40c2edf329df6e 100644 --- a/test/parallel/test-async-hooks-fatal-error.js +++ b/test/parallel/test-async-hooks-fatal-error.js @@ -2,6 +2,7 @@ require('../common'); const assert = require('assert'); const childProcess = require('child_process'); +const os = require('os'); if (process.argv[2] === 'child') { child(process.argv[3], process.argv[4]); @@ -46,7 +47,7 @@ function main() { encoding: 'utf8', }); assert.strictEqual(cp.status, 1, type); - assert.strictEqual(cp.stderr.trim().split('\n')[0], expect, type); + assert.strictEqual(cp.stderr.trim().split(os.EOL)[0], expect, type); } } } From 95bcddc5bf5e9a53a607f0a7a0f61a6a21982baf Mon Sep 17 00:00:00 2001 From: legendecas Date: Thu, 8 Apr 2021 01:00:17 +0800 Subject: [PATCH 3/3] fixup! Co-authored-by: Anna Henningsen --- lib/internal/async_hooks.js | 3 +-- 1 file changed, 1 insertion(+), 2 deletions(-) diff --git a/lib/internal/async_hooks.js b/lib/internal/async_hooks.js index db05386ff4f521..69ca8d6db987ed 100644 --- a/lib/internal/async_hooks.js +++ b/lib/internal/async_hooks.js @@ -161,8 +161,7 @@ function executionAsyncResource() { function inspectExceptionValue(e) { inspect ??= require('internal/util/inspect').inspect; - const o = { message: inspect(e) }; - return o; + return { message: inspect(e) }; } // Used to fatally abort the process if a callback throws.