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:
parent
75d41cf531
commit
a36aa049c8
@ -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);
|
||||||
|
@ -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) {
|
||||||
|
@ -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
|
||||||
};
|
};
|
||||||
|
@ -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);
|
||||||
|
@ -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) \
|
||||||
|
@ -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)
|
||||||
|
134
src/node.cc
134
src/node.cc
@ -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);
|
||||||
|
@ -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
|
||||||
|
@ -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
|
||||||
|
|
||||||
|
|
||||||
|
10
test/parallel/test-internal-process-binding.js
Normal file
10
test/parallel/test-internal-process-binding.js
Normal 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/);
|
Loading…
x
Reference in New Issue
Block a user