commit 89232b8b4890824f93121483626af582f13758fe
parent 1bd6e4469bb84bb49b342c10d9aa14ffd5f01187
Author: zeertzjq <zeertzjq@outlook.com>
Date: Thu, 5 Jan 2023 00:25:25 +0800
fix(tui): make a copy of data->params before unibi_format() (#21643)
Problem: When unibi_format() modifies params and data->buf overflows,
unibi_format() is called again, causing the params to be
modified twice. This can happen for escapes sequences that
use the %i terminfo format specifier (e.g. cursor_address),
which makes unibi_format() increase the param by 1.
Solution: Make a copy of data->params before calling unibi_format().
Diffstat:
2 files changed, 29 insertions(+), 1 deletion(-)
diff --git a/src/nvim/tui/tui.c b/src/nvim/tui/tui.c
@@ -1635,11 +1635,13 @@ static void unibi_goto(UI *ui, int row, int col)
} \
if (str) { \
unibi_var_t vars[26 + 26]; \
+ unibi_var_t params[9]; \
size_t orig_pos = data->bufpos; \
memset(&vars, 0, sizeof(vars)); \
data->cork = true; \
retry: \
- unibi_format(vars, vars + 26, str, data->params, out, ui, pad, ui); \
+ memcpy(params, data->params, sizeof(params)); \
+ unibi_format(vars, vars + 26, str, params, out, ui, pad, ui); \
if (data->overflow) { \
data->bufpos = orig_pos; \
flush_buf(ui); \
diff --git a/test/functional/terminal/tui_spec.lua b/test/functional/terminal/tui_spec.lua
@@ -13,6 +13,7 @@ local feed_command = helpers.feed_command
local feed_data = thelpers.feed_data
local clear = helpers.clear
local command = helpers.command
+local dedent = helpers.dedent
local exec = helpers.exec
local testprg = helpers.testprg
local retry = helpers.retry
@@ -1443,6 +1444,31 @@ describe('TUI', function()
child_session:request('nvim_call_function', 'setcellwidths', {{{0x2103, 0x2103, 1}}})
screen:expect(singlewidth_screen, attrs)
end)
+
+ it('draws correctly when cursor_address overflows #21643', function()
+ helpers.skip(helpers.is_ci('github'), 'FIXME: flaky on GitHub CI')
+ screen:try_resize(75, 60)
+ -- The composing character takes 3 bytes.
+ local composing = ('a︠'):sub(2)
+ -- The composed character takes 1 + 5 * 3 = 16 bytes.
+ local c = 'a' .. composing:rep(5)
+ -- Going to top-left corner needs 3 bytes.
+ -- With screen width 75, 4088 characters need 54 full screen lines.
+ -- Drawing each full screen line needs 75 * 16 + 2 = 1202 bytes (2 bytes for CR LF).
+ -- The incomplete screen line needs 38 * 16 + 8 + 3 = 619 bytes.
+ -- The whole line needs 3 + 54 * 1202 + 619 = 65530 bytes.
+ -- The cursor_address that comes after will overflow the 65535-byte buffer.
+ local line = c:rep(4088) .. ('b'):rep(8) .. '℃'
+ child_session:request('nvim_buf_set_lines', 0, 0, -1, true, {line, 'c'})
+ screen:expect(
+ '{1:' .. c .. '}' .. c:rep(74) .. '|\n' .. (c:rep(75) .. '|\n'):rep(53)
+ .. c:rep(38) .. ('b'):rep(8) .. '℃' .. (' '):rep(28) .. '|\n' .. dedent([[
+ c |
+ {4:~ }|
+ {5:[No Name] [+] }|
+ |
+ {3:-- TERMINAL --} |]]))
+ end)
end)
describe('TUI', function()