Repository: electron/electron (language: cpp)

Issue:
GPUInfoManager destruction crashes because of the destruction order of gin_helper::Promise and Node/V8
### Preflight Checklist

- [x] I have read the [Contributing Guidelines](https://github.com/electron/electron/blob/main/CONTRIBUTING.md) for this project.
- [x] I agree to follow the [Code of Conduct](https://github.com/electron/electron/blob/main/CODE_OF_CONDUCT.md) that this project adheres to.
- [x] I have searched the [issue tracker](https://www.github.com/electron/electron/issues) for a bug report that matches the one I want to file, without success.

### Electron Version

36.0.0

### What operating system(s) are you using?

Windows

### Operating System Version

Windows 10

### What arch are you using?

ia32

### Last Known Working Electron version

_No response_

### Expected Behavior

This code is not expected to crash.
```
// browser.js
const { app } = require('electron')

app.getGPUInfo('complete').then((info) => {
  console.log("app.getGPUInfo", info)
});

app.quit();
```

### Actual Behavior

The browser process crashes when desctructing gin_helper::Promise in GPUInfoManager

![Image](https://github.com/user-attachments/assets/7fb96c15-c6c1-4462-a459-eced883b7646)

### Testcase Gist URL

_No response_

### Additional Information

This issue is caused by the destruction order of  gin_helper::Promise in GPUInfoManager and Node/V8 environment. Promise must be destroyed before Node/V8, or the destruction of gin_helper::Promise will access Node/V8 and cause UAF. 

Node/V8 is destroyed in [ElectronBrowserMainParts::PostMainMessageLoopRun](https://github.com/electron/electron/blob/main/shell/browser/electron_browser_main_parts.cc#L591), which happens frist.
GPUInfoManager is destroyed by base::AtExitManager in [ContentMainRunnerImpl::Shutdown](https://source.chromium.org/chromium/chromium/src/+/main:content/app/content_main_runner_impl.cc;l=1335?q=ContentMainRunnerImpl::Shut), which happens later.



Discussion / hints:

Fix patch:
diff --git a/shell/browser/api/gpuinfo_manager.cc b/shell/browser/api/gpuinfo_manager.cc
index 4a2f4f30e4154..b68d7e27e62a9 100644
--- a/shell/browser/api/gpuinfo_manager.cc
+++ b/shell/browser/api/gpuinfo_manager.cc
@@ -17,7 +17,9 @@
 namespace electron {
 
 GPUInfoManager* GPUInfoManager::GetInstance() {
-  return base::Singleton<GPUInfoManager>::get();
+  // will be deleted by CleanedUpAtExit::DoCleanup
+  static GPUInfoManager* instance = new GPUInfoManager();
+  return instance;
 }
 
 GPUInfoManager::GPUInfoManager()
diff --git a/shell/browser/api/gpuinfo_manager.h b/shell/browser/api/gpuinfo_manager.h
index 3ee39475101e7..b2f5cae518ea1 100644
--- a/shell/browser/api/gpuinfo_manager.h
+++ b/shell/browser/api/gpuinfo_manager.h
@@ -11,6 +11,7 @@
 #include "content/browser/gpu/gpu_data_manager_impl.h"  // nogncheck
 #include "content/public/browser/gpu_data_manager.h"
 #include "content/public/browser/gpu_data_manager_observer.h"
+#include "shell/common/gin_helper/cleaned_up_at_exit.h"
 
 namespace gin_helper {
 template <typename T>
@@ -20,7 +21,8 @@ class Promise;
 namespace electron {
 
 // GPUInfoManager is a singleton used to manage and fetch GPUInfo
-class GPUInfoManager : private content::GpuDataManagerObserver {
+class GPUInfoManager : private content::GpuDataManagerObserver,
+                       public gin_helper::CleanedUpAtExit {
  public:
   static GPUInfoManager* GetInstance();
 


Test patch: