Low chrome Race 🔧 Commit mapped

Overview

Low
Severity
—
CVSS
No
Exploited ITW
Fixed
Fix Status
ImpactRace condition in Chromoting
DescriptionRace condition in Chromoting
ComponentChromoting
Sub-componentRemoting
Bug ClassRace
Tracker554874487
Fix commit75e458b78be9 (chromium/src) +192/-11
CISA KEVNot listed
CreditedGoogle
Disclosed2026-10-06

Changed Functions

FunctionChangeNotes
if
remoting/host/continue_window.cc
modified
if
remoting/host/continue_window_chromeos.cc
modified
if
remoting/host/continue_window_linux.cc
modified
if
remoting/host/continue_window_mac.mm
modified
if
remoting/host/continue_window_win.cc
modified

Files Changed

  • remoting/host/continue_window_mac.mm
  • remoting/host/continue_window.h
  • remoting/host/continue_window_win.cc
  • remoting/host/continue_window_unittest.cc
  • remoting/host/continue_window_linux.cc
  • remoting/host/continue_window_chromeos.cc
  • remoting/host/continue_window.cc
From 75e458b78be9f7a43901e8fb3bbe5a01d4ac5d13 Mon Sep 17 00:00:00 2001
From: Joe Downing <[email protected]>
Date: Wed, 02 Sep 2026 17:34:25 -0700
Subject: [PATCH] [remoting] Add 2-second initial activation delay to CRD ContinueWindow

When the CRD ContinueWindow appears, rapid keypresses or inadvertent
clicks could dismiss or interact with the dialog before the local user
has an opportunity to read the prompt.

This change introduces a 2-second initial activation delay upon
displaying the ContinueWindow:
- Disables interactive button controls during the initial 2-second
  delay.
- Ignores early continue and disconnect actions until the delay timer
  expires.
- Implements platform-specific UI button disablement for Windows, Linux
  (GTK), and macOS.
- Adds unit tests verifying that user interactions are ignored during
  the activation delay and handled properly once enabled.

TAG=agy
CONV=5cfca178-d091-43e2-8570-edfe330ad1c8

Bug: 554874487
Change-Id: Ic24af2ba615e01fe5119c63e49205aa0e59acc86
Reviewed-on: https://chromium-review.googlesource.com/c/chromium/src/+/8328799
Commit-Queue: Yuwei Huang <[email protected]>
Auto-Submit: Joe Downing <[email protected]>
Reviewed-by: Yuwei Huang <[email protected]>
Cr-Commit-Position: refs/heads/main@{#1691222}
---
diff --git a/remoting/host/continue_window.cc b/remoting/host/continue_window.cc
index 9462805..2a60f0a 100644
--- a/remoting/host/continue_window.cc
+++ b/remoting/host/continue_window.cc
@@ -15,6 +15,9 @@
 // window has been shown).
 constexpr base::TimeDelta kSessionDisconnectTimeout = base::Minutes(5);
 
+// Time to wait before enabling the Continue/Cancel buttons.
+constexpr base::TimeDelta kInitialActivationDelay = base::Seconds(2);
+
 namespace remoting {
 
 ContinueWindow::ContinueWindow() = default;
@@ -36,6 +39,10 @@
 void ContinueWindow::ContinueSession() {
   DCHECK_CALLED_ON_VALID_SEQUENCE(sequence_checker_);
 
+  if (activation_delay_timer_.IsRunning()) {
+    return;
+  }
+
   disconnect_timer_.Stop();
 
   if (!client_session_control_) {
@@ -53,6 +60,10 @@
 void ContinueWindow::DisconnectSession() {
   DCHECK_CALLED_ON_VALID_SEQUENCE(sequence_checker_);
 
+  if (activation_delay_timer_.IsRunning()) {
+    return;
+  }
+
   disconnect_timer_.Stop();
   if (client_session_control_) {
     client_session_control_->DisconnectSession(
@@ -73,8 +84,16 @@
 
   // Show the Continue window and wait for the local user input.
   ShowUi();
+  SetButtonsEnabled(false);
   disconnect_timer_.Start(FROM_HERE, kSessionDisconnectTimeout, this,
                           &ContinueWindow::DisconnectSession);
+  activation_delay_timer_.Start(FROM_HERE, kInitialActivationDelay, this,
+                                &ContinueWindow::OnActivationDelayExpired);
+}
+
+void ContinueWindow::OnActivationDelayExpired() {
+  DCHECK_CALLED_ON_VALID_SEQUENCE(sequence_checker_);
+  SetButtonsEnabled(true);
 }
 
 }  // namespace remoting
diff --git a/remoting/host/continue_window.h b/remoting/host/continue_window.h
index 3cfd6c7..7712c73 100644
--- a/remoting/host/continue_window.h
+++ b/remoting/host/continue_window.h
@@ -35,11 +35,17 @@
   virtual void ShowUi() = 0;
   virtual void HideUi() = 0;
 
+  // Enables or disables the UI buttons.
+  virtual void SetButtonsEnabled(bool enabled) = 0;
+
  private:
   // Invoked periodically to ask for the local user whether the session should
   // be continued.
   void OnSessionExpired();
 
+  // Invoked when the initial activation delay expires.
+  void OnActivationDelayExpired();
+
   // Used to disconnect the client session.
   base::WeakPtr<ClientSessionControl> client_session_control_;
 
@@ -48,6 +54,9 @@
 
   // Used to ask the local user whether the session should be continued.
   base::OneShotTimer session_expired_timer_;
+
+  // Used to delay enabling the default action / buttons.
+  base::OneShotTimer activation_delay_timer_;
 };
 
 }  // namespace remoting
diff --git a/remoting/host/continue_window_chromeos.cc b/remoting/host/continue_window_chromeos.cc
index 28ef559..b5ff72d 100644
--- a/remoting/host/continue_window_chromeos.cc
+++ b/remoting/host/continue_window_chromeos.cc
@@ -31,6 +31,7 @@
   // ContinueWindow interface.
   void ShowUi() override;
   void HideUi() override;
+  void SetButtonsEnabled(bool enabled) override;
 
  private:
   std::unique_ptr<MessageBox> message_box_;
@@ -57,6 +58,7 @@
       std::nullopt,                                        // icon
       base::BindOnce(&ContinueWindowAura::OnMessageBoxResult,
                      base::Unretained(this)));
+  message_box_->SetDisableInputs(true);
   message_box_->Show();
 }
 
@@ -64,6 +66,12 @@
   message_box_.reset();
 }
 
