Overview

High
Severity
CVSS
No
Exploited ITW
Fixed
Fix Status
ImpactUse after free in Chromoting
DescriptionUse after free in Chromoting
ComponentChromoting
Bug ClassUAF
Tracker513727494
Fix commitfa41b3366038 (chromium/src) +21/-16
CISA KEVNot listed
CreditedGoogle
Disclosed2026-06-30

Changed Functions

FunctionChangeNotes
GnomeDisplayConfigDBusClient
remoting/host/linux/gnome_display_config_dbus_client.cc
modified
GetWeakPtr
remoting/host/linux/gnome_display_config_dbus_client.cc
modified
if
remoting/host/linux/gnome_display_config_dbus_client.cc
modified

Files Changed

  • remoting/host/linux/gnome_display_config_dbus_client.cc
  • remoting/host/linux/gnome_display_config_dbus_client.h
From fa41b33660385134cc78ed1bd2cd3f815c1e8add Mon Sep 17 00:00:00 2001
From: Yuwei Huang <[email protected]>
Date: Wed, 20 May 2026 20:50:37 -0700
Subject: [PATCH] remoting: Fix potential UAF in GnomeDisplayConfigDBusClient

Pass CallbackInfo holding WeakPtr and TaskRunner instead of raw 'this'
pointer as user_data to asynchronous GLib D-Bus callbacks. This prevents
Use-After-Free when the GnomeDisplayConfigDBusClient is destroyed before
the callbacks fire.

TAG=agy
CONV=2205968e-bb7b-4a02-a47e-5b5ed1f30a1a

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

diff --git a/remoting/host/linux/gnome_display_config_dbus_client.cc b/remoting/host/linux/gnome_display_config_dbus_client.cc
index 45cdaa2..6ccab7e 100644
--- a/remoting/host/linux/gnome_display_config_dbus_client.cc
+++ b/remoting/host/linux/gnome_display_config_dbus_client.cc
@@ -31,6 +31,11 @@
   }
 }
 
+struct CallbackInfo {
+  base::WeakPtr<GnomeDisplayConfigDBusClient> weak_ptr;
+  scoped_refptr<base::SequencedTaskRunner> task_runner;
+};
+
 }  // namespace
 
 GnomeDisplayConfigDBusClient::Subscription::Subscription() = default;
@@ -51,7 +56,6 @@
     default;
 
 GnomeDisplayConfigDBusClient::GnomeDisplayConfigDBusClient() {
-  weak_ptr_ = weak_factory_.GetWeakPtr();
   DETACH_FROM_SEQUENCE(sequence_checker_);
 }
 
@@ -66,8 +70,10 @@
   DCHECK_CALLED_ON_VALID_SEQUENCE(sequence_checker_);
   caller_task_runner_ = base::SequencedTaskRunner::GetCurrentDefault();
   cancellable_ = TakeGObject(g_cancellable_new());
+  auto* info =
+      new CallbackInfo{weak_factory_.GetWeakPtr(), caller_task_runner_};
   g_bus_get(G_BUS_TYPE_SESSION, cancellable_.get(),
-            &GnomeDisplayConfigDBusClient::OnDBusGetReply, this);
+            &GnomeDisplayConfigDBusClient::OnDBusGetReply, info);
 }
 
 void GnomeDisplayConfigDBusClient::GetMonitorsConfig(
@@ -99,7 +105,7 @@
       "ApplyMonitorsConfig", parameters.get(),
       /*reply_type=*/nullptr, G_DBUS_CALL_FLAGS_NO_AUTO_START,
       /*timeout_msec=*/-1, cancellable_.get(),
-      &GnomeDisplayConfigDBusClient::OnApplyMonitorsConfigReply, this);
+      &GnomeDisplayConfigDBusClient::OnApplyMonitorsConfigReply, nullptr);
 }
 
 std::unique_ptr<GnomeDisplayConfigDBusClient::Subscription>
