From 31a51acdc3534f316dfedfaa37fea715f2a4814d Mon Sep 17 00:00:00 2001 From: Ibby <33922797+SleepySwords@users.noreply.github.com> Date: Sat, 24 Jun 2023 20:39:16 +1000 Subject: [PATCH] fix(extmarks): fix heap buffer overflow caused by inline virtual text (#23851) also fixes an edge case where the extends character would not be drawn if the real text exactly fits the grid. --- src/nvim/drawline.c | 31 +++++++++++++++++++++++-- test/functional/ui/decorations_spec.lua | 22 ++++++++++++++++++ 2 files changed, 51 insertions(+), 2 deletions(-) diff --git a/src/nvim/drawline.c b/src/nvim/drawline.c index 9728804ed7..b8dc1297cc 100644 --- a/src/nvim/drawline.c +++ b/src/nvim/drawline.c @@ -134,6 +134,7 @@ typedef struct { int skip_cells; // nr of cells to skip for virtual text int skipped_cells; // nr of skipped virtual text cells + bool more_virt_inline_chunks; // indicates if there is more inline virtual text after n_extra } winlinevars_T; /// for line_putchar. Contains the state that needs to be remembered from @@ -870,6 +871,25 @@ static void apply_cursorline_highlight(win_T *wp, winlinevars_T *wlv) } } +// Checks if there is more inline virtual text that need to be drawn +// and sets has_more_virt_inline_chunks to reflect that. +static bool has_more_inline_virt(winlinevars_T *wlv, ptrdiff_t v) +{ + DecorState *state = &decor_state; + for (size_t i = 0; i < kv_size(state->active); i++) { + DecorRange *item = &kv_A(state->active, i); + if (item->start_row != state->row + || !kv_size(item->decor.virt_text) + || item->decor.virt_text_pos != kVTInline) { + continue; + } + if (item->draw_col >= -1 && item->start_col >= v) { + return true; + } + } + return false; +} + static void handle_inline_virtual_text(win_T *wp, winlinevars_T *wlv, ptrdiff_t v) { while (wlv->n_extra == 0) { @@ -892,6 +912,7 @@ static void handle_inline_virtual_text(win_T *wp, winlinevars_T *wlv, ptrdiff_t break; } } + wlv->more_virt_inline_chunks = has_more_inline_virt(wlv, v); if (!kv_size(wlv->virt_inline)) { // no more inline virtual text here break; @@ -909,6 +930,11 @@ static void handle_inline_virtual_text(win_T *wp, winlinevars_T *wlv, ptrdiff_t wlv->c_final = NUL; wlv->extra_attr = vtc.hl_id ? syn_id2attr(vtc.hl_id) : 0; wlv->n_attr = mb_charlen(vtc.text); + + // Checks if there is more inline virtual text chunks that need to be drawn. + wlv->more_virt_inline_chunks = has_more_inline_virt(wlv, v) + || wlv->virt_inline_i < kv_size(wlv->virt_inline); + // If the text didn't reach until the first window // column we need to skip cells. if (wlv->skip_cells > 0) { @@ -2874,7 +2900,8 @@ int win_line(win_T *wp, linenr_T lnum, int startrow, int endrow, bool number_onl && !has_fold && (*ptr != NUL || lcs_eol_one > 0 - || (wlv.n_extra > 0 && (wlv.c_extra != NUL || *wlv.p_extra != NUL)))) { + || (wlv.n_extra > 0 && (wlv.c_extra != NUL || *wlv.p_extra != NUL)) + || wlv.more_virt_inline_chunks)) { c = wp->w_p_lcs_chars.ext; wlv.char_attr = win_hl_attr(wp, HLF_AT); mb_c = c; @@ -3064,7 +3091,7 @@ int win_line(win_T *wp, linenr_T lnum, int startrow, int endrow, bool number_onl || (wp->w_p_list && wp->w_p_lcs_chars.eol != NUL && wlv.p_extra != at_end_str) || (wlv.n_extra != 0 - && (wlv.c_extra != NUL || *wlv.p_extra != NUL)))) { + && (wlv.c_extra != NUL || *wlv.p_extra != NUL)) || wlv.more_virt_inline_chunks)) { bool wrap = wp->w_p_wrap // Wrapping enabled. && wlv.filler_todo <= 0 // Not drawing diff filler lines. && lcs_eol_one != -1 // Haven't printed the lcs_eol character. diff --git a/test/functional/ui/decorations_spec.lua b/test/functional/ui/decorations_spec.lua index 5de0106a8c..e8ad0d3f0d 100644 --- a/test/functional/ui/decorations_spec.lua +++ b/test/functional/ui/decorations_spec.lua @@ -2506,6 +2506,28 @@ bbbbbbb]]) | ]]} end) + + it('list "extends" is drawn with only inline virtual text offscreen', function() + command('set nowrap') + command('set list') + command('set listchars+=extends:c') + meths.buf_set_extmark(0, ns, 0, 0, + { virt_text = { { 'test', 'Special' } }, virt_text_pos = 'inline' }) + insert(string.rep('a', 50)) + feed('gg0') + screen:expect { grid = [[ + ^aaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaa{1:c}| + {1:~ }| + {1:~ }| + {1:~ }| + {1:~ }| + {1:~ }| + {1:~ }| + {1:~ }| + {1:~ }| + | + ]]} + end) end) describe('decorations: virtual lines', function()