Skip to content

Commit 5b8e46d

Browse files
addaleaxMylesBorins
authored andcommitted
src: harden JSStream callbacks
Since these are executing JS code, and in particular parts of that code may be provided by userland, handle such exceptions in C++. Refs: #17938 (comment) PR-URL: #18028 Reviewed-By: Ben Noordhuis <[email protected]> Reviewed-By: Colin Ihrig <[email protected]> Reviewed-By: James M Snell <[email protected]> Reviewed-By: Daniel Bevenius <[email protected]> Reviewed-By: Tiancheng "Timothy" Gu <[email protected]>
1 parent 07e8195 commit 5b8e46d

File tree

2 files changed

+63
-13
lines changed

2 files changed

+63
-13
lines changed

src/js_stream.cc

+44-13
Original file line numberDiff line numberDiff line change
@@ -14,9 +14,9 @@ using v8::FunctionCallbackInfo;
1414
using v8::FunctionTemplate;
1515
using v8::HandleScope;
1616
using v8::Local;
17-
using v8::MaybeLocal;
1817
using v8::Object;
1918
using v8::String;
19+
using v8::TryCatch;
2020
using v8::Value;
2121

2222

@@ -98,24 +98,41 @@ bool JSStream::IsAlive() {
9898
bool JSStream::IsClosing() {
9999
HandleScope scope(env()->isolate());
100100
Context::Scope context_scope(env()->context());
101-
return MakeCallback(env()->isclosing_string(), 0, nullptr)
102-
.ToLocalChecked()->IsTrue();
101+
TryCatch try_catch(env()->isolate());
102+
Local<Value> value;
103+
if (!MakeCallback(env()->isclosing_string(), 0, nullptr).ToLocal(&value)) {
104+
FatalException(env()->isolate(), try_catch);
105+
return true;
106+
}
107+
return value->IsTrue();
103108
}
104109

105110

106111
int JSStream::ReadStart() {
107112
HandleScope scope(env()->isolate());
108113
Context::Scope context_scope(env()->context());
109-
return MakeCallback(env()->onreadstart_string(), 0, nullptr)
110-
.ToLocalChecked()->Int32Value();
114+
TryCatch try_catch(env()->isolate());
115+
Local<Value> value;
116+
int value_int = UV_EPROTO;
117+
if (!MakeCallback(env()->onreadstart_string(), 0, nullptr).ToLocal(&value) ||
118+
!value->Int32Value(env()->context()).To(&value_int)) {
119+
FatalException(env()->isolate(), try_catch);
120+
}
121+
return value_int;
111122
}
112123

113124

114125
int JSStream::ReadStop() {
115126
HandleScope scope(env()->isolate());
116127
Context::Scope context_scope(env()->context());
117-
return MakeCallback(env()->onreadstop_string(), 0, nullptr)
118-
.ToLocalChecked()->Int32Value();
128+
TryCatch try_catch(env()->isolate());
129+
Local<Value> value;
130+
int value_int = UV_EPROTO;
131+
if (!MakeCallback(env()->onreadstop_string(), 0, nullptr).ToLocal(&value) ||
132+
!value->Int32Value(env()->context()).To(&value_int)) {
133+
FatalException(env()->isolate(), try_catch);
134+
}
135+
return value_int;
119136
}
120137

121138

@@ -128,10 +145,17 @@ int JSStream::DoShutdown(ShutdownWrap* req_wrap) {
128145
};
129146

130147
req_wrap->Dispatched();
131-
MaybeLocal<Value> res =
132-
MakeCallback(env()->onshutdown_string(), arraysize(argv), argv);
133148

134-
return res.ToLocalChecked()->Int32Value();
149+
TryCatch try_catch(env()->isolate());
150+
Local<Value> value;
151+
int value_int = UV_EPROTO;
152+
if (!MakeCallback(env()->onshutdown_string(),
153+
arraysize(argv),
154+
argv).ToLocal(&value) ||
155+
!value->Int32Value(env()->context()).To(&value_int)) {
156+
FatalException(env()->isolate(), try_catch);
157+
}
158+
return value_int;
135159
}
136160

137161

@@ -157,10 +181,17 @@ int JSStream::DoWrite(WriteWrap* w,
157181
};
158182

159183
w->Dispatched();
160-
MaybeLocal<Value> res =
161-
MakeCallback(env()->onwrite_string(), arraysize(argv), argv);
162184

163-
return res.ToLocalChecked()->Int32Value();
185+
TryCatch try_catch(env()->isolate());
186+
Local<Value> value;
187+
int value_int = UV_EPROTO;
188+
if (!MakeCallback(env()->onwrite_string(),
189+
arraysize(argv),
190+
argv).ToLocal(&value) ||
191+
!value->Int32Value(env()->context()).To(&value_int)) {
192+
FatalException(env()->isolate(), try_catch);
193+
}
194+
return value_int;
164195
}
165196

166197

Original file line numberDiff line numberDiff line change
@@ -0,0 +1,19 @@
1+
// Flags: --expose-internals
2+
'use strict';
3+
const common = require('../common');
4+
const assert = require('assert');
5+
const JSStreamWrap = require('internal/wrap_js_stream');
6+
const { Duplex } = require('stream');
7+
8+
process.once('uncaughtException', common.mustCall((err) => {
9+
assert.strictEqual(err.message, 'exception!');
10+
}));
11+
12+
const socket = new JSStreamWrap(new Duplex({
13+
read: common.mustCall(),
14+
write: common.mustCall((buffer, data, cb) => {
15+
throw new Error('exception!');
16+
})
17+
}));
18+
19+
assert.throws(() => socket.end('foo'), /Error: write EPROTO/);

0 commit comments

Comments
 (0)