CVE-2026-7898
Overview
Files Changed
remoting/host/linux/pipewire_capture_stream.ccremoting/host/linux/pipewire_capture_stream.h
Patch
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;
Original Bug Report
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.hremoting/host/linux/pipewire_capture_stream.ccthird_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:
callback_proxy_is destroyed and its heap memory is freed.- The background PipeWire thread receives a frame and calls
ProcessBuffer(). ProcessBuffer()checksif (observer_)(which is true, as the raw pointer is never cleared) and callsobserver_->OnDesktopFrameChanged().- This triggers a virtual method call on the freed
callback_proxy_memory. 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.
- An attacker establishes an authenticated Chrome Remote Desktop (CRD) connection to a Linux Wayland host.
- The attacker uses heap grooming techniques via other CRD messages to prepare for a UAF vtable hijack.
- The attacker sends a
SetVideoLayoutmessage configured to remove an existing virtual monitor. This message is processed by the host regardless of whether the OS lock screen is active. - The host processes the message, calling
GnomeCaptureStreamManager::RemoveVirtualStream, which requests the compositor to stop the stream. - The asynchronous callback
OnStreamStoppederases the stream info, triggering thePipewireCaptureStreamdestructor. - During the destruction race window, the active PipeWire thread attempts to process an incoming frame and executes
observer_->OnDesktopFrameChanged(). - 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
- Reorder Declarations: In
remoting/host/linux/pipewire_capture_stream.h, reorder the member variables so thatcallback_proxy_is declared beforestream_. This ensuresstream_(and its background thread) is destroyed prior tocallback_proxy_. - Explicit Cleanup: Alternatively, implement an explicit cleanup step in
PipewireCaptureStream::~PipewireCaptureStream()that callsstream_->SetObserver(nullptr)and/orstream_->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.