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';
|
||||
|
||||
const { ModuleWrap } = process.binding('module_wrap');
|
||||
const { ModuleWrap } =
|
||||
require('internal/process').internalBinding('module_wrap');
|
||||
const debug = require('util').debuglog('esm');
|
||||
const ArrayJoin = Function.call.bind(Array.prototype.join);
|
||||
const ArrayMap = Function.call.bind(Array.prototype.map);
|
||||
|
@ -3,7 +3,7 @@
|
||||
const { URL } = require('url');
|
||||
const CJSmodule = require('module');
|
||||
const errors = require('internal/errors');
|
||||
const { resolve } = process.binding('module_wrap');
|
||||
const { resolve } = require('internal/process').internalBinding('module_wrap');
|
||||
|
||||
module.exports = (target, base) => {
|
||||
if (base === undefined) {
|
||||
|
@ -4,6 +4,9 @@ const errors = require('internal/errors');
|
||||
const util = require('util');
|
||||
const constants = process.binding('constants').os.signals;
|
||||
|
||||
const internalBinding = process._internalBinding;
|
||||
delete process._internalBinding;
|
||||
|
||||
const assert = process.assert = function(x, msg) {
|
||||
if (!x) throw new errors.Error('ERR_ASSERTION', msg || 'assertion error');
|
||||
};
|
||||
@ -256,5 +259,6 @@ module.exports = {
|
||||
setupKillAndExit,
|
||||
setupSignalHandlers,
|
||||
setupChannel,
|
||||
setupRawDebug
|
||||
setupRawDebug,
|
||||
internalBinding
|
||||
};
|
||||
|
@ -303,7 +303,17 @@ inline Environment::Environment(IsolateData* isolate_data,
|
||||
v8::HandleScope handle_scope(isolate());
|
||||
v8::Context::Scope context_scope(context);
|
||||
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()));
|
||||
|
||||
AssignToContext(context);
|
||||
|
@ -302,6 +302,7 @@ class ModuleWrap;
|
||||
V(async_hooks_after_function, v8::Function) \
|
||||
V(async_hooks_promise_resolve_function, v8::Function) \
|
||||
V(binding_cache_object, v8::Object) \
|
||||
V(internal_binding_cache_object, v8::Object) \
|
||||
V(buffer_prototype_object, v8::Object) \
|
||||
V(context, v8::Context) \
|
||||
V(domain_array, v8::Array) \
|
||||
|
@ -7,6 +7,7 @@
|
||||
#include "node_url.h"
|
||||
#include "util.h"
|
||||
#include "util-inl.h"
|
||||
#include "node_internals.h"
|
||||
|
||||
namespace node {
|
||||
namespace loader {
|
||||
@ -523,5 +524,5 @@ void ModuleWrap::Initialize(Local<Object> target,
|
||||
} // namespace loader
|
||||
} // namespace node
|
||||
|
||||
NODE_MODULE_CONTEXT_AWARE_BUILTIN(module_wrap,
|
||||
node::loader::ModuleWrap::Initialize)
|
||||
NODE_MODULE_CONTEXT_AWARE_INTERNAL(module_wrap,
|
||||
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 node_module* modpending;
|
||||
static node_module* modlist_builtin;
|
||||
static node_module* modlist_internal;
|
||||
static node_module* modlist_linked;
|
||||
static node_module* modlist_addon;
|
||||
static bool trace_enabled = false;
|
||||
@ -2574,6 +2575,9 @@ extern "C" void node_module_register(void* m) {
|
||||
if (mp->nm_flags & NM_F_BUILTIN) {
|
||||
mp->nm_link = modlist_builtin;
|
||||
modlist_builtin = mp;
|
||||
} else if (mp->nm_flags & NM_F_INTERNAL) {
|
||||
mp->nm_link = modlist_internal;
|
||||
modlist_internal = mp;
|
||||
} else if (!node_is_initialized) {
|
||||
// "Linked" modules are included as part of the node project.
|
||||
// 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;
|
||||
|
||||
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)
|
||||
break;
|
||||
}
|
||||
|
||||
CHECK(mp == nullptr || (mp->nm_flags & NM_F_BUILTIN) != 0);
|
||||
return (mp);
|
||||
CHECK(mp == nullptr || (mp->nm_flags & flag) != 0);
|
||||
return mp;
|
||||
}
|
||||
|
||||
struct node_module* get_linked_module(const char* name) {
|
||||
struct node_module* mp;
|
||||
|
||||
for (mp = modlist_linked; mp != nullptr; mp = mp->nm_link) {
|
||||
if (strcmp(mp->nm_modname, name) == 0)
|
||||
break;
|
||||
}
|
||||
|
||||
CHECK(mp == nullptr || (mp->nm_flags & NM_F_LINKED) != 0);
|
||||
return mp;
|
||||
node_module* get_builtin_module(const char* name) {
|
||||
return FindModule(modlist_builtin, name, NM_F_BUILTIN);
|
||||
}
|
||||
node_module* get_internal_module(const char* name) {
|
||||
return FindModule(modlist_internal, name, NM_F_INTERNAL);
|
||||
}
|
||||
node_module* get_linked_module(const char* name) {
|
||||
return FindModule(modlist_linked, name, NM_F_LINKED);
|
||||
}
|
||||
|
||||
struct DLib {
|
||||
@ -2880,24 +2884,60 @@ void ProcessEmitWarning(Environment* env, const char* fmt, ...) {
|
||||
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) {
|
||||
Environment* env = Environment::GetCurrent(args);
|
||||
|
||||
Local<String> module = args[0]->ToString(env->isolate());
|
||||
node::Utf8Value module_v(env->isolate(), module);
|
||||
Local<String> module;
|
||||
if (!args[0]->ToString(env->context()).ToLocal(&module)) return;
|
||||
|
||||
Local<Object> cache = env->binding_cache_object();
|
||||
Local<Object> exports;
|
||||
|
||||
if (cache->Has(env->context(), module).FromJust()) {
|
||||
exports = cache->Get(module)->ToObject(env->isolate());
|
||||
args.GetReturnValue().Set(exports);
|
||||
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), "Binding %s", *module_v);
|
||||
|
||||
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));
|
||||
|
||||
node_module* mod = get_builtin_module(*module_v);
|
||||
Local<Object> exports;
|
||||
if (mod != nullptr) {
|
||||
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);
|
||||
cache->Set(module, exports);
|
||||
exports = InitModule(env, mod, module);
|
||||
} else if (!strcmp(*module_v, "constants")) {
|
||||
exports = Object::New(env->isolate());
|
||||
CHECK(exports->SetPrototype(env->context(),
|
||||
Null(env->isolate())).FromJust());
|
||||
DefineConstants(env->isolate(), exports);
|
||||
cache->Set(module, exports);
|
||||
} else if (!strcmp(*module_v, "natives")) {
|
||||
exports = Object::New(env->isolate());
|
||||
DefineJavaScript(env, exports);
|
||||
cache->Set(module, exports);
|
||||
} else {
|
||||
char errmsg[1024];
|
||||
snprintf(errmsg,
|
||||
sizeof(errmsg),
|
||||
"No such module: %s",
|
||||
*module_v);
|
||||
return env->ThrowError(errmsg);
|
||||
return ThrowIfNoSuchModule(env, *module_v);
|
||||
}
|
||||
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);
|
||||
}
|
||||
@ -2939,7 +2995,8 @@ static void Binding(const FunctionCallbackInfo<Value>& args) {
|
||||
static void LinkedBinding(const FunctionCallbackInfo<Value>& args) {
|
||||
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<Value> exports_v = cache->Get(module_name);
|
||||
@ -3674,6 +3731,7 @@ void SetupProcessObject(Environment* env,
|
||||
|
||||
env->SetMethod(process, "binding", Binding);
|
||||
env->SetMethod(process, "_linkedBinding", LinkedBinding);
|
||||
env->SetMethod(process, "_internalBinding", InternalBinding);
|
||||
|
||||
env->SetMethod(process, "_setupProcessObject", SetupProcessObject);
|
||||
env->SetMethod(process, "_setupNextTick", SetupNextTick);
|
||||
|
@ -421,8 +421,9 @@ typedef void (*addon_context_register_func)(
|
||||
v8::Local<v8::Context> context,
|
||||
void* priv);
|
||||
|
||||
#define NM_F_BUILTIN 0x01
|
||||
#define NM_F_LINKED 0x02
|
||||
#define NM_F_BUILTIN 0x01
|
||||
#define NM_F_LINKED 0x02
|
||||
#define NM_F_INTERNAL 0x04
|
||||
|
||||
struct node_module {
|
||||
int nm_version;
|
||||
@ -436,9 +437,6 @@ struct node_module {
|
||||
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);
|
||||
|
||||
#ifdef _WIN32
|
||||
|
@ -324,6 +324,9 @@ class InternalCallbackScope {
|
||||
bool closed_ = false;
|
||||
};
|
||||
|
||||
#define NODE_MODULE_CONTEXT_AWARE_INTERNAL(modname, regfunc) \
|
||||
NODE_MODULE_CONTEXT_AWARE_X(modname, regfunc, NULL, NM_F_INTERNAL) \
|
||||
|
||||
} // 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