Skip to content

Commit a9dd03b

Browse files
d3lmbmeck
authored andcommitted
src: add option to disable loading native addons
PR-URL: #39977 Reviewed-By: Anna Henningsen <[email protected]> Reviewed-By: Bradley Farias <[email protected]> Reviewed-By: Guy Bedford <[email protected]> Reviewed-By: Michael Dawson <[email protected]>
1 parent a42bd7e commit a9dd03b

File tree

23 files changed

+272
-9
lines changed

23 files changed

+272
-9
lines changed

doc/api/cli.md

Lines changed: 10 additions & 0 deletions
Original file line numberDiff line numberDiff line change
@@ -598,6 +598,15 @@ added: v7.10.0
598598

599599
This option is a no-op. It is kept for compatibility.
600600

601+
### `--no-addons`
602+
<!-- YAML
603+
added: REPLACEME
604+
-->
605+
606+
Disable the `node-addons` exports condition as well as disable loading
607+
native addons. When `--no-addons` is specified, calling `process.dlopen` or
608+
requiring a native C++ addon will fail and throw an exception.
609+
601610
### `--no-deprecation`
602611
<!-- YAML
603612
added: v0.8.0
@@ -1428,6 +1437,7 @@ Node.js options that are allowed are:
14281437
* `--inspect`
14291438
* `--max-http-header-size`
14301439
* `--napi-modules`
1440+
* `--no-addons`
14311441
* `--no-deprecation`
14321442
* `--no-experimental-repl-await`
14331443
* `--no-extra-info-on-fatal-exception`

doc/api/errors.md

Lines changed: 9 additions & 0 deletions
Original file line numberDiff line numberDiff line change
@@ -1027,6 +1027,14 @@ added:
10271027

10281028
The [debugger][] timed out waiting for the required host/port to be free.
10291029

1030+
<a id="ERR_DLOPEN_DISABLED"></a>
1031+
### `ERR_DLOPEN_DISABLED`
1032+
<!-- YAML
1033+
added: REPLACEME
1034+
-->
1035+
1036+
Loading native addons has been disabled using [`--no-addons`][].
1037+
10301038
<a id="ERR_DLOPEN_FAILED"></a>
10311039
### `ERR_DLOPEN_FAILED`
10321040
<!-- YAML
@@ -2879,6 +2887,7 @@ The native call from `process.cpuUsage` could not be processed.
28792887
[`'uncaughtException'`]: process.md#event-uncaughtexception
28802888
[`--disable-proto=throw`]: cli.md#--disable-protomode
28812889
[`--force-fips`]: cli.md#--force-fips
2890+
[`--no-addons`]: cli.md#--no-addons
28822891
[`Class: assert.AssertionError`]: assert.md#class-assertassertionerror
28832892
[`ERR_INVALID_ARG_TYPE`]: #err_invalid_arg_type
28842893
[`ERR_MISSING_MESSAGE_PORT_IN_TRANSFER_LIST`]: #err_missing_message_port_in_transfer_list

doc/api/packages.md

Lines changed: 16 additions & 4 deletions
Original file line numberDiff line numberDiff line change
@@ -537,6 +537,11 @@ Node.js implements the following conditions:
537537
* `"node"` - matches for any Node.js environment. Can be a CommonJS or ES
538538
module file. _This condition should always come after `"import"` or
539539
`"require"`._
540+
* `"node-addons"` - similar to `"node"` and matches for any Node.js environment.
541+
This condition can be used to provide an entry point which uses native C++
542+
addons as opposed to an entry point which is more universal and doesn't rely
543+
on native addons. This condition can be disabled via the
544+
[`--no-addons` flag][].
540545
* `"default"` - the generic fallback that always matches. Can be a CommonJS
541546
or ES module file. _This condition should always come last._
542547