+void ContinueWindowAura::SetButtonsEnabled(bool enabled) {
+  if (message_box_) {
+    message_box_->SetDisableInputs(!enabled);
+  }
+}
+
 }  // namespace
 
 // static
diff --git a/remoting/host/continue_window_linux.cc b/remoting/host/continue_window_linux.cc
index b263362..1c5b127 100644
--- a/remoting/host/continue_window_linux.cc
+++ b/remoting/host/continue_window_linux.cc
@@ -32,6 +32,7 @@
   // ContinueWindow overrides.
   void ShowUi() override;
   void HideUi() override;
+  void SetButtonsEnabled(bool enabled) override;
 
  private:
   void CreateWindow();
@@ -40,6 +41,8 @@
 
   raw_ptr<GtkWidget> continue_window_;
 
+  bool buttons_enabled_ = false;
+
   ScopedGSignal signal_;
 };
 
@@ -70,6 +73,18 @@
   }
 }
 
+void ContinueWindowGtk::SetButtonsEnabled(bool enabled) {
+  DCHECK_CALLED_ON_VALID_SEQUENCE(sequence_checker_);
+  buttons_enabled_ = enabled;
+  if (continue_window_) {
+    gtk_dialog_set_response_sensitive(GTK_DIALOG(continue_window_.get()),
+                                      GTK_RESPONSE_CANCEL,
+                                      enabled ? TRUE : FALSE);
+    gtk_dialog_set_response_sensitive(GTK_DIALOG(continue_window_.get()),
+                                      GTK_RESPONSE_OK, enabled ? TRUE : FALSE);
+  }
+}
+
 void ContinueWindowGtk::CreateWindow() {
   DCHECK_CALLED_ON_VALID_SEQUENCE(sequence_checker_);
   DCHECK(!continue_window_);
@@ -84,6 +99,10 @@
 
   gtk_dialog_set_default_response(GTK_DIALOG(continue_window_.get()),
                                   GTK_RESPONSE_CANCEL);
+  gtk_dialog_set_response_sensitive(GTK_DIALOG(continue_window_.get()),
+                                    GTK_RESPONSE_CANCEL, FALSE);
+  gtk_dialog_set_response_sensitive(GTK_DIALOG(continue_window_.get()),
+                                    GTK_RESPONSE_OK, FALSE);
   gtk_window_set_resizable(GTK_WINDOW(continue_window_.get()), FALSE);
 
   // Set always-on-top, otherwise this window tends to be obscured by the
@@ -121,6 +140,10 @@
 void ContinueWindowGtk::OnResponse(GtkDialog* dialog, int response_id) {
   DCHECK_CALLED_ON_VALID_SEQUENCE(sequence_checker_);
 
+  if (!buttons_enabled_) {
+    return;
+  }
+
   if (response_id == GTK_RESPONSE_OK) {
     ContinueSession();
   } else {
diff --git a/remoting/host/continue_window_mac.mm b/remoting/host/continue_window_mac.mm
index cd5effd8..33dcd406 100644
--- a/remoting/host/continue_window_mac.mm
+++ b/remoting/host/continue_window_mac.mm
@@ -20,6 +20,8 @@
  @private
   NSMutableArray<NSWindow*>* __strong _shades;
   NSAlert* __strong _continue_alert;
+  NSButton* __weak _cancel_button;
+  NSButton* __weak _continue_button;
   raw_ptr<remoting::ContinueWindow> _continue_window;
 }
 
@@ -27,6 +29,7 @@
 - (void)show;
 - (void)hide;
 - (void)cancelOperation:(id)sender;
+- (void)setButtonsEnabled:(BOOL)enabled;
 - (void)onCancel:(id)sender;
 - (void)onContinue:(id)sender;
 @end
@@ -48,6 +51,7 @@
   // ContinueWindow overrides.
   void ShowUi() override;
   void HideUi() override;
+  void SetButtonsEnabled(bool enabled) override;
 
  private:
   ContinueWindowMacController* __strong controller_;
@@ -80,6 +84,14 @@
   }
 }
 
