Skip to content

Commit add4c95

Browse files
committed
Try to guard certain wayland message size limit.
Wayland has a 4096 message size limit internally. For message that carries string, it can be arbitrarily long and causing issues. The two most relevant ones are preedit string and commit string. For preedit_string, there isn't much we can do, so we simply prevent such message from being sent. For commit_string, it can be sliced into smaller 4k pieces to send them one by one. Fix #1386
1 parent c2a95d2 commit add4c95

File tree

5 files changed

+72
-6
lines changed

5 files changed

+72
-6
lines changed

src/frontend/waylandim/waylandimserver.cpp

Lines changed: 10 additions & 2 deletions
Original file line numberDiff line numberDiff line change
@@ -630,8 +630,16 @@ void WaylandIMInputContextV1::updatePreeditDelegate(InputContext *ic) const {
630630
index += preedit.stringAt(i).size();
631631
}
632632
}
633-
ic_->preeditString(serial_, preedit.toString().c_str(),
634-
preedit.toStringForCommit().c_str());
633+
const std::string preeditString = preedit.toString();
634+
const std::string preeditCommitString = preedit.toStringForCommit();
635+
// Avoid hitting wayland message limit.
636+
if (preeditString.size() + preeditCommitString.size() >=
637+
WaylandIMServerBase::safeStringLimit) {
638+
return;
639+
}
640+
641+
ic_->preeditString(serial_, preeditString.c_str(),
642+
preeditCommitString.c_str());
635643
}
636644

637645
void WaylandIMInputContextV1::deleteSurroundingTextDelegate(

src/frontend/waylandim/waylandimserver.h

Lines changed: 3 additions & 1 deletion
Original file line numberDiff line numberDiff line change
@@ -93,7 +93,9 @@ class WaylandIMInputContextV1 : public VirtualInputContextGlue {
9393
if (!ic_) {
9494
return;
9595
}
96-
ic_->commitString(serial_, text.c_str());
96+
97+
WaylandIMServerBase::commitStringWrapper(
98+
text, [this](const char *str) { ic_->commitString(serial_, str); });
9799
}
98100
void deleteSurroundingTextDelegate(InputContext *ic, int offset,
99101
unsigned int size) const override;

src/frontend/waylandim/waylandimserverbase.h

Lines changed: 51 additions & 0 deletions
Original file line numberDiff line numberDiff line change
@@ -7,6 +7,8 @@
77
#ifndef _FCITX5_FRONTEND_WAYLANDIM_WAYLANDIMSERVERBASE_H_
88
#define _FCITX5_FRONTEND_WAYLANDIM_WAYLANDIMSERVERBASE_H_
99

10+
#include <cassert>
11+
#include <cstddef>
1012
#include <cstdint>
1113
#include <memory>
1214
#include <optional>
@@ -16,6 +18,7 @@
1618
#include <xkbcommon/xkbcommon.h>
1719
#include "fcitx-utils/key.h"
1820
#include "fcitx-utils/misc.h"
21+
#include "fcitx-utils/utf8.h"
1922
#include "fcitx/focusgroup.h"
2023
#include "display.h"
2124
#include "waylandim.h"
@@ -42,6 +45,20 @@ class WaylandIMServerBase {
4245
const std::shared_ptr<wayland::WlSeat> &seat,
4346
const std::optional<std::tuple<int32_t, int32_t>> &defaultValue) const;
4447

48+
template <typename Callback>
49+
static void commitStringWrapper(const std::string &text,
50+
const Callback &callback) {
51+
if (text.size() < safeStringLimit) {
52+
callback(text.data());
53+
return;
54+
}
55+
commitStringWrapperImpl(text, callback);
56+
}
57+
58+
// zwp_input_method_v2 mentioned that commit string should be less than 4000
59+
// bytes, use this number of avoid hit 4096 wayland message size limit.
60+
static constexpr size_t safeStringLimit = 4000;
61+
4562
protected:
4663
FocusGroup *group_;
4764
std::string name_;
@@ -55,6 +72,40 @@ class WaylandIMServerBase {
5572
KeyStates modifiers_;
5673

5774
private:
75+
template <typename Callback>
76+
static void commitStringWrapperImpl(std::string text,
77+
const Callback &callback) {
78+
char *start = text.data();
79+
char *end = text.data() + text.length();
80+
81+
while (start < end) {
82+
char *current = start;
83+
84+
while (current < end) {
85+
uint32_t c;
86+
char *newCurrent = utf8::getNextChar(current, end, &c);
87+
if (!utf8::isValidChar(c)) {
88+
return;
89+
}
90+
if (newCurrent > start + safeStringLimit) {
91+
break;
92+
}
93+
assert(current < newCurrent);
94+
current = newCurrent;
95+
}
96+
assert(current <= end);
97+
assert(start < current);
98+
// Set *current to \0, so [start, current) will become a nul
99+
// terminate string.
100+
const char pivot = *current;
101+
*current = '\0';
102+
callback(start);
103+
// Restore after we use it.
104+
*current = pivot;
105+
start = current;
106+
}
107+
}
108+
58109
std::optional<std::tuple<int32_t, int32_t>> repeatInfo(
59110
const std::shared_ptr<wayland::WlSeat> &seat,
60111
const std::optional<std::tuple<int32_t, int32_t>> &defaultValue) const;

src/frontend/waylandim/waylandimserverv2.cpp

Lines changed: 3 additions & 1 deletion
Original file line numberDiff line numberDiff line change
@@ -664,7 +664,9 @@ void WaylandIMInputContextV2::updatePreeditDelegate(InputContext *ic) const {
664664
}
665665
}
666666

667-
if (preedit.textLength()) {
667+
// Validate not empty and within wayland limit.
668+
if (preedit.textLength() > 0 &&
669+
preedit.textLength() < WaylandIMServerBase::safeStringLimit) {
668670
if (cursorStart < 0) {
669671
cursorStart = cursorEnd = preedit.textLength();
670672
}

src/frontend/waylandim/waylandimserverv2.h

Lines changed: 5 additions & 2 deletions
Original file line numberDiff line numberDiff line change
@@ -103,8 +103,11 @@ class WaylandIMInputContextV2 : public VirtualInputContextGlue {
103103
if (!ic_) {
104104
return;
105105
}
106-
ic_->commitString(text.c_str());
107-
ic_->commit(serial_);
106+
107+
WaylandIMServerBase::commitStringWrapper(text, [this](const char *str) {
108+
ic_->commitString(str);
109+
ic_->commit(serial_);
110+
});
108111
}
109112
void deleteSurroundingTextDelegate(InputContext *ic, int offset,
110113
unsigned int size) const override;

0 commit comments

Comments
 (0)