src: add internalBindings for binding isolation

This commit adds a method to internal/process that allows access to
bindings that are not intended to be used by user code. It has a
separate cache object and modlist in order to avoid collisions.

You can use NODE_MODULE_CONTEXT_AWARE_INTERNAL to register a C++
module as an internal.

PR-URL: https://github.com/nodejs/node/pull/15759
Reviewed-By: Ben Noordhuis <info@bnoordhuis.nl>
Reviewed-By: James M Snell <jasnell@gmail.com>
Reviewed-By: Brian White <mscdex@mscdex.net>
Reviewed-By: Refael Ackermann <refack@gmail.com>
This commit is contained in:
Bradley Farias 2017-10-03 10:07:48 -05:00
parent 75d41cf531
commit a36aa049c8
10 changed files with 135 additions and 49 deletions

View File

@ -1,6 +1,7 @@
'use strict'; 'use strict';
const { ModuleWrap } = process.binding('module_wrap'); const { ModuleWrap } =
require('internal/process').internalBinding('module_wrap');
const debug = require('util').debuglog('esm'); const debug = require('util').debuglog('esm');
const ArrayJoin = Function.call.bind(Array.prototype.join); const ArrayJoin = Function.call.bind(Array.prototype.join);
const ArrayMap = Function.call.bind(Array.prototype.map); const ArrayMap = Function.call.bind(Array.prototype.map);

View File

