Skip to content

Commit f091a7a

Browse files
committed
async_hooks: clear context frame for thrown microtasks
1 parent e9a9065 commit f091a7a

2 files changed

Lines changed: 57 additions & 8 deletions

File tree

lib/internal/process/task_queues.js

Lines changed: 18 additions & 8 deletions
Original file line numberDiff line numberDiff line change
@@ -145,14 +145,24 @@ function nextTick(callback) {
145145
}
146146

147147
function runMicrotask() {
148-
this.runInAsyncScope(() => {
149-
const callback = this.callback;
150-
try {
151-
callback();
152-
} finally {
153-
this.emitDestroy();
154-
}
155-
});
148+
try {
149+
this.runInAsyncScope(() => {
150+
const callback = this.callback;
151+
try {
152+
callback();
153+
} finally {
154+
this.emitDestroy();
155+
}
156+
});
157+
} catch (error) {
158+
// V8 restores the continuation-preserved embedder data for each
159+
// microtask, but currently does not clear it on exception paths before
160+
// reporting the exception. Clear it here so user code re-entered during
161+
// exception formatting cannot observe this microtask's AsyncLocalStorage
162+
// context.
163+
AsyncContextFrame.set(undefined);
164+
throw error;
165+
}
156166
}
157167

158168
const defaultMicrotaskResourceOpts = { requireManualDestroy: true };
Lines changed: 39 additions & 0 deletions
Original file line numberDiff line numberDiff line change
@@ -0,0 +1,39 @@
1+
// Flags: --async-context-frame
2+
'use strict';
3+
4+
const common = require('../common');
5+
const assert = require('assert');
6+
const { AsyncLocalStorage } = require('async_hooks');
7+
8+
const asyncLocalStorage = new AsyncLocalStorage();
9+
const sensitive = { secret: 'sensitive' };
10+
let toPrimitiveStore = 'not called';
11+
let downstreamStore = 'not called';
12+
13+
const thrown = {
14+
[Symbol.toPrimitive]: common.mustCall(() => {
15+
toPrimitiveStore = asyncLocalStorage.getStore();
16+
queueMicrotask(common.mustCall(() => {
17+
downstreamStore = asyncLocalStorage.getStore();
18+
assert.strictEqual(downstreamStore, undefined);
19+
}));
20+
return 'thrown';
21+
}),
22+
};
23+
24+
process.on('uncaughtException', common.mustCall((err) => {
25+
assert.strictEqual(err, thrown);
26+
assert.strictEqual(asyncLocalStorage.getStore(), undefined);
27+
}));
28+
29+
asyncLocalStorage.run(sensitive, () => {
30+
queueMicrotask(() => {
31+
throw thrown;
32+
});
33+
});
34+
35+
setImmediate(common.mustCall(() => {
36+
assert.strictEqual(toPrimitiveStore, undefined);
37+
assert.strictEqual(downstreamStore, undefined);
38+
assert.strictEqual(asyncLocalStorage.getStore(), undefined);
39+
}));

0 commit comments

Comments
 (0)