Skip to content
This repository has been archived by the owner on Aug 31, 2018. It is now read-only.

Commit

Permalink
worker: implement vm.moveMessagePortToContext()
Browse files Browse the repository at this point in the history
This should help a lot with actual sandboxing of JS code.
  • Loading branch information
addaleax committed Oct 19, 2017
1 parent 1ea4b76 commit 5cc4d78
Show file tree
Hide file tree
Showing 7 changed files with 124 additions and 0 deletions.
3 changes: 3 additions & 0 deletions lib/vm.js
Original file line number Diff line number Diff line change
Expand Up @@ -30,6 +30,8 @@ const {
runInDebugContext
} = process.binding('contextify');

const { moveMessagePortToContext } = process.binding('messaging');

// The binding provides a few useful primitives:
// - Script(code, { filename = "evalmachine.anonymous",
// displayErrors = true } = {})
Expand Down Expand Up @@ -143,6 +145,7 @@ module.exports = {
Script,
createContext,
createScript,
moveMessagePortToContext,
runInDebugContext,
runInContext,
runInNewContext,
Expand Down
4 changes: 4 additions & 0 deletions node.gyp
Original file line number Diff line number Diff line change
Expand Up @@ -258,6 +258,7 @@
'src/node_http2_core-inl.h',
'src/node_buffer.h',
'src/node_constants.h',
'src/node_contextify.h',
'src/node_debug_options.h',
'src/node_http2.h',
'src/node_http2_state.h',
Expand Down Expand Up @@ -687,9 +688,12 @@
'<(OBJ_PATH)<(OBJ_SEPARATOR)handle_wrap.<(OBJ_SUFFIX)',
'<(OBJ_PATH)<(OBJ_SEPARATOR)node.<(OBJ_SUFFIX)',
'<(OBJ_PATH)<(OBJ_SEPARATOR)node_buffer.<(OBJ_SUFFIX)',
'<(OBJ_PATH)<(OBJ_SEPARATOR)node_contextify.<(OBJ_SUFFIX)',
'<(OBJ_PATH)<(OBJ_SEPARATOR)node_i18n.<(OBJ_SUFFIX)',
'<(OBJ_PATH)<(OBJ_SEPARATOR)node_perf.<(OBJ_SUFFIX)',
'<(OBJ_PATH)<(OBJ_SEPARATOR)node_url.<(OBJ_SUFFIX)',
'<(OBJ_PATH)<(OBJ_SEPARATOR)node_watchdog.<(OBJ_SUFFIX)',
'<(OBJ_PATH)<(OBJ_SEPARATOR)node_worker.<(OBJ_SUFFIX)',
'<(OBJ_PATH)<(OBJ_SEPARATOR)util.<(OBJ_SUFFIX)',
'<(OBJ_PATH)<(OBJ_SEPARATOR)string_bytes.<(OBJ_SUFFIX)',
'<(OBJ_PATH)<(OBJ_SEPARATOR)stream_base.<(OBJ_SUFFIX)',
Expand Down
12 changes: 12 additions & 0 deletions src/node_contextify.cc
Original file line number Diff line number Diff line change
Expand Up @@ -1071,6 +1071,18 @@ void InitContextify(Local<Object> target,
}

} // anonymous namespace

MaybeLocal<Context> ContextFromContextifiedSandbox(
Environment* env,
Local<Object> sandbox) {
auto contextify_context =
ContextifyContext::ContextFromContextifiedSandbox(env, sandbox);
if (contextify_context == nullptr)
return MaybeLocal<Context>();
else
return contextify_context->context();
}

} // namespace node

NODE_MODULE_CONTEXT_AWARE_BUILTIN(contextify, node::InitContextify)
21 changes: 21 additions & 0 deletions src/node_contextify.h
Original file line number Diff line number Diff line change
@@ -0,0 +1,21 @@
#ifndef SRC_NODE_CONTEXTIFY_H_
#define SRC_NODE_CONTEXTIFY_H_

#if defined(NODE_WANT_INTERNALS) && NODE_WANT_INTERNALS

#include "v8.h"

namespace node {

class Environment;

v8::MaybeLocal<v8::Context> ContextFromContextifiedSandbox(
Environment* env,
v8::Local<v8::Object> sandbox);

} // namespace node

#endif // defined(NODE_WANT_INTERNALS) && NODE_WANT_INTERNALS


