mirror of
https://github.com/neovim/neovim.git
synced 2024-12-23 20:55:18 -07:00
fix(tui): grow termkey's internal buffer for large escape sequences (#26309)
Some escape sequences (in particular, OSC 52 paste responses) can be very large, even unbounded in length. These can easily overflow termkey's internal buffer. In order to process these long sequences, dynamically grow termkey's internal buffer.
This commit is contained in:
parent
01b91deec7
commit
884a83049b
@ -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)
|
||||
|
@ -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.
|
||||
|
Loading…
Reference in New Issue
Block a user