Skip to content

Commit d331383

Browse files
committed
crypto: ignore directory paths in OPENSSL_CONF
Signed-off-by: haramjeong <04harams77@gmail.com>
1 parent c8d5b39 commit d331383

1 file changed

Lines changed: 152 additions & 46 deletions

File tree

src/node.cc

Lines changed: 152 additions & 46 deletions
Original file line numberDiff line numberDiff line change
@@ -73,7 +73,7 @@
7373
#endif
7474

7575
#ifdef NODE_ENABLE_VTUNE_PROFILING
76-
#include "../deps/v8/src/third_party/vtune/v8-vtune.h"
76+
#include "../deps/v8/third_party/vtune/v8-vtune.h"
7777
#endif
7878

7979
#include "large_pages/node_large_page.h"
@@ -96,6 +96,7 @@
9696
// ========== global C headers ==========
9797

9898
#include <fcntl.h> // _O_RDWR
99+
#include <sys/stat.h>
99100
#include <sys/types.h>
100101

101102
#if defined(NODE_HAVE_I18N_SUPPORT)
@@ -255,37 +256,33 @@ MaybeLocal<Value> StartExecution(Environment* env, const char* main_script_id) {
255256
}
256257

257258
// Convert the result returned by an intermediate main script into
258-
// StartExecutionCallbackInfo. Currently the result is an array containing
259-
// [process, requireFunction, cjsRunner]
260-
std::optional<StartExecutionCallbackInfo> CallbackInfoFromArray(
261-
Local<Context> context, Local<Value> result) {
259+
// StartExecutionCallbackInfoWithModule. Currently the result is an array
260+
// containing [process, requireFunction, runModule].
261+
std::optional<StartExecutionCallbackInfoWithModule> CallbackInfoFromArray(
262+
Environment* env, Local<Value> result) {
262263
CHECK(result->IsArray());
263264
Local<Array> args = result.As<Array>();
264265
CHECK_EQ(args->Length(), 3);
265-
Local<Value> process_obj, require_fn, runcjs_fn;
266+
Local<Value> process_obj, require_fn, run_module;
267+
Local<Context> context = env->context();
266268
if (!args->Get(context, 0).ToLocal(&process_obj) ||
267269
!args->Get(context, 1).ToLocal(&require_fn) ||
268-
!args->Get(context, 2).ToLocal(&runcjs_fn)) {
270+
!args->Get(context, 2).ToLocal(&run_module)) {
269271
return std::nullopt;
270272
}
271273
CHECK(process_obj->IsObject());
272274
CHECK(require_fn->IsFunction());
273-
CHECK(runcjs_fn->IsFunction());
274-
// TODO(joyeecheung): some support for running ESM as an entrypoint
275-
// is needed. The simplest API would be to add a run_esm to
276-
// StartExecutionCallbackInfo which compiles, links (to builtins)
277-
// and evaluates a SourceTextModule.
278-
// TODO(joyeecheung): the env pointer should be part of
279-
// StartExecutionCallbackInfo, otherwise embedders are forced to use
280-
// lambdas to pass it into the callback, which can make the code
281-
// difficult to read.
282-
node::StartExecutionCallbackInfo info{process_obj.As<Object>(),
283-
require_fn.As<Function>(),
284-
runcjs_fn.As<Function>()};
275+
CHECK(run_module->IsFunction());
276+
StartExecutionCallbackInfoWithModule info;
277+
info.set_env(env);
278+
info.set_process_object(process_obj.As<Object>());
279+
info.set_native_require(require_fn.As<Function>());
280+
info.set_run_module(run_module.As<Function>());
285281
return info;
286282
}
287283

288-
MaybeLocal<Value> StartExecution(Environment* env, StartExecutionCallback cb) {
284+
MaybeLocal<Value> StartExecution(Environment* env,
285+
StartExecutionCallbackWithModule cb) {
289286
InternalCallbackScope callback_scope(
290287
env,
291288
Object::New(env->isolate()),
@@ -294,7 +291,7 @@ MaybeLocal<Value> StartExecution(Environment* env, StartExecutionCallback cb) {
294291

295292
// Only snapshot builder or embedder applications set the
296293
// callback.
297-
if (cb != nullptr) {
294+
if (cb) {
298295
EscapableHandleScope scope(env->isolate());
299296

300297
Local<Value> result;
@@ -308,9 +305,9 @@ MaybeLocal<Value> StartExecution(Environment* env, StartExecutionCallback cb) {
308305
}
309306
}
310307

311-
auto info = CallbackInfoFromArray(env->context(), result);
308+
auto info = CallbackInfoFromArray(env, result);
312309
if (!info.has_value()) {
313-
MaybeLocal<Value>();
310+
return MaybeLocal<Value>();
314311
}
315312
#if HAVE_INSPECTOR
316313
if (env->options()->debug_options().break_first_line) {
@@ -765,6 +762,13 @@ static ExitCode ProcessGlobalArgsInternal(std::vector<std::string>* args,
765762
v8_args.emplace_back("--harmony-import-attributes");
766763
}
767764

765+
if (!per_process::cli_options->per_isolate->max_old_space_size_percentage
766+
.empty()) {
767+
v8_args.emplace_back(
768+
"--max_old_space_size=" +
769+
per_process::cli_options->per_isolate->max_old_space_size);
770+
}
771+
768772
auto env_opts = per_process::cli_options->per_isolate->per_env;
769773
if (std::ranges::find(v8_args, "--abort-on-uncaught-exception") !=
770774
v8_args.end() ||
@@ -773,7 +777,8 @@ static ExitCode ProcessGlobalArgsInternal(std::vector<std::string>* args,
773777
env_opts->abort_on_uncaught_exception = true;
774778
}
775779

776-
if (env_opts->experimental_wasm_modules) {
780+
if (std::ranges::find(v8_args, "--no-js-source-phase-imports") ==
781+
v8_args.end()) {
777782
v8_args.emplace_back("--js-source-phase-imports");
778783
}
779784

@@ -866,6 +871,7 @@ static ExitCode InitializeNodeWithArgsInternal(
866871
HandleEnvOptions(per_process::cli_options->per_isolate->per_env);
867872

868873
std::string node_options;
874+
std::string node_options_from_dotenv;
869875
auto env_files = node::Dotenv::GetDataFromArgs(*argv);
870876

871877
if (!env_files.empty()) {
@@ -892,19 +898,26 @@ static ExitCode InitializeNodeWithArgsInternal(
892898
}
893899
}
894900

895-
per_process::dotenv_file.AssignNodeOptionsIfAvailable(&node_options);
901+
per_process::dotenv_file.AssignNodeOptionsIfAvailable(
902+
&node_options_from_dotenv);
896903
}
897904

898905
std::string node_options_from_config;
899-
if (auto path = per_process::config_reader.GetDataFromArgs(*argv)) {
900-
switch (per_process::config_reader.ParseConfig(*path)) {
906+
auto config_path = per_process::config_reader.GetDataFromArgs(argv);
907+
if (per_process::config_reader.HasInvalidDefaultConfigFileArgument()) {
908+
errors->push_back("--experimental-default-config-file does not take an "
909+
"argument");
910+
return ExitCode::kInvalidCommandLineArgument;
911+
}
912+
if (config_path) {
913+
switch (per_process::config_reader.ParseConfig(*config_path)) {
901914
case ParseResult::Valid:
902915
break;
903916
case ParseResult::InvalidContent:
904-
errors->push_back(std::string(*path) + ": invalid content");
917+
errors->push_back(std::string(*config_path) + ": invalid content");
905918
break;
906919
case ParseResult::FileError:
907-
errors->push_back(std::string(*path) + ": not found");
920+
errors->push_back(std::string(*config_path) + ": not found");
908921
break;
909922
default:
910923
UNREACHABLE();
@@ -922,11 +935,22 @@ static ExitCode InitializeNodeWithArgsInternal(
922935
errors->emplace_back("The number of NODE_OPTIONS doesn't match "
923936
"the number of flags in the config file");
924937
}
925-
node_options += node_options_from_config;
926938
}
927939

940+
node_options = node_options_from_config + node_options_from_dotenv;
941+
928942
#if !defined(NODE_WITHOUT_NODE_OPTIONS)
929-
if (!(flags & ProcessInitializationFlags::kDisableNodeOptionsEnv)) {
943+
bool should_parse_node_options =
944+
!(flags & ProcessInitializationFlags::kDisableNodeOptionsEnv);
945+
#ifndef DISABLE_SINGLE_EXECUTABLE_APPLICATION
946+
if (sea::IsSingleExecutable()) {
947+
sea::SeaResource sea_resource = sea::FindSingleExecutableResource();
948+
if (sea_resource.exec_argv_extension != sea::SeaExecArgvExtension::kEnv) {
949+
should_parse_node_options = false;
950+
}
951+
}
952+
#endif
953+
if (should_parse_node_options) {
930954
// NODE_OPTIONS environment variable is preferred over the file one.
931955
if (credentials::SafeGetenv("NODE_OPTIONS", &node_options) ||
932956
!node_options.empty()) {
@@ -1032,6 +1056,45 @@ static ExitCode InitializeNodeWithArgsInternal(
10321056
return ExitCode::kNoFailure;
10331057
}
10341058

1059+
#if NODE_USE_V8_WASM_TRAP_HANDLER
1060+
bool CanEnableWebAssemblyTrapHandler() {
1061+
// On POSIX, the machine may have a limit on the amount of virtual memory
1062+
// available, if it's not enough to allocate at least one cage for WASM,
1063+
// then the trap-handler-based bound checks cannot be used.
1064+
#ifdef __POSIX__
1065+
struct rlimit lim;
1066+
if (getrlimit(RLIMIT_AS, &lim) != 0 || lim.rlim_cur == RLIM_INFINITY) {
1067+
// Can't get the limit or there's no limit, assume trap handler can be
1068+
// enabled.
1069+
return true;
1070+
}
1071+
uint64_t virtual_memory_available = static_cast<uint64_t>(lim.rlim_cur);
1072+
1073+
size_t byte_capacity = 64 * 1024; // 64KB, the minimum size of a WASM memory.
1074+
uint64_t cage_size_needed_32 = V8::GetWasmMemoryReservationSizeInBytes(
1075+
V8::WasmMemoryType::kMemory32, byte_capacity);
1076+
uint64_t cage_size_needed_64 = V8::GetWasmMemoryReservationSizeInBytes(
1077+
V8::WasmMemoryType::kMemory64, byte_capacity);
1078+
uint64_t cage_size_needed =
1079+
std::max(cage_size_needed_32, cage_size_needed_64);
1080+
bool can_enable = virtual_memory_available >= cage_size_needed;
1081+
per_process::Debug(DebugCategory::BOOTSTRAP,
1082+
"Virtual memory available: %" PRIu64 " bytes,\n"
1083+
"cage size needed for 32-bit: %" PRIu64 " bytes,\n"
1084+
"cage size needed for 64-bit: %" PRIu64 " bytes,\n"
1085+
"Can%senable WASM trap handler\n",
1086+
virtual_memory_available,
1087+
cage_size_needed_32,
1088+
cage_size_needed_64,
1089+
can_enable ? " " : " not ");
1090+
1091+
return can_enable;
1092+
#else
1093+
return true;
1094+
#endif // __POSIX__
1095+
}
1096+
#endif // NODE_USE_V8_WASM_TRAP_HANDLER
1097+
10351098
static std::shared_ptr<InitializationResultImpl>
10361099
InitializeOncePerProcessInternal(const std::vector<std::string>& args,
10371100
ProcessInitializationFlags::Flags flags =
@@ -1153,6 +1216,28 @@ InitializeOncePerProcessInternal(const std::vector<std::string>& args,
11531216
conf_file = per_process::cli_options->openssl_config.c_str();
11541217
}
11551218

1219+
// If the configured OpenSSL config file is actually a directory (for
1220+
// example when an application sets `OPENSSL_CONF` to a directory), OpenSSL
1221+
// may attempt to fopen() it which yields an error and causes startup to
1222+
// fail. Detect and ignore directory paths here and emit a warning so the
1223+
// process can continue using default OpenSSL config instead.
1224+
if (conf_file != nullptr) {
1225+
struct stat st;
1226+
if (stat(conf_file, &st) == 0) {
1227+
#if defined(S_ISDIR)
1228+
if (S_ISDIR(st.st_mode)) {
1229+
#else
1230+
if ((st.st_mode & S_IFMT) == S_IFDIR) {
1231+
#endif
1232+
std::string warning = "Warning: OPENSSL_CONF path is a directory; "
1233+
"ignoring: ";
1234+
warning += conf_file;
1235+
fprintf(stderr, "%s\n", warning.c_str());
1236+
conf_file = nullptr;
1237+
}
1238+
}
1239+
}
1240+
11561241
OPENSSL_INIT_SETTINGS* settings = OPENSSL_INIT_new();
11571242
OPENSSL_INIT_set_config_filename(settings, conf_file);
11581243
OPENSSL_INIT_set_config_appname(settings, conf_section_name);
@@ -1163,14 +1248,11 @@ InitializeOncePerProcessInternal(const std::vector<std::string>& args,
11631248
OPENSSL_INIT_free(settings);
11641249

11651250
if (ERR_peek_error() != 0) {
1166-
// XXX: ERR_GET_REASON does not return something that is
1167-
// useful as an exit code at all.
1168-
result->exit_code_ =
1169-
static_cast<ExitCode>(ERR_GET_REASON(ERR_peek_error()));
1170-
result->early_return_ = true;
1171-
result->errors_.emplace_back("OpenSSL configuration error:\n" +
1172-
GetOpenSSLErrorString());
1173-
return result;
1251+
std::string warning =
1252+
"Warning: OpenSSL configuration error:\n" + GetOpenSSLErrorString();
1253+
fprintf(stderr, "%s\n", warning.c_str());
1254+
1255+
ERR_clear_error();
11741256
}
11751257
#else // OPENSSL_VERSION_MAJOR < 3
11761258
if (FIPS_mode()) {
@@ -1207,7 +1289,7 @@ InitializeOncePerProcessInternal(const std::vector<std::string>& args,
12071289
}
12081290

12091291
if (!(flags & ProcessInitializationFlags::kNoInitializeNodeV8Platform)) {
1210-
uv_thread_setname("MainThread");
1292+
uv_thread_setname("node-MainThread");
12111293
per_process::v8_platform.Initialize(
12121294
static_cast<int>(per_process::cli_options->v8_thread_pool_size));
12131295
result->platform_ = per_process::v8_platform.Platform();
@@ -1234,7 +1316,9 @@ InitializeOncePerProcessInternal(const std::vector<std::string>& args,
12341316
bool use_wasm_trap_handler =
12351317
!per_process::cli_options->disable_wasm_trap_handler;
12361318
if (!(flags & ProcessInitializationFlags::kNoDefaultSignalHandling) &&
1237-
use_wasm_trap_handler) {
1319+
use_wasm_trap_handler && CanEnableWebAssemblyTrapHandler()) {
1320+
per_process::Debug(DebugCategory::BOOTSTRAP,
1321+
"Enabling WebAssembly trap handler for bounds checks\n");
12381322
#if defined(_WIN32)
12391323
constexpr ULONG first = TRUE;
12401324
per_process::old_vectored_exception_handler =
@@ -1347,6 +1431,21 @@ ExitCode GenerateAndWriteSnapshotData(const SnapshotData** snapshot_data_ptr,
13471431
DCHECK(snapshot_config.builder_script_path.has_value());
13481432
const std::string& builder_script =
13491433
snapshot_config.builder_script_path.value();
1434+
1435+
// For the special builder node:generate_default_snapshot_source, generate
1436+
// the snapshot as C++ source and write it to snapshot.cc (for testing).
1437+
if (builder_script == "node:generate_default_snapshot_source") {
1438+
// Reset to empty to generate from scratch.
1439+
snapshot_config.builder_script_path = {};
1440+
exit_code =
1441+
node::SnapshotBuilder::GenerateAsSource("snapshot.cc",
1442+
args_maybe_patched,
1443+
result->exec_args(),
1444+
snapshot_config,
1445+
true /* use_array_literals */);
1446+
return exit_code;
1447+
}
1448+
13501449
// node:embedded_snapshot_main indicates that we are using the
13511450
// embedded snapshot and we are not supposed to clean it up.
13521451
if (builder_script == "node:embedded_snapshot_main") {
@@ -1508,14 +1607,21 @@ static ExitCode StartInternal(int argc, char** argv) {
15081607
uv_loop_configure(uv_default_loop(), UV_METRICS_IDLE_TIME);
15091608
std::string sea_config = per_process::cli_options->experimental_sea_config;
15101609
if (!sea_config.empty()) {
1511-
#if !defined(DISABLE_SINGLE_EXECUTABLE_APPLICATION)
1512-
return sea::BuildSingleExecutableBlob(
1513-
sea_config, result->args(), result->exec_args());
1514-
#else
1610+
#if defined(DISABLE_SINGLE_EXECUTABLE_APPLICATION)
15151611
fprintf(stderr, "Single executable application is disabled.\n");
15161612
return ExitCode::kGenericUserError;
1517-
#endif // !defined(DISABLE_SINGLE_EXECUTABLE_APPLICATION)
1613+
#else
1614+
return sea::WriteSingleExecutableBlob(
1615+
sea_config, result->args(), result->exec_args());
1616+
#endif
15181617
}
1618+
1619+
sea_config = per_process::cli_options->build_sea;
1620+
if (!sea_config.empty()) {
1621+
return sea::BuildSingleExecutable(
1622+
sea_config, result->args(), result->exec_args());
1623+
}
1624+
15191625
// --build-snapshot indicates that we are in snapshot building mode.
15201626
if (per_process::cli_options->per_isolate->build_snapshot) {
15211627
if (per_process::cli_options->per_isolate->build_snapshot_config.empty() &&

0 commit comments

Comments
 (0)