Skip to content

Commit aa29b31

Browse files
committed
lib: detect --frozen-intrinsics mismatch when loading
1 parent 9c4ca0a commit aa29b31

3 files changed

Lines changed: 102 additions & 4 deletions

File tree

src/node.h

Lines changed: 4 additions & 0 deletions
Original file line numberDiff line numberDiff line change
@@ -648,6 +648,10 @@ enum class SnapshotFlags : uint32_t {
648648
// in the snapshot at the expense of a bigger snapshot size and
649649
// potentially breaking portability of the snapshot.
650650
kWithoutCodeCache = 1 << 0,
651+
// Whether --frozen-intrinsics was active when the snapshot was built.
652+
// Loading a snapshot built with --frozen-intrinsics without the flag (or
653+
// vice versa) would result in inconsistent intrinsics state.
654+
kFrozenIntrinsics = 1 << 1,
651655
};
652656

653657
struct SnapshotConfig {

src/node_snapshotable.cc

Lines changed: 23 additions & 3 deletions
Original file line numberDiff line numberDiff line change
@@ -536,7 +536,7 @@ size_t SnapshotSerializer::Write(const EnvSerializeInfo& data) {
536536
// [ ... ] |length| bytes of node arch
537537
// [ 4/8 bytes ] length of the node platform string
538538
// [ ... ] |length| bytes of node platform
539-
// [ 4 bytes ] v8 cache version tag
539+
// [ 4 bytes ] SnapshotFlags
540540
template <>
541541
SnapshotMetadata SnapshotDeserializer::Read() {
542542
Debug("Read<SnapshotMetadata>()\n");
@@ -697,7 +697,23 @@ bool SnapshotData::Check() const {
697697
return false;
698698
}
699699

700-
// TODO(joyeecheung): check incompatible Node.js flags.
700+
// For fully customized snapshots, check that Node.js flags affecting the
701+
// snapshot were the same when the snapshot was built.
702+
if (metadata.type == SnapshotMetadata::Type::kFullyCustomized) {
703+
bool snapshot_frozen =
704+
static_cast<bool>(metadata.flags & SnapshotFlags::kFrozenIntrinsics);
705+
bool current_frozen =
706+
per_process::cli_options->per_isolate->per_env->frozen_intrinsics;
707+
if (snapshot_frozen != current_frozen) {
708+
fprintf(stderr,
709+
"Failed to load the startup snapshot because it was built "
710+
"%s--frozen-intrinsics but the current process is run "
711+
"%s--frozen-intrinsics.\n",
712+
snapshot_frozen ? "with " : "without ",
713+
current_frozen ? "with " : "without ");
714+
return false;
715+
}
716+
}
701717
return true;
702718
}
703719

@@ -1255,11 +1271,15 @@ ExitCode SnapshotBuilder::CreateSnapshot(SnapshotData* out,
12551271
return ExitCode::kStartupSnapshotFailure;
12561272
}
12571273

1274+
SnapshotFlags flags = config->flags;
1275+
if (env->options()->frozen_intrinsics) {
1276+
flags |= SnapshotFlags::kFrozenIntrinsics;
1277+
}
12581278
out->metadata = SnapshotMetadata{snapshot_type,
12591279
per_process::metadata.versions.node,
12601280
per_process::metadata.arch,
12611281
per_process::metadata.platform,
1262-
config->flags};
1282+
flags};
12631283

12641284
// We cannot resurrect the handles from the snapshot, so make sure that
12651285
// no handles are left open in the environment after the blob is created

test/parallel/test-snapshot-incompatible.js

Lines changed: 75 additions & 1 deletion
Original file line numberDiff line numberDiff line change
@@ -1,7 +1,7 @@
11
'use strict';
22

33
// This tests that Node.js refuses to load snapshots built with incompatible
4-
// V8 configurations.
4+
// configurations (V8 or Node.js flags).
55

66
require('../common');
77
const assert = require('assert');
@@ -73,3 +73,77 @@ const entry = fixtures.path('empty.js');
7373
assert.strictEqual(child.status, 0);
7474
}
7575
}
76+
77+
// Test that --frozen-intrinsics mismatch is detected.
78+
const frozenBlobPath = tmpdir.resolve('snapshot-frozen.blob');
79+
{
80+
// Build a snapshot with --frozen-intrinsics.
81+
const child = spawnSync(process.execPath, [
82+
'--frozen-intrinsics',
83+
'--snapshot-blob',
84+
frozenBlobPath,
85+
'--build-snapshot',
86+
entry,
87+
], {
88+
cwd: tmpdir.path
89+
});
90+
if (child.status !== 0) {
91+
console.log(child.stderr.toString());
92+
console.log(child.stdout.toString());
93+
assert.strictEqual(child.status, 0);
94+
}
95+
const stats = fs.statSync(frozenBlobPath);
96+
assert(stats.isFile());
97+
}
98+
99+
{
100+
// Load the snapshot without --frozen-intrinsics, which should fail.
101+
const child = spawnSync(process.execPath, [
102+
'--snapshot-blob',
103+
frozenBlobPath,
104+
], {
105+
cwd: tmpdir.path,
106+
env: { ...process.env }
107+
});
108+
109+
const stderr = child.stderr.toString().trim();
110+
assert.match(stderr, /Failed to load the startup snapshot/);
111+
assert.match(stderr, /--frozen-intrinsics/);
112+
assert.strictEqual(child.status, 14);
113+
}
114+
115+
{
116+
// Load it again with --frozen-intrinsics and it should work.
117+
const child = spawnSync(process.execPath, [
118+
'--frozen-intrinsics',
119+
'--snapshot-blob',
120+
frozenBlobPath,
121+
], {
122+
cwd: tmpdir.path,
123+
env: { ...process.env }
124+
});
125+
126+
if (child.status !== 0) {
127+
console.log(child.stderr.toString());
128+
console.log(child.stdout.toString());
129+
assert.strictEqual(child.status, 0);
130+
}
131+
}
132+
133+
{
134+
// Build a snapshot without --frozen-intrinsics and load with it, which
135+
// should also fail.
136+
const child = spawnSync(process.execPath, [
137+
'--frozen-intrinsics',
138+
'--snapshot-blob',
139+
blobPath,
140+
], {
141+
cwd: tmpdir.path,
142+
env: { ...process.env }
143+
});
144+
145+
const stderr = child.stderr.toString().trim();
146+
assert.match(stderr, /Failed to load the startup snapshot/);
147+
assert.match(stderr, /--frozen-intrinsics/);
148+
assert.strictEqual(child.status, 14);
149+
}

0 commit comments

Comments
 (0)