bootstrap: handle snapshot errors gracefully
This patch refactors the SnapshotBuilder::Generate() routines so that when running into errors during the snapshot building process, they can exit gracefully by printing the error and return a non-zero exit code. If the error is likely to be caused by internal scripts, the return code would be 12, if the error is caused by user scripts the return code would be 1. In addition this refactors the generation of embedded snapshots and directly writes to the output file stream instead of producing an intermediate string with string streams. PR-URL: https://github.com/nodejs/node/pull/43531 Refs: https://github.com/nodejs/node/issues/35711 Reviewed-By: Chengzhong Wu <legendecas@gmail.com> Reviewed-By: Darshan Sen <raisinten@gmail.com>
This commit is contained in:
parent
9bfabe8a79
commit
49bb1c6832
@ -3822,6 +3822,9 @@ cases:
|
|||||||
options were set, but the port number chosen was invalid or unavailable.
|
options were set, but the port number chosen was invalid or unavailable.
|
||||||
* `13` **Unfinished Top-Level Await**: `await` was used outside of a function
|
* `13` **Unfinished Top-Level Await**: `await` was used outside of a function
|
||||||
in the top-level code, but the passed `Promise` never resolved.
|
in the top-level code, but the passed `Promise` never resolved.
|
||||||
|
* `14` **Snapshot Failure**: Node.js was started to build a V8 startup
|
||||||
|
snapshot and it failed because certain requirements of the state of
|
||||||
|
the application were not met.
|
||||||
* `>128` **Signal Exits**: If Node.js receives a fatal signal such as
|
* `>128` **Signal Exits**: If Node.js receives a fatal signal such as
|
||||||
`SIGKILL` or `SIGHUP`, then its exit code will be `128` plus the
|
`SIGKILL` or `SIGHUP`, then its exit code will be `128` plus the
|
||||||
value of the signal code. This is a standard POSIX practice, since
|
value of the signal code. This is a standard POSIX practice, since
|
||||||
|
19
src/env.h
19
src/env.h
@ -963,13 +963,17 @@ struct EnvSerializeInfo {
|
|||||||
};
|
};
|
||||||
|
|
||||||
struct SnapshotData {
|
struct SnapshotData {
|
||||||
// The result of v8::SnapshotCreator::CreateBlob() during the snapshot
|
enum class DataOwnership { kOwned, kNotOwned };
|
||||||
// building process.
|
|
||||||
v8::StartupData v8_snapshot_blob_data;
|
|
||||||
|
|
||||||
static const size_t kNodeBaseContextIndex = 0;
|
static const size_t kNodeBaseContextIndex = 0;
|
||||||
static const size_t kNodeMainContextIndex = kNodeBaseContextIndex + 1;
|
static const size_t kNodeMainContextIndex = kNodeBaseContextIndex + 1;
|
||||||
|
|
||||||
|
DataOwnership data_ownership = DataOwnership::kOwned;
|
||||||
|
|
||||||
|
// The result of v8::SnapshotCreator::CreateBlob() during the snapshot
|
||||||
|
// building process.
|
||||||
|
v8::StartupData v8_snapshot_blob_data{nullptr, 0};
|
||||||
|
|
||||||
std::vector<size_t> isolate_data_indices;
|
std::vector<size_t> isolate_data_indices;
|
||||||
// TODO(joyeecheung): there should be a vector of env_info once we snapshot
|
// TODO(joyeecheung): there should be a vector of env_info once we snapshot
|
||||||
// the worker environments.
|
// the worker environments.
|
||||||
@ -979,6 +983,15 @@ struct SnapshotData {
|
|||||||
// read only space. We use native_module::CodeCacheInfo because
|
// read only space. We use native_module::CodeCacheInfo because
|
||||||
// v8::ScriptCompiler::CachedData is not copyable.
|
// v8::ScriptCompiler::CachedData is not copyable.
|
||||||
std::vector<native_module::CodeCacheInfo> code_cache;
|
std::vector<native_module::CodeCacheInfo> code_cache;
|
||||||
|
|
||||||
|
~SnapshotData();
|
||||||
|
|
||||||
|
SnapshotData(const SnapshotData&) = delete;
|
||||||
|
SnapshotData& operator=(const SnapshotData&) = delete;
|
||||||
|
SnapshotData(SnapshotData&&) = delete;
|
||||||
|
SnapshotData& operator=(SnapshotData&&) = delete;
|
||||||
|
|
||||||
|
SnapshotData() = default;
|
||||||
};
|
};
|
||||||
|
|
||||||
class Environment : public MemoryRetainer {
|
class Environment : public MemoryRetainer {
|
||||||
|
@ -15,13 +15,14 @@ struct SnapshotData;
|
|||||||
|
|
||||||
class NODE_EXTERN_PRIVATE SnapshotBuilder {
|
class NODE_EXTERN_PRIVATE SnapshotBuilder {
|
||||||
public:
|
public:
|
||||||
static std::string Generate(const std::vector<std::string> args,
|
static int Generate(std::ostream& out,
|
||||||
const std::vector<std::string> exec_args);
|
const std::vector<std::string> args,
|
||||||
|
const std::vector<std::string> exec_args);
|
||||||
|
|
||||||
// Generate the snapshot into out.
|
// Generate the snapshot into out.
|
||||||
static void Generate(SnapshotData* out,
|
static int Generate(SnapshotData* out,
|
||||||
const std::vector<std::string> args,
|
const std::vector<std::string> args,
|
||||||
const std::vector<std::string> exec_args);
|
const std::vector<std::string> exec_args);
|
||||||
|
|
||||||
// If nullptr is returned, the binary is not built with embedded
|
// If nullptr is returned, the binary is not built with embedded
|
||||||
// snapshot.
|
// snapshot.
|
||||||
|
@ -29,7 +29,6 @@ using v8::FunctionCallbackInfo;
|
|||||||
using v8::HandleScope;
|
using v8::HandleScope;
|
||||||
using v8::Isolate;
|
using v8::Isolate;
|
||||||
using v8::Local;
|
using v8::Local;
|
||||||
using v8::MaybeLocal;
|
|
||||||
using v8::Object;
|
using v8::Object;
|
||||||
using v8::ScriptCompiler;
|
using v8::ScriptCompiler;
|
||||||
using v8::ScriptOrigin;
|
using v8::ScriptOrigin;
|
||||||
@ -39,8 +38,15 @@ using v8::String;
|
|||||||
using v8::TryCatch;
|
using v8::TryCatch;
|
||||||
using v8::Value;
|
using v8::Value;
|
||||||
|
|
||||||
|
SnapshotData::~SnapshotData() {
|
||||||
|
if (data_ownership == DataOwnership::kOwned &&
|
||||||
|
v8_snapshot_blob_data.data != nullptr) {
|
||||||
|
delete[] v8_snapshot_blob_data.data;
|
||||||
|
}
|
||||||
|
}
|
||||||
|
|
||||||
template <typename T>
|
template <typename T>
|
||||||
void WriteVector(std::ostringstream* ss, const T* vec, size_t size) {
|
void WriteVector(std::ostream* ss, const T* vec, size_t size) {
|
||||||
for (size_t i = 0; i < size; i++) {
|
for (size_t i = 0; i < size; i++) {
|
||||||
*ss << std::to_string(vec[i]) << (i == size - 1 ? '\n' : ',');
|
*ss << std::to_string(vec[i]) << (i == size - 1 ? '\n' : ',');
|
||||||
}
|
}
|
||||||
@ -70,15 +76,14 @@ static std::string FormatSize(size_t size) {
|
|||||||
return buf;
|
return buf;
|
||||||
}
|
}
|
||||||
|
|
||||||
static void WriteStaticCodeCacheData(std::ostringstream* ss,
|
static void WriteStaticCodeCacheData(std::ostream* ss,
|
||||||
const native_module::CodeCacheInfo& info) {
|
const native_module::CodeCacheInfo& info) {
|
||||||
*ss << "static const uint8_t " << GetCodeCacheDefName(info.id) << "[] = {\n";
|
*ss << "static const uint8_t " << GetCodeCacheDefName(info.id) << "[] = {\n";
|
||||||
WriteVector(ss, info.data.data(), info.data.size());
|
WriteVector(ss, info.data.data(), info.data.size());
|
||||||
*ss << "};";
|
*ss << "};";
|
||||||
}
|
}
|
||||||
|
|
||||||
static void WriteCodeCacheInitializer(std::ostringstream* ss,
|
static void WriteCodeCacheInitializer(std::ostream* ss, const std::string& id) {
|
||||||
const std::string& id) {
|
|
||||||
std::string def_name = GetCodeCacheDefName(id);
|
std::string def_name = GetCodeCacheDefName(id);
|
||||||
*ss << " { \"" << id << "\",\n";
|
*ss << " { \"" << id << "\",\n";
|
||||||
*ss << " {" << def_name << ",\n";
|
*ss << " {" << def_name << ",\n";
|
||||||
@ -87,9 +92,7 @@ static void WriteCodeCacheInitializer(std::ostringstream* ss,
|
|||||||
*ss << " },\n";
|
*ss << " },\n";
|
||||||
}
|
}
|
||||||
|
|
||||||
std::string FormatBlob(SnapshotData* data) {
|
void FormatBlob(std::ostream& ss, SnapshotData* data) {
|
||||||
std::ostringstream ss;
|
|
||||||
|
|
||||||
ss << R"(#include <cstddef>
|
ss << R"(#include <cstddef>
|
||||||
#include "env.h"
|
#include "env.h"
|
||||||
#include "node_snapshot_builder.h"
|
#include "node_snapshot_builder.h"
|
||||||
@ -116,6 +119,9 @@ static const int v8_snapshot_blob_size = )"
|
|||||||
}
|
}
|
||||||
|
|
||||||
ss << R"(SnapshotData snapshot_data {
|
ss << R"(SnapshotData snapshot_data {
|
||||||
|
// -- data_ownership begins --
|
||||||
|
SnapshotData::DataOwnership::kNotOwned,
|
||||||
|
// -- data_ownership ends --
|
||||||
// -- v8_snapshot_blob_data begins --
|
// -- v8_snapshot_blob_data begins --
|
||||||
{ v8_snapshot_blob_data, v8_snapshot_blob_size },
|
{ v8_snapshot_blob_data, v8_snapshot_blob_size },
|
||||||
// -- v8_snapshot_blob_data ends --
|
// -- v8_snapshot_blob_data ends --
|
||||||
@ -148,8 +154,6 @@ const SnapshotData* SnapshotBuilder::GetEmbeddedSnapshotData() {
|
|||||||
}
|
}
|
||||||
} // namespace node
|
} // namespace node
|
||||||
)";
|
)";
|
||||||
|
|
||||||
return ss.str();
|
|
||||||
}
|
}
|
||||||
|
|
||||||
Mutex SnapshotBuilder::snapshot_data_mutex_;
|
Mutex SnapshotBuilder::snapshot_data_mutex_;
|
||||||
@ -166,175 +170,185 @@ void SnapshotBuilder::InitializeIsolateParams(const SnapshotData* data,
|
|||||||
const_cast<v8::StartupData*>(&(data->v8_snapshot_blob_data));
|
const_cast<v8::StartupData*>(&(data->v8_snapshot_blob_data));
|
||||||
}
|
}
|
||||||
|
|
||||||
void SnapshotBuilder::Generate(SnapshotData* out,
|
// TODO(joyeecheung): share these exit code constants across the code base.
|
||||||
const std::vector<std::string> args,
|
constexpr int UNCAUGHT_EXCEPTION_ERROR = 1;
|
||||||
const std::vector<std::string> exec_args) {
|
constexpr int BOOTSTRAP_ERROR = 10;
|
||||||
|
constexpr int SNAPSHOT_ERROR = 14;
|
||||||
|
|
||||||
|
int SnapshotBuilder::Generate(SnapshotData* out,
|
||||||
|
const std::vector<std::string> args,
|
||||||
|
const std::vector<std::string> exec_args) {
|
||||||
|
const std::vector<intptr_t>& external_references =
|
||||||
|
CollectExternalReferences();
|
||||||
Isolate* isolate = Isolate::Allocate();
|
Isolate* isolate = Isolate::Allocate();
|
||||||
isolate->SetCaptureStackTraceForUncaughtExceptions(
|
// Must be done before the SnapshotCreator creation so that the
|
||||||
true, 10, v8::StackTrace::StackTraceOptions::kDetailed);
|
// memory reducer can be initialized.
|
||||||
per_process::v8_platform.Platform()->RegisterIsolate(isolate,
|
per_process::v8_platform.Platform()->RegisterIsolate(isolate,
|
||||||
uv_default_loop());
|
uv_default_loop());
|
||||||
std::unique_ptr<NodeMainInstance> main_instance;
|
|
||||||
std::string result;
|
SnapshotCreator creator(isolate, external_references.data());
|
||||||
|
|
||||||
|
isolate->SetCaptureStackTraceForUncaughtExceptions(
|
||||||
|
true, 10, v8::StackTrace::StackTraceOptions::kDetailed);
|
||||||
|
|
||||||
|
Environment* env = nullptr;
|
||||||
|
std::unique_ptr<NodeMainInstance> main_instance =
|
||||||
|
NodeMainInstance::Create(isolate,
|
||||||
|
uv_default_loop(),
|
||||||
|
per_process::v8_platform.Platform(),
|
||||||
|
args,
|
||||||
|
exec_args);
|
||||||
|
|
||||||
|
// The cleanups should be done in case of an early exit due to errors.
|
||||||
|
auto cleanup = OnScopeLeave([&]() {
|
||||||
|
// Must be done while the snapshot creator isolate is entered i.e. the
|
||||||
|
// creator is still alive. The snapshot creator destructor will destroy
|
||||||
|
// the isolate.
|
||||||
|
if (env != nullptr) {
|
||||||
|
FreeEnvironment(env);
|
||||||
|
}
|
||||||
|
main_instance->Dispose();
|
||||||
|
per_process::v8_platform.Platform()->UnregisterIsolate(isolate);
|
||||||
|
});
|
||||||
|
|
||||||
{
|
{
|
||||||
const std::vector<intptr_t>& external_references =
|
HandleScope scope(isolate);
|
||||||
CollectExternalReferences();
|
TryCatch bootstrapCatch(isolate);
|
||||||
SnapshotCreator creator(isolate, external_references.data());
|
|
||||||
Environment* env;
|
auto print_Exception = OnScopeLeave([&]() {
|
||||||
|
if (bootstrapCatch.HasCaught()) {
|
||||||
|
PrintCaughtException(
|
||||||
|
isolate, isolate->GetCurrentContext(), bootstrapCatch);
|
||||||
|
}
|
||||||
|
});
|
||||||
|
|
||||||
|
out->isolate_data_indices =
|
||||||
|
main_instance->isolate_data()->Serialize(&creator);
|
||||||
|
|
||||||
|
// The default context with only things created by V8.
|
||||||
|
Local<Context> default_context = Context::New(isolate);
|
||||||
|
|
||||||
|
// The Node.js-specific context with primodials, can be used by workers
|
||||||
|
// TODO(joyeecheung): investigate if this can be used by vm contexts
|
||||||
|
// without breaking compatibility.
|
||||||
|
Local<Context> base_context = NewContext(isolate);
|
||||||
|
if (base_context.IsEmpty()) {
|
||||||
|
return BOOTSTRAP_ERROR;
|
||||||
|
}
|
||||||
|
|
||||||
|
Local<Context> main_context = NewContext(isolate);
|
||||||
|
if (main_context.IsEmpty()) {
|
||||||
|
return BOOTSTRAP_ERROR;
|
||||||
|
}
|
||||||
|
// Initialize the main instance context.
|
||||||
{
|
{
|
||||||
main_instance =
|
Context::Scope context_scope(main_context);
|
||||||
NodeMainInstance::Create(isolate,
|
|
||||||
uv_default_loop(),
|
|
||||||
per_process::v8_platform.Platform(),
|
|
||||||
args,
|
|
||||||
exec_args);
|
|
||||||
out->isolate_data_indices =
|
|
||||||
main_instance->isolate_data()->Serialize(&creator);
|
|
||||||
|
|
||||||
HandleScope scope(isolate);
|
// Create the environment.
|
||||||
|
env = new Environment(main_instance->isolate_data(),
|
||||||
|
main_context,
|
||||||
|
args,
|
||||||
|
exec_args,
|
||||||
|
nullptr,
|
||||||
|
node::EnvironmentFlags::kDefaultFlags,
|
||||||
|
{});
|
||||||
|
|
||||||
// The default context with only things created by V8.
|
// Run scripts in lib/internal/bootstrap/
|
||||||
creator.SetDefaultContext(Context::New(isolate));
|
if (env->RunBootstrapping().IsEmpty()) {
|
||||||
|
return BOOTSTRAP_ERROR;
|
||||||
auto CreateBaseContext = [&]() {
|
}
|
||||||
TryCatch bootstrapCatch(isolate);
|
// If --build-snapshot is true, lib/internal/main/mksnapshot.js would be
|
||||||
// Run the per-context scripts.
|
// loaded via LoadEnvironment() to execute process.argv[1] as the entry
|
||||||
Local<Context> base_context = NewContext(isolate);
|
// point (we currently only support this kind of entry point, but we
|
||||||
if (bootstrapCatch.HasCaught()) {
|
// could also explore snapshotting other kinds of execution modes
|
||||||
PrintCaughtException(isolate, base_context, bootstrapCatch);
|
// in the future).
|
||||||
abort();
|
if (per_process::cli_options->build_snapshot) {
|
||||||
|
#if HAVE_INSPECTOR
|
||||||
|
// TODO(joyeecheung): move this before RunBootstrapping().
|
||||||
|
env->InitializeInspector({});
|
||||||
|
#endif
|
||||||
|
if (LoadEnvironment(env, StartExecutionCallback{}).IsEmpty()) {
|
||||||
|
return UNCAUGHT_EXCEPTION_ERROR;
|
||||||
|
}
|
||||||
|
// FIXME(joyeecheung): right now running the loop in the snapshot
|
||||||
|
// builder seems to introduces inconsistencies in JS land that need to
|
||||||
|
// be synchronized again after snapshot restoration.
|
||||||
|
int exit_code = SpinEventLoop(env).FromMaybe(UNCAUGHT_EXCEPTION_ERROR);
|
||||||
|
if (exit_code != 0) {
|
||||||
|
return exit_code;
|
||||||
}
|
}
|
||||||
return base_context;
|
|
||||||
};
|
|
||||||
|
|
||||||
// The Node.js-specific context with primodials, can be used by workers
|
|
||||||
// TODO(joyeecheung): investigate if this can be used by vm contexts
|
|
||||||
// without breaking compatibility.
|
|
||||||
{
|
|
||||||
size_t index = creator.AddContext(CreateBaseContext());
|
|
||||||
CHECK_EQ(index, SnapshotData::kNodeBaseContextIndex);
|
|
||||||
}
|
}
|
||||||
|
|
||||||
// The main instance context.
|
if (per_process::enabled_debug_list.enabled(DebugCategory::MKSNAPSHOT)) {
|
||||||
{
|
env->PrintAllBaseObjects();
|
||||||
Local<Context> main_context = CreateBaseContext();
|
printf("Environment = %p\n", env);
|
||||||
Context::Scope context_scope(main_context);
|
}
|
||||||
TryCatch bootstrapCatch(isolate);
|
|
||||||
|
|
||||||
// Create the environment.
|
// Serialize the native states
|
||||||
env = new Environment(main_instance->isolate_data(),
|
out->env_info = env->Serialize(&creator);
|
||||||
main_context,
|
|
||||||
args,
|
|
||||||
exec_args,
|
|
||||||
nullptr,
|
|
||||||
node::EnvironmentFlags::kDefaultFlags,
|
|
||||||
{});
|
|
||||||
|
|
||||||
// Run scripts in lib/internal/bootstrap/
|
|
||||||
MaybeLocal<Value> result = env->RunBootstrapping();
|
|
||||||
if (bootstrapCatch.HasCaught()) {
|
|
||||||
// TODO(joyeecheung): fail by exiting with a non-zero exit code.
|
|
||||||
PrintCaughtException(isolate, main_context, bootstrapCatch);
|
|
||||||
abort();
|
|
||||||
}
|
|
||||||
result.ToLocalChecked();
|
|
||||||
// If --build-snapshot is true, lib/internal/main/mksnapshot.js would be
|
|
||||||
// loaded via LoadEnvironment() to execute process.argv[1] as the entry
|
|
||||||
// point (we currently only support this kind of entry point, but we
|
|
||||||
// could also explore snapshotting other kinds of execution modes
|
|
||||||
// in the future).
|
|
||||||
if (per_process::cli_options->build_snapshot) {
|
|
||||||
#if HAVE_INSPECTOR
|
|
||||||
env->InitializeInspector({});
|
|
||||||
#endif
|
|
||||||
// TODO(joyeecheung): we could use the result for something special,
|
|
||||||
// like setting up initializers that should be invoked at snapshot
|
|
||||||
// dehydration.
|
|
||||||
MaybeLocal<Value> result =
|
|
||||||
LoadEnvironment(env, StartExecutionCallback{});
|
|
||||||
if (bootstrapCatch.HasCaught()) {
|
|
||||||
// TODO(joyeecheung): fail by exiting with a non-zero exit code.
|
|
||||||
PrintCaughtException(isolate, main_context, bootstrapCatch);
|
|
||||||
abort();
|
|
||||||
}
|
|
||||||
result.ToLocalChecked();
|
|
||||||
// FIXME(joyeecheung): right now running the loop in the snapshot
|
|
||||||
// builder seems to introduces inconsistencies in JS land that need to
|
|
||||||
// be synchronized again after snapshot restoration.
|
|
||||||
int exit_code = SpinEventLoop(env).FromMaybe(1);
|
|
||||||
CHECK_EQ(exit_code, 0);
|
|
||||||
if (bootstrapCatch.HasCaught()) {
|
|
||||||
// TODO(joyeecheung): fail by exiting with a non-zero exit code.
|
|
||||||
PrintCaughtException(isolate, main_context, bootstrapCatch);
|
|
||||||
abort();
|
|
||||||
}
|
|
||||||
}
|
|
||||||
|
|
||||||
if (per_process::enabled_debug_list.enabled(
|
|
||||||
DebugCategory::MKSNAPSHOT)) {
|
|
||||||
env->PrintAllBaseObjects();
|
|
||||||
printf("Environment = %p\n", env);
|
|
||||||
}
|
|
||||||
|
|
||||||
// Serialize the native states
|
|
||||||
out->env_info = env->Serialize(&creator);
|
|
||||||
// Serialize the context
|
|
||||||
size_t index = creator.AddContext(
|
|
||||||
main_context, {SerializeNodeContextInternalFields, env});
|
|
||||||
CHECK_EQ(index, SnapshotData::kNodeMainContextIndex);
|
|
||||||
|
|
||||||
#ifdef NODE_USE_NODE_CODE_CACHE
|
#ifdef NODE_USE_NODE_CODE_CACHE
|
||||||
// Regenerate all the code cache.
|
// Regenerate all the code cache.
|
||||||
CHECK(native_module::NativeModuleEnv::CompileAllModules(main_context));
|
if (!native_module::NativeModuleEnv::CompileAllModules(main_context)) {
|
||||||
native_module::NativeModuleEnv::CopyCodeCache(&(out->code_cache));
|
return UNCAUGHT_EXCEPTION_ERROR;
|
||||||
for (const auto& item : out->code_cache) {
|
|
||||||
std::string size_str = FormatSize(item.data.size());
|
|
||||||
per_process::Debug(DebugCategory::MKSNAPSHOT,
|
|
||||||
"Generated code cache for %d: %s\n",
|
|
||||||
item.id.c_str(),
|
|
||||||
size_str.c_str());
|
|
||||||
}
|
|
||||||
#endif
|
|
||||||
}
|
}
|
||||||
|
native_module::NativeModuleEnv::CopyCodeCache(&(out->code_cache));
|
||||||
|
for (const auto& item : out->code_cache) {
|
||||||
|
std::string size_str = FormatSize(item.data.size());
|
||||||
|
per_process::Debug(DebugCategory::MKSNAPSHOT,
|
||||||
|
"Generated code cache for %d: %s\n",
|
||||||
|
item.id.c_str(),
|
||||||
|
size_str.c_str());
|
||||||
|
}
|
||||||
|
#endif
|
||||||
}
|
}
|
||||||
|
|
||||||
// Must be out of HandleScope
|
// Global handles to the contexts can't be disposed before the
|
||||||
out->v8_snapshot_blob_data =
|
// blob is created. So initialize all the contexts before adding them.
|
||||||
creator.CreateBlob(SnapshotCreator::FunctionCodeHandling::kClear);
|
// TODO(joyeecheung): figure out how to remove this restriction.
|
||||||
|
creator.SetDefaultContext(default_context);
|
||||||
// We must be able to rehash the blob when we restore it or otherwise
|
size_t index = creator.AddContext(base_context);
|
||||||
// the hash seed would be fixed by V8, introducing a vulnerability.
|
CHECK_EQ(index, SnapshotData::kNodeBaseContextIndex);
|
||||||
CHECK(out->v8_snapshot_blob_data.CanBeRehashed());
|
index = creator.AddContext(main_context,
|
||||||
|
{SerializeNodeContextInternalFields, env});
|
||||||
// We cannot resurrect the handles from the snapshot, so make sure that
|
CHECK_EQ(index, SnapshotData::kNodeMainContextIndex);
|
||||||
// no handles are left open in the environment after the blob is created
|
|
||||||
// (which should trigger a GC and close all handles that can be closed).
|
|
||||||
if (!env->req_wrap_queue()->IsEmpty()
|
|
||||||
|| !env->handle_wrap_queue()->IsEmpty()
|
|
||||||
|| per_process::enabled_debug_list.enabled(DebugCategory::MKSNAPSHOT)) {
|
|
||||||
PrintLibuvHandleInformation(env->event_loop(), stderr);
|
|
||||||
}
|
|
||||||
CHECK(env->req_wrap_queue()->IsEmpty());
|
|
||||||
CHECK(env->handle_wrap_queue()->IsEmpty());
|
|
||||||
|
|
||||||
// Must be done while the snapshot creator isolate is entered i.e. the
|
|
||||||
// creator is still alive.
|
|
||||||
FreeEnvironment(env);
|
|
||||||
main_instance->Dispose();
|
|
||||||
}
|
}
|
||||||
|
|
||||||
per_process::v8_platform.Platform()->UnregisterIsolate(isolate);
|
// Must be out of HandleScope
|
||||||
|
out->v8_snapshot_blob_data =
|
||||||
|
creator.CreateBlob(SnapshotCreator::FunctionCodeHandling::kClear);
|
||||||
|
|
||||||
|
// We must be able to rehash the blob when we restore it or otherwise
|
||||||
|
// the hash seed would be fixed by V8, introducing a vulnerability.
|
||||||
|
if (!out->v8_snapshot_blob_data.CanBeRehashed()) {
|
||||||
|
return SNAPSHOT_ERROR;
|
||||||
|
}
|
||||||
|
|
||||||
|
// We cannot resurrect the handles from the snapshot, so make sure that
|
||||||
|
// no handles are left open in the environment after the blob is created
|
||||||
|
// (which should trigger a GC and close all handles that can be closed).
|
||||||
|
bool queues_are_empty =
|
||||||
|
env->req_wrap_queue()->IsEmpty() && env->handle_wrap_queue()->IsEmpty();
|
||||||
|
if (!queues_are_empty ||
|
||||||
|
per_process::enabled_debug_list.enabled(DebugCategory::MKSNAPSHOT)) {
|
||||||
|
PrintLibuvHandleInformation(env->event_loop(), stderr);
|
||||||
|
}
|
||||||
|
if (!queues_are_empty) {
|
||||||
|
return SNAPSHOT_ERROR;
|
||||||
|
}
|
||||||
|
return 0;
|
||||||
}
|
}
|
||||||
|
|
||||||
std::string SnapshotBuilder::Generate(
|
int SnapshotBuilder::Generate(std::ostream& out,
|
||||||
const std::vector<std::string> args,
|
const std::vector<std::string> args,
|
||||||
const std::vector<std::string> exec_args) {
|
const std::vector<std::string> exec_args) {
|
||||||
SnapshotData data;
|
SnapshotData data;
|
||||||
Generate(&data, args, exec_args);
|
int exit_code = Generate(&data, args, exec_args);
|
||||||
std::string result = FormatBlob(&data);
|
if (exit_code != 0) {
|
||||||
delete[] data.v8_snapshot_blob_data.data;
|
return exit_code;
|
||||||
return result;
|
}
|
||||||
|
FormatBlob(out, &data);
|
||||||
|
return exit_code;
|
||||||
}
|
}
|
||||||
|
|
||||||
SnapshotableObject::SnapshotableObject(Environment* env,
|
SnapshotableObject::SnapshotableObject(Environment* env,
|
||||||
|
1
test/fixtures/snapshot/error.js
vendored
Normal file
1
test/fixtures/snapshot/error.js
vendored
Normal file
@ -0,0 +1 @@
|
|||||||
|
throw new Error('test');
|
@ -83,17 +83,18 @@ int BuildSnapshot(int argc, char* argv[]) {
|
|||||||
return 1;
|
return 1;
|
||||||
}
|
}
|
||||||
|
|
||||||
|
int exit_code = 0;
|
||||||
{
|
{
|
||||||
std::string snapshot =
|
exit_code =
|
||||||
node::SnapshotBuilder::Generate(result.args, result.exec_args);
|
node::SnapshotBuilder::Generate(out, result.args, result.exec_args);
|
||||||
out << snapshot;
|
if (exit_code == 0) {
|
||||||
|
if (!out) {
|
||||||
if (!out) {
|
std::cerr << "Failed to write " << out_path << "\n";
|
||||||
std::cerr << "Failed to write " << out_path << "\n";
|
exit_code = 1;
|
||||||
return 1;
|
}
|
||||||
}
|
}
|
||||||
}
|
}
|
||||||
|
|
||||||
node::TearDownOncePerProcess();
|
node::TearDownOncePerProcess();
|
||||||
return 0;
|
return exit_code;
|
||||||
}
|
}
|
||||||
|
Loading…
x
Reference in New Issue
Block a user