Skip to content

Commit ad292b8

Browse files
authored
inspector: prevent propagation of promise hooks to noPromise hooks
PR-URL: #58841 Reviewed-By: Chengzhong Wu <[email protected]>
1 parent eed1d33 commit ad292b8

File tree

2 files changed

+49
-10
lines changed

2 files changed

+49
-10
lines changed

lib/internal/async_hooks.js

Lines changed: 18 additions & 10 deletions
Original file line numberDiff line numberDiff line change
@@ -189,7 +189,7 @@ function lookupPublicResource(resource) {
189189
// Used by C++ to call all init() callbacks. Because some state can be setup
190190
// from C++ there's no need to perform all the same operations as in
191191
// emitInitScript.
192-
function emitInitNative(asyncId, type, triggerAsyncId, resource) {
192+
function emitInitNative(asyncId, type, triggerAsyncId, resource, isPromiseHook) {
193193
active_hooks.call_depth += 1;
194194
resource = lookupPublicResource(resource);
195195
// Use a single try/catch for all hooks to avoid setting up one per iteration.
@@ -199,6 +199,10 @@ function emitInitNative(asyncId, type, triggerAsyncId, resource) {
199199
// eslint-disable-next-line no-var
200200
for (var i = 0; i < active_hooks.array.length; i++) {
201201
if (typeof active_hooks.array[i][init_symbol] === 'function') {
202+
if (isPromiseHook &&
203+
active_hooks.array[i][kNoPromiseHook]) {
204+
continue;
205+
}
202206
active_hooks.array[i][init_symbol](
203207
asyncId, type, triggerAsyncId,
204208
resource,
@@ -222,7 +226,7 @@ function emitInitNative(asyncId, type, triggerAsyncId, resource) {
222226

223227
// Called from native. The asyncId stack handling is taken care of there
224228
// before this is called.
225-
function emitHook(symbol, asyncId) {
229+
function emitHook(symbol, asyncId, isPromiseHook) {
226230
active_hooks.call_depth += 1;
227231
// Use a single try/catch for all hook to avoid setting up one per
228232
// iteration.
@@ -232,6 +236,10 @@ function emitHook(symbol, asyncId) {
232236
// eslint-disable-next-line no-var
233237
for (var i = 0; i < active_hooks.array.length; i++) {
234238
if (typeof active_hooks.array[i][symbol] === 'function') {
239+
if (isPromiseHook &&
240+
active_hooks.array[i][kNoPromiseHook]) {
241+
continue;
242+
}
235243
active_hooks.array[i][symbol](asyncId);
236244
}
237245
}
@@ -321,7 +329,7 @@ function promiseInitHook(promise, parent) {
321329
trackPromise(promise, parent);
322330
const asyncId = promise[async_id_symbol];
323331
const triggerAsyncId = promise[trigger_async_id_symbol];
324-
emitInitScript(asyncId, 'PROMISE', triggerAsyncId, promise);
332+
emitInitScript(asyncId, 'PROMISE', triggerAsyncId, promise, true);
325333
}
326334

327335
function promiseInitHookWithDestroyTracking(promise, parent) {
@@ -339,14 +347,14 @@ function promiseBeforeHook(promise) {
339347
trackPromise(promise);
340348
const asyncId = promise[async_id_symbol];
341349
const triggerId = promise[trigger_async_id_symbol];
342-
emitBeforeScript(asyncId, triggerId, promise);
350+
emitBeforeScript(asyncId, triggerId, promise, true);
343351
}
344352

345353
function promiseAfterHook(promise) {
346354
trackPromise(promise);
347355
const asyncId = promise[async_id_symbol];
348356
if (hasHooks(kAfter)) {
349-
emitAfterNative(asyncId);
357+
emitAfterNative(asyncId, true);
350358
}
351359
if (asyncId === executionAsyncId()) {
352360
// This condition might not be true if async_hooks was enabled during
@@ -361,7 +369,7 @@ function promiseAfterHook(promise) {
361369
function promiseResolveHook(promise) {
362370
trackPromise(promise);
363371
const asyncId = promise[async_id_symbol];
364-
emitPromiseResolveNative(asyncId);
372+
emitPromiseResolveNative(asyncId, true);
365373
}
366374

367375
let wantPromiseHook = false;
@@ -492,7 +500,7 @@ function promiseResolveHooksExist() {
492500
}
493501

494502

495-
function emitInitScript(asyncId, type, triggerAsyncId, resource) {
503+
function emitInitScript(asyncId, type, triggerAsyncId, resource, isPromiseHook = false) {
496504
// Short circuit all checks for the common case. Which is that no hooks have
497505
// been set. Do this to remove performance impact for embedders (and core).
498506
if (!hasHooks(kInit))
@@ -502,15 +510,15 @@ function emitInitScript(asyncId, type, triggerAsyncId, resource) {
502510
triggerAsyncId = getDefaultTriggerAsyncId();
503511
}
504512

505-
emitInitNative(asyncId, type, triggerAsyncId, resource);
513+
emitInitNative(asyncId, type, triggerAsyncId, resource, isPromiseHook);
506514
}
507515

508516

509-
function emitBeforeScript(asyncId, triggerAsyncId, resource) {
517+
function emitBeforeScript(asyncId, triggerAsyncId, resource, isPromiseHook = false) {
510518
pushAsyncContext(asyncId, triggerAsyncId, resource);
511519

512520
if (hasHooks(kBefore))
513-
emitBeforeNative(asyncId);
521+
emitBeforeNative(asyncId, isPromiseHook);
514522
}
515523

516524

Lines changed: 31 additions & 0 deletions
Original file line numberDiff line numberDiff line change
@@ -0,0 +1,31 @@
1+
'use strict';
2+
const common = require('../common');
3+
common.skipIfInspectorDisabled();
4+
const test = require('node:test');
5+
const { NodeInstance } = require('../common/inspector-helper');
6+
7+
const script = `
8+
import { createHook } from "async_hooks"
9+
import fs from "fs"
10+
11+
const hook = createHook({
12+
after() {
13+
}
14+
});
15+
hook.enable(true);
16+
console.log('Async hook enabled');
17+
`;
18+
19+
test('inspector async hooks should not crash in debug build', async () => {
20+
const instance = new NodeInstance([
21+
'--inspect-brk=0',
22+
], script);
23+
const session = await instance.connectInspectorSession();
24+
await session.send({ method: 'NodeRuntime.enable' });
25+
await session.waitForNotification('NodeRuntime.waitingForDebugger');
26+
await session.send({ method: 'Runtime.enable' });
27+
await session.send({ method: 'Debugger.enable' });
28+
await session.send({ id: 6, method: 'Debugger.setAsyncCallStackDepth', params: { maxDepth: 32 } });
29+
await session.send({ method: 'Runtime.runIfWaitingForDebugger' });
30+
await session.waitForDisconnect();
31+
});

0 commit comments

Comments
 (0)