@@ -615,17 +620,23 @@ node --conditions=development main.js
615620
```
616621

617622
which would then resolve the `"development"` condition in package imports and
618-
exports, while resolving the existing `"node"`, `"default"`, `"import"`, and
619-
`"require"` conditions as appropriate.
623+
exports, while resolving the existing `"node"`, `"node-addons"`, `"default"`,
624+
`"import"`, and `"require"` conditions as appropriate.
620625

621626
Any number of custom conditions can be set with repeat flags.
622627

623628
### Conditions Definitions
624629

625-
The `"import"`, `"require"`, `"node"` and `"default"` conditions are defined
626-
and implemented in Node.js core,
630+
The `"import"`, `"require"`, `"node"`, `"node-addons"` and `"default"`
631+
conditions are defined and implemented in Node.js core,
627632
[as specified above](#conditional-exports).
628633

634+
The `"node-addons"` condition can be used to provide an entry point which
635+
uses native C++ addons. However, this condition can be disabled via the
636+
[`--no-addons` flag][]. When using `"node-addons"`, it's recommended to treat
637+
`"default"` as an enhancement that provides a more universal entry point, e.g.
638+
using WebAssembly instead of a native addon.
639+
629640
Other condition strings are unknown to Node.js and thus ignored by default.
630641
Runtimes or tools other than Node.js can use them at their discretion.
631642

@@ -1249,6 +1260,7 @@ This field defines [subpath imports][] for the current package.
12491260
[`"packageManager"`]: #packagemanager
12501261
[`"type"`]: #type
12511262
[`--conditions` flag]: #resolving-user-conditions
1263+
[`--no-addons` flag]: cli.md#--no-addons
12521264
[`ERR_PACKAGE_PATH_NOT_EXPORTED`]: errors.md#err_package_path_not_exported
12531265
[`esm`]: https://github.com/standard-things/esm#readme
12541266
[`package.json`]: #nodejs-packagejson-field-definitions

doc/node.1

Lines changed: 5 additions & 0 deletions
Original file line numberDiff line numberDiff line change
@@ -280,6 +280,11 @@ Hide extra information on fatal exception that causes exit.
280280
Disable runtime checks for `async_hooks`.
281281
These will still be enabled dynamically when `async_hooks` is enabled.
282282
.
283+
.It Fl -no-addons
284+
Disable the `node-addons` exports condition as well as disable loading native
285+
addons. When `--no-addons` is specified, calling `process.dlopen` or requiring
286+
a native C++ addon will fail and throw an exception.
287+
.
283288
.It Fl -no-warnings
284289
Silence all process warnings (including deprecations).
285290
.

lib/internal/modules/cjs/helpers.js

Lines changed: 9 additions & 1 deletion
Original file line numberDiff line numberDiff line change
@@ -30,8 +30,16 @@ let debug = require('internal/util/debuglog').debuglog('module', (fn) => {
3030
debug = fn;
3131
});
3232

33+
const noAddons = getOptionValue('--no-addons');
34+
const addonConditions = noAddons ? [] : ['node-addons'];
35+
3336
// TODO: Use this set when resolving pkg#exports conditions in loader.js.
34-
const cjsConditions = new SafeSet(['require', 'node', ...userConditions]);
37+
const cjsConditions = new SafeSet([
38+
'require',
39+
'node',
40+
...addonConditions,
41+
...userConditions,
42+
]);
3543

3644
function loadNativeModule(filename, request) {
3745
const mod = NativeModule.map.get(filename);

lib/internal/modules/esm/resolve.js

Lines changed: 10 additions & 1 deletion
Original file line numberDiff line numberDiff line change
@@ -58,7 +58,16 @@ const { Module: CJSModule } = require('internal/modules/cjs/loader');
5858

5959
const packageJsonReader = require('internal/modules/package_json_reader');
6060
const userConditions = getOptionValue('--conditions');
61-
const DEFAULT_CONDITIONS = ObjectFreeze(['node', 'import', ...userConditions]);
61+
const noAddons = getOptionValue('--no-addons');
62+
const addonConditions = noAddons ? [] : ['node-addons'];
63+
64+
const DEFAULT_CONDITIONS = ObjectFreeze([
65+
'node',
66+
'import',
67+
...addonConditions,
68+
...userConditions,
69+
]);
70+
6271
const DEFAULT_CONDITIONS_SET = new SafeSet(DEFAULT_CONDITIONS);
6372

6473
/**

src/env-inl.h

Lines changed: 5 additions & 0 deletions
Original file line numberDiff line numberDiff line change
@@ -861,6 +861,11 @@ inline bool Environment::is_main_thread() const {
861861
return worker_context() == nullptr;
862862
}
863863

864+
inline bool Environment::no_native_addons() const {
865+
return (flags_ & EnvironmentFlags::kNoNativeAddons) ||
866+
!options_->allow_native_addons;
867+
}
868+
864869
inline bool Environment::should_not_register_esm_loader() const {
865870
return flags_ & EnvironmentFlags::kNoRegisterESMLoader;
866871
}

src/env.h

Lines changed: 1 addition & 0 deletions
Original file line numberDiff line numberDiff line change
@@ -1197,6 +1197,7 @@ class Environment : public MemoryRetainer {
11971197
inline void set_has_serialized_options(bool has_serialized_options);
11981198

11991199
inline bool is_main_thread() const;
1200+
inline bool no_native_addons() const;
12001201
inline bool should_not_register_esm_loader() const;
12011202
inline bool owns_process_state() const;
12021203
inline bool owns_inspector() const;

src/node.h

Lines changed: 7 additions & 1 deletion
Original file line numberDiff line numberDiff line change
@@ -407,7 +407,13 @@ enum Flags : uint64_t {
407407
// Set this flag to force hiding console windows when spawning child
408408
// processes. This is usually used when embedding Node.js in GUI programs on
409409
// Windows.
410-
kHideConsoleWindows = 1 << 5
410+
kHideConsoleWindows = 1 << 5,
411+
// Set this flag to disable loading native addons via `process.dlopen`.
412+
// This environment flag is especially important for worker threads
413+
// so that a worker thread can't load a native addon even if `execArgv`
414+
// is overwritten and `--no-addons` is not specified but was specified
415+
// for this Environment instance.
416+
kNoNativeAddons = 1 << 6
411417
};
412418
} // namespace EnvironmentFlags
413419

src/node_binding.cc

Lines changed: 6 additions & 0 deletions
Original file line numberDiff line numberDiff line change
@@ -415,6 +415,12 @@ inline napi_addon_register_func GetNapiInitializerCallback(DLib* dlib) {
415415
// cache that's a plain C list or hash table that's shared across contexts?
416416
void DLOpen(const FunctionCallbackInfo<Value>& args) {
417417
Environment* env = Environment::GetCurrent(args);
418+
419+
if (env->no_native_addons()) {
420+
return THROW_ERR_DLOPEN_DISABLED(
421+
env, "Cannot load native addon because loading addons is disabled.");
422+
}
423+
418424
auto context = env->context();
419425

420426
CHECK_NULL(thread_local_modpending);

0 commit comments

Comments
 (0)