CVE-2026-10890
Overview
Background
- `openscreen_platform::UdpSocket`
- the Chromium-side adapter that wraps a Mojo
udp_socket_and bridges incoming UDP datagrams to an Open Screenclient_used by the Cast stack. - `OnReceived`
- the receive-completion handler invoked per datagram; it forwards the result to
client_->OnRead(...)and then re-arms the socket withudp_socket_->ReceiveMore(1). - `base::WeakPtr`
- a non-owning pointer that automatically becomes null when the referent object is destroyed, allowing a caller to detect destruction before dereferencing.
Root Cause Analysis
In UdpSocket::OnReceived, the handler delivers each datagram to the client via client_->OnRead(this, ...) and then unconditionally calls udp_socket_->ReceiveMore(1) to request the next read. The call to client_->OnRead is a synchronous callback into client code, and that client is permitted to destroy the UdpSocket instance from within the callback. When it does, the subsequent udp_socket_->ReceiveMore(1) dereferences the udp_socket_ member of an already-freed this, a use-after-free.
The fix captures weak_this = weak_ptr_factory_.GetWeakPtr() at function entry and only calls ReceiveMore(1) if weak_this is still valid, so a client that destroys the socket during OnRead short-circuits the re-arm. The invariant violated was that OnReceived assumed this outlived the client callback; the weak-pointer guard restores that assumption by detecting self-destruction.
base::WeakPtr liveness check so the post-callback ReceiveMore(1) is skipped once the object has been destroyed.Attack Path
- Establish a Cast UDP flow
An attacker drives the Cast/Open Screen path so that
UdpSocket::OnReceivedis invoked for an inbound datagram with a registeredclient_. - Destroy the socket inside the callback
The client’s
OnReadhandler, under attacker-influenced state, synchronously tears down the owning object, freeing theUdpSocketand itsudp_socket_member. - Dangling re-arm
Control returns to
OnReceived, which executesudp_socket_->ReceiveMore(1)on the freed instance, dereferencing freed memory (use-after-free). - Downstream corruption Exploitation of the freed allocation into a read/write primitive or code execution is not shown by the diff (inferred).
Impact Assessment
OnRead callback to destroy the UdpSocket triggers a use-after-free on the udp_socket_ member within the process hosting the Cast/Open Screen networking stack. The vendor advisory rates this critical and labels it a use-after-free in Cast; any escalation beyond the dangling dereference (to a controllable primitive or code execution) is (inferred) and not demonstrated by the patch. Preconditions are the ability to influence client-side teardown during datagram delivery; the advisory does not state in-the-wild exploitation.Changed Functions
| Function | Change | Notes |
|---|---|---|
ifcomponents/openscreen_platform/udp_socket.cc |
modified |
Files Changed
components/openscreen_platform/udp_socket.cc
Audit Directions
- Member access after synchronous callbackAudit any handler that invokes a client/delegate callback and then touches
thisor an instance member; the callback may delete the object. - Re-arm / continuation patternsReview completion handlers that re-schedule I/O (
ReceiveMore,Read,Watch) after notifying a consumer, ensuring aWeakPtror equivalent guard precedes the continuation. - Open Screen / Cast adaptersSweep other
openscreen_platformbridge classes where Mojo-backed resources are re-armed after delivering data to Open Screen clients.
Patch
From 034c57cb7a2a215024822b53ab27b860e05fd348 Mon Sep 17 00:00:00 2001 From: Jordan Bayles <[email protected]> Date: Mon, 18 May 2026 16:34:50 -0700 Subject: [PATCH] Fix UAF in openscreen_platform::UdpSocket::OnReceived This CL adds a weak pointer guard before calling udp_socket_->ReceiveMore(1) in UdpSocket::OnReceived to prevent a Use-After-Free if the client's OnRead callback synchronously destroys the UdpSocket instance. Bug: 513136593 Change-Id: I6253d17915b1c03488d403117dbf6edca30dc4bd Reviewed-on: https://chromium-review.googlesource.com/c/chromium/src/+/7856453 Reviewed-by: Muyao Xu <[email protected]> Commit-Queue: Jordan Bayles <[email protected]> Commit-Queue: Muyao Xu <[email protected]> Auto-Submit: Jordan Bayles <[email protected]> Cr-Commit-Position: refs/heads/main@{#1632485} --- diff --git a/components/openscreen_platform/udp_socket.cc b/components/openscreen_platform/udp_socket.cc index 1fc4bf3..ff858f8a 100644 --- a/components/openscreen_platform/udp_socket.cc +++ b/components/openscreen_platform/udp_socket.cc @@ -152,6 +152,7 @@ int32_t net_result, const std::optional<net::IPEndPoint>& source_endpoint, std::optional<base::span<const uint8_t>> data) { + base::WeakPtr<UdpSocket> weak_this = weak_ptr_factory_.GetWeakPtr(); if (net_result != net::OK) { client_->OnRead(this, Error::Code::kSocketReadFailure); } else if (data) { @@ -163,7 +164,9 @@ client_->OnRead(this, std::move(packet)); } - udp_socket_->ReceiveMore(1); + if (weak_this) { + udp_socket_->ReceiveMore(1); + } } void UdpSocket::BindCallback(int32_t result,
Original Bug Report
Potential Browser-process UAF in openscreen_platform::UdpSocket::OnReceived
Project Fortify, an experimental security project, has identified the following potential security issue. If you’re a feature owner CC-ed on this bug, please do your best to review these reports without the Chrome Security team. Please see https://chromium.googlesource.com/chromium/src/+/main/docs/security/ai-generated-security-bugs-faq.md for more information.
Overview: A Use-After-Free (UAF) vulnerability potentially exists in the browser process within openscreen_platform::UdpSocket::OnReceived. A reentrant callback can synchronously trigger the destruction of the UdpSocket instance, leading to an invalid member access when execution returns to the function.
Affected files:
components/openscreen_platform/udp_socket.cccomponents/openscreen_platform/udp_socket.h
Estimated timestamp from git blame: 2019-10-21
Root Cause Analysis
A potential Use-After-Free (UAF) has been identified in openscreen_platform::UdpSocket::OnReceived in components/openscreen_platform/udp_socket.cc. This method is the handler for network::mojom::UDPSocketListener, which is dispatched by a mojo::Receiver stored within the UdpSocket object.
When a network error or a specific packet is received, OnReceived calls a client callback (client_->OnRead). In certain configurations, such as the Cast receiver’s streaming application, this callback can trigger a synchronous teardown of the ownership chain that includes the UdpSocket instance. Because this destruction is synchronous, the UdpSocket object (including its this pointer and all members) is freed before the callback returns. Execution then proceeds to access the udp_socket_ member of the now-deleted object.
In components/openscreen_platform/udp_socket.cc:
void UdpSocket::OnReceived(
int32_t net_result,
const std::optional<net::IPEndPoint>& source_endpoint,
std::optional<base::span<const uint8_t>> data) {
if (net_result != net::OK) {
client_->OnRead(this, Error::Code::kSocketReadFailure); // (A) Synchronous callback
} else if (data) {
// ... packet processing ...
client_->OnRead(this, std::move(packet)); // (A') Synchronous callback
}
udp_socket_->ReceiveMore(1); // (B) UAF access here
}
The udp_socket_ member is a mojo::Remote<network::mojom::UDPSocket>. Since the access occurs on a freed this pointer and the member is a value type, MiraclePtr (BRP) does not provide protection in this specific code path.
Potential Destruction Chain
Based on code analysis, a suggested sequence for triggering the synchronous destruction in a Cast receiver context is:
UdpSocket::OnReceivedis called with a network error (net_result != net::OK).client_->OnReadis called, which notifies theopenscreen::cast::Environment(third_party/openscreen/src/cast/streaming/public/environment.cc:145).- The error propagates synchronously:
Environment::OnError->ReceiverSession::OnSocketInvalid->ReceiverSessionClient::OnError(components/cast_streaming/browser/cast_streaming_session.cc:487). - The application layer handles the error by calling
StopApplication(), which executesreceiver_session_client_.reset()(components/cast_receiver/browser/streaming_runtime_application.cc:100). - This triggers a synchronous cascade of destructors, eventually calling
~UdpSocket()and freeing the memory whileOnReceivedis still on the stack. - Control returns to
UdpSocket::OnReceived, which dereferences the destructedudp_socket_member.
Impact
This is a browser-process UAF. An attacker who can trigger this state (e.g., a compromised network process or potentially via crafted local network traffic in a Cast receiver environment) could achieve arbitrary code execution in the browser process by performing heap grooming during the synchronous teardown to control the freed memory.
Suggested Fix
The call to udp_socket_->ReceiveMore(1) should be guarded by a weak pointer check to ensure the object still exists after the client callback returns.
void UdpSocket::OnReceived(...) {
base::WeakPtr<UdpSocket> weak_this = weak_ptr_factory_.GetWeakPtr();
// ... existing callback logic ...
if (weak_this) {
udp_socket_->ReceiveMore(1);
}
}
Alternatively, ensure that the destruction of the streaming session always occurs asynchronously (e.g., via base::SequencedTaskRunner::PostTask).
Evaluated with Chrome root at commit: b3153093eb3c78c3e88ccf562bcbc20437a04b0e
Results so far have been promising, but there can be wrong deductions. Feel free to adjust as follows:
- If you are familiar with the severity guidelines, you may adjust the severity.
- If this is a false positive, and there’s no work to be done, please close as WAI.
- If there is work to do here but not a vulnerability, please change the issue type to Task/Bug/FR.
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.