+void ContinueWindowMac::SetButtonsEnabled(bool enabled) {
+  DCHECK_CALLED_ON_VALID_SEQUENCE(sequence_checker_);
+
+  if (controller_) {
+    [controller_ setButtonsEnabled:enabled ? YES : NO];
+  }
+}
+
 // static
 std::unique_ptr<HostWindow> HostWindow::CreateContinueWindow() {
   return std::make_unique<ContinueWindowMac>();
@@ -126,16 +138,18 @@
   _continue_alert = [[NSAlert alloc] init];
   _continue_alert.messageText = l10n_util::GetNSString(IDS_CONTINUE_PROMPT);
 
-  NSButton* cancel_button = [_continue_alert
+  _cancel_button = [_continue_alert
       addButtonWithTitle:l10n_util::GetNSString(IDS_STOP_SHARING_BUTTON)];
-  cancel_button.action = @selector(onCancel:);
-  cancel_button.target = self;
+  _cancel_button.action = @selector(onCancel:);
+  _cancel_button.target = self;
+  _cancel_button.enabled = NO;
 
-  NSButton* continue_button = [_continue_alert
+  _continue_button = [_continue_alert
       addButtonWithTitle:l10n_util::GetNSString(IDS_CONTINUE_BUTTON)];
-  continue_button.action = @selector(onContinue:);
-  continue_button.target = self;
-  continue_button.keyEquivalent = @"";
+  _continue_button.action = @selector(onContinue:);
+  _continue_button.target = self;
+  _continue_button.keyEquivalent = @"";
+  _continue_button.enabled = NO;
 
   NSBundle* bundle = [NSBundle bundleForClass:[self class]];
   NSString* imagePath = [bundle pathForResource:@"chromoting128" ofType:@"png"];
@@ -168,6 +182,13 @@
     [_continue_alert.window close];
     _continue_alert = nil;
   }
+  _cancel_button = nil;
+  _continue_button = nil;
+}
+
+- (void)setButtonsEnabled:(BOOL)enabled {
+  _cancel_button.enabled = enabled;
+  _continue_button.enabled = enabled;
 }
 
 - (void)cancelOperation:(id)sender {
@@ -175,11 +196,19 @@
 }
 
 - (void)onCancel:(id)sender {
+  if (!_cancel_button.enabled) {
+    return;
+  }
+
   [self hide];
   _continue_window->DisconnectSession();
 }
 
 - (void)onContinue:(id)sender {
+  if (!_continue_button.enabled) {
+    return;
+  }
+
   [self hide];
   _continue_window->ContinueSession();
 }
diff --git a/remoting/host/continue_window_win.cc b/remoting/host/continue_window_win.cc
index 71edc751..4f9a377 100644
--- a/remoting/host/continue_window_win.cc
+++ b/remoting/host/continue_window_win.cc
@@ -35,6 +35,7 @@
   // ContinueWindow overrides.
   void ShowUi() override;
   void HideUi() override;
+  void SetButtonsEnabled(bool enabled) override;
 
  private:
   static BOOL CALLBACK DialogProc(HWND hwmd,
@@ -47,6 +48,7 @@
   void EndDialog();
 
   HWND hwnd_;
+  bool buttons_enabled_ = false;
 };
 
 ContinueWindowWin::ContinueWindowWin() : hwnd_(nullptr) {}
@@ -66,6 +68,7 @@
     return;
   }
 
