Skip to content

Commit 0e2c206

Browse files
legendecasjuanarbol
authored andcommitted
report: get stack trace with cross origin contexts
When a new context with a different security token is entered, or when no context is entered, `StackTrace::CurrentStackTrace` need to be explicitly set with flag `kExposeFramesAcrossSecurityOrigins` to avoid crashing. PR-URL: #44398 Reviewed-By: Rafael Gonzaga <[email protected]>
1 parent 5355a46 commit 0e2c206

File tree

4 files changed

+57
-12
lines changed

4 files changed

+57
-12
lines changed

‎src/node_errors.cc‎

Lines changed: 5 additions & 0 deletions
Original file line numberDiff line numberDiff line change
@@ -471,6 +471,11 @@ void OOMErrorHandler(const char* location, bool is_heap_oom){
471471
}
472472

473473
if (report_on_fatalerror){
474+
// Trigger report with the isolate. Environment::GetCurrent may return
475+
// nullptr here:
476+
// - If the OOM is reported by a young generation space allocation,
477+
// Isolate::GetCurrentContext returns an empty handle.
478+
// - Otherwise, Isolate::GetCurrentContext returns a non-empty handle.
474479
TriggerNodeReport(isolate, message, "OOMError", "", Local<Object>());
475480
}
476481

‎src/node_report.cc‎

Lines changed: 5 additions & 1 deletion
Original file line numberDiff line numberDiff line change
@@ -470,8 +470,12 @@ static void PrintJavaScriptStack(JSONWriter* writer,
470470
void* samples[MAX_FRAME_COUNT];
471471
isolate->GetStackSample(state, samples, MAX_FRAME_COUNT, &info);
472472

473+
constexpr StackTrace::StackTraceOptions stack_trace_options =
474+
static_cast<StackTrace::StackTraceOptions>(
475+
StackTrace::kDetailed |
476+
StackTrace::kExposeFramesAcrossSecurityOrigins);
473477
Local<StackTrace> stack = StackTrace::CurrentStackTrace(
474-
isolate, MAX_FRAME_COUNT, StackTrace::kDetailed);
478+
isolate, MAX_FRAME_COUNT, stack_trace_options);
475479

476480
if (stack->GetFrameCount() == 0){
477481
PrintEmptyJavaScriptStack(writer);

‎test/addons/report-api/binding.cc‎

Lines changed: 27 additions & 0 deletions
Original file line numberDiff line numberDiff line change
@@ -1,6 +1,7 @@
11
#include<node.h>
22
#include<v8.h>
33

4+
using v8::Context;
45
using v8::FunctionCallbackInfo;
56
using v8::Isolate;
67
using v8::Local;
@@ -43,11 +44,37 @@ void TriggerReportNoEnv(const FunctionCallbackInfo<Value>& args){
4344
Local<Value>());
4445
}
4546

47+
voidTriggerReportNoContext(const FunctionCallbackInfo<Value>& args){
48+
Isolate* isolate = args.GetIsolate();
49+
Local<Context> context = isolate->GetCurrentContext();
50+
context->Exit();
51+
52+
if (isolate->GetCurrentContext().IsEmpty()){
53+
node::TriggerNodeReport(
54+
isolate, "FooMessage", "BarTrigger", std::string(), Local<Value>());
55+
}
56+
57+
// Restore current context to avoid crashing in Context::Scope in
58+
// SpinEventLoop.
59+
context->Enter();
60+
}
61+
62+
voidTriggerReportNewContext(const FunctionCallbackInfo<Value>& args){
63+
Isolate* isolate = args.GetIsolate();
64+
Local<Context> context = Context::New(isolate);
65+
Context::Scope context_scope(context);
66+
67+
node::TriggerNodeReport(
68+
isolate, "FooMessage", "BarTrigger", std::string(), Local<Value>());
69+
}
70+
4671
voidinit(Local<Object> exports){
4772
NODE_SET_METHOD(exports, "triggerReport", TriggerReport);
4873
NODE_SET_METHOD(exports, "triggerReportNoIsolate", TriggerReportNoIsolate);
4974
NODE_SET_METHOD(exports, "triggerReportEnv", TriggerReportEnv);
5075
NODE_SET_METHOD(exports, "triggerReportNoEnv", TriggerReportNoEnv);
76+
NODE_SET_METHOD(exports, "triggerReportNoContext", TriggerReportNoContext);
77+
NODE_SET_METHOD(exports, "triggerReportNewContext", TriggerReportNewContext);
5178
}
5279

5380
NODE_MODULE(NODE_GYP_MODULE_NAME, init)

‎test/addons/report-api/test.js‎

Lines changed: 20 additions & 11 deletions
Original file line numberDiff line numberDiff line change
@@ -9,7 +9,7 @@ const tmpdir = require('../../common/tmpdir');
99
constbinding=path.resolve(__dirname,`./build/${common.buildType}/binding`);
1010
constaddon=require(binding);
1111

12-
functionmyAddonMain(method,hasJavaScriptFrames){
12+
functionmyAddonMain(method,{ hasIsolate, hasEnv }){
1313
tmpdir.refresh();
1414
process.report.directory=tmpdir.path;
1515

@@ -19,26 +19,35 @@ function myAddonMain(method, hasJavaScriptFrames){
1919
assert.strictEqual(reports.length,1);
2020

2121
constreport=reports[0];
22-
helper.validate(report);
22+
helper.validate(report,[
23+
['header.event','FooMessage'],
24+
['header.trigger','BarTrigger'],
25+
]);
2326

2427
constcontent=require(report);
25-
assert.strictEqual(content.header.event,'FooMessage');
26-
assert.strictEqual(content.header.trigger,'BarTrigger');
2728

2829
// Check that the javascript stack is present.
29-
if(hasJavaScriptFrames){
30+
if(hasIsolate){
3031
assert.strictEqual(content.javascriptStack.stack.findIndex((frame)=>frame.match('myAddonMain')),0);
3132
}else{
3233
assert.strictEqual(content.javascriptStack,undefined);
3334
}
35+
36+
if(hasEnv){
37+
assert.strictEqual(content.header.threadId,0);
38+
}else{
39+
assert.strictEqual(content.header.threadId,null);
40+
}
3441
}
3542

3643
constmethods=[
37-
['triggerReport',true],
38-
['triggerReportNoIsolate',false],
39-
['triggerReportEnv',true],
40-
['triggerReportNoEnv',false],
44+
['triggerReport',true,true],
45+
['triggerReportNoIsolate',false,false],
46+
['triggerReportEnv',true,true],
47+
['triggerReportNoEnv',false,false],
48+
['triggerReportNoContext',true,false],
49+
['triggerReportNewContext',true,false],
4150
];
42-
for(const[method,hasJavaScriptFrames]ofmethods){
43-
myAddonMain(method,hasJavaScriptFrames);
51+
for(const[method,hasIsolate,hasEnv]ofmethods){
52+
myAddonMain(method,{ hasIsolate, hasEnv });
4453
}

0 commit comments

Comments
(0)