Skip to content

Commit 5ce4580

Browse files
committed
contextify: handle infinite recursion errors
Try to be consistent with v0.10 and emit "Maximum call stack size reached", even if it happens when allocating context or doing other stuff. fix nodejs#7045
1 parent bae545d commit 5ce4580

2 files changed

Lines changed: 70 additions & 13 deletions

File tree

src/node_contextify.cc

Lines changed: 40 additions & 13 deletions
Original file line numberDiff line numberDiff line change
@@ -69,15 +69,23 @@ class ContextifyContext {
6969
: env_(env),
7070
sandbox_(env->isolate(), sandbox),
7171
context_(env->isolate(), CreateV8Context(env)),
72-
proxy_global_(env->isolate(), context()->Global()),
7372
// Wait for sandbox_, proxy_global_, and context_ to die
74-
references_(3) {
73+
references_(0) {
7574
sandbox_.MakeWeak(this, WeakCallback);
7675
sandbox_.MarkIndependent();
76+
references_++;
77+
78+
// Allocation failure or maximum call stack size reached
79+
if (context_.IsEmpty())
80+
return;
7781
context_.MakeWeak(this, WeakCallback);
7882
context_.MarkIndependent();
83+
references_++;
84+
85+
proxy_global_.Reset(env->isolate(), context()->Global());
7986
proxy_global_.MakeWeak(this, WeakCallback);
8087
proxy_global_.MarkIndependent();
88+
references_++;
8189
}
8290

8391

@@ -180,6 +188,9 @@ class ContextifyContext {
180188
HandleScope scope(node_isolate);
181189
Local<Object> wrapper =
182190
env->script_data_constructor_function()->NewInstance();
191+
if (wrapper.IsEmpty())
192+
return scope.Close(Handle<Value>());
193+
183194
Wrap<ContextifyContext>(wrapper, this);
184195
return scope.Close(wrapper);
185196
}
@@ -232,7 +243,17 @@ class ContextifyContext {
232243
// Don't allow contextifying a sandbox multiple times.
233244
assert(sandbox->GetHiddenValue(hidden_name).IsEmpty());
234245

246+
TryCatch try_catch;
235247
ContextifyContext* context = new ContextifyContext(env, sandbox);
248+
249+
if (try_catch.HasCaught()) {
250+
try_catch.ReThrow();
251+
return;
252+
}
253+
254+
if (context->context().IsEmpty())
255+
return;
256+
236257
Local<External> hidden_context = External::New(context);
237258
sandbox->SetHiddenValue(hidden_name, hidden_context);
238259
}
@@ -490,14 +511,18 @@ class ContextifyScript : public BaseObject {
490511
"sandbox argument must have been converted to a context.");
491512
}
492513

514+
if (contextify_context->context().IsEmpty())
515+
return;
516+
493517
// Do the eval within the context
494518
Context::Scope context_scope(contextify_context->context());
495-
EvalMachine(contextify_context->env(),
496-
timeout,
497-
display_errors,
498-
args,
499-
try_catch);
500-
contextify_context->CopyProperties();
519+
if (EvalMachine(contextify_context->env(),
520+
timeout,
521+
display_errors,
522+
args,
523+
try_catch)) {
524+
contextify_context->CopyProperties();
525+
}
501526
}
502527

503528
static int64_t GetTimeoutArg(const FunctionCallbackInfo<Value>& args,
@@ -565,14 +590,14 @@ class ContextifyScript : public BaseObject {
565590
}
566591

567592

568-
static void EvalMachine(Environment* env,
593+
static bool EvalMachine(Environment* env,
569594
const int64_t timeout,
570595
const bool display_errors,
571596
const FunctionCallbackInfo<Value>& args,
572597
TryCatch& try_catch) {
573598
if (!ContextifyScript::InstanceOf(env, args.This())) {
574-
return ThrowTypeError(
575-
"Script methods can only be called on script instances.");
599+
ThrowTypeError("Script methods can only be called on script instances.");
600+
return false;
576601
}
577602

578603
ContextifyScript* wrapped_script =
@@ -590,7 +615,8 @@ class ContextifyScript : public BaseObject {
590615

591616
if (try_catch.HasCaught() && try_catch.HasTerminated()) {
592617
V8::CancelTerminateExecution(args.GetIsolate());
593-
return ThrowError("Script execution timed out.");
618+
ThrowError("Script execution timed out.");
619+
return false;
594620
}
595621

596622
if (result.IsEmpty()) {
@@ -599,10 +625,11 @@ class ContextifyScript : public BaseObject {
599625
DisplayExceptionLine(try_catch.Message());
600626
}
601627
try_catch.ReThrow();
602-
return;
628+
return false;
603629
}
604630

605631
args.GetReturnValue().Set(result);
632+
return true;
606633
}
607634

608635

Lines changed: 30 additions & 0 deletions
Original file line numberDiff line numberDiff line change
@@ -0,0 +1,30 @@
1+
// Copyright Joyent, Inc. and other Node contributors.
2+
//
3+
// Permission is hereby granted, free of charge, to any person obtaining a
4+
// copy of this software and associated documentation files (the
5+
// "Software"), to deal in the Software without restriction, including
6+
// without limitation the rights to use, copy, modify, merge, publish,
7+
// distribute, sublicense, and/or sell copies of the Software, and to permit
8+
// persons to whom the Software is furnished to do so, subject to the
9+
// following conditions:
10+
//
11+
// The above copyright notice and this permission notice shall be included
12+
// in all copies or substantial portions of the Software.
13+
//
14+
// THE SOFTWARE IS PROVIDED "AS IS", WITHOUT WARRANTY OF ANY KIND, EXPRESS
15+
// OR IMPLIED, INCLUDING BUT NOT LIMITED TO THE WARRANTIES OF
16+
// MERCHANTABILITY, FITNESS FOR A PARTICULAR PURPOSE AND NONINFRINGEMENT. IN
17+
// NO EVENT SHALL THE AUTHORS OR COPYRIGHT HOLDERS BE LIABLE FOR ANY CLAIM,
18+
// DAMAGES OR OTHER LIABILITY, WHETHER IN AN ACTION OF CONTRACT, TORT OR
19+
// OTHERWISE, ARISING FROM, OUT OF OR IN CONNECTION WITH THE SOFTWARE OR THE
20+
// USE OR OTHER DEALINGS IN THE SOFTWARE.
21+
22+
// Flags: --stack-size=128
23+
24+
var assert = require('assert');
25+
var vm = require('vm');
26+
var s = 'vm.runInNewContext(s, { vm: vm, s: s })';
27+
28+
assert.throws(function() {
29+
eval(s);
30+
}, /Maximum call stack/);

0 commit comments

Comments
 (0)