Low chrome UAF 🔧 Commit mapped

Overview

Low
Severity
—
CVSS
No
Exploited ITW
Fixed
Fix Status
ImpactUse after free in Chromoting
DescriptionUse after free in Chromoting
ComponentChromoting
Sub-componentRemoting host
Bug ClassUAF
Tracker562999766
Fix commite99a5890a1a4 (chromium/src) +102/-30
CISA KEVNot listed
CreditedGoogle
Disclosed2026-10-20

Changed Functions

FunctionChangeNotes
if
remoting/host/disconnect_window_linux.cc
modified
TEST_F
remoting/host/disconnect_window_linux_unittest.cc
modified
if
remoting/host/disconnect_window_linux_unittest.cc
modified

Files Changed

  • remoting/host/disconnect_window_linux.cc
  • remoting/host/disconnect_window_linux_unittest.cc
From e99a5890a1a441d3b1dae1af3ec464f2efdb8ad5 Mon Sep 17 00:00:00 2001
From: Lambros Lambrou <[email protected]>
Date: Thu, 24 Sep 2026 15:55:34 -0700
Subject: [PATCH] [remoting host] Position Linux disconnect window using monitor workarea

Update DisconnectWindowGtk::SetDialogPosition() to query gdk_monitor_get_workarea() instead of gdk_monitor_get_geometry() and remove the hardcoded kTopMargin and kBottomMargin offsets. Also check for workarea changes during the periodic Raise() timer callback so the dialog is repositioned when desktop panels or docks are resized.

Bug: 562999766
Change-Id: I5b424ad3c7af2333d3b42a4faa8f39b0299de106
Reviewed-on: https://chromium-review.googlesource.com/c/chromium/src/+/8453671
Auto-Submit: Lambros Lambrou <[email protected]>
Commit-Queue: Lambros Lambrou <[email protected]>
Reviewed-by: Jamie Walch <[email protected]>
Cr-Commit-Position: refs/heads/main@{#1704542}
---
diff --git a/remoting/host/disconnect_window_linux.cc b/remoting/host/disconnect_window_linux.cc
index 57d3ecf8..e9461ae 100644
--- a/remoting/host/disconnect_window_linux.cc
+++ b/remoting/host/disconnect_window_linux.cc
@@ -31,11 +31,6 @@
 
 namespace {
 
-// Margins from screen edges to ensure the dialog is not obscured by the top bar
-// or an auto-hiding dock/panel at the bottom.
-constexpr int kTopMargin = 40;
-constexpr int kBottomMargin = 60;
-
 // Padding and spacing for the window contents.
 constexpr int kButtonRowSpacing = 12;
 constexpr int kHorizontalPadding = 12;
@@ -87,6 +82,7 @@
   gboolean OnConfigure(GtkWidget* widget, GdkEventConfigure* event);
   gboolean OnDraw(GtkWidget* widget, cairo_t* cr);
 #if !GTK_CHECK_VERSION(3, 90, 0)
+  GdkMonitor* GetCurrentMonitor() const;
   void OnMonitorsChanged(GdkScreen* screen);
   gboolean OnWindowState(GtkWidget* window, GdkEventWindowState* event);
   void Raise();
@@ -110,6 +106,9 @@
 
   base::RepeatingTimer raise_timer_;
 
+#if !GTK_CHECK_VERSION(3, 90, 0)
+  GdkRectangle current_workarea_ = {0, 0, 0, 0};
+#endif
   std::vector<ScopedGSignal> signals_;
 };
 
@@ -375,32 +374,14 @@
   DCHECK_CALLED_ON_VALID_SEQUENCE(sequence_checker_);
 
 #if !GTK_CHECK_VERSION(3, 90, 0)
-  if (!disconnect_window_) {
-    return;
-  }
-
-  GdkDisplay* display = gtk_widget_get_display(disconnect_window_.get());
-  if (!display) {
-    return;
-  }
-
-  GdkMonitor* monitor = nullptr;
-  GdkWindow* gdk_window = gtk_widget_get_window(disconnect_window_.get());
-  if (gdk_window) {
-    monitor = gdk_display_get_monitor_at_window(display, gdk_window);
-  }
-  if (!monitor) {
-    monitor = gdk_display_get_primary_monitor(display);
-  }
-  if (!monitor) {
-    monitor = gdk_display_get_monitor(display, 0);
-  }
+  GdkMonitor* monitor = GetCurrentMonitor();
   if (!monitor) {
     return;
   }
 
-  GdkRectangle geometry;
-  gdk_monitor_get_geometry(monitor, &geometry);
+  GdkRectangle workarea;
+  gdk_monitor_get_workarea(monitor, &workarea);
+  current_workarea_ = workarea;
 
   int width = current_width_;
   int height = current_height_;
@@ -414,10 +395,10 @@
     current_height_ = height;
   }
 
