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:
Gregory Anders 2023-11-30 08:04:33 -06:00 committed by GitHub
parent 01b91deec7
commit 884a83049b
No known key found for this signature in database
GPG Key ID: 4AEE18F83AFDEB23
2 changed files with 59 additions and 7 deletions

View File

@ -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)

View File

@ -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.