+  SetButtonsEnabled(false);
   ShowWindow(hwnd_, SW_SHOW);
 }
 
@@ -75,6 +78,21 @@
   EndDialog();
 }
 
+void ContinueWindowWin::SetButtonsEnabled(bool enabled) {
+  DCHECK_CALLED_ON_VALID_SEQUENCE(sequence_checker_);
+  buttons_enabled_ = enabled;
+  if (hwnd_) {
+    HWND cancel_button = GetDlgItem(hwnd_, IDC_CONTINUE_CANCEL);
+    if (cancel_button) {
+      EnableWindow(cancel_button, enabled ? TRUE : FALSE);
+    }
+    HWND continue_button = GetDlgItem(hwnd_, IDC_CONTINUE_BUTTON);
+    if (continue_button) {
+      EnableWindow(continue_button, enabled ? TRUE : FALSE);
+    }
+  }
+}
+
 BOOL CALLBACK ContinueWindowWin::DialogProc(HWND hwnd,
                                             UINT msg,
                                             WPARAM wParam,
@@ -111,15 +129,20 @@
     case WM_COMMAND:
       switch (LOWORD(wParam)) {
         case IDC_CONTINUE_BUTTON:
-          ContinueSession();
-          EndDialog();
+          if (buttons_enabled_) {
+            ContinueSession();
+            EndDialog();
+          }
           return TRUE;
         case IDC_CONTINUE_CANCEL:
         case IDCANCEL:
-          DisconnectSession();
-          EndDialog();
+          if (buttons_enabled_) {
+            DisconnectSession();
+            EndDialog();
+          }
           return TRUE;
       }
+      return FALSE;
   }
   return FALSE;
 }
diff --git a/remoting/host/continue_window_unittest.cc b/remoting/host/continue_window_unittest.cc
index 0859ae2..0433d544 100644
--- a/remoting/host/continue_window_unittest.cc
+++ b/remoting/host/continue_window_unittest.cc
@@ -25,6 +25,7 @@
 
   MOCK_METHOD(void, ShowUi, (), (override));
   MOCK_METHOD(void, HideUi, (), (override));
+  MOCK_METHOD(void, SetButtonsEnabled, (bool), (override));
 };
... (file diff truncated)
Loading diff…

Regression Test / PoC

shipped with the fix
diff --git a/remoting/host/continue_window_unittest.cc b/remoting/host/continue_window_unittest.cc
index 0859ae2..0433d544 100644
--- a/remoting/host/continue_window_unittest.cc
+++ b/remoting/host/continue_window_unittest.cc
@@ -25,6 +25,7 @@
 
   MOCK_METHOD(void, ShowUi, (), (override));
   MOCK_METHOD(void, HideUi, (), (override));
+  MOCK_METHOD(void, SetButtonsEnabled, (bool), (override));
 };
 
 }  // namespace
@@ -48,6 +49,7 @@
 
   EXPECT_CALL(client_session_control_, SetDisableInputs(true));
   EXPECT_CALL(window, ShowUi());
+  EXPECT_CALL(window, SetButtonsEnabled(false));
   task_environment_.FastForwardBy(base::Minutes(30));
 }
 
@@ -57,14 +59,19 @@
 
   EXPECT_CALL(client_session_control_, SetDisableInputs(true));
   EXPECT_CALL(window, ShowUi());
+  EXPECT_CALL(window, SetButtonsEnabled(false));
   task_environment_.FastForwardBy(base::Minutes(30));
 