-  int left = geometry.x + std::max(0, (geometry.width - width) / 2);
+  int left = workarea.x + std::max(0, (workarea.width - width) / 2);
   int top = (current_anchor() == WindowAnchor::kTop)
-                ? (geometry.y + kTopMargin)
-                : (geometry.y + geometry.height - height - kBottomMargin);
+                ? workarea.y
+                : (workarea.y + workarea.height - height);
 
   SetExpectedPosition(left, top);
   gtk_window_move(GTK_WINDOW(disconnect_window_.get()), left, top);
@@ -439,6 +420,28 @@
 }
 
 #if !GTK_CHECK_VERSION(3, 90, 0)
+GdkMonitor* DisconnectWindowGtk::GetCurrentMonitor() const {
+  if (!disconnect_window_) {
+    return nullptr;
+  }
+  GdkDisplay* display = gtk_widget_get_display(disconnect_window_.get());
+  if (!display) {
+    return nullptr;
+  }
+  GdkMonitor* monitor = nullptr;
+  GdkWindow* gdk_window = gtk_widget_get_window(disconnect_window_.get());
+  if (gdk_window) {
+    monitor = gdk_display_get_monitor_at_window(display, gdk_window);
+  }
+  if (!monitor) {
+    monitor = gdk_display_get_primary_monitor(display);
+  }
+  if (!monitor) {
+    monitor = gdk_display_get_monitor(display, 0);
+  }
+  return monitor;
+}
+
 void DisconnectWindowGtk::OnMonitorsChanged(GdkScreen* screen) {
   DCHECK_CALLED_ON_VALID_SEQUENCE(sequence_checker_);
   ResetRepositionAttempts();
@@ -473,6 +476,14 @@
   if (!disconnect_window_) {
     return;
   }
+  if (GdkMonitor* monitor = GetCurrentMonitor()) {
+    GdkRectangle workarea;
+    gdk_monitor_get_workarea(monitor, &workarea);
+    if (!gdk_rectangle_equal(&workarea, &current_workarea_)) {
+      ResetRepositionAttempts();
+      SetDialogPosition();
+    }
+  }
   GdkWindow* gdk_window = gtk_widget_get_window(disconnect_window_.get());
   if (gdk_window) {
     gdk_window_raise(gdk_window);
diff --git a/remoting/host/disconnect_window_linux_unittest.cc b/remoting/host/disconnect_window_linux_unittest.cc
index d7b9837..5c4379f 100644
--- a/remoting/host/disconnect_window_linux_unittest.cc
+++ b/remoting/host/disconnect_window_linux_unittest.cc
@@ -4,6 +4,7 @@
 
 #include <gtk/gtk.h>
 
+#include <algorithm>
 #include <memory>
 #include <string>
 #include <string_view>
@@ -14,6 +15,7 @@
 #include "base/test/task_environment.h"
 #include "remoting/base/string_resources.h"
 #include "remoting/host/client_session_control.h"
+#include "remoting/host/disconnect_window_base.h"
 #include "remoting/host/host_mock_objects.h"
 #include "remoting/host/host_window.h"
 #include "testing/gmock/include/gmock/gmock.h"
@@ -272,6 +274,7 @@
   ~DisconnectWindowLinuxTest() override = default;
 
   void TearDown() override {
+    DisconnectWindowBase::ResetCurrentAnchorForTesting();
     // Pump the event loop to ensure GTK widget destruction and cleanup is
     // processed.
     task_environment_.RunUntilIdle();
@@ -693,6 +696,64 @@
 
   window.reset();
 }
+
+TEST_F(DisconnectWindowLinuxTest, PositionsAtWorkareaEdges) {
+  if (!InitializeGtk()) {
+    GTEST_SKIP() << "No display available for GTK.";
+  }
+
+  FakeClientSessionControl session_control(kTestUserJid);
+  std::unique_ptr<HostWindow> window = HostWindow::CreateDisconnectWindow();
+  ASSERT_TRUE(window);
+  window->Start(session_control.GetWeakPtr());
+
+  GtkWindow* gtk_window = FindDisconnectWindow();
+  ASSERT_NE(gtk_window, nullptr);
+
+  GdkDisplay* display = gtk_widget_get_display(GTK_WIDGET(gtk_window));
+  ASSERT_NE(display, nullptr);
+  GdkMonitor* monitor = nullptr;
+  GdkWindow* gdk_window = gtk_widget_get_window(GTK_WIDGET(gtk_window));
+  if (gdk_window) {
+    monitor = gdk_display_get_monitor_at_window(display, gdk_window);
+  }
+  if (!monitor) {
+    monitor = gdk_display_get_primary_monitor(display);
+  }
+  if (!monitor) {
+    monitor = gdk_display_get_monitor(display, 0);
+  }
+  ASSERT_NE(monitor, nullptr);
+
+  GdkRectangle workarea;
+  gdk_monitor_get_workarea(monitor, &workarea);
+
+  int width = GPOINTER_TO_INT(
+      g_object_get_data(G_OBJECT(gtk_window), kCurrentWidthKey));
+  int height = GPOINTER_TO_INT(
+      g_object_get_data(G_OBJECT(gtk_window), kCurrentHeightKey));
+  int expected_x =
+      GPOINTER_TO_INT(g_object_get_data(G_OBJECT(gtk_window), kExpectedXKey));
+  int bottom_expected_y =
+      GPOINTER_TO_INT(g_object_get_data(G_OBJECT(gtk_window), kExpectedYKey));
+
+  EXPECT_EQ(expected_x, workarea.x + std::max(0, (workarea.width - width) / 2));
+  EXPECT_EQ(bottom_expected_y, workarea.y + workarea.height - height);
+
+  // Toggle to top anchor and verify it aligns directly with workarea.y.
+  GtkWidget* toggle_button =
+      FindButtonByLabel(GTK_WIDGET(gtk_window), kUpArrow);
+  ASSERT_NE(toggle_button, nullptr);
+  g_signal_emit_by_name(toggle_button, "clicked");
+
+  int top_expected_y =
+      GPOINTER_TO_INT(g_object_get_data(G_OBJECT(gtk_window), kExpectedYKey));
+  EXPECT_EQ(top_expected_y, workarea.y);
+
+  // Restore bottom anchor.
+  g_signal_emit_by_name(toggle_button, "clicked");
+  window.reset();
+}
 #endif
 
 }  // namespace remoting
Loading diff…

Regression Test / PoC

shipped with the fix
diff --git a/remoting/host/disconnect_window_linux_unittest.cc b/remoting/host/disconnect_window_linux_unittest.cc
index d7b9837..5c4379f 100644
--- a/remoting/host/disconnect_window_linux_unittest.cc
+++ b/remoting/host/disconnect_window_linux_unittest.cc
@@ -4,6 +4,7 @@
 
 #include <gtk/gtk.h>
 
+#include <algorithm>
 #include <memory>
 #include <string>
 #include <string_view>
@@ -14,6 +15,7 @@
 #include "base/test/task_environment.h"
 #include "remoting/base/string_resources.h"
 #include "remoting/host/client_session_control.h"
+#include "remoting/host/disconnect_window_base.h"
 #include "remoting/host/host_mock_objects.h"
 #include "remoting/host/host_window.h"
 #include "testing/gmock/include/gmock/gmock.h"
@@ -272,6 +274,7 @@
   ~DisconnectWindowLinuxTest() override = default;
 
   void TearDown() override {
+    DisconnectWindowBase::ResetCurrentAnchorForTesting();
     // Pump the event loop to ensure GTK widget destruction and cleanup is
     // processed.
     task_environment_.RunUntilIdle();
@@ -693,6 +696,64 @@
 
   window.reset();
 }
+
+TEST_F(DisconnectWindowLinuxTest, PositionsAtWorkareaEdges) {
+  if (!InitializeGtk()) {
+    GTEST_SKIP() << "No display available for GTK.";
+  }
+
+  FakeClientSessionControl session_control(kTestUserJid);
+  std::unique_ptr<HostWindow> window = HostWindow::CreateDisconnectWindow();
+  ASSERT_TRUE(window);
+  window->Start(session_control.GetWeakPtr());
+
+  GtkWindow* gtk_window = FindDisconnectWindow();
+  ASSERT_NE(gtk_window, nullptr);
+
+  GdkDisplay* display = gtk_widget_get_display(GTK_WIDGET(gtk_window));
+  ASSERT_NE(display, nullptr);
+  GdkMonitor* monitor = nullptr;
+  GdkWindow* gdk_window = gtk_widget_get_window(GTK_WIDGET(gtk_window));
+  if (gdk_window) {
+    monitor = gdk_display_get_monitor_at_window(display, gdk_window);
+  }
+  if (!monitor) {
+    monitor = gdk_display_get_primary_monitor(display);
+  }
+  if (!monitor) {
+    monitor = gdk_display_get_monitor(display, 0);
+  }
+  ASSERT_NE(monitor, nullptr);
+
+  GdkRectangle workarea;
+  gdk_monitor_get_workarea(monitor, &workarea);
+
+  int width = GPOINTER_TO_INT(
+      g_object_get_data(G_OBJECT(gtk_window), kCurrentWidthKey));
+  int height = GPOINTER_TO_INT(
+      g_object_get_data(G_OBJECT(gtk_window), kCurrentHeightKey));
+  int expected_x =
+      GPOINTER_TO_INT(g_object_get_data(G_OBJECT(gtk_window), kExpectedXKey));
+  int bottom_expected_y =
+      GPOINTER_TO_INT(g_object_get_data(G_OBJECT(gtk_window), kExpectedYKey));
+
+  EXPECT_EQ(expected_x, workarea.x + std::max(0, (workarea.width - width) / 2));
+  EXPECT_EQ(bottom_expected_y, workarea.y + workarea.height - height);
+
+  // Toggle to top anchor and verify it aligns directly with workarea.y.
+  GtkWidget* toggle_button =
+      FindButtonByLabel(GTK_WIDGET(gtk_window), kUpArrow);
+  ASSERT_NE(toggle_button, nullptr);
+  g_signal_emit_by_name(toggle_button, "clicked");
+
+  int top_expected_y =
+      GPOINTER_TO_INT(g_object_get_data(G_OBJECT(gtk_window), kExpectedYKey));
+  EXPECT_EQ(top_expected_y, workarea.y);
+
+  // Restore bottom anchor.
+  g_signal_emit_by_name(toggle_button, "clicked");
+  window.reset();
+}
 #endif
 
 }  // namespace remoting
Loading diff…

Original Bug Report

The reporter's bug is still restricted on the tracker. Chrome de-restricts security bugs ~30–90 days after the fix ships; a later run will backfill it here.