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().
This commit is contained in:
zeertzjq 2023-01-05 00:25:25 +08:00 committed by GitHub
parent 1bd6e4469b
commit 89232b8b48
No known key found for this signature in database
GPG Key ID: 4AEE18F83AFDEB23
2 changed files with 29 additions and 1 deletions

View File

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

View File

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