Introduction
A use-after-free in Electron's offscreen rendering path quietly introduced a memory corruption risk for any desktop application that combines offscreen rendering with permissive child window creation. Given that Electron powers applications like VS Code, Slack, Discord, Signal, Docker, Notion, and AI tools from OpenAI and Anthropic, the potential blast radius of framework-level memory safety bugs is substantial, even when the vulnerable configuration is narrow.
CVE-2026-34774 carries a CVSS 8.1 (High) score and affects Electron versions prior to 39.8.1, 40.7.0, and 41.0.0. The flaw is triggered when a parent offscreen WebContents is destroyed while a child window spawned via window.open() remains alive. Subsequent paint frames on the orphaned child dereference freed memory belonging to the now-destroyed parent. Applications that do not use offscreen rendering (webPreferences.offscreen: true) or that deny child windows through their setWindowOpenHandler are entirely unaffected.
Technical Information
Root Cause: Unsafe base::Unretained(this) in Paint Callback Binding
The vulnerability originates in how Electron wires up the paint callback when creating a child window from an offscreen-rendered parent. Inside MaybeOverrideCreateParamsForNewWindow in shell/browser/api/electron_api_web_contents.cc, the code constructs an OffScreenWebContentsView for the child and binds its paint callback directly to the parent WebContents using base::Unretained(this):
// Before the fix — vulnerable code path auto* view = new OffScreenWebContentsView( false, offscreen_use_shared_texture_, offscreen_shared_texture_pixel_format_, base::BindRepeating(&WebContents::OnPaint, base::Unretained(this)));
The problem is one of object lifetimes. At the point where MaybeOverrideCreateParamsForNewWindow executes, the child WebContents does not yet exist. The code fills this gap by pointing the callback at the parent. base::Unretained(this) creates a raw, non prevent dangling pointer; it performs no prevent lifetime checks and does not invalidate when the referenced object is destroyed.
The Use-After-Free Trigger
The sequence that triggers the vulnerability is:
- An Electron application enables offscreen rendering (
webPreferences.offscreen: true) and configuressetWindowOpenHandlerto permit child windows. - A child window is opened via
window.open(). During creation, the child'sOffScreenWebContentsViewpaint callback is bound to the parentWebContentsviabase::Unretained(this). - The parent offscreen
WebContentsis destroyed (for example, the parentBrowserWindowis closed or garbage collected). - The child window remains open and continues to render. When the next paint frame fires, the child's callback invokes
WebContents::OnPainton the now-freed parent memory. - This dereference of freed memory constitutes a use-after-free (CWE-416). Depending on the state of the heap, this results in a crash or memory corruption.
Attack Vector Analysis
The CVSS 3.1 vector is AV:N/AC:H/PR:N/UI:N/S:U/C:H/I:H/A:H. The network attack vector (AV:N) means the exploit could be delivered remotely through malicious web content loaded into the vulnerable Electron application. The high attack complexity (AC:H) reflects the need for an attacker to carefully orchestrate the timing of parent destruction relative to child paint frames. Notably, no privileges (PR:N) and no user interaction (UI:N) are required. If successfully exploited, the impact spans confidentiality, integrity, and availability, all rated High.
An attacker targeting this vulnerability would need to:
- Identify an Electron application that uses offscreen rendering and permits child windows.
- Deliver content (likely via a loaded URL or injected script) that opens a child window via
window.open(). - Trigger destruction of the parent offscreen
WebContentswhile the child window is still alive. - Wait for the child window to render a paint frame, invoking the dangling callback into freed memory.
The high attack complexity stems from the precise timing required and the specific configuration prerequisites.
Patch Information
The Electron team addressed CVE-2026-34774 in commit 5fded6ae, merged on March 10, 2026, via PR #50184. The fix was published as part of Electron versions 39.8.1, 40.7.0, and 41.0.0, and is tracked under GitHub Security Advisory GHSA-532v-xpq5-8h95.
The patch is a compact but precise 17 line addition across three files, correcting two design flaws in a single targeted refactor. Here is a walkthrough of each change.
Step 1: Neutralize the Unsafe Binding at Creation Time
In shell/browser/api/electron_api_web_contents.cc, within MaybeOverrideCreateParamsForNewWindow, the dangerous base::BindRepeating(&WebContents::OnPaint, base::Unretained(this)) was replaced with base::DoNothing():
// shell/browser/api/electron_api_web_contents.cc — MaybeOverrideCreateParamsForNewWindow if (is_offscreen) { + // Use a no-op callback here. The real OnPaint callback will be bound + // to the child WebContents in AddNewContents via SetCallback(). auto* view = new OffScreenWebContentsView( false, offscreen_use_shared_texture_, - offscreen_shared_texture_pixel_format_, - base::BindRepeating(&WebContents::OnPaint, base::Unretained(this))); + offscreen_shared_texture_pixel_format_, base::DoNothing()); create_params->view = view; create_params->delegate_view = view; }
At the time the create params are being assembled, the child WebContents does not yet exist. Previously, the code filled the gap by pointing at the parent, introducing the dangling pointer hazard. Now, a harmless no-op placeholder takes its place.
Step 2: Rebind the Callback to the Correct Owner
Later in the same file, inside AddNewContents (called once the child WebContents has been fully created), a new block rebinds the paint callback to the child using GetWeakPtr() instead of a raw pointer:
// shell/browser/api/electron_api_web_contents.cc — AddNewContents auto api_web_contents = CreateAndTake(isolate, std::move(new_contents), type); + // Rebind the paint callback to the child WebContents. + if (auto* osr_view = api_web_contents->GetOffScreenWebContentsView()) { + osr_view->SetCallback(base::BindRepeating(&WebContents::OnPaint, + api_web_contents->GetWeakPtr())); + }
The use of GetWeakPtr() is the critical safety improvement. A weak pointer automatically invalidates when the referenced object is destroyed, so even if lifetimes mismatch in an unexpected way, the callback will simply no-op rather than dereference freed memory.
Step 3: Expose SetCallback on the View Class
To support the deferred rebinding, a new public method SetCallback was added to OffScreenWebContentsView:
// shell/browser/osr/osr_web_contents_view.h void SetWebContents(content::WebContents*); void SetNativeWindow(NativeWindow* window); + void SetCallback(const OnPaintCallback& callback);
// shell/browser/osr/osr_web_contents_view.cc +void OffScreenWebContentsView::SetCallback(const OnPaintCallback& callback) { + callback_ = callback; +}
This method overwrites the stored callback_ member, replacing the initial base::DoNothing() placeholder with the properly bound child callback.
In summary, the patch eliminates the cross-lifetime ownership hazard by never binding the paint callback to the parent WebContents, and adds a safety net by using GetWeakPtr() rather than base::Unretained() for the replacement binding. The total change is 17 additions and 2 deletions across three files, touching only the offscreen rendering path.
Affected Systems and Versions
The vulnerability affects Electron applications that meet both of the following conditions:
- Offscreen rendering is enabled (
webPreferences.offscreen: true) - The
setWindowOpenHandlerpermits child windows
Applications that do not use offscreen rendering, or that deny child windows, are not affected.
| Release Branch | Vulnerable Versions | Patched Version |
|---|---|---|
| Branch 39 | Prior to 39.8.1 | 39.8.1 |
| Branch 40 | 40.0.0-alpha.1 to prior to 40.7.0 | 40.7.0 |
| Branch 41 | 41.0.0-alpha.1 to prior to 41.0.0 | 41.0.0 |
Vendor Security History
Electron operates under the OpenJS Foundation as an Impact Project, having graduated from incubation in June 2020. The project maintains a dedicated Security Working Group responsible for handling vulnerability reports and coordinating fixes. Their security policy provides a clear reporting process and an escalation path to the OpenJS Foundation if responses are delayed. For critical issues, the Security Working Group issues advance Need to Know notifications to trusted partners via private channels, allowing major application developers to prepare fixes before public disclosure. The timeline for CVE-2026-34774 reflects this maturity: the fix was merged on March 10, 2026, the GitHub Security Advisory was published on April 2, 2026, and the CVE record followed on April 3, 2026.
References
- GitHub Security Advisory GHSA-532v-xpq5-8h95
- CVE Record: CVE-2026-34774
- NVD: CVE-2026-34774
- Patch Commit 5fded6ae
- GitHub Advisory Database: CVE-2026-34774
- Snyk Vulnerability: SNYK-JS-ELECTRON-15875869
- Electron Security Documentation
- Electron Security Working Group: Membership and Notifications
- Electron SECURITY.md
- Electron: Why Electron
- Electron becomes an OpenJS Foundation Impact Project



