Skip to content

Commit cdba9f2

Browse files
committed
src: handle fatal error when Environment is not assigned to context
Previously when an uncaught JS error is thrown before Environment was assigned to the context (e.g. a SyntaxError in a per-context script), it triggered an infinite recursion: 1. The error message listener `node::OnMessage()` triggered `node::FatalException()` 2. `node::FatalException()` attempted to get the Environment assigned to the context entered using `Environment::GetCurrent()` 3. `Environment::GetCurrent()` previously incorrectly accepted out-of-bound access with the length of the embedder data array as index, and called `context->GetAlignedPointerFromEmbedderData()` 4. The out-of-bound access in `GetAlignedPointerFromEmbedderData()` triggered a fatal error, which was handled by `node::FatalError()` 5. `node::FatalError()` called `Environment::GetCurrent()`, then we went back to 3. This patch fixes the incorrect guard in 3. When `Environment::GetCurrent()` returns nullptr (when Environment is not yet assigned to the context) in 2, it now prints the JS stack trace and crashes directly. PR-URL: #27236 Reviewed-By: Ben Noordhuis <[email protected]> Reviewed-By: James M Snell <[email protected]> Reviewed-By: Anna Henningsen <[email protected]>
1 parent 83d1ca7 commit cdba9f2

File tree

2 files changed

+39
-17
lines changed

2 files changed

+39
-17
lines changed

src/env-inl.h

+10-7
Original file line numberDiff line numberDiff line change
@@ -302,15 +302,18 @@ inline Environment* Environment::GetCurrent(v8::Isolate* isolate) {
302302
}
303303

304304
inline Environment* Environment::GetCurrent(v8::Local<v8::Context> context) {
305-
if (UNLIKELY(context.IsEmpty() ||
306-
context->GetNumberOfEmbedderDataFields() <
307-
ContextEmbedderIndex::kContextTag ||
308-
context->GetAlignedPointerFromEmbedderData(
309-
ContextEmbedderIndex::kContextTag) !=
310-
Environment::kNodeContextTagPtr)) {
305+
if (UNLIKELY(context.IsEmpty())) {
306+
return nullptr;
307+
}
308+
if (UNLIKELY(context->GetNumberOfEmbedderDataFields() <=
309+
ContextEmbedderIndex::kContextTag)) {
310+
return nullptr;
311+
}
312+
if (UNLIKELY(context->GetAlignedPointerFromEmbedderData(
313+
ContextEmbedderIndex::kContextTag) !=
314+
Environment::kNodeContextTagPtr)) {
311315
return nullptr;
312316
}
313-
314317
return static_cast<Environment*>(
315318
context->GetAlignedPointerFromEmbedderData(
316319
ContextEmbedderIndex::kEnvironment));

src/node_errors.cc

+29-10
Original file line numberDiff line numberDiff line change
@@ -6,6 +6,7 @@
66
#ifdef NODE_REPORT
77
#include "node_report.h"
88
#endif
9+
#include "node_v8_platform-inl.h"
910

1011
namespace node {
1112

@@ -171,21 +172,27 @@ void PrintStackTrace(Isolate* isolate, Local<StackTrace> stack) {
171172
fflush(stderr);
172173
}
173174

174-
void PrintCaughtException(Isolate* isolate,
175-
Local<Context> context,
176-
const v8::TryCatch& try_catch) {
177-
CHECK(try_catch.HasCaught());
178-
Local<Value> err = try_catch.Exception();
179-
Local<Message> message = try_catch.Message();
180-
Local<v8::StackTrace> stack = message->GetStackTrace();
175+
void PrintException(Isolate* isolate,
176+
Local<Context> context,
177+
Local<Value> err,
178+
Local<Message> message) {
181179
node::Utf8Value reason(isolate,
182180
err->ToDetailString(context).ToLocalChecked());
183181
bool added_exception_line = false;
184182
std::string source =
185183
GetErrorSource(isolate, context, message, &added_exception_line);
186184
fprintf(stderr, "%s\n", source.c_str());
187185
fprintf(stderr, "%s\n", *reason);
188-
PrintStackTrace(isolate, stack);
186+
187+
Local<v8::StackTrace> stack = message->GetStackTrace();
188+
if (!stack.IsEmpty()) PrintStackTrace(isolate, stack);
189+
}
190+
191+
void PrintCaughtException(Isolate* isolate,
192+
Local<Context> context,
193+
const v8::TryCatch& try_catch) {
194+
CHECK(try_catch.HasCaught());
195+
PrintException(isolate, context, try_catch.Exception(), try_catch.Message());
189196
}
190197

191198
void AppendExceptionLine(Environment* env,
@@ -777,8 +784,20 @@ void FatalException(Isolate* isolate,
777784
CHECK(!error.IsEmpty());
778785
HandleScope scope(isolate);
779786

780-
Environment* env = Environment::GetCurrent(isolate);
781-
CHECK_NOT_NULL(env); // TODO(addaleax): Handle nullptr here.
787+
CHECK(isolate->InContext());
788+
Local<Context> context = isolate->GetCurrentContext();
789+
Environment* env = Environment::GetCurrent(context);
790+
if (env == nullptr) {
791+
// This means that the exception happens before Environment is assigned
792+
// to the context e.g. when there is a SyntaxError in a per-context
793+
// script - which usually indicates that there is a bug because no JS
794+
// error is supposed to be thrown at this point.
795+
// Since we don't have access to Environment here, there is not
796+
// much we can do, so we just print whatever is useful and crash.
797+
PrintException(isolate, context, error, message);
798+
Abort();
799+
}
800+
782801
Local<Object> process_object = env->process_object();
783802
Local<String> fatal_exception_string = env->fatal_exception_string();
784803
Local<Value> fatal_exception_function =

0 commit comments

Comments
 (0)