From e9d5977bde5651870de805e6dd6ab7f18aaa43d5 Mon Sep 17 00:00:00 2001 From: "trop[bot]" <37223003+trop[bot]@users.noreply.github.com> Date: Tue, 21 Apr 2026 18:11:21 +0200 Subject: [PATCH] fix: add crash diagnostics for ARM64 power notification crash (#51205) On ARM64 Windows, UnregisterSuspendResumeNotification (user32) forwards to PowerUnregisterSuspendResumeNotification (powrprof), which treats the HPOWERNOTIFY handle as a pointer and dereferences it. The user32 API returns an opaque handle, not a pointer-backed allocation, causing an access violation at shutdown. Add crash keys (pm-reg-handle, pm-reg-memstate, pm-unreg-memstate) to capture - The handle value - VirtualQuery memory state at both registration and unregistration If the handle address is MEM_FREE, it confirms the handle is an opaque index and powrprof is incorrectly dereferencing it. If MEM_COMMIT, it would indicate a use-after-free of the underlying allocation. Refs https://github.com/MicrosoftDocs/sdk-api/blob/docs/sdk-api-src/content/powerbase/nf-powerbase-powerunregistersuspendresumenotification.md Co-authored-by: trop[bot] <37223003+trop[bot]@users.noreply.github.com> Co-authored-by: deepak1556 --- .../api/electron_api_power_monitor_win.cc | 40 +++++++++++++++++++ 1 file changed, 40 insertions(+) diff --git a/shell/browser/api/electron_api_power_monitor_win.cc b/shell/browser/api/electron_api_power_monitor_win.cc index 9c223dcaf7..6267699e72 100644 --- a/shell/browser/api/electron_api_power_monitor_win.cc +++ b/shell/browser/api/electron_api_power_monitor_win.cc @@ -7,9 +7,14 @@ #include #include +#include "base/debug/alias.h" +#include "base/debug/crash_logging.h" #include "base/logging.h" +#include "base/strings/string_number_conversions.h" +#include "base/win/windows_handle_util.h" #include "base/win/windows_types.h" #include "base/win/wrapped_window_proc.h" +#include "components/crash/core/common/crash_key.h" #include "content/public/browser/browser_task_traits.h" #include "content/public/browser/browser_thread.h" #include "ui/gfx/win/hwnd_util.h" @@ -20,6 +25,18 @@ namespace { const wchar_t kPowerMonitorWindowClass[] = L"Electron_PowerMonitorHostWindow"; +std::string DescribeMemoryState(void* address) { + MEMORY_BASIC_INFORMATION mbi = {}; + if (!VirtualQuery(address, &mbi, sizeof(mbi))) { + return "VirtualQuery failed err=" + base::NumberToString(::GetLastError()); + } + // Refs + // https://learn.microsoft.com/en-us/windows/win32/api/winnt/ns-winnt-memory_basic_information + return "state=" + base::NumberToString(mbi.State) + + " protect=" + base::NumberToString(mbi.Protect) + + " type=" + base::NumberToString(mbi.Type); +} + } // namespace namespace api { @@ -49,12 +66,35 @@ void PowerMonitor::InitPlatformSpecificMonitors() { static_cast(window_), DEVICE_NOTIFY_WINDOW_HANDLE); PLOG_IF(ERROR, !power_notify_handle_) << "RegisterSuspendResumeNotification failed"; + + // On ARM64 Windows, UnregisterSuspendResumeNotification may + // crash in powrprof!PowerUnregisterSuspendResumeNotification by dereferencing + // the HPOWERNOTIFY handle. VirtualQuery on the handle address reveals + // whether it was ever a valid pointer. + // Use static crash keys (not SCOPED_) so they persist until the crash. + if (power_notify_handle_) { + static crash_reporter::CrashKeyString<16> reg_handle_key("pm-reg-handle"); + static crash_reporter::CrashKeyString<64> reg_memstate_key( + "pm-reg-memstate"); + reg_handle_key.Set( + base::NumberToString(base::win::HandleToUint32(power_notify_handle_))); + reg_memstate_key.Set(DescribeMemoryState(power_notify_handle_)); + } } void PowerMonitor::DestroyPlatformSpecificMonitors() { if (window_) { WTSUnRegisterSessionNotification(window_); if (power_notify_handle_) { + // Capture handle value and memory state at unregistration time. + // debug::Alias forces the raw value onto the stack. + auto handle_value = base::win::HandleToUint32(power_notify_handle_); + base::debug::Alias(&handle_value); + + static crash_reporter::CrashKeyString<64> unreg_memstate_key( + "pm-unreg-memstate"); + unreg_memstate_key.Set(DescribeMemoryState(power_notify_handle_)); + UnregisterSuspendResumeNotification(power_notify_handle_); power_notify_handle_ = nullptr; }