CVE-2026-8016
Overview
Changed Functions
| Function | Change | Notes |
|---|---|---|
ifthird_party/blink/renderer/modules/peerconnection/peer_connection_tracker.cc |
modified |
Files Changed
third_party/blink/renderer/modules/peerconnection/peer_connection_tracker.ccthird_party/blink/renderer/modules/peerconnection/rtc_peer_connection_handler.cc
Patch
From ac1c38ac1687520c54e6f3634c7a97c5a2ba7714 Mon Sep 17 00:00:00 2001 From: Andrew Paseltiner <[email protected]> Date: Tue, 31 Mar 2026 05:34:05 -0700 Subject: [PATCH] [RTC] Clear frame field in RTCPeerConnectionHandler::CloseAndUnregister To ensure that it isn't accessed after the connection is unregistered. Also add a null check for the frame in PeerConnectionTracker when processing stats reports to avoid potential null dereferences during asynchronous tasks. Ideally we would replace raw_ptr with WeakPersistent, but it is complicated by the layering requirements in this directory. We will investigate making that change in a followup. Fixed: 497695401 Change-Id: Ic7ab9b7d9d3f0a35692205c7e064143d75b12e58 Reviewed-on: https://chromium-review.googlesource.com/c/chromium/src/+/7711324 Commit-Queue: Andrew Paseltiner <[email protected]> Reviewed-by: Daniel Cheng <[email protected]> Cr-Commit-Position: refs/heads/main@{#1607798} --- diff --git a/third_party/blink/renderer/modules/peerconnection/peer_connection_tracker.cc b/third_party/blink/renderer/modules/peerconnection/peer_connection_tracker.cc index 85a26b88..c9191f7 100644 --- a/third_party/blink/renderer/modules/peerconnection/peer_connection_tracker.cc +++ b/third_party/blink/renderer/modules/peerconnection/peer_connection_tracker.cc @@ -369,7 +369,7 @@ base::ListValue result_list; - if (!pc_handler_) { + if (!pc_handler_ || !pc_handler_->frame()) { return result_list; } auto* local_frame = To<WebLocalFrameImpl>(*pc_handler_->frame()).GetFrame(); diff --git a/third_party/blink/renderer/modules/peerconnection/rtc_peer_connection_handler.cc b/third_party/blink/renderer/modules/peerconnection/rtc_peer_connection_handler.cc index f79506bb..388c2572 100644 --- a/third_party/blink/renderer/modules/peerconnection/rtc_peer_connection_handler.cc +++ b/third_party/blink/renderer/modules/peerconnection/rtc_peer_connection_handler.cc @@ -830,6 +830,7 @@ // Clear the pointer to client_ so that it does not interfere with // garbage collection. client_ = nullptr; + frame_ = nullptr; is_unregistered_ = true; // Reset the `PeerConnectionDependencyFactory` so we don't prevent it from
Original Bug Report
Potential UAF in RTCPeerConnectionHandler via dangling WebLocalFrame pointer
Project Fortify, an experimental security project, has identified the following potential security issue.
Overview: A Use-After-Free (UAF) vulnerability exists in RTCPeerConnectionHandler because it retains a raw_ptr to an Oilpan-managed WebLocalFrameImpl after the frame is removed. This pointer can be dereferenced during asynchronous WebRTC statistics collection (e.g., via chrome://webrtc-internals), leading to a virtual method call on freed memory.
Affected files:
third_party/blink/renderer/modules/peerconnection/rtc_peer_connection_handler.hthird_party/blink/renderer/modules/peerconnection/peer_connection_tracker.ccthird_party/blink/renderer/modules/peerconnection/rtc_peer_connection_handler.cc
Estimated timestamp from git blame: 2024-11-13
Description
A Use-After-Free (UAF) vulnerability exists in RTCPeerConnectionHandler because it maintains a raw_ptr<WebLocalFrame> frame_ that is not cleared when the associated WebLocalFrame is destroyed and garbage-collected.
RTCPeerConnectionHandler is owned by an RTCPeerConnection object. While the handler’s lifecycle is typically tied to the frame it resides in, an attacker can keep the RTCPeerConnection (and thus its handler) alive by maintaining a JavaScript reference to it in a parent window after the associated iframe has been removed.
When the iframe is removed, RTCPeerConnection::ContextDestroyed() is called, which triggers RTCPeerConnectionHandler::CloseAndUnregister(). However, CloseAndUnregister() fails to clear the frame_ member. Since WebLocalFrame (specifically its implementation WebLocalFrameImpl) is an Oilpan-managed object, it will eventually be garbage-collected after the iframe is removed. Because raw_ptr does not provide BackupRefPtr (MiraclePtr) protection for Oilpan-managed memory, the pointer remains dangling.
The vulnerability is triggered when asynchronous WebRTC statistics collection occurs. This is commonly performed by chrome://webrtc-internals, which polls for statistics every second. The polling mechanism (PeerConnectionTracker::GetStandardStats()) creates an InternalStandardStatsObserver that holds a WeakPtr to the RTCPeerConnectionHandler.
If the statistics are delivered after the frame has been garbage-collected but while the handler is still alive, the observer’s WeakPtr check passes. It then calls pc_handler_->frame() and dereferences the dangling pointer in InternalStandardStatsObserver::ReportToList (at peer_connection_tracker.cc:375).
Impact
This is a Use-After-Free on the Oilpan heap. The call to To<WebLocalFrameImpl>(*pc_handler_->frame()) invokes IsA<WebLocalFrameImpl>, which triggers a virtual function call (IsWebLocalFrame()) on the freed object. This can potentially be exploited for Remote Code Execution (RCE) in the renderer process by using Oilpan heap spraying to control the memory of the freed WebLocalFrameImpl object and hijacking the virtual method call.
Potential steps an attacker would follow to trigger the vulnerability
Note: These are theoretical steps, as our tooling agent cannot yet run code.
- Prerequisite: The attacker convinces the user to open
chrome://webrtc-internalsin one tab (or leverages an existing debugging session). This triggers periodic 1-second polling for WebRTC statistics across all renderer processes viaPeerConnectionTrackerHost::GetStandardStats(). - In a separate tab, the user loads a malicious HTML page controlled by the attacker.
- The malicious page creates a child
<iframe>and injects JavaScript execution into it. - Inside the iframe, the attacker’s script creates a new
RTCPeerConnectionobject. - The
RTCPeerConnectionHandlerstores araw_ptr<WebLocalFrame> frame_pointing to the iframe’sWebLocalFrameImpl. - To prevent the
RTCPeerConnectionfrom being destroyed when the iframe is removed, the attacker’s script escapes a reference to it into the parent window’s global scope (e.g.,window.parent.pc = pc;). - The 1-second timer from
chrome://webrtc-internalsfires, callingPeerConnectionTracker::GetStandardStats(). This iterates over active peer connections and creates an asynchronousInternalStandardStatsObserver, passing it to the native WebRTC engine to gather stats on a background thread. - The Race Condition: While the WebRTC thread is asynchronously gathering stats, the attacker’s script in the parent window removes the iframe from the DOM (
iframe.remove()). - Removing the iframe calls
RTCPeerConnectionHandler::CloseAndUnregister(). This unregisters the handler but fails to clear theframe_member. - The attacker forces a Blink/V8 garbage collection using
window.gc(). Since the iframe’s execution context was destroyed, theWebLocalFrameImplobject loses its strong references and is reclaimed by the Oilpan garbage collector. Theframe_member is now a dangling pointer. - The WebRTC signaling thread finishes stats collection and calls
InternalStandardStatsObserver::OnStatsDelivered(), which posts a task to the main thread to executeReportToList(). - On the main thread,
ReportToList()checks if theWeakPtrto theRTCPeerConnectionHandleris valid. Because the parent window kept the JS object alive, the check passes. ReportToList()executesauto* local_frame = To<WebLocalFrameImpl>(*pc_handler_->frame()).GetFrame();. This returns the danglingraw_ptrpointing to the freedWebLocalFrameImplobject.- The custom Blink cast helper
To<WebLocalFrameImpl>invokes a virtual method call (IsWebLocalFrame()) on the freed object. If the attacker sprayed the Oilpan heap prior to this step, they hijack the vtable, leading to RCE.
Proposed Fix
There are two primary ways to fix this vulnerability:
- Clear the pointer on close: Explicitly set
frame_ = nullptr;withinRTCPeerConnectionHandler::CloseAndUnregister(). This ensures the dangling pointer is neutralized when the context is destroyed. - Use Oilpan Smart Pointers (Preferred): Change the
raw_ptr<WebLocalFrame> frame_member inRTCPeerConnectionHandlerto aWeakMember<WebLocalFrame>. This will automatically clear the pointer when theWebLocalFrameImplis garbage-collected by Oilpan, preventing the UAF entirely. IfRTCPeerConnectionHandleris not currently an Oilpan-managed object, it may need to be made one, or it could hold the frame as aWeakPersistent<WebLocalFrame>(thoughWeakPersistentis generally discouraged in favor of making the owner garbage-collected).
Evaluated with Chrome root at commit: 876d480da1f794d87813cfa2e6ff4fcf9771e939
Results so far have been promising, but there can be wrong deductions. If this proves to be a false positive, please close as WAI; data from false positives will be used to improve accuracy over time. And please feel free to reach out to me directly if you have concerns or feedback on the project.