@ -3,7 +3,7 @@
const { URL } = require('url'); const { URL } = require('url');
const CJSmodule = require('module'); const CJSmodule = require('module');
const errors = require('internal/errors'); const errors = require('internal/errors');
const { resolve } = process.binding('module_wrap'); const { resolve } = require('internal/process').internalBinding('module_wrap');
module.exports = (target, base) => { module.exports = (target, base) => {
if (base === undefined) { if (base === undefined) {

View File

@ -4,6 +4,9 @@ const errors = require('internal/errors');
const util = require('util'); const util = require('util');
const constants = process.binding('constants').os.signals; const constants = process.binding('constants').os.signals;
const internalBinding = process._internalBinding;
delete process._internalBinding;
const assert = process.assert = function(x, msg) { const assert = process.assert = function(x, msg) {
if (!x) throw new errors.Error('ERR_ASSERTION', msg || 'assertion error'); if (!x) throw new errors.Error('ERR_ASSERTION', msg || 'assertion error');
}; };
@ -256,5 +259,6 @@ module.exports = {
setupKillAndExit, setupKillAndExit,
setupSignalHandlers, setupSignalHandlers,
setupChannel, setupChannel,
setupRawDebug setupRawDebug,
internalBinding
}; };

View File

@ -303,7 +303,17 @@ inline Environment::Environment(IsolateData* isolate_data,
v8::HandleScope handle_scope(isolate()); v8::HandleScope handle_scope(isolate());
v8::Context::Scope context_scope(context); v8::Context::Scope context_scope(context);
set_as_external(v8::External::New(isolate(), this)); set_as_external(v8::External::New(isolate(), this));
set_binding_cache_object(v8::Object::New(isolate()));
v8::Local<v8::Primitive> null = v8::Null(isolate());
v8::Local<v8::Object> binding_cache_object = v8::Object::New(isolate());
CHECK(binding_cache_object->SetPrototype(context, null).FromJust());
set_binding_cache_object(binding_cache_object);
v8::Local<v8::Object> internal_binding_cache_object =
v8::Object::New(isolate());
CHECK(internal_binding_cache_object->SetPrototype(context, null).FromJust());
set_internal_binding_cache_object(internal_binding_cache_object);
set_module_load_list_array(v8::Array::New(isolate())); set_module_load_list_array(v8::Array::New(isolate()));
AssignToContext(context); AssignToContext(context);

View File

@ -302,6 +302,7 @@ class ModuleWrap;
V(async_hooks_after_function, v8::Function) \ V(async_hooks_after_function, v8::Function) \
V(async_hooks_promise_resolve_function, v8::Function) \ V(async_hooks_promise_resolve_function, v8::Function) \
V(binding_cache_object, v8::Object) \ V(binding_cache_object, v8::Object) \
V(internal_binding_cache_object, v8::Object) \
V(buffer_prototype_object, v8::Object) \ V(buffer_prototype_object, v8::Object) \
V(context, v8::Context) \ V(context, v8::Context) \
V(domain_array, v8::Array) \ V(domain_array, v8::Array) \

View File

@ -7,6 +7,7 @@
#include "node_url.h" #include "node_url.h"
#include "util.h" #include "util.h"
#include "util-inl.h" #include "util-inl.h"
#include "node_internals.h"
namespace node { namespace node {
namespace loader { namespace loader {
@ -523,5 +524,5 @@ void ModuleWrap::Initialize(Local<Object> target,
} // namespace loader } // namespace loader
} // namespace node } // namespace node
NODE_MODULE_CONTEXT_AWARE_BUILTIN(module_wrap, NODE_MODULE_CONTEXT_AWARE_INTERNAL(module_wrap,
node::loader::ModuleWrap::Initialize) node::loader::ModuleWrap::Initialize)

View File

@ -183,6 +183,7 @@ static bool v8_is_profiling = false;
static bool node_is_initialized = false; static bool node_is_initialized = false;
static node_module* modpending; static node_module* modpending;
static node_module* modlist_builtin; static node_module* modlist_builtin;
static node_module* modlist_internal;
static node_module* modlist_linked; static node_module* modlist_linked;
static node_module* modlist_addon; static node_module* modlist_addon;
static bool trace_enabled = false; static bool trace_enabled = false;
@ -2574,6 +2575,9 @@ extern "C" void node_module_register(void* m) {
if (mp->nm_flags & NM_F_BUILTIN) { if (mp->nm_flags & NM_F_BUILTIN) {
mp->nm_link = modlist_builtin; mp->nm_link = modlist_builtin;
modlist_builtin = mp; modlist_builtin = mp;
} else if (mp->nm_flags & NM_F_INTERNAL) {
mp->nm_link = modlist_internal;
modlist_internal = mp;
} else if (!node_is_initialized) { } else if (!node_is_initialized) {
// "Linked" modules are included as part of the node project. // "Linked" modules are included as part of the node project.
// Like builtins they are registered *before* node::Init runs. // Like builtins they are registered *before* node::Init runs.
@ -2585,28 +2589,28 @@ extern "C" void node_module_register(void* m) {
} }
} }
struct node_module* get_builtin_module(const char* name) { inline struct node_module* FindModule(struct node_module* list,
const char* name,
int flag) {
struct node_module* mp; struct node_module* mp;
for (mp = modlist_builtin; mp != nullptr; mp = mp->nm_link) { for (mp = list; mp != nullptr; mp = mp->nm_link) {
if (strcmp(mp->nm_modname, name) == 0) if (strcmp(mp->nm_modname, name) == 0)
break; break;
} }
CHECK(mp == nullptr || (mp->nm_flags & NM_F_BUILTIN) != 0); CHECK(mp == nullptr || (mp->nm_flags & flag) != 0);
return (mp); return mp;
} }
struct node_module* get_linked_module(const char* name) { node_module* get_builtin_module(const char* name) {
struct node_module* mp; return FindModule(modlist_builtin, name, NM_F_BUILTIN);
}
for (mp = modlist_linked; mp != nullptr; mp = mp->nm_link) { node_module* get_internal_module(const char* name) {
if (strcmp(mp->nm_modname, name) == 0) return FindModule(modlist_internal, name, NM_F_INTERNAL);
break; }
} node_module* get_linked_module(const char* name) {
return FindModule(modlist_linked, name, NM_F_LINKED);
CHECK(mp == nullptr || (mp->nm_flags & NM_F_LINKED) != 0);
return mp;
} }
struct DLib { struct DLib {
@ -2880,24 +2884,60 @@ void ProcessEmitWarning(Environment* env, const char* fmt, ...) {
f.As<v8::Function>()->Call(process, 1, &arg); f.As<v8::Function>()->Call(process, 1, &arg);
} }
static bool PullFromCache(Environment* env,
const FunctionCallbackInfo<Value>& args,
Local<String> module,
Local<Object> cache) {
Local<Context> context = env->context();
Local<Value> exports_v;
Local<Object> exports;
if (cache->Get(context, module).ToLocal(&exports_v) &&
exports_v->IsObject() &&
exports_v->ToObject(context).ToLocal(&exports)) {
args.GetReturnValue().Set(exports);
return true;
}
return false;
}
static Local<Object> InitModule(Environment* env,
node_module* mod,
Local<String> module) {
Local<Object> exports = Object::New(env->isolate());
// Internal bindings don't have a "module" object, only exports.
CHECK_EQ(mod->nm_register_func, nullptr);
CHECK_NE(mod->nm_context_register_func, nullptr);
Local<Value> unused = Undefined(env->isolate());
mod->nm_context_register_func(exports,
unused,
env->context(),
mod->nm_priv);
return exports;
}
static void ThrowIfNoSuchModule(Environment* env, const char* module_v) {
char errmsg[1024];
snprintf(errmsg,
sizeof(errmsg),
"No such module: %s",
module_v);
env->ThrowError(errmsg);
}
static void Binding(const FunctionCallbackInfo<Value>& args) { static void Binding(const FunctionCallbackInfo<Value>& args) {
Environment* env = Environment::GetCurrent(args); Environment* env = Environment::GetCurrent(args);
Local<String> module = args[0]->ToString(env->isolate()); Local<String> module;
node::Utf8Value module_v(env->isolate(), module); if (!args[0]->ToString(env->context()).ToLocal(&module)) return;
Local<Object> cache = env->binding_cache_object(); Local<Object> cache = env->binding_cache_object();
Local<Object> exports;
if (cache->Has(env->context(), module).FromJust()) { if (PullFromCache(env, args, module, cache))
exports = cache->Get(module)->ToObject(env->isolate());
args.GetReturnValue().Set(exports);
return; return;
}
// Append a string to process.moduleLoadList // Append a string to process.moduleLoadList
char buf[1024]; char buf[1024];
node::Utf8Value module_v(env->isolate(), module);
snprintf(buf, sizeof(buf), "Binding %s", *module_v); snprintf(buf, sizeof(buf), "Binding %s", *module_v);
Local<Array> modules = env->module_load_list_array(); Local<Array> modules = env->module_load_list_array();
@ -2905,33 +2945,49 @@ static void Binding(const FunctionCallbackInfo<Value>& args) {
modules->Set(l, OneByteString(env->isolate(), buf)); modules->Set(l, OneByteString(env->isolate(), buf));
node_module* mod = get_builtin_module(*module_v); node_module* mod = get_builtin_module(*module_v);
Local<Object> exports;
if (mod != nullptr) { if (mod != nullptr) {
exports = Object::New(env->isolate()); exports = InitModule(env, mod, module);
// Internal bindings don't have a "module" object, only exports.
CHECK_EQ(mod->nm_register_func, nullptr);
CHECK_NE(mod->nm_context_register_func, nullptr);
Local<Value> unused = Undefined(env->isolate());
mod->nm_context_register_func(exports, unused,
env->context(), mod->nm_priv);
cache->Set(module, exports);
} else if (!strcmp(*module_v, "constants")) { } else if (!strcmp(*module_v, "constants")) {
exports = Object::New(env->isolate()); exports = Object::New(env->isolate());
CHECK(exports->SetPrototype(env->context(), CHECK(exports->SetPrototype(env->context(),
Null(env->isolate())).FromJust()); Null(env->isolate())).FromJust());
DefineConstants(env->isolate(), exports); DefineConstants(env->isolate(), exports);
cache->Set(module, exports);
} else if (!strcmp(*module_v, "natives")) { } else if (!strcmp(*module_v, "natives")) {
exports = Object::New(env->isolate()); exports = Object::New(env->isolate());
DefineJavaScript(env, exports); DefineJavaScript(env, exports);
cache->Set(module, exports);
} else { } else {
char errmsg[1024]; return ThrowIfNoSuchModule(env, *module_v);
snprintf(errmsg,
sizeof(errmsg),
"No such module: %s",
*module_v);
return env->ThrowError(errmsg);
} }
cache->Set(module, exports);
args.GetReturnValue().Set(exports);
}
static void InternalBinding(const FunctionCallbackInfo<Value>& args) {
Environment* env = Environment::GetCurrent(args);
Local<String> module;
if (!args[0]->ToString(env->context()).ToLocal(&module)) return;
Local<Object> cache = env->internal_binding_cache_object();
if (PullFromCache(env, args, module, cache))
return;
// Append a string to process.moduleLoadList
char buf[1024];
node::Utf8Value module_v(env->isolate(), module);
snprintf(buf, sizeof(buf), "Internal Binding %s", *module_v);
Local<Array> modules = env->module_load_list_array();
uint32_t l = modules->Length();
modules->Set(l, OneByteString(env->isolate(), buf));
node_module* mod = get_internal_module(*module_v);
if (mod == nullptr) return ThrowIfNoSuchModule(env, *module_v);
Local<Object> exports = InitModule(env, mod, module);
cache->Set(module, exports);
args.GetReturnValue().Set(exports); args.GetReturnValue().Set(exports);
} }
@ -2939,7 +2995,8 @@ static void Binding(const FunctionCallbackInfo<Value>& args) {
static void LinkedBinding(const FunctionCallbackInfo<Value>& args) { static void LinkedBinding(const FunctionCallbackInfo<Value>& args) {
Environment* env = Environment::GetCurrent(args.GetIsolate()); Environment* env = Environment::GetCurrent(args.GetIsolate());
Local<String> module_name = args[0]->ToString(env->isolate()); Local<String> module_name;
if (!args[0]->ToString(env->context()).ToLocal(&module_name)) return;
Local<Object> cache = env->binding_cache_object(); Local<Object> cache = env->binding_cache_object();
Local<Value> exports_v = cache->Get(module_name); Local<Value> exports_v = cache->Get(module_name);
@ -3674,6 +3731,7 @@ void SetupProcessObject(Environment* env,
env->SetMethod(process, "binding", Binding); env->SetMethod(process, "binding", Binding);
env->SetMethod(process, "_linkedBinding", LinkedBinding); env->SetMethod(process, "_linkedBinding", LinkedBinding);
env->SetMethod(process, "_internalBinding", InternalBinding);
env->SetMethod(process, "_setupProcessObject", SetupProcessObject); env->SetMethod(process, "_setupProcessObject", SetupProcessObject);
env->SetMethod(process, "_setupNextTick", SetupNextTick); env->SetMethod(process, "_setupNextTick", SetupNextTick);

View File

@ -421,8 +421,9 @@ typedef void (*addon_context_register_func)(
v8::Local<v8::Context> context, v8::Local<v8::Context> context,
void* priv); void* priv);
#define NM_F_BUILTIN 0x01 #define NM_F_BUILTIN 0x01
#define NM_F_LINKED 0x02 #define NM_F_LINKED 0x02
#define NM_F_INTERNAL 0x04
struct node_module { struct node_module {
int nm_version; int nm_version;
@ -436,9 +437,6 @@ struct node_module {
struct node_module* nm_link; struct node_module* nm_link;
}; };
node_module* get_builtin_module(const char *name);
node_module* get_linked_module(const char *name);
extern "C" NODE_EXTERN void node_module_register(void* mod); extern "C" NODE_EXTERN void node_module_register(void* mod);
#ifdef _WIN32 #ifdef _WIN32

View File

@ -324,6 +324,9 @@ class InternalCallbackScope {
bool closed_ = false; bool closed_ = false;
}; };
#define NODE_MODULE_CONTEXT_AWARE_INTERNAL(modname, regfunc) \
NODE_MODULE_CONTEXT_AWARE_X(modname, regfunc, NULL, NM_F_INTERNAL) \
} // namespace node } // namespace node

View File

@ -0,0 +1,10 @@
'use strict';
require('../common');
const assert = require('assert');
assert.strictEqual(undefined, process._internalBinding);
assert.strictEqual(undefined, process.internalBinding);
assert.throws(() => {
process.binding('module_wrap');
}, /No such module/);