Skip to content

Commit 5be0be7

Browse files
VerteDindeckerr
andauthored
fix: ensure snapshot is valid (#48105)
* feat: add support for embedder snapshot validation * chore: e patches all --------- Co-authored-by: Charles Kerr <[email protected]>
1 parent df232d1 commit 5be0be7

File tree

5 files changed

+138
-1
lines changed

5 files changed

+138
-1
lines changed

BUILD.gn

Lines changed: 12 additions & 0 deletions
Original file line numberDiff line numberDiff line change
@@ -518,6 +518,10 @@ source_set("electron_lib") {
518518
"//v8:v8_libplatform",
519519
]
520520

521+
if (v8_use_external_startup_data && use_v8_context_snapshot) {
522+
deps += [ ":mksnapshot_checksum_gen" ]
523+
}
524+
521525
public_deps = [
522526
"//base",
523527
"//base:i18n",
@@ -772,6 +776,14 @@ source_set("electron_lib") {
772776
}
773777
}
774778

779+
action("mksnapshot_checksum_gen") {
780+
script = "build/checksum_header.py"
781+
outputs = [ "$target_gen_dir/snapshot_checksum.h" ]
782+
inputs = [ "$root_out_dir/$v8_context_snapshot_filename" ]
783+
args = rebase_path(inputs) + rebase_path(outputs)
784+
deps = [ "//tools/v8_context_snapshot" ]
785+
}
786+
775787
electron_paks("packed_resources") {
776788
if (is_mac) {
777789
output_dir = "$root_gen_dir/electron_repack"

build/checksum_header.py

Lines changed: 37 additions & 0 deletions
Original file line numberDiff line numberDiff line change
@@ -0,0 +1,37 @@
1+
#!/usr/bin/env python3
2+
3+
import os
4+
import sys
5+
import hashlib
6+
7+
dir_path = os.path.dirname(os.path.realpath(__file__))
8+
9+
TEMPLATE_H = """
10+
#ifndef ELECTRON_SNAPSHOT_CHECKSUM_H_
11+
#define ELECTRON_SNAPSHOT_CHECKSUM_H_
12+
13+
namespace electron::snapshot_checksum {
14+
15+
const std::string kChecksum = "{checksum}";
16+
17+
} // namespace electron::snapshot_checksum
18+
19+
#endif // ELECTRON_SNAPSHOT_CHECKSUM_H_
20+
"""
21+
22+
def calculate_sha256(filepath):
23+
sha256_hash = hashlib.sha256()
24+
with open(filepath, "rb") as f:
25+
for byte_block in iter(lambda: f.read(4096), b""):
26+
sha256_hash.update(byte_block)
27+
return sha256_hash.hexdigest()
28+
29+
input_file = sys.argv[1]
30+
output_file = sys.argv[2]
31+
32+
checksum = calculate_sha256(input_file)
33+
34+
checksum_h = TEMPLATE_H.replace("{checksum}", checksum)
35+
36+
with open(output_file, 'w') as f:
37+
f.write(checksum_h)

patches/chromium/.patches

Lines changed: 1 addition & 0 deletions
Original file line numberDiff line numberDiff line change
@@ -154,3 +154,4 @@ make_focus_methods_in_webcontentsviewchildframe_notimplemented.patch
154154
cherry-pick-295a4a1b14b8.patch
155155
cherry-pick-c3568ceda9d8.patch
156156
cherry-pick-f1e6422a355c.patch
157+
feat_add_support_for_embedder_snapshot_validation.patch
Lines changed: 67 additions & 0 deletions
Original file line numberDiff line numberDiff line change
@@ -0,0 +1,67 @@
1+
From 0000000000000000000000000000000000000000 Mon Sep 17 00:00:00 2001
2+
From: Samuel Attard <[email protected]>
3+
Date: Fri, 15 Aug 2025 14:58:12 -0700
4+
Subject: feat: add support for embedder snapshot validation
5+
6+
IsValid is not exposed despite being commented as for embedders, this exposes something that works for us.
7+
8+
diff --git a/gin/v8_initializer.cc b/gin/v8_initializer.cc
9+
index 9b80a6f0cd185e6a9ccdaf3977040a241a25e568..f3f9c6d4d3b7d32243de9d091277fc1a361880cb 100644
10+
--- a/gin/v8_initializer.cc
11+
+++ b/gin/v8_initializer.cc
12+
@@ -75,11 +75,23 @@ bool GenerateEntropy(unsigned char* buffer, size_t amount) {
13+
return true;
14+
}
15+
16+
+static base::RepeatingCallback<void(v8::StartupData*)>& SnapshotValidator() {
17+
+ static base::NoDestructor<base::RepeatingCallback<void(v8::StartupData*)>>
18+
+ validator(
19+
+ base::BindRepeating([](v8::StartupData* data) -> void { /* empty */ }));
20+
+ return *validator;
21+
+}
22+
+
23+
+void SetV8SnapshotValidatorInner(const base::RepeatingCallback<void(v8::StartupData*)>& callback) {
24+
+ SnapshotValidator() = std::move(callback);
25+
+}
26+
+
27+
void GetMappedFileData(base::MemoryMappedFile* mapped_file,
28+
v8::StartupData* data) {
29+
if (mapped_file) {
30+
data->data = reinterpret_cast<const char*>(mapped_file->data());
31+
data->raw_size = static_cast<int>(mapped_file->length());
32+
+ SnapshotValidator().Run(data);
33+
} else {
34+
data->data = nullptr;
35+
data->raw_size = 0;
36+
@@ -223,6 +235,10 @@ constexpr std::string_view kV8FlagFeaturePrefix = "V8Flag_";
37+
38+
} // namespace
39+
40+
+void SetV8SnapshotValidator(const base::RepeatingCallback<void(v8::StartupData*)>& callback) {
41+
+ SetV8SnapshotValidatorInner(std::move(callback));
42+
+}
43+
+
44+
class V8FeatureVisitor : public base::FeatureVisitor {
45+
public:
46+
void Visit(const std::string& feature_name,
47+
diff --git a/gin/v8_initializer.h b/gin/v8_initializer.h
48+
index 6f7382cd600cd34916d9382878aee4b469dae5d0..61ed0f46437d2e1abbcebcfb64df06d17c8d9139 100644
49+
--- a/gin/v8_initializer.h
50+
+++ b/gin/v8_initializer.h
51+
@@ -11,6 +11,7 @@
52+
53+
#include "base/files/file.h"
54+
#include "base/files/memory_mapped_file.h"
55+
+#include "base/functional/callback.h"
56+
#include "build/build_config.h"
57+
#include "gin/array_buffer.h"
58+
#include "gin/gin_export.h"
59+
@@ -28,6 +29,8 @@ class StartupData;
60+
61+
namespace gin {
62+
63+
+void SetV8SnapshotValidator(const base::RepeatingCallback<void(v8::StartupData*)>& callback);
64+
+
65+
class GIN_EXPORT V8Initializer {
66+
public:
67+
// This should be called by IsolateHolder::Initialize().

shell/app/electron_main_delegate.cc

Lines changed: 21 additions & 1 deletion
Original file line numberDiff line numberDiff line change
@@ -23,10 +23,13 @@
2323
#include "components/content_settings/core/common/content_settings_pattern.h"
2424
#include "content/public/app/initialize_mojo_core.h"
2525
#include "content/public/common/content_switches.h"
26+
#include "crypto/hash.h"
2627
#include "electron/buildflags/buildflags.h"
2728
#include "electron/fuses.h"
2829
#include "electron/mas.h"
30+
#include "electron/snapshot_checksum.h"
2931
#include "extensions/common/constants.h"
32+
#include "gin/v8_initializer.h"
3033
#include "ipc/ipc_buildflags.h"
3134
#include "sandbox/policy/switches.h"
3235
#include "services/tracing/public/cpp/stack_sampling/tracing_sampler_profiler.h"
@@ -49,6 +52,7 @@
4952
#include "third_party/abseil-cpp/absl/types/variant.h"
5053
#include "ui/base/resource/resource_bundle.h"
5154
#include "ui/base/ui_base_switches.h"
55+
#include "v8/include/v8-snapshot.h"
5256

5357
#if BUILDFLAG(IS_MAC)
5458
#include "shell/app/electron_main_delegate_mac.h"
@@ -206,6 +210,20 @@ void RegisterPathProvider() {
206210
PATH_END);
207211
}
208212

213+
void ValidateV8Snapshot(v8::StartupData* data) {
214+
if (data->data &&
215+
electron::fuses::IsEmbeddedAsarIntegrityValidationEnabled()) {
216+
CHECK(data->raw_size, 0);
217+
UNSAFE_BUFFERS({
218+
base::span<const char> span_data(
219+
data->data, static_cast<unsigned long>(data->raw_size));
220+
CHECK(base::ToLowerASCII(base::HexEncode(
221+
crypto::hash::Sha256(base::as_bytes(span_data)))) ==
222+
electron::snapshot_checksum::kChecksum);
223+
})
224+
}
225+
}
226+
209227
} // namespace
210228

211229
std::string LoadResourceBundle(const std::string& locale) {
@@ -229,7 +247,9 @@ std::string LoadResourceBundle(const std::string& locale) {
229247
return loaded_locale;
230248
}
231249

232-
ElectronMainDelegate::ElectronMainDelegate() = default;
250+
ElectronMainDelegate::ElectronMainDelegate() {
251+
gin::SetV8SnapshotValidator(base::BindRepeating(&ValidateV8Snapshot));
252+
}
233253

234254
ElectronMainDelegate::~ElectronMainDelegate() = default;
235255

0 commit comments

Comments
 (0)