From adb4f89453c890033288bf2ed6f36fa76f5caec5 Mon Sep 17 00:00:00 2001 From: wildoranges Date: Sat, 29 Aug 2026 14:11:35 +0800 Subject: [PATCH] fix(client): validate LSYN payload length before parsing remote layouts fixes: #10107 A malicious server can send a language synchronisation (LSYN) message with a single-byte payload. size()-2 then underflows to SIZE_MAX and substr(2, 2) throws std::out_of_range, terminating the client. Reject odd-length payloads before mutating state, mirroring the existing DSOP even-length check; an empty payload is still treated as a normal clear. --- src/lib/deskflow/KeyboardLayoutManager.cpp | 13 ++++++++----- .../deskflow/KeyboardLayoutManagerTests.cpp | 16 ++++++++++++++++ .../deskflow/KeyboardLayoutManagerTests.h | 2 ++ 3 files changed, 26 insertions(+), 5 deletions(-) diff --git a/src/lib/deskflow/KeyboardLayoutManager.cpp b/src/lib/deskflow/KeyboardLayoutManager.cpp index e77d0f43c..906aeee64 100644 --- a/src/lib/deskflow/KeyboardLayoutManager.cpp +++ b/src/lib/deskflow/KeyboardLayoutManager.cpp @@ -35,12 +35,15 @@ KeyboardLayoutManager::KeyboardLayoutManager(const std::vector &loc void KeyboardLayoutManager::setRemoteLayouts(const std::string_view &remoteLayouts) { + if (!remoteLayouts.empty() && remoteLayouts.size() % 2 != 0) { + LOG_ERR("remote layouts are the incorrect size, can not process them"); + return; + } + m_remoteLayouts.clear(); - if (!remoteLayouts.empty()) { - for (size_t i = 0; i <= remoteLayouts.size() - 2; i += 2) { - auto rLangs = remoteLayouts.substr(i, 2); - m_remoteLayouts.emplace_back(rLangs); - } + for (size_t i = 0; i + 2 <= remoteLayouts.size(); i += 2) { + auto rLangs = remoteLayouts.substr(i, 2); + m_remoteLayouts.emplace_back(rLangs); } LOG_INFO("remote layouts: %s", vectorToString(m_remoteLayouts, ", ").c_str()); } diff --git a/src/unittests/deskflow/KeyboardLayoutManagerTests.cpp b/src/unittests/deskflow/KeyboardLayoutManagerTests.cpp index 4182667d8..5e162e569 100644 --- a/src/unittests/deskflow/KeyboardLayoutManagerTests.cpp +++ b/src/unittests/deskflow/KeyboardLayoutManagerTests.cpp @@ -26,6 +26,22 @@ void KeyboardLayoutManagerTests::remoteLayouts() QVERIFY(manager.getRemoteLayouts().empty()); } +void KeyboardLayoutManagerTests::remoteLayouts_tooShort_returnsEmpty() +{ + deskflow::KeyboardLayoutManager manager({"ru", "en", "uk"}); + + manager.setRemoteLayouts("a"); + QVERIFY(manager.getRemoteLayouts().empty()); +} + +void KeyboardLayoutManagerTests::remoteLayouts_oddLength_returnsEmpty() +{ + deskflow::KeyboardLayoutManager manager({"ru", "en", "uk"}); + + manager.setRemoteLayouts("rue"); + QVERIFY(manager.getRemoteLayouts().empty()); +} + void KeyboardLayoutManagerTests::localLayout() { std::vector localLayouts = {"ru", "en", "uk"}; diff --git a/src/unittests/deskflow/KeyboardLayoutManagerTests.h b/src/unittests/deskflow/KeyboardLayoutManagerTests.h index 14ba6ffd7..e0f03075a 100644 --- a/src/unittests/deskflow/KeyboardLayoutManagerTests.h +++ b/src/unittests/deskflow/KeyboardLayoutManagerTests.h @@ -15,6 +15,8 @@ private Q_SLOTS: void initTestCase(); // Test are run in order top to bottom void remoteLayouts(); + void remoteLayouts_tooShort_returnsEmpty(); + void remoteLayouts_oddLength_returnsEmpty(); void localLayout(); void missedLayout(); void serializeLocalLayouts();