Skip to content

Commit 530447a

Browse files
author
Node Contributor
committed
errors: handle V8 warnings in DisallowJavascriptExecutionScope
Defer non-critical warnings to the next event loop iteration when can_call_into_js() returns false. This prevents crashes when V8 emits warnings during REPL preview evaluation or other contexts where JavaScript execution is temporarily forbidden. When a warning is emitted inside DisallowJavascriptExecutionScope, ProcessEmitWarningGeneric cannot be called immediately. Instead, use env->SetImmediate() to queue the warning emission for after the scope exits. This preserves full warning formatting, deprecation codes, and --redirect-warnings routing. Fixes: #63473
1 parent 5ff701a commit 530447a

2 files changed

Lines changed: 79 additions & 1 deletion

File tree

src/node_errors.cc

Lines changed: 12 additions & 1 deletion
Original file line numberDiff line numberDiff line change
@@ -1064,7 +1064,18 @@ void PerIsolateMessageListener(Local<Message> message, Local<Value> error) {
10641064
filename,
10651065
message->GetLineNumber(env->context()).FromMaybe(-1),
10661066
msg);
1067-
USE(ProcessEmitWarningGeneric(env, warning, "V8"));
1067+
// If we're inside a DisallowJavascriptExecutionScope (e.g., REPL preview),
1068+
// defer the warning to the next event loop iteration when JS execution is
1069+
// allowed. This prevents crashes when V8 emits warnings during code
1070+
// evaluation with throwOnSideEffect.
1071+
if (!env->can_call_into_js()) {
1072+
std::string warning_str = warning;
1073+
env->SetImmediate([warning_str](Environment* env) {
1074+
ProcessEmitWarningGeneric(env, warning_str, "V8");
1075+
});
1076+
} else {
1077+
USE(ProcessEmitWarningGeneric(env, warning, "V8"));
1078+
}
10681079
break;
10691080
}
10701081
case Isolate::MessageErrorLevel::kMessageError:
Lines changed: 67 additions & 0 deletions
Original file line numberDiff line numberDiff line change
@@ -0,0 +1,67 @@
1+
'use strict';
2+
3+
// Regression test for issue #63473:
4+
// When asm.js code is evaluated in the Node.js REPL with previews enabled,
5+
// V8 emits a deprecation warning inside a DisallowJavascriptExecutionScope.
6+
// This test verifies that the warning is deferred and the process does not crash.
7+
8+
const common = require('../common');
9+
const assert = require('assert');
10+
const spawn = require('child_process').spawn;
11+
12+
// asm.js code that may trigger a deprecation warning in V8
13+
const asmCode = `
14+
function asm(stdlib, foreign, heap) {
15+
"use asm";
16+
function f() { return 1; }
17+
return { f: f };
18+
}
19+
asm(this, null, new ArrayBuffer(1024));
20+
`;
21+
22+
const child = spawn(process.execPath, ['-i'], {
23+
stdio: ['pipe', 'pipe', 'pipe'],
24+
});
25+
26+
let stdout = '';
27+
let stderr = '';
28+
let crashed = false;
29+
30+
child.stdout.setEncoding('utf8');
31+
child.stdout.on('data', (chunk) => {
32+
stdout += chunk;
33+
process.stdout.write(chunk);
34+
});
35+
36+
child.stderr.setEncoding('utf8');
37+
child.stderr.on('data', (chunk) => {
38+
stderr += chunk;
39+
process.stderr.write(chunk);
40+
// Check for crash indicators
41+
if (chunk.includes('FATAL ERROR') || chunk.includes('Segmentation fault')) {
42+
crashed = true;
43+
}
44+
});
45+
46+
// Feed the asm.js code to REPL via stdin
47+
child.stdout.once('data', function() {
48+
child.stdin.write(asmCode);
49+
child.stdin.write('\n');
50+
setTimeout(() => {
51+
child.stdin.end();
52+
}, 500);
53+
});
54+
55+
child.on('close', common.mustCall((exitCode) => {
56+
// Most important: process should not crash (exit code 0 or normal termination)
57+
assert.strictEqual(crashed, false, 'Process should not crash');
58+
// In Node.js, exit code 0 is normal, but we also allow the process to exit
59+
// naturally without a crash (which would have exit code 0)
60+
// The main assertion is that it doesn't crash with FATAL ERROR or segfault
61+
if (exitCode !== 0 && exitCode !== null) {
62+
// Note: We only fail if there's clear evidence of a crash in stderr
63+
if (stderr.includes('FATAL ERROR') || stderr.includes('Segmentation')) {
64+
assert.fail(`Process crashed with exit code ${exitCode}\nstderr: ${stderr}`);
65+
}
66+
}
67+
}));

0 commit comments

Comments
 (0)