Overview

Critical
Severity
CVSS
No
Exploited ITW
Fixed
Fix Status
ImpactUse after free in Chromoting
DescriptionUse after free in Chromoting
ComponentChromoting
Bug ClassUAF
Tracker504587882
Fix commit6a81578f1c26 (chromium/src) +7/-2
CISA KEVNot listed
CreditedGoogle
Disclosed2026-05-05

Files Changed

  • remoting/host/linux/pipewire_capture_stream.cc
  • remoting/host/linux/pipewire_capture_stream.h
From 6a81578f1c262475cd22c854c66dcbd7b689e47e Mon Sep 17 00:00:00 2001
From: Yuwei Huang <[email protected]>
Date: Tue, 21 Apr 2026 07:25:30 -0700
Subject: [PATCH] Fix UAF in PipewireCaptureStream due to member destruction order

Bug: 504587882
Change-Id: Id0839d82f626b0553e6d09cb56051f50d86798e5
Reviewed-on: https://chromium-review.googlesource.com/c/chromium/src/+/7781064
Auto-Submit: Yuwei Huang <[email protected]>
Commit-Queue: Joe Downing <[email protected]>
Reviewed-by: Joe Downing <[email protected]>
Cr-Commit-Position: refs/heads/main@{#1618163}
---

diff --git a/remoting/host/linux/pipewire_capture_stream.cc b/remoting/host/linux/pipewire_capture_stream.cc
index 70e0d48..bdacc0a 100644
--- a/remoting/host/linux/pipewire_capture_stream.cc
+++ b/remoting/host/linux/pipewire_capture_stream.cc
@@ -151,6 +151,11 @@
 
 PipewireCaptureStream::~PipewireCaptureStream() {
   DCHECK_CALLED_ON_VALID_SEQUENCE(sequence_checker_);
+
+  // StopScreenCastStream() joins the PipeWire thread, and SetObserver(null)
+  // clears the dangling Observer* before `callback_proxy_` is destroyed.
+  StopVideoCapture();
+  stream_->SetObserver(nullptr);
 }
 
 void PipewireCaptureStream::SetPipeWireStream(
diff --git a/remoting/host/linux/pipewire_capture_stream.h b/remoting/host/linux/pipewire_capture_stream.h
index d1c2468..32d7bbd 100644
--- a/remoting/host/linux/pipewire_capture_stream.h
+++ b/remoting/host/linux/pipewire_capture_stream.h
@@ -98,11 +98,11 @@
   webrtc::ScreenId screen_id_ GUARDED_BY_CONTEXT(sequence_checker_) = -1;
   base::WeakPtr<webrtc::DesktopCapturer::Callback> callback_
       GUARDED_BY_CONTEXT(sequence_checker_);
+  std::unique_ptr<CallbackProxy> callback_proxy_
+      GUARDED_BY_CONTEXT(sequence_checker_);
   webrtc::scoped_refptr<webrtc::SharedScreenCastStream> stream_
       GUARDED_BY_CONTEXT(sequence_checker_) =
           webrtc::SharedScreenCastStream::CreateDefault();
-  std::unique_ptr<CallbackProxy> callback_proxy_
-      GUARDED_BY_CONTEXT(sequence_checker_);
   base::ObserverList<CaptureStream::CursorObserver> cursor_observers_
       GUARDED_BY_CONTEXT(sequence_checker_);
   bool is_capturing_frame_ GUARDED_BY_CONTEXT(sequence_checker_) = false;
Loading diff…

Original Bug Report

reported by [email protected]

Potential Cross-thread UAF in PipewireCaptureStream due to member destruction order

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 potential Use-After-Free (UAF) vulnerability exists in the Linux Chrome Remote Desktop host due to the class member declaration order in PipewireCaptureStream. During stream teardown, a background PipeWire thread can make virtual method calls on an already-freed CallbackProxy object. An authenticated remote attacker could trigger this via SetVideoLayout messages, potentially leading to arbitrary code execution in the unsandboxed host process.

Affected files:

  • remoting/host/linux/pipewire_capture_stream.h
  • remoting/host/linux/pipewire_capture_stream.cc
  • third_party/webrtc/modules/desktop_capture/linux/wayland/shared_screencast_stream.cc

Estimated timestamp from git blame: 2025-08-28

Summary

A race condition leading to a potential heap use-after-free (UAF) exists in the Linux Wayland Remoting Host’s PipewireCaptureStream. The vulnerability is caused by an incorrect member declaration order in the PipewireCaptureStream class, where callback_proxy_ is destroyed before stream_. Because the background PipeWire thread loop managed by stream_ is only stopped when stream_ is destroyed, there is a race window where the background thread can access the already-freed callback_proxy_ and perform virtual calls, leading to a UAF.

Root Cause Analysis

In remoting/host/linux/pipewire_capture_stream.h, the class members are declared as follows:

webrtc::scoped_refptr<webrtc::SharedScreenCastStream> stream_
    GUARDED_BY_CONTEXT(sequence_checker_) =
        webrtc::SharedScreenCastStream::CreateDefault();
std::unique_ptr<CallbackProxy> callback_proxy_
    GUARDED_BY_CONTEXT(sequence_checker_);

In C++, class members are destroyed in the reverse order of their declaration. Therefore, when PipewireCaptureStream is destroyed, the callback_proxy_ is freed first, followed by the release of the stream_ reference.

The PipewireCaptureStream constructor registers callback_proxy_ as an observer using a raw pointer: stream_->SetObserver(callback_proxy_.get());.

The SharedScreenCastStream (in third_party/webrtc/modules/desktop_capture/linux/wayland/shared_screencast_stream.cc) maintains a background pw_thread_loop. This thread continues to invoke methods on the raw observer_ pointer until it is explicitly stopped via pw_thread_loop_stop() in the SharedScreenCastStreamPrivate destructor.

During the destruction of PipewireCaptureStream:

  1. callback_proxy_ is destroyed and its heap memory is freed.
  2. The background PipeWire thread receives a frame and calls ProcessBuffer().
  3. ProcessBuffer() checks if (observer_) (which is true, as the raw pointer is never cleared) and calls observer_->OnDesktopFrameChanged().
  4. This triggers a virtual method call on the freed callback_proxy_ memory.
  5. stream_ is released and the thread loop is finally stopped.

Potential Exploitation Steps

Note: These steps outline a potential path; our tooling agent has not executed a working proof of concept to verify arbitrary code execution.

  1. An attacker establishes an authenticated Chrome Remote Desktop (CRD) connection to a Linux Wayland host.
  2. The attacker uses heap grooming techniques via other CRD messages to prepare for a UAF vtable hijack.
  3. The attacker sends a SetVideoLayout message configured to remove an existing virtual monitor. This message is processed by the host regardless of whether the OS lock screen is active.
  4. The host processes the message, calling GnomeCaptureStreamManager::RemoveVirtualStream, which requests the compositor to stop the stream.
  5. The asynchronous callback OnStreamStopped erases the stream info, triggering the PipewireCaptureStream destructor.
  6. During the destruction race window, the active PipeWire thread attempts to process an incoming frame and executes observer_->OnDesktopFrameChanged().
  7. If the attacker successfully reclaimed the freed callback_proxy_ memory, the virtual call dereferences the attacker-controlled vtable, resulting in arbitrary code execution (RCE).

Impact

This vulnerability provides a classic primitive for Remote Code Execution (RCE) via a virtual call UAF. Because the remoting_me2me_host process runs unsandboxed with the privileges of the local user, successful exploitation results in full account compromise, effectively bypassing the OS lock screen.

Suggested Fix

  1. Reorder Declarations: In remoting/host/linux/pipewire_capture_stream.h, reorder the member variables so that callback_proxy_ is declared before stream_. This ensures stream_ (and its background thread) is destroyed prior to callback_proxy_.
  2. Explicit Cleanup: Alternatively, implement an explicit cleanup step in PipewireCaptureStream::~PipewireCaptureStream() that calls stream_->SetObserver(nullptr) and/or stream_->StopScreenCastStream() before the class members are naturally destroyed.

Evaluated with Chrome root at commit: 7353d249d9cacf9c7218e1d7b8a39cf39c72d646


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.

View on issue tracker