diff options
author | Bram Moolenaar <Bram@vim.org> | 2022-09-24 19:20:30 +0100 |
---|---|---|
committer | Bram Moolenaar <Bram@vim.org> | 2022-09-24 19:20:30 +0100 |
commit | 0ff01835a40f549c5c4a550502f62a2ac9ac447c (patch) | |
tree | b960fbf355eb410814b87b37a4416e0022d3edd3 | |
parent | 865bf2ed3039deeaa66312319f3e13db146a9701 (diff) | |
download | vim-git-0ff01835a40f549c5c4a550502f62a2ac9ac447c.tar.gz |
patch 9.0.0579: using freed memory when 'tagfunc' wipes out bufferv9.0.0579
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.
-rw-r--r-- | src/insexpand.c | 40 | ||||
-rw-r--r-- | src/move.c | 1 | ||||
-rw-r--r-- | src/testdir/test_ins_complete.vim | 20 | ||||
-rw-r--r-- | src/version.c | 2 |
4 files changed, 52 insertions, 11 deletions
diff --git a/src/insexpand.c b/src/insexpand.c index 37162a48c..d3a1f881e 100644 --- a/src/insexpand.c +++ b/src/insexpand.c @@ -2490,7 +2490,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) @@ -3188,9 +3189,10 @@ enum */ typedef struct { - char_u *e_cpt; // current entry in 'complete' + char_u *e_cpt_copy; // copy of 'complete' + char_u *e_cpt; // current entry in "e_cpt_copy" buf_T *ins_buf; // buffer being scanned - pos_T *cur_match_pos; // current match position + pos_T *cur_match_pos; // current match position pos_T prev_match_pos; // previous match position int set_match_pos; // save first_match_pos/last_match_pos pos_T first_match_pos; // first match position @@ -3257,7 +3259,8 @@ process_next_cpt_value( st->set_match_pos = TRUE; } else if (vim_strchr((char_u *)"buwU", *st->e_cpt) != NULL - && (st->ins_buf = ins_compl_next_buf(st->ins_buf, *st->e_cpt)) != curbuf) + && (st->ins_buf = ins_compl_next_buf( + st->ins_buf, *st->e_cpt)) != curbuf) { // Scan a buffer, but not the current one. if (st->ins_buf->b_ml.ml_mfp != NULL) // loaded buffer @@ -3756,19 +3759,30 @@ get_next_completion_match(int type, ins_compl_next_state_T *st, pos_T *ini) static int ins_compl_get_exp(pos_T *ini) { - static ins_compl_next_state_T st; + static ins_compl_next_state_T st; + static int st_cleared = FALSE; int i; int found_new_match; int type = ctrl_x_mode; if (!compl_started) { - FOR_ALL_BUFFERS(st.ins_buf) - st.ins_buf->b_scanned = 0; + buf_T *buf; + + FOR_ALL_BUFFERS(buf) + buf->b_scanned = 0; + if (!st_cleared) + { + CLEAR_FIELD(st); + st_cleared = TRUE; + } st.found_all = FALSE; st.ins_buf = curbuf; - st.e_cpt = (compl_cont_status & CONT_LOCAL) - ? (char_u *)"." : curbuf->b_p_cpt; + vim_free(st.e_cpt_copy); + // Make a copy of 'complete', if case the buffer is wiped out. + st.e_cpt_copy = vim_strsave((compl_cont_status & CONT_LOCAL) + ? (char_u *)"." : curbuf->b_p_cpt); + st.e_cpt = st.e_cpt_copy == NULL ? (char_u *)"" : st.e_cpt_copy; st.last_match_pos = st.first_match_pos = *ini; } else if (st.ins_buf != curbuf && !buf_valid(st.ins_buf)) @@ -4112,6 +4126,7 @@ ins_compl_next( int todo = count; 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. @@ -4144,6 +4159,13 @@ ins_compl_next( &num_matches) == -1) return -1; + 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 a51d2d128..967dd77fb 100644 --- a/src/move.c +++ b/src/move.c @@ -683,6 +683,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 379db7fe7..3fc55ec1c 100644 --- a/src/testdir/test_ins_complete.vim +++ b/src/testdir/test_ins_complete.vim @@ -547,9 +547,8 @@ func Test_pum_with_preview_win() call writefile(lines, 'Xpreviewscript') let buf = RunVimInTerminal('-S Xpreviewscript', #{rows: 12}) - call TermWait(buf, 50) call term_sendkeys(buf, "Gi\<C-X>\<C-O>") - call TermWait(buf, 100) + call TermWait(buf, 200) call term_sendkeys(buf, "\<C-N>") call VerifyScreenDump(buf, 'Test_pum_with_preview_win', {}) @@ -2172,4 +2171,21 @@ 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 + + " vim: shiftwidth=2 sts=2 expandtab diff --git a/src/version.c b/src/version.c index ff8264415..1061e4c67 100644 --- a/src/version.c +++ b/src/version.c @@ -700,6 +700,8 @@ static char *(features[]) = static int included_patches[] = { /* Add new patch number below this line */ /**/ + 579, +/**/ 578, /**/ 577, |