fix: add crash diagnostics for ARM64 power notification crash (#51198)

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
This commit is contained in:
Robo
2026-04-21 21:20:51 +09:00
committed by GitHub
parent 9861250310
commit 7ef359d95c

View File

@@ -7,9 +7,14 @@
#include <windows.h>
#include <wtsapi32.h>
#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<HANDLE>(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;
}