From 0ff01835a40f549c5c4a550502f62a2ac9ac447c Mon Sep 17 00:00:00 2001 From: Bram Moolenaar Date: Sat, 24 Sep 2022 19:20:30 +0100 Subject: [PATCH] patch 9.0.0579: using freed memory when 'tagfunc' wipes out buffer Problem: Using freed memory when 'tagfunc' wipes out buffer that holds 'complete'. Solution: Make a copy of the option. Make sure cursor position is valid. --- src/insexpand.c | 39 +++++++++++++++++++++++++++---- src/move.c | 1 + src/testdir/test_ins_complete.vim | 16 +++++++++++++ 3 files changed, 51 insertions(+), 5 deletions(-) diff --git a/src/insexpand.c b/src/insexpand.c index a23d2d6..647297d 100644 --- a/src/insexpand.c +++ b/src/insexpand.c @@ -2210,7 +2210,8 @@ ins_compl_next_buf(buf_T *buf, int flag) if (flag == 'w') // just windows { - if (buf == curbuf || wp == NULL) // first call for this flag/expansion + if (buf == curbuf || !win_valid(wp)) + // first call for this flag/expansion or window was closed wp = curwin; while ((wp = (wp->w_next != NULL ? wp->w_next : firstwin)) != curwin && wp->w_buffer->b_scanned) @@ -2672,6 +2673,7 @@ ins_compl_get_exp(pos_T *ini) static pos_T first_match_pos; static pos_T last_match_pos; static char_u *e_cpt = (char_u *)""; // curr. entry in 'complete' + static char_u *e_cpt_copy = (char_u *)""; // copy of 'complete' static int found_all = FALSE; // Found all matches of a // certain type. static buf_T *ins_buf = NULL; // buffer being scanned @@ -2690,15 +2692,34 @@ ins_compl_get_exp(pos_T *ini) char_u *dict = NULL; int dict_f = 0; int set_match_pos; + static int st_cleared = FALSE; if (!compl_started) { - FOR_ALL_BUFFERS(ins_buf) - ins_buf->b_scanned = 0; + buf_T *buf; + + FOR_ALL_BUFFERS(buf) + buf->b_scanned = 0; + if (!st_cleared) + { + e_cpt = NULL; + e_cpt_copy = NULL; + ins_buf = NULL; + set_match_pos = 0; + vim_memset(&first_match_pos, 0, sizeof(first_match_pos)); + vim_memset(&last_match_pos, 0, sizeof(last_match_pos)); + found_all = FALSE; + dict = NULL; + dict_f = 0; + st_cleared = TRUE; + } found_all = FALSE; ins_buf = curbuf; - e_cpt = (compl_cont_status & CONT_LOCAL) - ? (char_u *)"." : curbuf->b_p_cpt; + vim_free(e_cpt_copy); + // Make a copy of 'complete', if case the buffer is wiped out. + e_cpt_copy = vim_strsave((compl_cont_status & CONT_LOCAL) + ? (char_u *)"." : curbuf->b_p_cpt); + e_cpt = e_cpt_copy == NULL ? (char_u *)"" : e_cpt_copy; last_match_pos = first_match_pos = *ini; } else if (ins_buf != curbuf && !buf_valid(ins_buf)) @@ -3204,6 +3225,7 @@ ins_compl_next( int found_end = FALSE; int advance; int started = compl_started; + buf_T *orig_curbuf = curbuf; // When user complete function return -1 for findstart which is next // time of 'always', compl_shown_match become NULL. @@ -3336,6 +3358,13 @@ ins_compl_next( } } + if (curbuf != orig_curbuf) + { + // In case some completion function switched buffer, don't want to + // insert the completion elsewhere. + return -1; + } + // Insert the text of the new completion, or the compl_leader. if (compl_no_insert && !started) { diff --git a/src/move.c b/src/move.c index 10165ef..1d7bcfb 100644 --- a/src/move.c +++ b/src/move.c @@ -637,6 +637,7 @@ cursor_valid(void) void validate_cursor(void) { + check_cursor(); check_cursor_moved(curwin); if ((curwin->w_valid & (VALID_WCOL|VALID_WROW)) != (VALID_WCOL|VALID_WROW)) curs_columns(TRUE); diff --git a/src/testdir/test_ins_complete.vim b/src/testdir/test_ins_complete.vim index 39ece18..1b53987 100644 --- a/src/testdir/test_ins_complete.vim +++ b/src/testdir/test_ins_complete.vim @@ -427,3 +427,19 @@ func Test_ins_complete_end_of_line() bwipe! endfunc + +func s:Tagfunc(t,f,o) + bwipe! + return [] +endfunc + +" This was using freed memory, since 'complete' was in a wiped out buffer. +" Also using a window that was closed. +func Test_tagfunc_wipes_out_buffer() + new + set complete=.,t,w,b,u,i + se tagfunc=s:Tagfunc + sil norm i + + bwipe! +endfunc -- 2.33.0