Chrome · Chromoting › Remoting
CVE-2026-106385
Race in Chromoting (Remoting)
Overview
Low
Severity
—
CVSS
No
Exploited ITW
Fixed
Fix Status
Changed Functions
| Function | Change | Notes |
|---|---|---|
ifremoting/host/continue_window.cc |
modified | |
ifremoting/host/continue_window_chromeos.cc |
modified | |
ifremoting/host/continue_window_linux.cc |
modified | |
ifremoting/host/continue_window_mac.mm |
modified | |
ifremoting/host/continue_window_win.cc |
modified |
Files Changed
remoting/host/continue_window_mac.mmremoting/host/continue_window.hremoting/host/continue_window_win.ccremoting/host/continue_window_unittest.ccremoting/host/continue_window_linux.ccremoting/host/continue_window_chromeos.ccremoting/host/continue_window.cc
Patch
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.
References
On This Page