Critical watch CVSS 8.8 chrome UAF 📄 Reporter bug report 🔧 Commit mapped

Overview

Critical
Severity
8.8
CVSS
No
Exploited ITW
Fixed
Fix Status
ImpactUse after free in Cast
DescriptionUse after free in Cast
ComponentCast
Sub-componentInternals>Cast
Bug ClassUAF
Action tierwatch — corruption primitive but gated (uncommon) — not default-reachable
Computed by PatchGap's deterministic exploitability ruler from the fixing commit. patch-now = RCE-class & attacker-reachable in a default config, or exploited in the wild; watch = real security signal below that bar; noise = no security relevance.
Tracker513136593
Fix commit034c57cb7a2a (chromium/src) +4/-1
CWECWE-416 (Use-after-free)
CVSS vectorCVSS:3.1/AV:A/AC:L/PR:N/UI:N/S:U/C:H/I:H/A:H
CISA KEVNot listed
CreditedGoogle
Reported2026-05-14 (per vendor advisory)
Fix landed2026-05-18 (public git)
Disclosed2026-06-02
Patch gap15 days — fix public in source before the advisory

Background

`openscreen_platform::UdpSocket`
the Chromium-side adapter that wraps a Mojo udp_socket_ and bridges incoming UDP datagrams to an Open Screen client_ 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 with udp_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.

Key insight
The code re-armed the socket by touching an instance member after invoking a synchronous client callback that is allowed to free the instance; the fix adds a base::WeakPtr liveness check so the post-callback ReceiveMore(1) is skipped once the object has been destroyed.

Attack Path

  1. Establish a Cast UDP flow An attacker drives the Cast/Open Screen path so that UdpSocket::OnReceived is invoked for an inbound datagram with a registered client_.
  2. Destroy the socket inside the callback The client’s OnRead handler, under attacker-influenced state, synchronously tears down the owning object, freeing the UdpSocket and its udp_socket_ member.
  3. Dangling re-arm Control returns to OnReceived, which executes udp_socket_->ReceiveMore(1) on the freed instance, dereferencing freed memory (use-after-free).
  4. Downstream corruption Exploitation of the freed allocation into a read/write primitive or code execution is not shown by the diff (inferred).

Impact Assessment

An attacker who can arrange for a Cast client’s 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

FunctionChangeNotes
if
components/openscreen_platform/udp_socket.cc
modified

Files Changed

  • components/openscreen_platform/udp_socket.cc

Audit Directions

  • Member access after synchronous callback
    Audit any handler that invokes a client/delegate callback and then touches this or an instance member; the callback may delete the object.
  • Re-arm / continuation patterns
    Review completion handlers that re-schedule I/O (ReceiveMore, Read, Watch) after notifying a consumer, ensuring a WeakPtr or equivalent guard precedes the continuation.
  • Open Screen / Cast adapters
    Sweep other openscreen_platform bridge classes where Mojo-backed resources are re-armed after delivering data to Open Screen clients.
From 034c57cb7a2a215024822b53ab27b860e05fd348 Mon Sep 17 00:00:00 2001
From: Jordan Bayles <jophba@chromium.org>
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 <muyaoxu@google.com>
Commit-Queue: Jordan Bayles <jophba@chromium.org>
Commit-Queue: Muyao Xu <muyaoxu@google.com>
Auto-Submit: Jordan Bayles <jophba@chromium.org>
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,
Loading diff…

Original Bug Report

reported by vm...@google.com

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.cc
  • components/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:

  1. UdpSocket::OnReceived is called with a network error (net_result != net::OK).
  2. client_->OnRead is called, which notifies the openscreen::cast::Environment (third_party/openscreen/src/cast/streaming/public/environment.cc:145).
  3. The error propagates synchronously: Environment::OnError -> ReceiverSession::OnSocketInvalid -> ReceiverSessionClient::OnError (components/cast_streaming/browser/cast_streaming_session.cc:487).
  4. The application layer handles the error by calling StopApplication(), which executes receiver_session_client_.reset() (components/cast_receiver/browser/streaming_runtime_application.cc:100).
  5. This triggers a synchronous cascade of destructors, eventually calling ~UdpSocket() and freeing the memory while OnReceived is still on the stack.
  6. Control returns to UdpSocket::OnReceived, which dereferences the destructed udp_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.

View on issue tracker