CVE-2025-55305
Description
Electron is a framework for writing cross-platform desktop applications using JavaScript, HTML and CSS. In versions below 35.7.5, 36.0.0-alpha.1 through 36.8.0, 37.0.0-alpha.1 through 37.3.1 and 38.0.0-alpha.1 through 38.0.0-beta.6, ASAR Integrity Bypass via resource modification. This only impacts apps that have the embeddedAsarIntegrityValidation and onlyLoadAppFromAsar fuses enabled. Apps without these fuses enabled are not impacted. This issue is fixed in versions 35.7.5, 36.8.1, 37.3.1 and 38.0.0-beta.6.
Affected packages
Versions sourced from the GitHub Security Advisory.
| Package | Affected versions | Patched versions |
|---|---|---|
electronnpm | < 35.7.5 | 35.7.5 |
electronnpm | >= 36.0.0-alpha.1, < 36.8.1 | 36.8.1 |
electronnpm | >= 37.0.0-alpha.1, < 37.3.1 | 37.3.1 |
electronnpm | >= 38.0.0-alpha.1, < 38.0.0-beta.6 | 38.0.0-beta.6 |
Affected products
1- Range: v0.1.0, v0.1.1, v0.1.2, …
Patches
586d839a881b6build: correct CHECK syntax (#48106)
1 file changed · +1 −1
shell/app/electron_main_delegate.cc+1 −1 modified@@ -213,7 +213,7 @@ void RegisterPathProvider() { void ValidateV8Snapshot(v8::StartupData* data) { if (data->data && electron::fuses::IsEmbeddedAsarIntegrityValidationEnabled()) { - CHECK(data->raw_size, 0); + CHECK_GT(data->raw_size, 0); UNSAFE_BUFFERS({ base::span<const char> span_data( data->data, static_cast<unsigned long>(data->raw_size));
3f92511cdeccfix: ensure snapshot is valid (#48104)
5 files changed · +138 −1
build/checksum_header.py+37 −0 added@@ -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)
BUILD.gn+12 −0 modified@@ -518,6 +518,10 @@ source_set("electron_lib") { "//v8:v8_libplatform", ] + if (v8_use_external_startup_data && use_v8_context_snapshot) { + deps += [ ":mksnapshot_checksum_gen" ] + } + public_deps = [ "//base", "//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") { if (is_mac) { output_dir = "$root_gen_dir/electron_repack"
patches/chromium/feat_add_support_for_embedder_snapshot_validation.patch+67 −0 added@@ -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().
patches/chromium/.patches+1 −0 modified@@ -149,3 +149,4 @@ do_not_check_the_order_of_display_id_order_on_windows.patch make_focus_methods_in_webcontentsviewchildframe_notimplemented.patch cherry-pick-f1e6422a355c.patch fix_resolve_dynamic_background_material_update_issue_on_windows_11.patch +feat_add_support_for_embedder_snapshot_validation.patch
shell/app/electron_main_delegate.cc+21 −1 modified@@ -23,10 +23,13 @@ #include "components/content_settings/core/common/content_settings_pattern.h" #include "content/public/app/initialize_mojo_core.h" #include "content/public/common/content_switches.h" +#include "crypto/hash.h" #include "electron/buildflags/buildflags.h" #include "electron/fuses.h" #include "electron/mas.h" +#include "electron/snapshot_checksum.h" #include "extensions/common/constants.h" +#include "gin/v8_initializer.h" #include "ipc/ipc_buildflags.h" #include "sandbox/policy/switches.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 "ui/base/resource/resource_bundle.h" #include "ui/base/ui_base_switches.h" +#include "v8/include/v8-snapshot.h" #if BUILDFLAG(IS_MAC) #include "shell/app/electron_main_delegate_mac.h" @@ -206,6 +210,20 @@ void RegisterPathProvider() { 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 std::string LoadResourceBundle(const std::string& locale) { @@ -229,7 +247,9 @@ std::string LoadResourceBundle(const std::string& locale) { return loaded_locale; } -ElectronMainDelegate::ElectronMainDelegate() = default; +ElectronMainDelegate::ElectronMainDelegate() { + gin::SetV8SnapshotValidator(base::BindRepeating(&ValidateV8Snapshot)); +} ElectronMainDelegate::~ElectronMainDelegate() = default;
23a02934510ffix: ensure snapshot is valid (#48103)
5 files changed · +140 −1
build/checksum_header.py+37 −0 added@@ -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)
BUILD.gn+12 −0 modified@@ -518,6 +518,10 @@ source_set("electron_lib") { "//v8:v8_libplatform", ] + if (v8_use_external_startup_data && use_v8_context_snapshot) { + deps += [ ":mksnapshot_checksum_gen" ] + } + public_deps = [ "//base", "//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") { if (is_mac) { output_dir = "$root_gen_dir/electron_repack"
patches/chromium/feat_add_support_for_embedder_snapshot_validation.patch+67 −0 added@@ -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 03c9bc18566794a668981bba6235b226e07eff74..9ad6909dc6084202e9227801a9d200355b30d164 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().
patches/chromium/.patches+1 −0 modified@@ -142,3 +142,4 @@ chore_grandfather_in_electron_views_and_delegates.patch refactor_patch_electron_permissiontypes_into_blink.patch fix_add_macos_memory_query_fallback_to_avoid_crash.patch fix_resolve_dynamic_background_material_update_issue_on_windows_11.patch +feat_add_support_for_embedder_snapshot_validation.patch
shell/app/electron_main_delegate.cc+23 −1 modified@@ -19,15 +19,20 @@ #include "base/logging.h" #include "base/path_service.h" #include "base/strings/cstring_view.h" +#include "base/strings/string_number_conversions.cc" +#include "base/strings/string_util_internal.h" #include "chrome/common/chrome_paths.h" #include "chrome/common/chrome_switches.h" #include "components/content_settings/core/common/content_settings_pattern.h" #include "content/public/app/initialize_mojo_core.h" #include "content/public/common/content_switches.h" +#include "crypto/hash.h" #include "electron/buildflags/buildflags.h" #include "electron/fuses.h" #include "electron/mas.h" +#include "electron/snapshot_checksum.h" #include "extensions/common/constants.h" +#include "gin/v8_initializer.h" #include "ipc/ipc_buildflags.h" #include "sandbox/policy/switches.h" #include "services/tracing/public/cpp/stack_sampling/tracing_sampler_profiler.h" @@ -50,6 +55,7 @@ #include "third_party/abseil-cpp/absl/types/variant.h" #include "ui/base/resource/resource_bundle.h" #include "ui/base/ui_base_switches.h" +#include "v8/include/v8-snapshot.h" #if BUILDFLAG(IS_MAC) #include "shell/app/electron_main_delegate_mac.h" @@ -208,6 +214,20 @@ void RegisterPathProvider() { 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 std::string LoadResourceBundle(const std::string& locale) { @@ -231,7 +251,9 @@ std::string LoadResourceBundle(const std::string& locale) { return loaded_locale; } -ElectronMainDelegate::ElectronMainDelegate() = default; +ElectronMainDelegate::ElectronMainDelegate() { + gin::SetV8SnapshotValidator(base::BindRepeating(&ValidateV8Snapshot)); +} ElectronMainDelegate::~ElectronMainDelegate() = default;
2e5a0b7220ebfix: ensure snapshot is valid (#48102)
5 files changed · +140 −1
build/checksum_header.py+37 −0 added@@ -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)
BUILD.gn+12 −0 modified@@ -518,6 +518,10 @@ source_set("electron_lib") { "//v8:v8_libplatform", ] + if (v8_use_external_startup_data && use_v8_context_snapshot) { + deps += [ ":mksnapshot_checksum_gen" ] + } + public_deps = [ "//base", "//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") { if (is_mac) { output_dir = "$root_gen_dir/electron_repack"
patches/chromium/feat_add_support_for_embedder_snapshot_validation.patch+67 −0 added@@ -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 f83461b3a1aff229164358e53847065ddae5ddf1..12faa71c2e14d2f13ab612526f349e9b027e5d3d 100644 +--- a/gin/v8_initializer.cc ++++ b/gin/v8_initializer.cc +@@ -76,11 +76,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; +@@ -225,6 +237,10 @@ constexpr std::string_view kV8FlagParam = "V8FlagParam"; + + } // 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().
patches/chromium/.patches+1 −0 modified@@ -137,3 +137,4 @@ build_set_mac_sdk_minimum_to_10.patch fix_add_macos_memory_query_fallback_to_avoid_crash.patch fix_resolve_dynamic_background_material_update_issue_on_windows_11.patch chore_restore_some_deprecated_wrapper_utility_in_gin.patch +feat_add_support_for_embedder_snapshot_validation.patch
shell/app/electron_main_delegate.cc+23 −1 modified@@ -19,15 +19,20 @@ #include "base/logging.h" #include "base/path_service.h" #include "base/strings/cstring_view.h" +#include "base/strings/string_number_conversions.cc" +#include "base/strings/string_util_internal.h" #include "chrome/common/chrome_paths.h" #include "chrome/common/chrome_switches.h" #include "components/content_settings/core/common/content_settings_pattern.h" #include "content/public/app/initialize_mojo_core.h" #include "content/public/common/content_switches.h" +#include "crypto/hash.h" #include "electron/buildflags/buildflags.h" #include "electron/fuses.h" #include "electron/mas.h" +#include "electron/snapshot_checksum.h" #include "extensions/common/constants.h" +#include "gin/v8_initializer.h" #include "sandbox/policy/switches.h" #include "services/tracing/public/cpp/stack_sampling/tracing_sampler_profiler.h" #include "shell/app/command_line_args.h" @@ -49,6 +54,7 @@ #include "third_party/abseil-cpp/absl/types/variant.h" #include "ui/base/resource/resource_bundle.h" #include "ui/base/ui_base_switches.h" +#include "v8/include/v8-snapshot.h" #if BUILDFLAG(IS_MAC) #include "shell/app/electron_main_delegate_mac.h" @@ -207,6 +213,20 @@ void RegisterPathProvider() { 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 std::string LoadResourceBundle(const std::string& locale) { @@ -230,7 +250,9 @@ std::string LoadResourceBundle(const std::string& locale) { return loaded_locale; } -ElectronMainDelegate::ElectronMainDelegate() = default; +ElectronMainDelegate::ElectronMainDelegate() { + gin::SetV8SnapshotValidator(base::BindRepeating(&ValidateV8Snapshot)); +} ElectronMainDelegate::~ElectronMainDelegate() = default;
fdf29ce83870fix: ensure snapshot is valid (#48101)
5 files changed · +144 −1
build/checksum_header.py+37 −0 added@@ -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)
BUILD.gn+16 −0 modified@@ -518,6 +518,10 @@ source_set("electron_lib") { "//v8:v8_libplatform", ] + if (v8_use_external_startup_data && use_v8_context_snapshot) { + deps += [ ":mksnapshot_checksum_gen" ] + } + public_deps = [ "//base", "//base:i18n", @@ -772,6 +776,18 @@ 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) + + args += rebase_path(outputs) + + deps = [ "//tools/v8_context_snapshot" ] +} + electron_paks("packed_resources") { if (is_mac) { output_dir = "$root_gen_dir/electron_repack"
patches/chromium/feat_add_support_for_embedder_snapshot_validation.patch+67 −0 added@@ -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 1abeb30142251c7abea1b33f7921f79d85532475..758022b4ca4c5cb69ff3df0399194dfca5c1b432 100644 +--- a/gin/v8_initializer.cc ++++ b/gin/v8_initializer.cc +@@ -76,11 +76,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; +@@ -225,6 +237,10 @@ constexpr std::string_view kV8FlagParam = "V8FlagParam"; + + } // 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().
patches/chromium/.patches+1 −0 modified@@ -137,3 +137,4 @@ build_set_mac_sdk_minimum_to_10.patch fix_add_macos_memory_query_fallback_to_avoid_crash.patch fix_resolve_dynamic_background_material_update_issue_on_windows_11.patch chore_restore_some_deprecated_wrapper_utility_in_gin.patch +feat_add_support_for_embedder_snapshot_validation.patch
shell/app/electron_main_delegate.cc+23 −1 modified@@ -19,15 +19,20 @@ #include "base/logging.h" #include "base/path_service.h" #include "base/strings/cstring_view.h" +#include "base/strings/string_number_conversions.cc" +#include "base/strings/string_util_internal.h" #include "chrome/common/chrome_paths.h" #include "chrome/common/chrome_switches.h" #include "components/content_settings/core/common/content_settings_pattern.h" #include "content/public/app/initialize_mojo_core.h" #include "content/public/common/content_switches.h" +#include "crypto/hash.h" #include "electron/buildflags/buildflags.h" #include "electron/fuses.h" #include "electron/mas.h" +#include "electron/snapshot_checksum.h" #include "extensions/common/constants.h" +#include "gin/v8_initializer.h" #include "sandbox/policy/switches.h" #include "services/tracing/public/cpp/stack_sampling/tracing_sampler_profiler.h" #include "shell/app/command_line_args.h" @@ -49,6 +54,7 @@ #include "third_party/abseil-cpp/absl/types/variant.h" #include "ui/base/resource/resource_bundle.h" #include "ui/base/ui_base_switches.h" +#include "v8/include/v8-snapshot.h" #if BUILDFLAG(IS_MAC) #include "shell/app/electron_main_delegate_mac.h" @@ -207,6 +213,20 @@ void RegisterPathProvider() { 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 std::string LoadResourceBundle(const std::string& locale) { @@ -230,7 +250,9 @@ std::string LoadResourceBundle(const std::string& locale) { return loaded_locale; } -ElectronMainDelegate::ElectronMainDelegate() = default; +ElectronMainDelegate::ElectronMainDelegate() { + gin::SetV8SnapshotValidator(base::BindRepeating(&ValidateV8Snapshot)); +} ElectronMainDelegate::~ElectronMainDelegate() = default;
Vulnerability mechanics
Generated by null/stub on May 9, 2026. Inputs: CWE entries + fix-commit diffs from this CVE's patches. Citations validated against bundle.
References
11- github.com/advisories/GHSA-vmqv-hx8q-j7mgghsaADVISORY
- nvd.nist.gov/vuln/detail/CVE-2025-55305ghsaADVISORY
- github.com/electron/electron/commit/23a02934510fcf951428e14573d9b2d2a3c4f28bnvdWEB
- github.com/electron/electron/commit/2e5a0b7220ebf955c6785cc5adb2e2b1cf77dac1nvdWEB
- github.com/electron/electron/commit/3f92511cdecc39f46b0e86cce40a0c691e301c9dnvdWEB
- github.com/electron/electron/commit/fdf29ce83870109d403f5c23ae529dbd0e8f4feenvdWEB
- github.com/electron/electron/pull/48101nvdWEB
- github.com/electron/electron/pull/48102nvdWEB
- github.com/electron/electron/pull/48103nvdWEB
- github.com/electron/electron/pull/48104nvdWEB
- github.com/electron/electron/security/advisories/GHSA-vmqv-hx8q-j7mgnvdWEB
News mentions
0No linked articles in our index yet.