@@ -133,13 +139,14 @@
 
 base::WeakPtr<GnomeDisplayConfigDBusClient>
 GnomeDisplayConfigDBusClient::GetWeakPtr() {
-  return weak_ptr_;
+  return weak_factory_.GetWeakPtr();
 }
 
 // static
 void GnomeDisplayConfigDBusClient::OnDBusGetReply(GObject* object,
                                                   GAsyncResult* result,
                                                   gpointer user_data) {
+  auto info = base::WrapUnique(static_cast<CallbackInfo*>(user_data));
   webrtc::Scoped<GError> error;
   ScopedGObject<GDBusConnection> dbus_connection =
       TakeGObject(g_bus_get_finish(result, error.receive()));
@@ -149,10 +156,9 @@
     return;
   }
 
-  auto* that = static_cast<GnomeDisplayConfigDBusClient*>(user_data);
-  that->caller_task_runner_->PostTask(
+  info->task_runner->PostTask(
       FROM_HERE, base::BindOnce(&GnomeDisplayConfigDBusClient::OnDBusGet,
-                                that->weak_ptr_, std::move(dbus_connection)));
+                                info->weak_ptr, std::move(dbus_connection)));
 }
 
 // static
@@ -160,28 +166,27 @@
     GObject* object,
     GAsyncResult* result,
     gpointer user_data) {
+  auto info = base::WrapUnique(static_cast<CallbackInfo*>(user_data));
   auto* connection = reinterpret_cast<GDBusConnection*>(object);
   webrtc::Scoped<GError> error;
   ScopedGVariant config = TakeGVariant(
       g_dbus_connection_call_finish(connection, result, error.receive()));
 
-  auto* that = static_cast<GnomeDisplayConfigDBusClient*>(user_data);
-
   if (!config) {
     LOG(ERROR) << "Failed to get current display configuration: "
                << error->message;
-    that->caller_task_runner_->PostTask(
+    info->task_runner->PostTask(
         FROM_HERE,
         base::BindOnce(
             &GnomeDisplayConfigDBusClient::OnDisplayConfigCurrentStateError,
-            that->weak_ptr_));
+            info->weak_ptr));
     return;
   }
 
-  that->caller_task_runner_->PostTask(
+  info->task_runner->PostTask(
       FROM_HERE,
       base::BindOnce(&GnomeDisplayConfigDBusClient::OnDisplayConfigCurrentState,
-                     that->weak_ptr_, std::move(config)));
+                     info->weak_ptr, std::move(config)));
 }
 
 // static
