// Copyright Joyent, Inc. and other Node contributors. // // Permission is hereby granted, free of charge, to any person obtaining a // copy of this software and associated documentation files (the // "Software"), to deal in the Software without restriction, including // without limitation the rights to use, copy, modify, merge, publish, // distribute, sublicense, and/or sell copies of the Software, and to permit // persons to whom the Software is furnished to do so, subject to the // following conditions: // // The above copyright notice and this permission notice shall be included // in all copies or substantial portions of the Software. // // THE SOFTWARE IS PROVIDED "AS IS", WITHOUT WARRANTY OF ANY KIND, EXPRESS // OR IMPLIED, INCLUDING BUT NOT LIMITED TO THE WARRANTIES OF // MERCHANTABILITY, FITNESS FOR A PARTICULAR PURPOSE AND NONINFRINGEMENT. IN // NO EVENT SHALL THE AUTHORS OR COPYRIGHT HOLDERS BE LIABLE FOR ANY CLAIM, // DAMAGES OR OTHER LIABILITY, WHETHER IN AN ACTION OF CONTRACT, TORT OR // OTHERWISE, ARISING FROM, OUT OF OR IN CONNECTION WITH THE SOFTWARE OR THE // USE OR OTHER DEALINGS IN THE SOFTWARE. #include "node_buffer.h" #include "node_constants.h" #include "node_context_data.h" #include "node_errors.h" #include "node_internals.h" #include "node_native_module.h" #include "node_perf.h" #include "node_platform.h" #include "node_revert.h" #include "node_version.h" #include "tracing/traced_value.h" #if HAVE_OPENSSL #include "node_crypto.h" #endif #if defined(NODE_HAVE_I18N_SUPPORT) #include "node_i18n.h" #endif #if HAVE_INSPECTOR #include "inspector_io.h" #endif #if defined HAVE_DTRACE || defined HAVE_ETW #include "node_dtrace.h" #endif #include "ares.h" #include "async_wrap-inl.h" #include "env-inl.h" #include "handle_wrap.h" #ifdef NODE_EXPERIMENTAL_HTTP # include "llhttp.h" #else /* !NODE_EXPERIMENTAL_HTTP */ # include "http_parser.h" #endif /* NODE_EXPERIMENTAL_HTTP */ #include "nghttp2/nghttp2ver.h" #include "req_wrap-inl.h" #include "string_bytes.h" #include "tracing/agent.h" #include "tracing/node_trace_writer.h" #include "util.h" #include "uv.h" #if NODE_USE_V8_PLATFORM #include "libplatform/libplatform.h" #endif // NODE_USE_V8_PLATFORM #include "v8-profiler.h" #include "zlib.h" #ifdef NODE_ENABLE_VTUNE_PROFILING #include "../deps/v8/src/third_party/vtune/v8-vtune.h" #endif #ifdef NODE_ENABLE_LARGE_CODE_PAGES #include "large_pages/node_large_page.h" #endif #include #include // _O_RDWR #include // PATH_MAX #include #include #include #include #include #include #include #if defined(NODE_HAVE_I18N_SUPPORT) #include #endif #if defined(LEAK_SANITIZER) #include #endif #if defined(_MSC_VER) #include #include #define umask _umask typedef int mode_t; #else #include #include // getrlimit, setrlimit #include // setuid, getuid #endif #if defined(__POSIX__) && !defined(__ANDROID__) && !defined(__CloudABI__) #include // getpwnam() #include // getgrnam() #endif #if defined(__POSIX__) #include #endif // This is used to load built-in modules. Instead of using // __attribute__((constructor)), we call the _register_ // function for each built-in modules explicitly in // node::RegisterBuiltinModules(). This is only forward declaration. // The definitions are in each module's implementation when calling // the NODE_BUILTIN_MODULE_CONTEXT_AWARE. #define V(modname) void _register_##modname(); NODE_BUILTIN_MODULES(V) #undef V namespace node { using native_module::NativeModuleLoader; using options_parser::kAllowedInEnvironment; using options_parser::kDisallowedInEnvironment; using v8::Array; using v8::Boolean; using v8::Context; using v8::DEFAULT; using v8::DontEnum; using v8::EscapableHandleScope; using v8::Exception; using v8::Function; using v8::FunctionCallbackInfo; using v8::HandleScope; using v8::Int32; using v8::Integer; using v8::Isolate; using v8::Just; using v8::Local; using v8::Locker; using v8::Maybe; using v8::MaybeLocal; using v8::Message; using v8::MicrotasksPolicy; using v8::NamedPropertyHandlerConfiguration; using v8::NewStringType; using v8::None; using v8::Nothing; using v8::Null; using v8::Object; using v8::ObjectTemplate; using v8::PropertyAttribute; using v8::ReadOnly; using v8::Script; using v8::ScriptOrigin; using v8::SealHandleScope; using v8::SideEffectType; using v8::String; using v8::TracingController; using v8::TryCatch; using v8::Undefined; using v8::V8; using v8::Value; static bool v8_is_profiling = false; static bool node_is_initialized = false; static uv_once_t init_modpending_once = UV_ONCE_INIT; static uv_key_t thread_local_modpending; static node_module* modlist_builtin; static node_module* modlist_internal; static node_module* modlist_linked; static node_module* modlist_addon; #ifdef NODE_EXPERIMENTAL_HTTP static const char llhttp_version[] = NODE_STRINGIFY(LLHTTP_VERSION_MAJOR) "." NODE_STRINGIFY(LLHTTP_VERSION_MINOR) "." NODE_STRINGIFY(LLHTTP_VERSION_PATCH); #else /* !NODE_EXPERIMENTAL_HTTP */ static const char http_parser_version[] = NODE_STRINGIFY(HTTP_PARSER_VERSION_MAJOR) "." NODE_STRINGIFY(HTTP_PARSER_VERSION_MINOR) "." NODE_STRINGIFY(HTTP_PARSER_VERSION_PATCH); #endif /* NODE_EXPERIMENTAL_HTTP */ // Bit flag used to track security reverts (see node_revert.h) unsigned int reverted = 0; bool v8_initialized = false; bool linux_at_secure = false; // process-relative uptime base, initialized at start-up double prog_start_time; Mutex per_process_opts_mutex; std::shared_ptr per_process_opts { new PerProcessOptions() }; NativeModuleLoader per_process_loader; static Mutex node_isolate_mutex; static Isolate* node_isolate; // Ensures that __metadata trace events are only emitted // when tracing is enabled. class NodeTraceStateObserver : public TracingController::TraceStateObserver { public: void OnTraceEnabled() override { char name_buffer[512]; if (uv_get_process_title(name_buffer, sizeof(name_buffer)) == 0) { // Only emit the metadata event if the title can be retrieved // successfully. Ignore it otherwise. TRACE_EVENT_METADATA1("__metadata", "process_name", "name", TRACE_STR_COPY(name_buffer)); } TRACE_EVENT_METADATA1("__metadata", "version", "node", NODE_VERSION_STRING); TRACE_EVENT_METADATA1("__metadata", "thread_name", "name", "JavaScriptMainThread"); auto trace_process = tracing::TracedValue::Create(); trace_process->BeginDictionary("versions"); #ifdef NODE_EXPERIMENTAL_HTTP trace_process->SetString("llhttp", llhttp_version); #else /* !NODE_EXPERIMENTAL_HTTP */ trace_process->SetString("http_parser", http_parser_version); #endif /* NODE_EXPERIMENTAL_HTTP */ const char node_napi_version[] = NODE_STRINGIFY(NAPI_VERSION); const char node_modules_version[] = NODE_STRINGIFY(NODE_MODULE_VERSION); trace_process->SetString("node", NODE_VERSION_STRING); trace_process->SetString("v8", V8::GetVersion()); trace_process->SetString("uv", uv_version_string()); trace_process->SetString("zlib", ZLIB_VERSION); trace_process->SetString("ares", ARES_VERSION_STR); trace_process->SetString("modules", node_modules_version); trace_process->SetString("nghttp2", NGHTTP2_VERSION); trace_process->SetString("napi", node_napi_version); #if HAVE_OPENSSL trace_process->SetString("openssl", crypto::GetOpenSSLVersion()); #endif trace_process->EndDictionary(); trace_process->SetString("arch", NODE_ARCH); trace_process->SetString("platform", NODE_PLATFORM); trace_process->BeginDictionary("release"); trace_process->SetString("name", NODE_RELEASE); #if NODE_VERSION_IS_LTS trace_process->SetString("lts", NODE_VERSION_LTS_CODENAME); #endif trace_process->EndDictionary(); TRACE_EVENT_METADATA1("__metadata", "node", "process", std::move(trace_process)); // This only runs the first time tracing is enabled controller_->RemoveTraceStateObserver(this); delete this; } void OnTraceDisabled() override { // Do nothing here. This should never be called because the // observer removes itself when OnTraceEnabled() is called. UNREACHABLE(); } explicit NodeTraceStateObserver(TracingController* controller) : controller_(controller) {} ~NodeTraceStateObserver() override {} private: TracingController* controller_; }; static struct { #if NODE_USE_V8_PLATFORM void Initialize(int thread_pool_size) { tracing_agent_.reset(new tracing::Agent()); node::tracing::TraceEventHelper::SetAgent(tracing_agent_.get()); auto controller = tracing_agent_->GetTracingController(); controller->AddTraceStateObserver(new NodeTraceStateObserver(controller)); StartTracingAgent(); // Tracing must be initialized before platform threads are created. platform_ = new NodePlatform(thread_pool_size, controller); V8::InitializePlatform(platform_); } void Dispose() { platform_->Shutdown(); delete platform_; platform_ = nullptr; // Destroy tracing after the platform (and platform threads) have been // stopped. tracing_agent_.reset(nullptr); } void DrainVMTasks(Isolate* isolate) { platform_->DrainTasks(isolate); } void CancelVMTasks(Isolate* isolate) { platform_->CancelPendingDelayedTasks(isolate); } #if HAVE_INSPECTOR bool StartInspector(Environment* env, const char* script_path, std::shared_ptr options) { // Inspector agent can't fail to start, but if it was configured to listen // right away on the websocket port and fails to bind/etc, this will return // false. return env->inspector_agent()->Start( script_path == nullptr ? "" : script_path, options, true); } bool InspectorStarted(Environment* env) { return env->inspector_agent()->IsListening(); } #endif // HAVE_INSPECTOR void StartTracingAgent() { if (per_process_opts->trace_event_categories.empty()) { tracing_file_writer_ = tracing_agent_->DefaultHandle(); } else { tracing_file_writer_ = tracing_agent_->AddClient( ParseCommaSeparatedSet(per_process_opts->trace_event_categories), std::unique_ptr( new tracing::NodeTraceWriter( per_process_opts->trace_event_file_pattern)), tracing::Agent::kUseDefaultCategories); } } void StopTracingAgent() { tracing_file_writer_.reset(); } tracing::AgentWriterHandle* GetTracingAgentWriter() { return &tracing_file_writer_; } NodePlatform* Platform() { return platform_; } std::unique_ptr tracing_agent_; tracing::AgentWriterHandle tracing_file_writer_; NodePlatform* platform_; #else // !NODE_USE_V8_PLATFORM void Initialize(int thread_pool_size) {} void Dispose() {} void DrainVMTasks(Isolate* isolate) {} void CancelVMTasks(Isolate* isolate) {} bool StartInspector(Environment* env, const char* script_path, const DebugOptions& options) { env->ThrowError("Node compiled with NODE_USE_V8_PLATFORM=0"); return true; } void StartTracingAgent() { if (!trace_enabled_categories.empty()) { fprintf(stderr, "Node compiled with NODE_USE_V8_PLATFORM=0, " "so event tracing is not available.\n"); } } void StopTracingAgent() {} tracing::AgentWriterHandle* GetTracingAgentWriter() { return nullptr; } NodePlatform* Platform() { return nullptr; } #endif // !NODE_USE_V8_PLATFORM #if !NODE_USE_V8_PLATFORM || !HAVE_INSPECTOR bool InspectorStarted(Environment* env) { return false; } #endif // !NODE_USE_V8_PLATFORM || !HAVE_INSPECTOR } v8_platform; tracing::AgentWriterHandle* GetTracingAgentWriter() { return v8_platform.GetTracingAgentWriter(); } #ifdef __POSIX__ static const unsigned kMaxSignal = 32; #endif const char* signo_string(int signo) { #define SIGNO_CASE(e) case e: return #e; switch (signo) { #ifdef SIGHUP SIGNO_CASE(SIGHUP); #endif #ifdef SIGINT SIGNO_CASE(SIGINT); #endif #ifdef SIGQUIT SIGNO_CASE(SIGQUIT); #endif #ifdef SIGILL SIGNO_CASE(SIGILL); #endif #ifdef SIGTRAP SIGNO_CASE(SIGTRAP); #endif #ifdef SIGABRT SIGNO_CASE(SIGABRT); #endif #ifdef SIGIOT # if SIGABRT != SIGIOT SIGNO_CASE(SIGIOT); # endif #endif #ifdef SIGBUS SIGNO_CASE(SIGBUS); #endif #ifdef SIGFPE SIGNO_CASE(SIGFPE); #endif #ifdef SIGKILL SIGNO_CASE(SIGKILL); #endif #ifdef SIGUSR1 SIGNO_CASE(SIGUSR1); #endif #ifdef SIGSEGV SIGNO_CASE(SIGSEGV); #endif #ifdef SIGUSR2 SIGNO_CASE(SIGUSR2); #endif #ifdef SIGPIPE SIGNO_CASE(SIGPIPE); #endif #ifdef SIGALRM SIGNO_CASE(SIGALRM); #endif SIGNO_CASE(SIGTERM); #ifdef SIGCHLD SIGNO_CASE(SIGCHLD); #endif #ifdef SIGSTKFLT SIGNO_CASE(SIGSTKFLT); #endif #ifdef SIGCONT SIGNO_CASE(SIGCONT); #endif #ifdef SIGSTOP SIGNO_CASE(SIGSTOP); #endif #ifdef SIGTSTP SIGNO_CASE(SIGTSTP); #endif #ifdef SIGBREAK SIGNO_CASE(SIGBREAK); #endif #ifdef SIGTTIN SIGNO_CASE(SIGTTIN); #endif #ifdef SIGTTOU SIGNO_CASE(SIGTTOU); #endif #ifdef SIGURG SIGNO_CASE(SIGURG); #endif #ifdef SIGXCPU SIGNO_CASE(SIGXCPU); #endif #ifdef SIGXFSZ SIGNO_CASE(SIGXFSZ); #endif #ifdef SIGVTALRM SIGNO_CASE(SIGVTALRM); #endif #ifdef SIGPROF SIGNO_CASE(SIGPROF); #endif #ifdef SIGWINCH SIGNO_CASE(SIGWINCH); #endif #ifdef SIGIO SIGNO_CASE(SIGIO); #endif #ifdef SIGPOLL # if SIGPOLL != SIGIO SIGNO_CASE(SIGPOLL); # endif #endif #ifdef SIGLOST # if SIGLOST != SIGABRT SIGNO_CASE(SIGLOST); # endif #endif #ifdef SIGPWR # if SIGPWR != SIGLOST SIGNO_CASE(SIGPWR); # endif #endif #ifdef SIGINFO # if !defined(SIGPWR) || SIGINFO != SIGPWR SIGNO_CASE(SIGINFO); # endif #endif #ifdef SIGSYS SIGNO_CASE(SIGSYS); #endif default: return ""; } } // Look up environment variable unless running as setuid root. bool SafeGetenv(const char* key, std::string* text) { #if !defined(__CloudABI__) && !defined(_WIN32) if (linux_at_secure || getuid() != geteuid() || getgid() != getegid()) goto fail; #endif { Mutex::ScopedLock lock(environ_mutex); if (const char* value = getenv(key)) { *text = value; return true; } } fail: text->clear(); return false; } void* ArrayBufferAllocator::Allocate(size_t size) { if (zero_fill_field_ || per_process_opts->zero_fill_all_buffers) return UncheckedCalloc(size); else return UncheckedMalloc(size); } namespace { bool ShouldAbortOnUncaughtException(Isolate* isolate) { HandleScope scope(isolate); Environment* env = Environment::GetCurrent(isolate); return env != nullptr && env->should_abort_on_uncaught_toggle()[0] && !env->inside_should_not_abort_on_uncaught_scope(); } } // anonymous namespace void AddPromiseHook(Isolate* isolate, promise_hook_func fn, void* arg) { Environment* env = Environment::GetCurrent(isolate); CHECK_NOT_NULL(env); env->AddPromiseHook(fn, arg); } void AddEnvironmentCleanupHook(Isolate* isolate, void (*fun)(void* arg), void* arg) { Environment* env = Environment::GetCurrent(isolate); CHECK_NOT_NULL(env); env->AddCleanupHook(fun, arg); } void RemoveEnvironmentCleanupHook(Isolate* isolate, void (*fun)(void* arg), void* arg) { Environment* env = Environment::GetCurrent(isolate); CHECK_NOT_NULL(env); env->RemoveCleanupHook(fun, arg); } MaybeLocal InternalMakeCallback(Environment* env, Local recv, const Local callback, int argc, Local argv[], async_context asyncContext) { CHECK(!recv.IsEmpty()); InternalCallbackScope scope(env, recv, asyncContext); if (scope.Failed()) { return MaybeLocal(); } Local domain_cb = env->domain_callback(); MaybeLocal ret; if (asyncContext.async_id != 0 || domain_cb.IsEmpty() || recv.IsEmpty()) { ret = callback->Call(env->context(), recv, argc, argv); } else { std::vector> args(1 + argc); args[0] = callback; std::copy(&argv[0], &argv[argc], args.begin() + 1); ret = domain_cb->Call(env->context(), recv, args.size(), &args[0]); } if (ret.IsEmpty()) { scope.MarkAsFailed(); return MaybeLocal(); } scope.Close(); if (scope.Failed()) { return MaybeLocal(); } return ret; } // Public MakeCallback()s MaybeLocal MakeCallback(Isolate* isolate, Local recv, const char* method, int argc, Local argv[], async_context asyncContext) { Local method_string = String::NewFromUtf8(isolate, method, NewStringType::kNormal) .ToLocalChecked(); return MakeCallback(isolate, recv, method_string, argc, argv, asyncContext); } MaybeLocal MakeCallback(Isolate* isolate, Local recv, Local symbol, int argc, Local argv[], async_context asyncContext) { Local callback_v = recv->Get(isolate->GetCurrentContext(), symbol).ToLocalChecked(); if (callback_v.IsEmpty()) return Local(); if (!callback_v->IsFunction()) return Local(); Local callback = callback_v.As(); return MakeCallback(isolate, recv, callback, argc, argv, asyncContext); } MaybeLocal MakeCallback(Isolate* isolate, Local recv, Local callback, int argc, Local argv[], async_context asyncContext) { // Observe the following two subtleties: // // 1. The environment is retrieved from the callback function's context. // 2. The context to enter is retrieved from the environment. // // Because of the AssignToContext() call in src/node_contextify.cc, // the two contexts need not be the same. Environment* env = Environment::GetCurrent(callback->CreationContext()); CHECK_NOT_NULL(env); Context::Scope context_scope(env->context()); MaybeLocal ret = InternalMakeCallback(env, recv, callback, argc, argv, asyncContext); if (ret.IsEmpty() && env->makecallback_depth() == 0) { // This is only for legacy compatiblity and we may want to look into // removing/adjusting it. return Undefined(env->isolate()); } return ret; } // Legacy MakeCallback()s Local MakeCallback(Isolate* isolate, Local recv, const char* method, int argc, Local* argv) { EscapableHandleScope handle_scope(isolate); return handle_scope.Escape( MakeCallback(isolate, recv, method, argc, argv, {0, 0}) .FromMaybe(Local())); } Local MakeCallback(Isolate* isolate, Local recv, Local symbol, int argc, Local* argv) { EscapableHandleScope handle_scope(isolate); return handle_scope.Escape( MakeCallback(isolate, recv, symbol, argc, argv, {0, 0}) .FromMaybe(Local())); } Local MakeCallback(Isolate* isolate, Local recv, Local callback, int argc, Local* argv) { EscapableHandleScope handle_scope(isolate); return handle_scope.Escape( MakeCallback(isolate, recv, callback, argc, argv, {0, 0}) .FromMaybe(Local())); } // Executes a str within the current v8 context. static MaybeLocal ExecuteString(Environment* env, Local source, Local filename) { EscapableHandleScope scope(env->isolate()); TryCatch try_catch(env->isolate()); // try_catch must be nonverbose to disable FatalException() handler, // we will handle exceptions ourself. try_catch.SetVerbose(false); ScriptOrigin origin(filename); MaybeLocal