Skip to content

Commit b050c65

Browse files
d3lmrichardlau
authored andcommitted
src: add option to disable loading native addons
Backport-PR-URL: #40094 PR-URL: #39977 Reviewed-By: Anna Henningsen <anna@addaleax.net> Reviewed-By: Bradley Farias <bradley.meck@gmail.com> Reviewed-By: Guy Bedford <guybedford@gmail.com> Reviewed-By: Michael Dawson <midawson@redhat.com>
1 parent 2755d39 commit b050c65

File tree

23 files changed

+274
-9
lines changed

23 files changed

+274
-9
lines changed

doc/api/cli.md

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

588588
This option is a no-op. It is kept for compatibility.
589589

590+
### `--no-addons`
591+
<!-- YAML
592+
added: REPLACEME
593+
-->
594+
595+
Disable the `node-addons` exports condition as well as disable loading
596+
native addons. When `--no-addons` is specified, calling `process.dlopen` or
597+
requiring a native C++ addon will fail and throw an exception.
598+
590599
### `--no-deprecation`
591600
<!-- YAML
592601
added: v0.8.0
@@ -1357,6 +1366,7 @@ Node.js options that are allowed are:
13571366
* `--inspect`
13581367
* `--max-http-header-size`
13591368
* `--napi-modules`
1369+
* `--no-addons`
13601370
* `--no-deprecation`
13611371
* `--no-force-async-hooks-checks`
13621372
* `--no-warnings`

doc/api/errors.md

Lines changed: 9 additions & 0 deletions
Original file line numberDiff line numberDiff line change
@@ -876,6 +876,14 @@ An unknown cipher was specified.
876876
An unknown Diffie-Hellman group name was given. See
877877
[`crypto.getDiffieHellman()`][] for a list of valid group names.
878878

879+
<a id="ERR_DLOPEN_DISABLED"></a>
880+
### `ERR_DLOPEN_DISABLED`
881+
<!-- YAML
882+
added: REPLACEME
883+
-->
884+
885+
Loading native addons has been disabled using [`--no-addons`][].
886+
879887
<a id="ERR_DLOPEN_FAILED"></a>
880888
### `ERR_DLOPEN_FAILED`
881889
<!-- YAML
@@ -2599,6 +2607,7 @@ closed.
25992607
[`'uncaughtException'`]: process.md#process_event_uncaughtexception
26002608
[`--disable-proto=throw`]: cli.md#cli_disable_proto_mode
26012609
[`--force-fips`]: cli.md#cli_force_fips
2610+
[`--no-addons`]: cli.md#cli_no_addons
26022611
[`Class: assert.AssertionError`]: assert.md#assert_class_assert_assertionerror
26032612
[`ERR_INVALID_ARG_TYPE`]: #ERR_INVALID_ARG_TYPE
26042613
[`EventEmitter`]: events.md#events_class_eventemitter

doc/api/packages.md

Lines changed: 16 additions & 4 deletions
Original file line numberDiff line numberDiff line change
@@ -477,6 +477,11 @@ Node.js implements the following conditions:
477477
* `"node"` - matches for any Node.js environment. Can be a CommonJS or ES
478478
module file. _This condition should always come after `"import"` or
479479
`"require"`._
480+
* `"node-addons"` - similar to `"node"` and matches for any Node.js environment.
481+
This condition can be used to provide an entry point which uses native C++
482+
addons as opposed to an entry point which is more universal and doesn't rely
483+
on native addons. This condition can be disabled via the
484+
[`--no-addons` flag][].
480485
* `"default"` - the generic fallback that always matches. Can be a CommonJS
481486
or ES module file. _This condition should always come last._
482487

@@ -555,17 +560,23 @@ node --conditions=development main.js
555560
```
556561

557562
which would then resolve the `"development"` condition in package imports and
558-
exports, while resolving the existing `"node"`, `"default"`, `"import"`, and
559-
`"require"` conditions as appropriate.
563+
exports, while resolving the existing `"node"`, `"node-addons"`, `"default"`,
564+
`"import"`, and `"require"` conditions as appropriate.
560565

561566
Any number of custom conditions can be set with repeat flags.
562567

563568
### Conditions Definitions
564569

565-
The `"import"`, `"require"`, `"node"` and `"default"` conditions are defined
566-
and implemented in Node.js core,
570+
The `"import"`, `"require"`, `"node"`, `"node-addons"` and `"default"`
571+
conditions are defined and implemented in Node.js core,
567572
[as specified above](#packages_conditional_exports).
568573

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

@@ -1161,6 +1172,7 @@ This field defines [subpath imports][] for the current package.
11611172
[`"main"`]: #packages_main
11621173
[`"name"`]: #packages_name
11631174
[`"type"`]: #packages_type
1175+
[`--no-addons` flag]: cli.md#cli_no_addons
11641176
[`ERR_PACKAGE_PATH_NOT_EXPORTED`]: errors.md#errors_err_package_path_not_exported
11651177
[`esm`]: https://github.com/standard-things/esm#readme
11661178
[`package.json`]: #packages_node_js_package_json_field_definitions

doc/node.1

Lines changed: 5 additions & 0 deletions
Original file line numberDiff line numberDiff line change
@@ -282,6 +282,11 @@ Silence deprecation warnings.
282282
Disable runtime checks for `async_hooks`.
283283
These will still be enabled dynamically when `async_hooks` is enabled.
284284
.
285+
.It Fl -no-addons
286+
Disable the `node-addons` exports condition as well as disable loading native
287+
addons. When `--no-addons` is specified, calling `process.dlopen` or requiring
288+
a native C++ addon will fail and throw an exception.
289+
.
285290
.It Fl -no-warnings
286291
Silence all process warnings (including deprecations).
287292
.

lib/internal/modules/cjs/helpers.js

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

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

3543
function loadNativeModule(filename, request) {
3644
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
@@ -57,7 +57,16 @@ const { Module: CJSModule } = require('internal/modules/cjs/loader');
5757

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

6372
const pendingDeprecation = getOptionValue('--pending-deprecation');

src/env-inl.h

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

893+
inline bool Environment::no_native_addons() const {
894+
return (flags_ & EnvironmentFlags::kNoNativeAddons) ||
895+
!options_->allow_native_addons;
896+
}
897+
893898
inline bool Environment::should_not_register_esm_loader() const {
894899
return flags_ & EnvironmentFlags::kNoRegisterESMLoader;
895900
}

src/env.h

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

10561056
inline bool is_main_thread() const;
1057+
inline bool no_native_addons() const;
10571058
inline bool should_not_register_esm_loader() const;
10581059
inline bool owns_process_state() const;
10591060
inline bool owns_inspector() const;

src/node.h

Lines changed: 7 additions & 1 deletion
Original file line numberDiff line numberDiff line change
@@ -428,7 +428,13 @@ enum Flags : uint64_t {
428428
// Set this flag to force hiding console windows when spawning child
429429
// processes. This is usually used when embedding Node.js in GUI programs on
430430
// Windows.
431-
kHideConsoleWindows = 1 << 5
431+
kHideConsoleWindows = 1 << 5,
432+
// Set this flag to disable loading native addons via `process.dlopen`.
433+
// This environment flag is especially important for worker threads
434+
// so that a worker thread can't load a native addon even if `execArgv`
435+
// is overwritten and `--no-addons` is not specified but was specified
436+
// for this Environment instance.
437+
kNoNativeAddons = 1 << 6
432438
};
433439
} // namespace EnvironmentFlags
434440

src/node_binding.cc

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

418424
CHECK_NULL(thread_local_modpending);

0 commit comments

Comments
 (0)