Chrome · Chromoting › Remoting
CVE-2026-113061
Memory Corruption in Chromoting (Remoting)
Overview
Low
Severity
—
CVSS
No
Exploited ITW
Fixed
Fix Status
Changed Functions
| Function | Change | Notes |
|---|---|---|
ifremoting/host/input_monitor/local_hotkey_input_monitor_x11.cc |
modified | |
forremoting/host/input_monitor/local_hotkey_input_monitor_x11.cc |
modified | |
LocalHotkeyInputMonitorX11Testremoting/host/input_monitor/local_hotkey_input_monitor_x11_unittest.cc |
modified | |
forremoting/host/input_monitor/local_hotkey_input_monitor_x11_unittest.cc |
modified | |
ifremoting/host/input_monitor/local_hotkey_input_monitor_x11_unittest.cc |
modified | |
TEST_Fremoting/host/input_monitor/local_hotkey_input_monitor_x11_unittest.cc |
modified | |
ifremoting/host/BUILD.gn |
modified |
Files Changed
remoting/host/BUILD.gnremoting/host/input_monitor/local_hotkey_input_monitor_x11.ccremoting/host/input_monitor/local_hotkey_input_monitor_x11.hremoting/host/input_monitor/local_hotkey_input_monitor_x11_unittest.cc
Patch
From 9af78877f4641a7830d4a01b1cb57248c393e9d1 Mon Sep 17 00:00:00 2001 From: Lambros Lambrou <[email protected]> Date: Fri, 18 Sep 2026 10:43:05 -0700 Subject: [PATCH] [remoting] Ignore XTEST events in LocalHotkeyInputMonitorX11 Filter out synthetic XTEST device events in LocalHotkeyInputMonitorX11::Core::OnEvent() by checking raw->sourceid against XTEST slave device IDs queried via XIQueryDevice(). Track pressed Left/Right Control and Alt keys in flat_sets (pressed_ctrl_keys_ and pressed_alt_keys_) so releasing one side (e.g. Control_R) does not clear modifier state while the other side (e.g. Control_L) remains physically held. Bug: 554557866 Change-Id: I3b2c5b049714b50b35ed657cbbbe8910a92d568f Reviewed-on: https://chromium-review.googlesource.com/c/chromium/src/+/8428380 Reviewed-by: Yuwei Huang <[email protected]> Commit-Queue: Lambros Lambrou <[email protected]> Cr-Commit-Position: refs/heads/main@{#1700934} --- diff --git a/remoting/host/input_monitor/local_hotkey_input_monitor_x11.cc b/remoting/host/input_monitor/local_hotkey_input_monitor_x11.cc index 12a3a39..b72fc0f 100644 --- a/remoting/host/input_monitor/local_hotkey_input_monitor_x11.cc +++ b/remoting/host/input_monitor/local_hotkey_input_monitor_x11.cc @@ -4,17 +4,14 @@ #include "remoting/host/input_monitor/local_hotkey_input_monitor_x11.h" -#include <sys/select.h> -#include <unistd.h> - #include <utility> -#include "base/compiler_specific.h" #include "base/functional/bind.h" #include "base/functional/callback.h" #include "base/location.h" #include "base/logging.h" #include "remoting/host/input_monitor/local_input_monitor_x11_common.h" +#include "ui/gfx/x/future.h" #include "ui/gfx/x/keysyms/keysyms.h" #include "ui/gfx/x/xinput.h" @@ -73,6 +70,8 @@ return; } + UpdateXTestDeviceIds(); + auto mask = CommonXIEventMaskForRootWindow(); connection_->xinput().XISelectEvents( {connection_->default_root(), @@ -85,6 +84,30 @@ connection_->RemoveEventObserver(this); } +void LocalHotkeyInputMonitorX11::Core::UpdateXTestDeviceIds() { + DCHECK(input_task_runner_->BelongsToCurrentThread()); + xtest_device_ids_.clear(); + + auto reply = + connection_->xinput().XIQueryDevice({x11::Input::DeviceId::All}).Sync(); + if (!reply) { + LOG(ERROR) << "XIQueryDevice failed."; + return; + } + for (const auto& info : reply->infos) { + if (info.name.ends_with("XTEST keyboard") || + info.name.ends_with("XTEST pointer")) { + xtest_device_ids_.insert(info.deviceid); + } + } +} + +bool LocalHotkeyInputMonitorX11::Core::IsXTestDevice( + x11::Input::DeviceId device_id) const { + DCHECK(input_task_runner_->BelongsToCurrentThread()); + return xtest_device_ids_.contains(device_id); +} + void LocalHotkeyInputMonitorX11::Core::OnEvent(const x11::Event& event) { DCHECK(input_task_runner_->BelongsToCurrentThread()); @@ -104,15 +127,30 @@ return; } + // Ignore events injected via XTEST so remote input cannot manipulate local + // modifier state or trigger local hotkeys. + if (IsXTestDevice(raw->sourceid)) { + return; + } + const bool down = raw->opcode == x11::Input::RawDeviceEvent::RawKeyPress; const auto key_sym = connection_->KeycodeToKeysym(static_cast<x11::KeyCode>(raw->detail), 0); if (key_sym == XK_Control_L || key_sym == XK_Control_R) { - ctrl_pressed_ = down; + if (down) { + pressed_ctrl_keys_.insert(key_sym); + } else { + pressed_ctrl_keys_.erase(key_sym); + } } else if (key_sym == XK_Alt_L || key_sym == XK_Alt_R) { - alt_pressed_ = down; - } else if (key_sym == XK_Escape && down && alt_pressed_ && ctrl_pressed_) { + if (down) { + pressed_alt_keys_.insert(key_sym); + } else { + pressed_alt_keys_.erase(key_sym); + } + } else if (key_sym == XK_Escape && down && !pressed_alt_keys_.empty() && + !pressed_ctrl_keys_.empty()) { caller_task_runner_->PostTask(FROM_HERE, std::move(disconnect_callback_)); } } diff --git a/remoting/host/input_monitor/local_hotkey_input_monitor_x11.h b/remoting/host/input_monitor/local_hotkey_input_monitor_x11.h index fb1c4dd..22b1487 100644 --- a/remoting/host/input_monitor/local_hotkey_input_monitor_x11.h +++ b/remoting/host/input_monitor/local_hotkey_input_monitor_x11.h @@ -5,6 +5,9 @@ #ifndef REMOTING_HOST_INPUT_MONITOR_LOCAL_HOTKEY_INPUT_MONITOR_X11_H_ #define REMOTING_HOST_INPUT_MONITOR_LOCAL_HOTKEY_INPUT_MONITOR_X11_H_ +#include <cstdint> + +#include "base/containers/flat_set.h" #include "base/memory/raw_ptr.h" #include "base/memory/ref_counted.h" #include "base/sequence_checker.h" @@ -12,7 +15,7 @@ #include "remoting/host/input_monitor/local_hotkey_input_monitor.h" #include "ui/gfx/x/connection.h" #include "ui/gfx/x/event.h" -#include "ui/gfx/x/future.h" +#include "ui/gfx/x/xinput.h" namespace remoting { @@ -45,6 +48,8 @@ ~Core() override; void StartOnInputThread(); void StopOnInputThread(); + void UpdateXTestDeviceIds(); + bool IsXTestDevice(x11::Input::DeviceId device_id) const; // x11::EventObserver: void OnEvent(const x11::Event& event) override; // Task runner on which public methods of this class must be called. @@ -53,10 +58,11 @@ scoped_refptr<base::SingleThreadTaskRunner> input_task_runner_; // Used to send session disconnect requests. base::OnceClosure disconnect_callback_; - // True when Alt is pressed. - bool alt_pressed_ = false; - // True when Ctrl is pressed. - bool ctrl_pressed_ = false; + // Local Control and Alt keysyms currently held down. + base::flat_set<uint32_t> pressed_ctrl_keys_; + base::flat_set<uint32_t> pressed_alt_keys_; + // Cached XTEST slave device IDs to ignore synthetic events. + base::flat_set<x11::Input::DeviceId> xtest_device_ids_; raw_ptr<x11::Connection> connection_ = nullptr; }; scoped_refptr<Core> core_; diff --git a/remoting/host/input_monitor/local_hotkey_input_monitor_x11_unittest.cc b/remoting/host/input_monitor/local_hotkey_input_monitor_x11_unittest.cc new file mode 100644 index 0000000..a31d2e9 --- /dev/null +++ b/remoting/host/input_monitor/local_hotkey_input_monitor_x11_unittest.cc @@ -0,0 +1,187 @@ +// Copyright 2026 The Chromium Authors +// Use of this source code is governed by a BSD-style license that can be +// found in the LICENSE file. + +#include <cstdint> +#include <memory> +#include <utility> + +#include "base/run_loop.h" +#include "base/test/task_environment.h" +#include "base/test/test_future.h" +#include "remoting/host/input_monitor/local_hotkey_input_monitor.h" +#include "testing/gtest/include/gtest/gtest.h" +#include "ui/gfx/x/connection.h" +#include "ui/gfx/x/event.h" +#include "ui/gfx/x/future.h" +#include "ui/gfx/x/keysyms/keysyms.h" +#include "ui/gfx/x/xinput.h" +#include "ui/gfx/x/xproto.h" +#include "ui/gfx/x/xtest.h" + +namespace remoting { + +class LocalHotkeyInputMonitorX11Test : public testing::Test { + public: + LocalHotkeyInputMonitorX11Test() + : task_environment_(base::test::TaskEnvironment::MainThreadType::UI) {} + + void SetUp() override { + connection_ = x11::Connection::Get(); + ASSERT_NE(connection_, nullptr); + ASSERT_TRUE(connection_->Ready()); + ASSERT_TRUE(connection_->xinput().present()); + + auto reply = + connection_->xinput().XIQueryDevice({x11::Input::DeviceId::All}).Sync(); + ASSERT_TRUE(reply); + for (const auto& info : reply->infos) { + if (info.type == x11::Input::DeviceType::MasterKeyboard) { + master_keyboard_id_ = info.deviceid; + } else if (info.name.ends_with("XTEST keyboard")) { + xtest_keyboard_id_ = info.deviceid; + } + } + ASSERT_NE(master_keyboard_id_, x11::Input::DeviceId{}); + ASSERT_NE(xtest_keyboard_id_, x11::Input::DeviceId{}); + ASSERT_NE(xtest_keyboard_id_, kLocalKeyboardId); + + auto task_runner = task_environment_.GetMainThreadTaskRunner(); + monitor_ = + LocalHotkeyInputMonitor::Create(task_runner, task_runner, task_runner, + disconnect_future_.GetCallback()); + FlushTaskRunner(); + connection_->Sync(); + connection_->DispatchAll(); + } + + void TearDown() override { + monitor_.reset(); + FlushTaskRunner(); + } + + void DispatchRawKeyEvent(uint32_t keysym, + bool down, + x11::Input::DeviceId source_id) { + x11::KeyCode keycode = connection_->KeysymToKeycode(keysym); + ASSERT_NE(keycode, x11::KeyCode{}); + + x11::Input::RawDeviceEvent raw_event; + raw_event.opcode = down ? x11::Input::RawDeviceEvent::RawKeyPress + : x11::Input::RawDeviceEvent::RawKeyRelease; + raw_event.deviceid = master_keyboard_id_; + raw_event.sourceid = source_id; + raw_event.detail = static_cast<uint32_t>(keycode); + + x11::Event event(false, std::move(raw_event)); + connection_->DispatchEvent(event); + FlushTaskRunner(); + } + + void InjectXTestKeyEvent(uint32_t keysym, bool down) { + ASSERT_TRUE(connection_->xtest().present()); + x11::KeyCode keycode = connection_->KeysymToKeycode(keysym); + ASSERT_NE(keycode, x11::KeyCode{}); + + uint8_t opcode = down ? x11::KeyEvent::Press : x11::KeyEvent::Release; + connection_->xtest().FakeInput({opcode, static_cast<uint8_t>(keycode)}); + connection_->Sync(); + connection_->DispatchAll(); + FlushTaskRunner(); + } + + void FlushTaskRunner() { + base::RunLoop run_loop; + task_environment_.GetMainThreadTaskRunner()->PostTask( + FROM_HERE, run_loop.QuitClosure()); + run_loop.Run(); + } + + protected: + // Headless Xorg (used by testing/xvfb.py on CQ bots) only creates the master + // devices and the virtual XTEST slave devices, with no physical slave + // keyboard attached. Use a distinct synthetic DeviceId to represent a + // physical slave keyboard when dispatching RawDeviceEvents. + static constexpr x11::Input::DeviceId kLocalKeyboardId{100}; + + base::test::SingleThreadTaskEnvironment task_environment_; + raw_ptr<x11::Connection> connection_ = nullptr; + x11::Input::DeviceId master_keyboard_id_{}; + x11::Input::DeviceId xtest_keyboard_id_{}; + x11::Input::DeviceId local_keyboard_id_{kLocalKeyboardId}; + base::test::TestFuture<void> disconnect_future_; + std::unique_ptr<LocalHotkeyInputMonitor> monitor_; +}; + +TEST_F(LocalHotkeyInputMonitorX11Test, LocalCtrlAltEscTriggersDisconnect) { + DispatchRawKeyEvent(XK_Control_L, /*down=*/true, local_keyboard_id_); + DispatchRawKeyEvent(XK_Alt_L, /*down=*/true, local_keyboard_id_); + DispatchRawKeyEvent(XK_Escape, /*down=*/true, local_keyboard_id_); + + EXPECT_TRUE(disconnect_future_.IsReady()); +} + +TEST_F(LocalHotkeyInputMonitorX11Test, + RemoteModifierReleaseDoesNotSuppressDisconnect) { + // Local user physically holds Control and Alt. + DispatchRawKeyEvent(XK_Control_L, /*down=*/true, local_keyboard_id_); + DispatchRawKeyEvent(XK_Alt_L, /*down=*/true, local_keyboard_id_); + + // Remote client streams modifier release events via the XTEST device. + DispatchRawKeyEvent(XK_Control_L, /*down=*/false, xtest_keyboard_id_); + DispatchRawKeyEvent(XK_Alt_L, /*down=*/false, xtest_keyboard_id_); + + // Local user presses Escape while still physically holding Control and Alt. + DispatchRawKeyEvent(XK_Escape, /*down=*/true, local_keyboard_id_); + + EXPECT_TRUE(disconnect_future_.IsReady()); +} + +TEST_F(LocalHotkeyInputMonitorX11Test, + LiveXTestModifierChurnDoesNotSuppressDisconnect) { + // Local user physically holds Control and Alt. + DispatchRawKeyEvent(XK_Control_L, /*down=*/true, local_keyboard_id_); + DispatchRawKeyEvent(XK_Alt_L, /*down=*/true, local_keyboard_id_); + + // Remote client injects modifier press/release churn through live XTest. + InjectXTestKeyEvent(XK_Control_L, /*down=*/true); + InjectXTestKeyEvent(XK_Control_L, /*down=*/false); + InjectXTestKeyEvent(XK_Alt_L, /*down=*/true); + InjectXTestKeyEvent(XK_Alt_L, /*down=*/false); + + // Local user presses Escape. + DispatchRawKeyEvent(XK_Escape, /*down=*/true, local_keyboard_id_); + + EXPECT_TRUE(disconnect_future_.IsReady()); +} + +TEST_F(LocalHotkeyInputMonitorX11Test, + RemoteInjectedHotkeysDoNotTriggerDisconnect) { + DispatchRawKeyEvent(XK_Control_L, /*down=*/true, xtest_keyboard_id_); + DispatchRawKeyEvent(XK_Alt_L, /*down=*/true, xtest_keyboard_id_); + DispatchRawKeyEvent(XK_Escape, /*down=*/true, xtest_keyboard_id_); + + EXPECT_FALSE(disconnect_future_.IsReady()); +} + +TEST_F(LocalHotkeyInputMonitorX11Test, LocalModifierReleasePreventsDisconnect) { + DispatchRawKeyEvent(XK_Control_L, /*down=*/true, local_keyboard_id_); + DispatchRawKeyEvent(XK_Control_L, /*down=*/false, local_keyboard_id_); + DispatchRawKeyEvent(XK_Alt_L, /*down=*/true, local_keyboard_id_); + DispatchRawKeyEvent(XK_Escape, /*down=*/true, local_keyboard_id_); + + EXPECT_FALSE(disconnect_future_.IsReady()); +} + +TEST_F(LocalHotkeyInputMonitorX11Test, + ReleasingOneCtrlWhileOtherCtrlHeldStillTriggersDisconnect) { + DispatchRawKeyEvent(XK_Control_L, /*down=*/true, local_keyboard_id_); + DispatchRawKeyEvent(XK_Control_R, /*down=*/true, local_keyboard_id_); + DispatchRawKeyEvent(XK_Control_R, /*down=*/false, local_keyboard_id_); + DispatchRawKeyEvent(XK_Alt_L, /*down=*/true, local_keyboard_id_); + DispatchRawKeyEvent(XK_Escape, /*down=*/true, local_keyboard_id_); + + EXPECT_TRUE(disconnect_future_.IsReady()); +} + +} // namespace remoting diff --git a/remoting/host/BUILD.gn b/remoting/host/BUILD.gn index 482ff1ef..dadad5e 100644 --- a/remoting/host/BUILD.gn +++ b/remoting/host/BUILD.gn @@ -1049,7 +1049,11 @@ } if (remoting_use_x11) { - deps += [ "//remoting/host/linux:platform_impls" ] + sources += [ "input_monitor/local_hotkey_input_monitor_x11_unittest.cc" ] + deps += [ + "//remoting/host/linux:platform_impls", + "//ui/gfx/x", + ] } if (is_chromeos) {
Loading diff…
Regression Test / PoC
shipped with the fix
diff --git a/remoting/host/input_monitor/local_hotkey_input_monitor_x11_unittest.cc b/remoting/host/input_monitor/local_hotkey_input_monitor_x11_unittest.cc
new file mode 100644
index 0000000..a31d2e9
--- /dev/null
+++ b/remoting/host/input_monitor/local_hotkey_input_monitor_x11_unittest.cc
@@ -0,0 +1,187 @@
+// Copyright 2026 The Chromium Authors
+// Use of this source code is governed by a BSD-style license that can be
+// found in the LICENSE file.
+
+#include <cstdint>
+#include <memory>
+#include <utility>
+
+#include "base/run_loop.h"
+#include "base/test/task_environment.h"
+#include "base/test/test_future.h"
+#include "remoting/host/input_monitor/local_hotkey_input_monitor.h"
+#include "testing/gtest/include/gtest/gtest.h"
+#include "ui/gfx/x/connection.h"
+#include "ui/gfx/x/event.h"
+#include "ui/gfx/x/future.h"
+#include "ui/gfx/x/keysyms/keysyms.h"
+#include "ui/gfx/x/xinput.h"
+#include "ui/gfx/x/xproto.h"
+#include "ui/gfx/x/xtest.h"
+
+namespace remoting {
+
+class LocalHotkeyInputMonitorX11Test : public testing::Test {
+ public:
+ LocalHotkeyInputMonitorX11Test()
+ : task_environment_(base::test::TaskEnvironment::MainThreadType::UI) {}
+
+ void SetUp() override {
+ connection_ = x11::Connection::Get();
+ ASSERT_NE(connection_, nullptr);
+ ASSERT_TRUE(connection_->Ready());
+ ASSERT_TRUE(connection_->xinput().present());
+
+ auto reply =
+ connection_->xinput().XIQueryDevice({x11::Input::DeviceId::All}).Sync();
+ ASSERT_TRUE(reply);
+ for (const auto& info : reply->infos) {
+ if (info.type == x11::Input::DeviceType::MasterKeyboard) {
+ master_keyboard_id_ = info.deviceid;
+ } else if (info.name.ends_with("XTEST keyboard")) {
+ xtest_keyboard_id_ = info.deviceid;
+ }
+ }
+ ASSERT_NE(master_keyboard_id_, x11::Input::DeviceId{});
+ ASSERT_NE(xtest_keyboard_id_, x11::Input::DeviceId{});
+ ASSERT_NE(xtest_keyboard_id_, kLocalKeyboardId);
+
+ auto task_runner = task_environment_.GetMainThreadTaskRunner();
+ monitor_ =
+ LocalHotkeyInputMonitor::Create(task_runner, task_runner, task_runner,
+ disconnect_future_.GetCallback());
+ FlushTaskRunner();
+ connection_->Sync();
+ connection_->DispatchAll();
+ }
+
+ void TearDown() override {
+ monitor_.reset();
+ FlushTaskRunner();
+ }
+
+ void DispatchRawKeyEvent(uint32_t keysym,
+ bool down,
+ x11::Input::DeviceId source_id) {
+ x11::KeyCode keycode = connection_->KeysymToKeycode(keysym);
+ ASSERT_NE(keycode, x11::KeyCode{});
+
+ x11::Input::RawDeviceEvent raw_event;
+ raw_event.opcode = down ? x11::Input::RawDeviceEvent::RawKeyPress
+ : x11::Input::RawDeviceEvent::RawKeyRelease;
+ raw_event.deviceid = master_keyboard_id_;
+ raw_event.sourceid = source_id;
+ raw_event.detail = static_cast<uint32_t>(keycode);
+
+ x11::Event event(false, std::move(raw_event));
+ connection_->DispatchEvent(event);
+ FlushTaskRunner();
+ }
+
+ void InjectXTestKeyEvent(uint32_t keysym, bool down) {
+ ASSERT_TRUE(connection_->xtest().present());
+ x11::KeyCode keycode = connection_->KeysymToKeycode(keysym);
+ ASSERT_NE(keycode, x11::KeyCode{});
+
+ uint8_t opcode = down ? x11::KeyEvent::Press : x11::KeyEvent::Release;
+ connection_->xtest().FakeInput({opcode, static_cast<uint8_t>(keycode)});
+ connection_->Sync();
+ connection_->DispatchAll();
+ FlushTaskRunner();
+ }
+
+ void FlushTaskRunner() {
+ base::RunLoop run_loop;
+ task_environment_.GetMainThreadTaskRunner()->PostTask(
+ FROM_HERE, run_loop.QuitClosure());
+ run_loop.Run();
+ }
+
+ protected:
+ // Headless Xorg (used by testing/xvfb.py on CQ bots) only creates the master
+ // devices and the virtual XTEST slave devices, with no physical slave
+ // keyboard attached. Use a distinct synthetic DeviceId to represent a
+ // physical slave keyboard when dispatching RawDeviceEvents.
+ static constexpr x11::Input::DeviceId kLocalKeyboardId{100};
+
+ base::test::SingleThreadTaskEnvironment task_environment_;
+ raw_ptr<x11::Connection> connection_ = nullptr;
+ x11::Input::DeviceId master_keyboard_id_{};
+ x11::Input::DeviceId xtest_keyboard_id_{};
+ x11::Input::DeviceId local_keyboard_id_{kLocalKeyboardId};
+ base::test::TestFuture<void> disconnect_future_;
+ std::unique_ptr<LocalHotkeyInputMonitor> monitor_;
+};
+
+TEST_F(LocalHotkeyInputMonitorX11Test, LocalCtrlAltEscTriggersDisconnect) {
+ DispatchRawKeyEvent(XK_Control_L, /*down=*/true, local_keyboard_id_);
+ DispatchRawKeyEvent(XK_Alt_L, /*down=*/true, local_keyboard_id_);
+ DispatchRawKeyEvent(XK_Escape, /*down=*/true, local_keyboard_id_);
+
+ EXPECT_TRUE(disconnect_future_.IsReady());
+}
+
+TEST_F(LocalHotkeyInputMonitorX11Test,
+ RemoteModifierReleaseDoesNotSuppressDisconnect) {
+ // Local user physically holds Control and Alt.
+ DispatchRawKeyEvent(XK_Control_L, /*down=*/true, local_keyboard_id_);
+ DispatchRawKeyEvent(XK_Alt_L, /*down=*/true, local_keyboard_id_);
+
+ // Remote client streams modifier release events via the XTEST device.
+ DispatchRawKeyEvent(XK_Control_L, /*down=*/false, xtest_keyboard_id_);
+ DispatchRawKeyEvent(XK_Alt_L, /*down=*/false, xtest_keyboard_id_);
+
+ // Local user presses Escape while still physically holding Control and Alt.
+ DispatchRawKeyEvent(XK_Escape, /*down=*/true, local_keyboard_id_);
+
+ EXPECT_TRUE(disconnect_future_.IsReady());
+}
+
+TEST_F(LocalHotkeyInputMonitorX11Test,
+ LiveXTestModifierChurnDoesNotSuppressDisconnect) {
+ // Local user physically holds Control and Alt.
+ DispatchRawKeyEvent(XK_Control_L, /*down=*/true, local_keyboard_id_);
+ DispatchRawKeyEvent(XK_Alt_L, /*down=*/true, local_keyboard_id_);
+
+ // Remote client injects modifier press/release churn through live XTest.
+ InjectXTestKeyEvent(XK_Control_L, /*down=*/true);
+ InjectXTestKeyEvent(XK_Control_L, /*down=*/false);
+ InjectXTestKeyEvent(XK_Alt_L, /*down=*/true);
+ InjectXTestKeyEvent(XK_Alt_L, /*down=*/false);
+
+ // Local user presses Escape.
+ DispatchRawKeyEvent(XK_Escape, /*down=*/true, local_keyboard_id_);
+
+ EXPECT_TRUE(disconnect_future_.IsReady());
+}
+
+TEST_F(LocalHotkeyInputMonitorX11Test,
+ RemoteInjectedHotkeysDoNotTriggerDisconnect) {
+ DispatchRawKeyEvent(XK_Control_L, /*down=*/true, xtest_keyboard_id_);
+ DispatchRawKeyEvent(XK_Alt_L, /*down=*/true, xtest_keyboard_id_);
+ DispatchRawKeyEvent(XK_Escape, /*down=*/true, xtest_keyboard_id_);
+
+ EXPECT_FALSE(disconnect_future_.IsReady());
+}
+
+TEST_F(LocalHotkeyInputMonitorX11Test, LocalModifierReleasePreventsDisconnect) {
+ DispatchRawKeyEvent(XK_Control_L, /*down=*/true, local_keyboard_id_);
+ DispatchRawKeyEvent(XK_Control_L, /*down=*/false, local_keyboard_id_);
+ DispatchRawKeyEvent(XK_Alt_L, /*down=*/true, local_keyboard_id_);
+ DispatchRawKeyEvent(XK_Escape, /*down=*/true, local_keyboard_id_);
+
+ EXPECT_FALSE(disconnect_future_.IsReady());
+}
+
+TEST_F(LocalHotkeyInputMonitorX11Test,
+ ReleasingOneCtrlWhileOtherCtrlHeldStillTriggersDisconnect) {
+ DispatchRawKeyEvent(XK_Control_L, /*down=*/true, local_keyboard_id_);
+ DispatchRawKeyEvent(XK_Control_R, /*down=*/true, local_keyboard_id_);
+ DispatchRawKeyEvent(XK_Control_R, /*down=*/false, local_keyboard_id_);
+ DispatchRawKeyEvent(XK_Alt_L, /*down=*/true, local_keyboard_id_);
+ DispatchRawKeyEvent(XK_Escape, /*down=*/true, local_keyboard_id_);
+
+ EXPECT_TRUE(disconnect_future_.IsReady());
+}
+
+} // 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