aboutsummaryrefslogtreecommitdiff
diff options
context:
space:
mode:
-rw-r--r--src/nvim/tui/input.c41
-rw-r--r--test/functional/terminal/tui_spec.lua25
2 files changed, 59 insertions, 7 deletions
diff --git a/src/nvim/tui/input.c b/src/nvim/tui/input.c
index 639bfc0f79..7ef10c0acd 100644
--- a/src/nvim/tui/input.c
+++ b/src/nvim/tui/input.c
@@ -27,6 +27,11 @@
#define READ_STREAM_SIZE 0xfff
#define KEY_BUFFER_SIZE 0xfff
+/// Size of libtermkey's internal input buffer. The buffer may grow larger than
+/// this when processing very long escape sequences, but will shrink back to
+/// this size afterward
+#define INPUT_BUFFER_SIZE 256
+
static const struct kitty_key_map_entry {
int key;
const char *name;
@@ -139,6 +144,7 @@ void tinput_init(TermInput *input, Loop *loop)
input->tk = termkey_new_abstract(term,
TERMKEY_FLAG_UTF8 | TERMKEY_FLAG_NOSTART);
+ termkey_set_buffer_size(input->tk, INPUT_BUFFER_SIZE);
termkey_hook_terminfo_getstr(input->tk, input->tk_ti_hook_fn, input);
termkey_start(input->tk);
@@ -147,7 +153,6 @@ void tinput_init(TermInput *input, Loop *loop)
// setup input handle
rstream_init_fd(loop, &input->read_stream, input->in_fd, READ_STREAM_SIZE);
- termkey_set_buffer_size(input->tk, rbuffer_capacity(input->read_stream.buffer));
// initialize a timer handle for handling ESC with libtermkey
time_watcher_init(loop, &input->timer_handle, input);
@@ -691,20 +696,42 @@ static void handle_raw_buffer(TermInput *input, bool force)
}
// Push through libtermkey (translates to "<keycode>" strings, etc.).
RBUFFER_UNTIL_EMPTY(input->read_stream.buffer, ptr, len) {
- size_t consumed = termkey_push_bytes(input->tk, ptr, MIN(count, len));
- // termkey_push_bytes can return (size_t)-1, so it is possible that
- // `consumed > rbuffer_size(input->read_stream.buffer)`, but since tk_getkeys is
- // called soon, it shouldn't happen.
+ const size_t size = MIN(count, len);
+ if (size > termkey_get_buffer_remaining(input->tk)) {
+ // We are processing a very long escape sequence. Increase termkey's
+ // internal buffer size. We don't handle out of memory situations so
+ // assert the result
+ const size_t delta = size - termkey_get_buffer_remaining(input->tk);
+ const size_t bufsize = termkey_get_buffer_size(input->tk);
+ const bool success = termkey_set_buffer_size(input->tk, MAX(bufsize + delta, bufsize * 2));
+ assert(success);
+ }
+
+ size_t consumed = termkey_push_bytes(input->tk, ptr, size);
+
+ // We resize termkey's buffer when it runs out of space, so this should
+ // never happen
assert(consumed <= rbuffer_size(input->read_stream.buffer));
rbuffer_consumed(input->read_stream.buffer, consumed);
- // Process the keys now: there is no guarantee `count` will
- // fit into libtermkey's input buffer.
+
+ // Process the input buffer now for any keys
tk_getkeys(input, false);
+
if (!(count -= consumed)) {
break;
}
}
} while (rbuffer_size(input->read_stream.buffer));
+
+ const size_t tk_size = termkey_get_buffer_size(input->tk);
+ const size_t tk_remaining = termkey_get_buffer_remaining(input->tk);
+ const size_t tk_count = tk_size - tk_remaining;
+ if (tk_count < INPUT_BUFFER_SIZE && tk_size > INPUT_BUFFER_SIZE) {
+ // If the termkey buffer was resized to handle a large input sequence then
+ // shrink it back down to its original size.
+ const bool success = termkey_set_buffer_size(input->tk, INPUT_BUFFER_SIZE);
+ assert(success);
+ }
}
static void tinput_read_cb(Stream *stream, RBuffer *buf, size_t count_, void *data, bool eof)
diff --git a/test/functional/terminal/tui_spec.lua b/test/functional/terminal/tui_spec.lua
index b17eed00f9..96ae0c4662 100644
--- a/test/functional/terminal/tui_spec.lua
+++ b/test/functional/terminal/tui_spec.lua
@@ -2557,6 +2557,31 @@ describe("TUI", function()
end)
end)
+ it('does not crash on large inputs #26099', function()
+ nvim_tui()
+
+ screen:expect([[
+ {1: } |
+ {4:~ }|
+ {4:~ }|
+ {4:~ }|
+ {4:~ }|
+ |
+ {3:-- TERMINAL --} |
+ ]])
+
+ feed_data(string.format('\027]52;c;%s\027\\', string.rep('A', 8192)))
+
+ screen:expect{grid=[[
+ {1: } |
+ {4:~ }|
+ {4:~ }|
+ {4:~ }|
+ {4:~ }|
+ |
+ {3:-- TERMINAL --} |
+ ]], unchanged=true}
+ end)
end)
-- See test/unit/tui_spec.lua for unit tests.