#endif // SRC_NODE_CONTEXTIFY_H_
34 changes: 34 additions & 0 deletions src/node_messaging.cc
Original file line number Diff line number Diff line change
@@ -1,4 +1,5 @@
#include "node_messaging.h"
#include "node_contextify.h"
#include "node_internals.h"
#include "node_buffer.h"
#include "util.h"
Expand Down Expand Up @@ -603,6 +604,37 @@ void MessagePort::StopBinding(const FunctionCallbackInfo<Value>& args) {
port->Stop();
}

void MessagePort::MoveToContext(const FunctionCallbackInfo<Value>& args) {
Environment* env = Environment::GetCurrent(args);
MessagePort* port;
if (!args[0]->IsObject() ||
(port = Unwrap<MessagePort>(args[0].As<Object>())) == nullptr) {
env->ThrowTypeError("First argument needs to be a MessagePort instance");
}
if (!port->data_) {
env->ThrowError("Cannot transfer a closed MessagePort");
return;
}
if (port->is_privileged_ || port->fm_listener_) {
env->ThrowError("Cannot transfer MessagePort with special semantics");
return;
}
Local<Value> context_arg = args[1];
Local<Context> context;
if (!context_arg->IsObject() ||
!ContextFromContextifiedSandbox(env, context_arg.As<Object>())
.ToLocal(&context)) {
env->ThrowError("Invalid context argument");
return;
}
Context::Scope context_scope(context);
MessagePort* target =
MessagePort::New(env, context, nullptr, port->Detach());
if (target) {
args.GetReturnValue().Set(target->object());
}
}

size_t MessagePort::self_size() const {
Mutex::ScopedLock lock(data_->mutex_);
size_t sz = sizeof(*this) + sizeof(*data_);
Expand Down Expand Up @@ -686,6 +718,8 @@ static void InitMessaging(Local<Object> target,
templ->GetFunction(context).ToLocalChecked()).FromJust();
}

env->SetMethod(target, "moveMessagePortToContext",
MessagePort::MoveToContext);
target->Set(context,
env->message_port_constructor_string(),
GetMessagePortConstructor(env, context).ToLocalChecked())
Expand Down
5 changes: 5 additions & 0 deletions src/node_messaging.h
Original file line number Diff line number Diff line change
Expand Up @@ -157,11 +157,16 @@ class MessagePort : public HandleWrap {
// Stop processing messages on this port as a receiving end.
void Stop();

/* constructor */
static void New(const v8::FunctionCallbackInfo<v8::Value>& args);
/* prototype methods */
static void PostMessage(const v8::FunctionCallbackInfo<v8::Value>& args);
static void StartBinding(const v8::FunctionCallbackInfo<v8::Value>& args);
static void StopBinding(const v8::FunctionCallbackInfo<v8::Value>& args);

/* static */
static void MoveToContext(const v8::FunctionCallbackInfo<v8::Value>& args);

// Turns `a` and `b` into siblings, i.e. connects the sending side of one
// to the receiving side of the other. This is not thread-safe.
static void Entangle(MessagePort* a, MessagePort* b);
Expand Down
45 changes: 45 additions & 0 deletions test/parallel/test-message-channel-move.js
Original file line number Diff line number Diff line change
@@ -0,0 +1,45 @@
/* eslint-disable prefer-assert-methods */
'use strict';
const common = require('../common');
const assert = require('assert');
const vm = require('vm');
const { MessageChannel } = require('worker');

{
const context = vm.createContext();
const channel = new MessageChannel();
context.port = vm.moveMessagePortToContext(channel.port1, context);
context.global = context;
const port = channel.port2;
vm.runInContext('(' + function() {
function assert(condition) { if (!condition) throw new Error(); }

{
assert(port instanceof Object);
assert(port.onmessage === undefined);
assert(port.postMessage instanceof Function);
port.onmessage = function(msg) {
assert(msg instanceof Object);
port.postMessage(msg);
};
port.start();
}

{
let threw = false;
try {
port.postMessage(global);
} catch (e) {
assert(e instanceof Object);
assert(e instanceof Error);
threw = true;
}
assert(threw);
}
} + ')()', context);
port.on('message', common.mustCall((msg) => {
assert(msg instanceof Object);
port.close();
}));
port.postMessage({});
}

0 comments on commit 5cc4d78

Please sign in to comment.