contextify: dealloc only after global and sandbox
authorFedor Indutny <fedor.indutny@gmail.com>
Thu, 12 Sep 2013 13:51:55 +0000 (17:51 +0400)
committerFedor Indutny <fedor.indutny@gmail.com>
Thu, 12 Sep 2013 15:40:43 +0000 (19:40 +0400)
Functions created using: `vm.runInNewContext('(function() { })')` will
reference only `proxy_global_` object and not `sandbox_`. Thus in case,
where there're no references to sandbox (such as in example above),
`ContextifyContext` will be destroyed and use-after-free might happen.

src/node_contextify.cc
test/simple/test-vm-run-in-new-context.js

index da63655..1c9d9be 100644 (file)
@@ -58,15 +58,20 @@ class ContextifyContext {
   Persistent<Object> sandbox_;
   Persistent<Context> context_;
   Persistent<Object> proxy_global_;
+  int references_;
 
  public:
   explicit ContextifyContext(Environment* env, Local<Object> sandbox)
       : env_(env)
       , sandbox_(env->isolate(), sandbox)
       , context_(env->isolate(), CreateV8Context(env))
-      , proxy_global_(env->isolate(), context()->Global()) {
+      , proxy_global_(env->isolate(), context()->Global())
+        // Wait for both sandbox_'s and proxy_global_'s death
+      , references_(2) {
     sandbox_.MakeWeak(this, SandboxFreeCallback);
     sandbox_.MarkIndependent();
+    proxy_global_.MakeWeak(this, SandboxFreeCallback);
+    proxy_global_.MarkIndependent();
   }
 
 
@@ -173,7 +178,8 @@ class ContextifyContext {
   static void SandboxFreeCallback(Isolate* isolate,
                                   Persistent<Object>* target,
                                   ContextifyContext* context) {
-    delete context;
+    if (--context->references_ == 0)
+      delete context;
   }
 
 
index 77b74d5..ecb80bd 100644 (file)
 // OTHERWISE, ARISING FROM, OUT OF OR IN CONNECTION WITH THE SOFTWARE OR THE
 // USE OR OTHER DEALINGS IN THE SOFTWARE.
 
+// Flags: --expose-gc
+
 var common = require('../common');
 var assert = require('assert');
 var vm = require('vm');
 
+assert.equal(typeof gc, 'function', 'Run this test with --expose-gc');
+
 common.globalCheck = false;
 
 console.error('run a string');
@@ -60,3 +64,8 @@ var f = { a: 1 };
 vm.runInNewContext('f.a = 2', { f: f });
 assert.equal(f.a, 2);
 
+console.error('use function in context without referencing context');
+var fn = vm.runInNewContext('(function() { obj.p = {}; })', { obj: {} })
+gc();
+fn();
+// Should not crash