@@ -201,13 +206,15 @@
 void GnomeDisplayConfigDBusClient::CallDBusGetCurrentState() {
   DCHECK_CALLED_ON_VALID_SEQUENCE(sequence_checker_);
   DCHECK(dbus_connection_.is_initialized());
+  auto* info =
+      new CallbackInfo{weak_factory_.GetWeakPtr(), caller_task_runner_};
   g_dbus_connection_call(
       dbus_connection_.raw(), kDisplayConfigInterfaceName,
       kDisplayConfigObjectPath, kDisplayConfigInterfaceName, "GetCurrentState",
       /*parameters=*/nullptr,
       /*reply_type=*/nullptr, G_DBUS_CALL_FLAGS_NO_AUTO_START,
       /*timeout_msec=*/-1, cancellable_.get(),
-      &GnomeDisplayConfigDBusClient::OnDisplayConfigCurrentStateReply, this);
+      &GnomeDisplayConfigDBusClient::OnDisplayConfigCurrentStateReply, info);
 }
 
 void GnomeDisplayConfigDBusClient::OnDBusGet(
diff --git a/remoting/host/linux/gnome_display_config_dbus_client.h b/remoting/host/linux/gnome_display_config_dbus_client.h
index cdc80e9..c211f6e 100644
--- a/remoting/host/linux/gnome_display_config_dbus_client.h
+++ b/remoting/host/linux/gnome_display_config_dbus_client.h
@@ -132,8 +132,6 @@
   // Called by OnDisplayConfigCurrentStateReply() on error.
   void OnDisplayConfigCurrentStateError();
 
-  base::WeakPtr<GnomeDisplayConfigDBusClient> weak_ptr_;
-
   scoped_refptr<base::SequencedTaskRunner> caller_task_runner_;
 
   ScopedGObject<GCancellable> cancellable_
Loading diff…

Original Bug Report

reported by [email protected]

Potential Cross-thread Use-After-Free in GnomeDisplayConfigDBusClient

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. 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 vulnerability exists in GnomeDisplayConfigDBusClient due to passing a raw pointer to asynchronous GLib D-Bus callbacks. If the object is destroyed while a D-Bus request is pending, the callback dereferences a dangling pointer during execution. This impacts the Chrome Remote Desktop host on Linux when running in a GNOME environment.

Affected files:

  • remoting/host/linux/gnome_display_config_dbus_client.cc
  • remoting/host/linux/gnome_display_config_dbus_client.h

Estimated timestamp from git blame: 2023-11-29

Summary

A potential cross-thread Use-After-Free (UAF) vulnerability has been identified in the GnomeDisplayConfigDBusClient class within the Chrome Remote Desktop host for Linux. The issue stems from passing a raw this pointer as user_data to GLib asynchronous D-Bus operations. Since GLib guarantees that asynchronous callbacks will be invoked exactly once even upon cancellation, the callback may execute after the GnomeDisplayConfigDBusClient object has been destroyed, leading to a dereference of a dangling pointer.

Root Cause

In remoting/host/linux/gnome_display_config_dbus_client.cc, the class uses g_dbus_connection_call() and g_bus_get() for asynchronous D-Bus operations. These functions take a gpointer user_data argument, which is populated with a raw this pointer (lines 70, 102, 210).

The class destructor calls g_cancellable_cancel() on its GCancellable member (line 61). However, per the GIO library documentation, the registered GAsyncReadyCallback is always invoked exactly once, even if the operation is cancelled. In the event of cancellation, the callback receives a G_IO_ERROR_CANCELLED error.

The static callback methods, such as OnDisplayConfigCurrentStateReply (line 159), cast user_data back to GnomeDisplayConfigDBusClient*. Even when the operation fails or is cancelled (resulting in a null config variant), the code attempts to dereference this pointer to access caller_task_runner_ or weak_ptr_ (lines 173-177):

if (!config) {
  LOG(ERROR) << "Failed to get current display configuration: "
             << error->message;
  that->caller_task_runner_->PostTask(
      FROM_HERE,
      base::BindOnce(
          &GnomeDisplayConfigDBusClient::OnDisplayConfigCurrentStateError,
          that->weak_ptr_));
  return;
}

If the GnomeDisplayConfigDBusClient instance was destroyed shortly before this callback fires (e.g., during session disconnection), that points to freed memory, resulting in a UAF.

Potential Impact and Exploitation

This vulnerability occurs in the remoting_me2me_host (specifically the desktop process), which on Linux is an unsandboxed process running with the privileges of the logged-in user. A successful exploit could allow an authenticated remote attacker to achieve native code execution by grooming the heap and controlling the state of the freed object, particularly the TaskRunner virtual table.

Suggested Reproduction Steps

Note: These are potential steps as a functional PoC has not been executed.

  1. Establish a Chrome Remote Desktop session on a Linux host running GNOME.
  2. Trigger a display resolution change from the client to initiate a D-Bus request (GetCurrentState) to Mutter.
  3. Disconnect the CRD session while the D-Bus response is in flight.
  4. Observe the crash in the desktop process when the GLib event loop attempts to execute the OnDisplayConfigCurrentStateReply callback.

Avoid passing raw this pointers as user_data to GLib callbacks. Instead, use a lifetime-managed context object or a wrapper that integrates with base::WeakPtr. Alternatively, the callback should check for cancellation and avoid dereferencing user_data if the operation was cancelled, although a more robust solution would involve managing the lifetime of the user_data itself.

Evaluated with Chrome root at commit: 1a8d40fc44df2088d5945c0bf53584038aa1614a


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