Skip to content

Commit 807b1e5

Browse files
legendecasRafaelGSS
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 <rafael.nunu@hotmail.com>
1 parent b17cc87 commit 807b1e5

File tree

4 files changed

+57
-12
lines changed

4 files changed

+57
-12
lines changed

src/node_errors.cc

+5
Original file line numberDiff line numberDiff line change
@@ -513,6 +513,11 @@ void OOMErrorHandler(const char* location, bool is_heap_oom) {
513513
}
514514

515515
if (report_on_fatalerror) {
516+
// Trigger report with the isolate. Environment::GetCurrent may return
517+
// nullptr here:
518+
// - If the OOM is reported by a young generation space allocation,
519+
// Isolate::GetCurrentContext returns an empty handle.
520+
// - Otherwise, Isolate::GetCurrentContext returns a non-empty handle.
516521
TriggerNodeReport(isolate, message, "OOMError", "", Local<Object>());
517522
}
518523

src/node_report.cc

+5-1
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

+27
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+
void TriggerReportNoContext(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+
void TriggerReportNewContext(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
void init(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

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

12-
function myAddonMain(method, hasJavaScriptFrames) {
12+
function myAddonMain(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
const report = reports[0];
22-
helper.validate(report);
22+
helper.validate(report, [
23+
['header.event', 'FooMessage'],
24+
['header.trigger', 'BarTrigger'],
25+
]);
2326

2427
const content = 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
const methods = [
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] of methods) {
43-
myAddonMain(method, hasJavaScriptFrames);
51+
for (const [method, hasIsolate, hasEnv] of methods) {
52+
myAddonMain(method, { hasIsolate, hasEnv });
4453
}

0 commit comments

Comments
 (0)