fix: ensure snapshot is valid (#48104)
feat: add support for embedder snapshot validation
This commit is contained in:
parent
e94855be21
commit
3f92511cde
5 changed files with 138 additions and 1 deletions
12
BUILD.gn
12
BUILD.gn
|
@ -518,6 +518,10 @@ source_set("electron_lib") {
|
||||||
"//v8:v8_libplatform",
|
"//v8:v8_libplatform",
|
||||||
]
|
]
|
||||||
|
|
||||||
|
if (v8_use_external_startup_data && use_v8_context_snapshot) {
|
||||||
|
deps += [ ":mksnapshot_checksum_gen" ]
|
||||||
|
}
|
||||||
|
|
||||||
public_deps = [
|
public_deps = [
|
||||||
"//base",
|
"//base",
|
||||||
"//base:i18n",
|
"//base:i18n",
|
||||||
|
@ -772,6 +776,14 @@ source_set("electron_lib") {
|
||||||
}
|
}
|
||||||
}
|
}
|
||||||
|
|
||||||
|
action("mksnapshot_checksum_gen") {
|
||||||
|
script = "build/checksum_header.py"
|
||||||
|
outputs = [ "$target_gen_dir/snapshot_checksum.h" ]
|
||||||
|
inputs = [ "$root_out_dir/$v8_context_snapshot_filename" ]
|
||||||
|
args = rebase_path(inputs) + rebase_path(outputs)
|
||||||
|
deps = [ "//tools/v8_context_snapshot" ]
|
||||||
|
}
|
||||||
|
|
||||||
electron_paks("packed_resources") {
|
electron_paks("packed_resources") {
|
||||||
if (is_mac) {
|
if (is_mac) {
|
||||||
output_dir = "$root_gen_dir/electron_repack"
|
output_dir = "$root_gen_dir/electron_repack"
|
||||||
|
|
37
build/checksum_header.py
Normal file
37
build/checksum_header.py
Normal file
|
@ -0,0 +1,37 @@
|
||||||
|
#!/usr/bin/env python3
|
||||||
|
|
||||||
|
import os
|
||||||
|
import sys
|
||||||
|
import hashlib
|
||||||
|
|
||||||
|
dir_path = os.path.dirname(os.path.realpath(__file__))
|
||||||
|
|
||||||
|
TEMPLATE_H = """
|
||||||
|
#ifndef ELECTRON_SNAPSHOT_CHECKSUM_H_
|
||||||
|
#define ELECTRON_SNAPSHOT_CHECKSUM_H_
|
||||||
|
|
||||||
|
namespace electron::snapshot_checksum {
|
||||||
|
|
||||||
|
const std::string kChecksum = "{checksum}";
|
||||||
|
|
||||||
|
} // namespace electron::snapshot_checksum
|
||||||
|
|
||||||
|
#endif // ELECTRON_SNAPSHOT_CHECKSUM_H_
|
||||||
|
"""
|
||||||
|
|
||||||
|
def calculate_sha256(filepath):
|
||||||
|
sha256_hash = hashlib.sha256()
|
||||||
|
with open(filepath, "rb") as f:
|
||||||
|
for byte_block in iter(lambda: f.read(4096), b""):
|
||||||
|
sha256_hash.update(byte_block)
|
||||||
|
return sha256_hash.hexdigest()
|
||||||
|
|
||||||
|
input_file = sys.argv[1]
|
||||||
|
output_file = sys.argv[2]
|
||||||
|
|
||||||
|
checksum = calculate_sha256(input_file)
|
||||||
|
|
||||||
|
checksum_h = TEMPLATE_H.replace("{checksum}", checksum)
|
||||||
|
|
||||||
|
with open(output_file, 'w') as f:
|
||||||
|
f.write(checksum_h)
|
|
@ -149,3 +149,4 @@ do_not_check_the_order_of_display_id_order_on_windows.patch
|
||||||
make_focus_methods_in_webcontentsviewchildframe_notimplemented.patch
|
make_focus_methods_in_webcontentsviewchildframe_notimplemented.patch
|
||||||
cherry-pick-f1e6422a355c.patch
|
cherry-pick-f1e6422a355c.patch
|
||||||
fix_resolve_dynamic_background_material_update_issue_on_windows_11.patch
|
fix_resolve_dynamic_background_material_update_issue_on_windows_11.patch
|
||||||
|
feat_add_support_for_embedder_snapshot_validation.patch
|
||||||
|
|
|
@ -0,0 +1,67 @@
|
||||||
|
From 0000000000000000000000000000000000000000 Mon Sep 17 00:00:00 2001
|
||||||
|
From: Samuel Attard <sattard@anthropic.com>
|
||||||
|
Date: Fri, 15 Aug 2025 14:58:12 -0700
|
||||||
|
Subject: feat: add support for embedder snapshot validation
|
||||||
|
|
||||||
|
IsValid is not exposed despite being commented as for embedders, this exposes something that works for us.
|
||||||
|
|
||||||
|
diff --git a/gin/v8_initializer.cc b/gin/v8_initializer.cc
|
||||||
|
index e07bdaeccecc8015462e35d5cf4606335e2e962c..28b133626cf3488dee6f43bfd0ac9b6b14ed3980 100644
|
||||||
|
--- a/gin/v8_initializer.cc
|
||||||
|
+++ b/gin/v8_initializer.cc
|
||||||
|
@@ -75,11 +75,23 @@ bool GenerateEntropy(unsigned char* buffer, size_t amount) {
|
||||||
|
return true;
|
||||||
|
}
|
||||||
|
|
||||||
|
+static base::RepeatingCallback<void(v8::StartupData*)>& SnapshotValidator() {
|
||||||
|
+ static base::NoDestructor<base::RepeatingCallback<void(v8::StartupData*)>>
|
||||||
|
+ validator(
|
||||||
|
+ base::BindRepeating([](v8::StartupData* data) -> void { /* empty */ }));
|
||||||
|
+ return *validator;
|
||||||
|
+}
|
||||||
|
+
|
||||||
|
+void SetV8SnapshotValidatorInner(const base::RepeatingCallback<void(v8::StartupData*)>& callback) {
|
||||||
|
+ SnapshotValidator() = std::move(callback);
|
||||||
|
+}
|
||||||
|
+
|
||||||
|
void GetMappedFileData(base::MemoryMappedFile* mapped_file,
|
||||||
|
v8::StartupData* data) {
|
||||||
|
if (mapped_file) {
|
||||||
|
data->data = reinterpret_cast<const char*>(mapped_file->data());
|
||||||
|
data->raw_size = static_cast<int>(mapped_file->length());
|
||||||
|
+ SnapshotValidator().Run(data);
|
||||||
|
} else {
|
||||||
|
data->data = nullptr;
|
||||||
|
data->raw_size = 0;
|
||||||
|
@@ -223,6 +235,10 @@ constexpr std::string_view kV8FlagFeaturePrefix = "V8Flag_";
|
||||||
|
|
||||||
|
} // namespace
|
||||||
|
|
||||||
|
+void SetV8SnapshotValidator(const base::RepeatingCallback<void(v8::StartupData*)>& callback) {
|
||||||
|
+ SetV8SnapshotValidatorInner(std::move(callback));
|
||||||
|
+}
|
||||||
|
+
|
||||||
|
class V8FeatureVisitor : public base::FeatureVisitor {
|
||||||
|
public:
|
||||||
|
void Visit(const std::string& feature_name,
|
||||||
|
diff --git a/gin/v8_initializer.h b/gin/v8_initializer.h
|
||||||
|
index 6f7382cd600cd34916d9382878aee4b469dae5d0..61ed0f46437d2e1abbcebcfb64df06d17c8d9139 100644
|
||||||
|
--- a/gin/v8_initializer.h
|
||||||
|
+++ b/gin/v8_initializer.h
|
||||||
|
@@ -11,6 +11,7 @@
|
||||||
|
|
||||||
|
#include "base/files/file.h"
|
||||||
|
#include "base/files/memory_mapped_file.h"
|
||||||
|
+#include "base/functional/callback.h"
|
||||||
|
#include "build/build_config.h"
|
||||||
|
#include "gin/array_buffer.h"
|
||||||
|
#include "gin/gin_export.h"
|
||||||
|
@@ -28,6 +29,8 @@ class StartupData;
|
||||||
|
|
||||||
|
namespace gin {
|
||||||
|
|
||||||
|
+void SetV8SnapshotValidator(const base::RepeatingCallback<void(v8::StartupData*)>& callback);
|
||||||
|
+
|
||||||
|
class GIN_EXPORT V8Initializer {
|
||||||
|
public:
|
||||||
|
// This should be called by IsolateHolder::Initialize().
|
|
@ -23,10 +23,13 @@
|
||||||
#include "components/content_settings/core/common/content_settings_pattern.h"
|
#include "components/content_settings/core/common/content_settings_pattern.h"
|
||||||
#include "content/public/app/initialize_mojo_core.h"
|
#include "content/public/app/initialize_mojo_core.h"
|
||||||
#include "content/public/common/content_switches.h"
|
#include "content/public/common/content_switches.h"
|
||||||
|
#include "crypto/hash.h"
|
||||||
#include "electron/buildflags/buildflags.h"
|
#include "electron/buildflags/buildflags.h"
|
||||||
#include "electron/fuses.h"
|
#include "electron/fuses.h"
|
||||||
#include "electron/mas.h"
|
#include "electron/mas.h"
|
||||||
|
#include "electron/snapshot_checksum.h"
|
||||||
#include "extensions/common/constants.h"
|
#include "extensions/common/constants.h"
|
||||||
|
#include "gin/v8_initializer.h"
|
||||||
#include "ipc/ipc_buildflags.h"
|
#include "ipc/ipc_buildflags.h"
|
||||||
#include "sandbox/policy/switches.h"
|
#include "sandbox/policy/switches.h"
|
||||||
#include "services/tracing/public/cpp/stack_sampling/tracing_sampler_profiler.h"
|
#include "services/tracing/public/cpp/stack_sampling/tracing_sampler_profiler.h"
|
||||||
|
@ -49,6 +52,7 @@
|
||||||
#include "third_party/abseil-cpp/absl/types/variant.h"
|
#include "third_party/abseil-cpp/absl/types/variant.h"
|
||||||
#include "ui/base/resource/resource_bundle.h"
|
#include "ui/base/resource/resource_bundle.h"
|
||||||
#include "ui/base/ui_base_switches.h"
|
#include "ui/base/ui_base_switches.h"
|
||||||
|
#include "v8/include/v8-snapshot.h"
|
||||||
|
|
||||||
#if BUILDFLAG(IS_MAC)
|
#if BUILDFLAG(IS_MAC)
|
||||||
#include "shell/app/electron_main_delegate_mac.h"
|
#include "shell/app/electron_main_delegate_mac.h"
|
||||||
|
@ -206,6 +210,20 @@ void RegisterPathProvider() {
|
||||||
PATH_END);
|
PATH_END);
|
||||||
}
|
}
|
||||||
|
|
||||||
|
void ValidateV8Snapshot(v8::StartupData* data) {
|
||||||
|
if (data->data &&
|
||||||
|
electron::fuses::IsEmbeddedAsarIntegrityValidationEnabled()) {
|
||||||
|
CHECK_GT(data->raw_size, 0);
|
||||||
|
UNSAFE_BUFFERS({
|
||||||
|
base::span<const char> span_data(
|
||||||
|
data->data, static_cast<unsigned long>(data->raw_size));
|
||||||
|
CHECK(base::ToLowerASCII(base::HexEncode(
|
||||||
|
crypto::hash::Sha256(base::as_bytes(span_data)))) ==
|
||||||
|
electron::snapshot_checksum::kChecksum);
|
||||||
|
})
|
||||||
|
}
|
||||||
|
}
|
||||||
|
|
||||||
} // namespace
|
} // namespace
|
||||||
|
|
||||||
std::string LoadResourceBundle(const std::string& locale) {
|
std::string LoadResourceBundle(const std::string& locale) {
|
||||||
|
@ -229,7 +247,9 @@ std::string LoadResourceBundle(const std::string& locale) {
|
||||||
return loaded_locale;
|
return loaded_locale;
|
||||||
}
|
}
|
||||||
|
|
||||||
ElectronMainDelegate::ElectronMainDelegate() = default;
|
ElectronMainDelegate::ElectronMainDelegate() {
|
||||||
|
gin::SetV8SnapshotValidator(base::BindRepeating(&ValidateV8Snapshot));
|
||||||
|
}
|
||||||
|
|
||||||
ElectronMainDelegate::~ElectronMainDelegate() = default;
|
ElectronMainDelegate::~ElectronMainDelegate() = default;
|
||||||
|
|
||||||
|
|
Loading…
Add table
Add a link
Reference in a new issue