Overview

Low
Severity
—
CVSS
No
Exploited ITW
Fixed
Fix Status
ImpactIncomplete cleanup in Chromoting
DescriptionIncomplete cleanup in Chromoting
ComponentChromoting
Sub-componentServices (Use Subcomponents)>Chromoting
Bug ClassLogic Error
Tracker556215048
Fix commit7de3e28dde19 (chromium/src) +7/-6
CISA KEVNot listed
CreditedGoogle
Disclosed2026-10-06

Files Changed

  • remoting/host/peer_session_impl.cc
  • remoting/host/peer_session_impl_unittest.cc
From 7de3e28dde19f5f5dcbc7ce01049b5ea953ad33f Mon Sep 17 00:00:00 2001
From: Jamie Walch <[email protected]>
Date: Fri, 04 Sep 2026 09:56:58 -0700
Subject: [PATCH] Disable local echo expectation on macOS in PeerSessionImpl.

This change adds macOS to the preprocessor guard that disables local echo expectation. Since LocalMouseInputMonitorMac filters out injected input echoes before they reach the remote input filter, expecting local echo is unnecessary on macOS and can lead to false
positives where local input is mistakenly ignored as a remote echo.

Fixed: 556215048
Change-Id: I52f3fad28e3e2915ae61e6ab474356cf097fffe7
Reviewed-on: https://chromium-review.googlesource.com/c/chromium/src/+/8353531
Auto-Submit: Jamie Walch <[email protected]>
Commit-Queue: Joe Downing <[email protected]>
Reviewed-by: Joe Downing <[email protected]>
Cr-Commit-Position: refs/heads/main@{#1692540}
---
diff --git a/remoting/host/peer_session_impl.cc b/remoting/host/peer_session_impl.cc
index e087a7e..2a4c3f7 100644
--- a/remoting/host/peer_session_impl.cc
+++ b/remoting/host/peer_session_impl.cc
@@ -149,11 +149,12 @@
       connection_(std::move(connection)) {
   connection_->SetEventHandler(this);
 
-#if BUILDFLAG(IS_WIN) || BUILDFLAG(IS_CHROMEOS)
-  // LocalMouseInputMonitorWin and LocalPointerInputMonitorChromeos filter out
-  // an echo of the injected input before it reaches `remote_input_filter_`.
+#if BUILDFLAG(IS_WIN) || BUILDFLAG(IS_CHROMEOS) || BUILDFLAG(IS_MAC)
+  // LocalMouseInputMonitorWin, LocalPointerInputMonitorChromeos, and
+  // LocalMouseInputMonitorMac filter out an echo of the injected input before
+  // it reaches `remote_input_filter_`.
   input_pipeline_.remote_input_filter()->SetExpectLocalEcho(false);
-#endif  // BUILDFLAG(IS_WIN) || BUILDFLAG(IS_CHROMEOS)
+#endif  // BUILDFLAG(IS_WIN) || BUILDFLAG(IS_CHROMEOS) || BUILDFLAG(IS_MAC)
 }
 
 void PeerSessionImpl::Start(
diff --git a/remoting/host/peer_session_impl_unittest.cc b/remoting/host/peer_session_impl_unittest.cc
index 22490aef..901e447 100644
--- a/remoting/host/peer_session_impl_unittest.cc
+++ b/remoting/host/peer_session_impl_unittest.cc
@@ -520,11 +520,11 @@
   connection_->input_stub()->InjectMouseEvent(MakeFractionalMouseMoveEvent(
       100, 101, kDisplay1Id, kDisplay1Width, kDisplay1Height));
 
-#if !BUILDFLAG(IS_WIN) && !BUILDFLAG(IS_CHROMEOS)
+#if !BUILDFLAG(IS_WIN) && !BUILDFLAG(IS_CHROMEOS) && !BUILDFLAG(IS_MAC)
   // The OS echoes the injected event back.
   peer_session_->OnLocalPointerMoved(webrtc::DesktopVector(100, 101),
                                      ui::EventType::kMouseMoved);
-#endif  // !BUILDFLAG(IS_WIN)
+#endif  // !BUILDFLAG(IS_WIN) && !BUILDFLAG(IS_CHROMEOS) && !BUILDFLAG(IS_MAC)
 
   // This one should get through as well.
   connection_->input_stub()->InjectMouseEvent(MakeFractionalMouseMoveEvent(
Loading diff…

Regression Test / PoC

shipped with the fix
diff --git a/remoting/host/peer_session_impl_unittest.cc b/remoting/host/peer_session_impl_unittest.cc
index 22490aef..901e447 100644
--- a/remoting/host/peer_session_impl_unittest.cc
+++ b/remoting/host/peer_session_impl_unittest.cc
@@ -520,11 +520,11 @@
   connection_->input_stub()->InjectMouseEvent(MakeFractionalMouseMoveEvent(
       100, 101, kDisplay1Id, kDisplay1Width, kDisplay1Height));
 
-#if !BUILDFLAG(IS_WIN) && !BUILDFLAG(IS_CHROMEOS)
+#if !BUILDFLAG(IS_WIN) && !BUILDFLAG(IS_CHROMEOS) && !BUILDFLAG(IS_MAC)
   // The OS echoes the injected event back.
   peer_session_->OnLocalPointerMoved(webrtc::DesktopVector(100, 101),
                                      ui::EventType::kMouseMoved);
-#endif  // !BUILDFLAG(IS_WIN)
+#endif  // !BUILDFLAG(IS_WIN) && !BUILDFLAG(IS_CHROMEOS) && !BUILDFLAG(IS_MAC)
 
   // This one should get through as well.
   connection_->input_stub()->InjectMouseEvent(MakeFractionalMouseMoveEvent(
Loading diff…

Original Bug Report

reported by [email protected]

Potential bypass of CRD local input priority on macOS due to enabled expect_local_echo

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: Chrome Remote Desktop on macOS omits BUILDFLAG(IS_MAC) when disabling echo filtering in PeerSessionImpl, leaving expect_local_echo enabled. However, LocalMouseInputMonitorMac already filters out synthetic events using process ID checks, preventing echo events from draining the 50-entry injected position queue. Consequently, genuine local mouse inputs matching previously injected coordinates can be misidentified as echoes, suppressing local input priority and the 2-second remote input lockout.

Affected files:

  • remoting/host/peer_session_impl.cc
  • remoting/host/remote_input_filter.cc
  • remoting/host/input_monitor/local_mouse_input_monitor_mac.mm

Estimated timestamp from git blame: 2011-10-21

Potential suppression of local input priority in Chrome Remote Desktop on macOS

Summary

In Chrome Remote Desktop (CRD), local input priority is designed to protect users by preempting remote input, releasing held keys/buttons via InputEventTracker::ReleaseAll(), and blocking remote input for 2000 ms (kRemoteBlockTimeoutMillis) whenever local physical input is detected.

On Windows and ChromeOS, where the platform input monitors filter out synthetic input events before reporting them, PeerSessionImpl disables echo filtering via SetExpectLocalEcho(false). However, remoting/host/peer_session_impl.cc omits macOS from this configuration. On macOS, LocalMouseInputMonitorMac also filters out synthetic input events by checking kCGEventSourceUnixProcessID == 0, meaning echo events are never delivered to RemoteInputFilter. Because echoes never arrive, the 50-entry injected_mouse_positions_ queue never drains, causing genuine local mouse movements matching any of the last 50 injected positions to be misclassified as echoes and ignored.

Root Cause Analysis

  1. Echo filter enabled by default: In remoting/host/remote_input_filter.cc:53, expect_local_echo_ defaults to true.
  2. Missing platform check: In remoting/host/peer_session_impl.cc:152-156:
#if BUILDFLAG(IS_WIN) || BUILDFLAG(IS_CHROMEOS)
  // LocalMouseInputMonitorWin and LocalPointerInputMonitorChromeos filter out
  // an echo of the injected input before it reaches `remote_input_filter_`.
  input_pipeline_.remote_input_filter()->SetExpectLocalEcho(false);
#endif  // BUILDFLAG(IS_WIN) || BUILDFLAG(IS_CHROMEOS)

BUILDFLAG(IS_MAC) is missing, so expect_local_echo_ remains true on macOS.

  1. Monitor self-filtering: In remoting/host/input_monitor/local_mouse_input_monitor_mac.mm:79-90, the event tap callback inspects the event creator PID:
static CGEventRef LocalMouseMoved(CGEventTapProxy proxy,
                                  CGEventType type,
                                  CGEventRef event,
                                  void* context) {
  int64_t pid = CGEventGetIntegerValueField(event, kCGEventSourceUnixProcessID);
  if (pid == 0) {
    CGPoint cgMousePos = CGEventGetLocation(event);
    webrtc::DesktopVector mousePos(cgMousePos.x, cgMousePos.y);
    [(__bridge LocalInputMonitorManager*)context localMouseMoved:mousePos];
  }
  return nullptr;
}

Synthetic events injected by CRD carry the host’s non-zero PID (pid != 0) and are dropped. They are never forwarded to RemoteInputFilter::LocalPointerMoved.

  1. Non-draining queue & false echo classification: When the remote client moves the mouse, RemoteInputFilter::InjectMouseEvent (remoting/host/remote_input_filter.cc:140-147) appends up to 50 coordinates to injected_mouse_positions_. When the local user physically moves the mouse (pid == 0), RemoteInputFilter::LocalPointerMoved (remoting/host/remote_input_filter.cc:65-84) compares the position against injected_mouse_positions_ using PositionsRoughlyEqual (±1 px tolerance). If a match is found, it removes the entry and returns false, skipping LocalInputDetected() (:86).

Impact

When genuine local mouse movement is classified as an echo:

  • InputEventTracker::ReleaseAll() is not called, allowing a malicious peer to keep buttons and keys held.
  • The 2-second remote input block is not engaged (latest_local_input_time_ is not updated).
  • PeerSessionImpl::OnLocalPointerMoved treats is_local as false, skipping visibility notifications and input-based session termination.

While fast mouse movements outside the ±1 px fuzzy match window will eventually trigger local input detection, slow or deliberate local movements (e.g. aiming for the disconnect button) can be suppressed or delayed by an active remote peer.

Potential Reproduction Steps

(Note: These are potential steps based on code analysis, as our tooling cannot currently execute live code.)

  1. Establish a CRD session (It2Me or Me2Me) with a macOS host.
  2. From the remote client, send mouse move events across a designated region to populate the 50-entry queue in RemoteInputFilter.
  3. On the host, physically move the mouse slowly within ±1 px of the injected positions.
  4. Observe that RemoteInputFilter::LocalPointerMoved returns false on matches, and remote input is not blocked for 2000 ms.

Suggested Fix

Include BUILDFLAG(IS_MAC) (or BUILDFLAG(IS_APPLE)) in the #if check in remoting/host/peer_session_impl.cc:

#if BUILDFLAG(IS_WIN) || BUILDFLAG(IS_CHROMEOS) || BUILDFLAG(IS_MAC)
  // LocalMouseInputMonitorWin, LocalPointerInputMonitorChromeos, and
  // LocalMouseInputMonitorMac filter out an echo of the injected input before
  // it reaches `remote_input_filter_`.
  input_pipeline_.remote_input_filter()->SetExpectLocalEcho(false);
#endif  // BUILDFLAG(IS_WIN) || BUILDFLAG(IS_CHROMEOS) || BUILDFLAG(IS_MAC)

Evaluated with Chrome root at commit: 53c437ac4ea52cc89d0265eb9e6d68666fdb303e


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