+  EXPECT_CALL(window, SetButtonsEnabled(true));
+  task_environment_.FastForwardBy(base::Seconds(2));
+
   EXPECT_CALL(window, HideUi());
   EXPECT_CALL(client_session_control_, SetDisableInputs(false));
   window.ContinueSession();
 
   EXPECT_CALL(client_session_control_, SetDisableInputs(true));
   EXPECT_CALL(window, ShowUi());
+  EXPECT_CALL(window, SetButtonsEnabled(false));
   task_environment_.FastForwardBy(base::Minutes(30));
 }
 
@@ -74,8 +81,12 @@
 
   EXPECT_CALL(client_session_control_, SetDisableInputs(true));
   EXPECT_CALL(window, ShowUi());
+  EXPECT_CALL(window, SetButtonsEnabled(false));
   task_environment_.FastForwardBy(base::Minutes(30));
 
+  EXPECT_CALL(window, SetButtonsEnabled(true));
+  task_environment_.FastForwardBy(base::Seconds(2));
+
   EXPECT_CALL(
       client_session_control_,
       DisconnectSession(ErrorCode::MAX_SESSION_LENGTH, testing::_, testing::_));
@@ -88,12 +99,71 @@
 
   EXPECT_CALL(client_session_control_, SetDisableInputs(true));
   EXPECT_CALL(window, ShowUi());
+  EXPECT_CALL(window, SetButtonsEnabled(false));
   task_environment_.FastForwardBy(base::Minutes(30));
 
+  EXPECT_CALL(window, SetButtonsEnabled(true));
+  task_environment_.FastForwardBy(base::Seconds(2));
+
   EXPECT_CALL(
       client_session_control_,
       DisconnectSession(ErrorCode::MAX_SESSION_LENGTH, testing::_, testing::_));
   task_environment_.FastForwardBy(base::Minutes(5));
 }
 
+TEST_F(ContinueWindowTest, ActivationDelayBlocksEarlyUserActions) {
+  TestContinueWindow window;
+  window.Start(client_session_control_factory_.GetWeakPtr());
+
+  EXPECT_CALL(client_session_control_, SetDisableInputs(true));
+  EXPECT_CALL(window, ShowUi());
+  EXPECT_CALL(window, SetButtonsEnabled(false));
+  task_environment_.FastForwardBy(base::Minutes(30));
+
+  // Before delay expires (1 second in), user actions should be ignored.
+  task_environment_.FastForwardBy(base::Seconds(1));
+  EXPECT_CALL(window, HideUi()).Times(0);
+  EXPECT_CALL(client_session_control_, SetDisableInputs(false)).Times(0);
+  EXPECT_CALL(client_session_control_,
+              DisconnectSession(testing::_, testing::_, testing::_))
+      .Times(0);
+
+  window.ContinueSession();
+  window.DisconnectSession();
+
+  // Once the 2-second delay expires, buttons are enabled and action succeeds.
+  EXPECT_CALL(window, SetButtonsEnabled(true));
+  task_environment_.FastForwardBy(base::Seconds(1));
+
+  EXPECT_CALL(window, HideUi());
+  EXPECT_CALL(client_session_control_, SetDisableInputs(false));
+  window.ContinueSession();
+}
+
+TEST_F(ContinueWindowTest, ActivationDelayBlocksEarlyDisconnect) {
+  TestContinueWindow window;
+  window.Start(client_session_control_factory_.GetWeakPtr());
+
+  EXPECT_CALL(client_session_control_, SetDisableInputs(true));
+  EXPECT_CALL(window, ShowUi());
+  EXPECT_CALL(window, SetButtonsEnabled(false));
+  task_environment_.FastForwardBy(base::Minutes(30));
+
+  // Early disconnect attempt during activation delay is ignored.
+  task_environment_.FastForwardBy(base::Seconds(1));
+  EXPECT_CALL(client_session_control_,
+              DisconnectSession(testing::_, testing::_, testing::_))
+      .Times(0);
+  window.DisconnectSession();
+
+  // Once the 2-second delay expires, DisconnectSession succeeds.
+  EXPECT_CALL(window, SetButtonsEnabled(true));
+  task_environment_.FastForwardBy(base::Seconds(1));
+
+  EXPECT_CALL(
+      client_session_control_,
+      DisconnectSession(ErrorCode::MAX_SESSION_LENGTH, testing::_, testing::_));
+  window.DisconnectSession();
+}
+
 }  // 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.