Patch 9.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.
Files: src/insexpand.c, src/move.c, src/testdir/test_ins_complete.vim
*** ../vim-9.0.0578/src/insexpand.c 2022-09-24 11:17:48.373970710 +0100
--- src/insexpand.c 2022-09-24 19:08:29.753607924 +0100
***************
*** 2490,2496 ****
if (flag == 'w') // just windows
{
! if (buf == curbuf || wp == NULL) // first call for this flag/expansion
wp = curwin;
while ((wp = (wp->w_next != NULL ? wp->w_next : firstwin)) != curwin
&& wp->w_buffer->b_scanned)
--- 2490,2497 ----
if (flag == 'w') // just windows
{
! 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,3196 ****
*/
typedef struct
{
! char_u *e_cpt; // current entry in 'complete'
buf_T *ins_buf; // buffer being scanned
! 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
--- 3189,3198 ----
*/
typedef struct
{
! 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 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,3263 ****
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)
{
// Scan a buffer, but not the current one.
if (st->ins_buf->b_ml.ml_mfp != NULL) // loaded buffer
--- 3259,3266 ----
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)
{
// Scan a buffer, but not the current one.
if (st->ins_buf->b_ml.ml_mfp != NULL) // loaded buffer
***************
*** 3756,3774 ****
static int
ins_compl_get_exp(pos_T *ini)
{
! static ins_compl_next_state_T st;
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;
st.found_all = FALSE;
st.ins_buf = curbuf;
! st.e_cpt = (compl_cont_status & CONT_LOCAL)
! ? (char_u *)"." : curbuf->b_p_cpt;
st.last_match_pos = st.first_match_pos = *ini;
}
else if (st.ins_buf != curbuf && !buf_valid(st.ins_buf))
--- 3759,3788 ----
static int
ins_compl_get_exp(pos_T *ini)
{
! 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)
{
! 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;
! 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,4117 ****
--- 4126,4132 ----
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,4149 ****
--- 4159,4171 ----
&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)
{
*** ../vim-9.0.0578/src/move.c 2022-09-19 16:45:26.202239861 +0100
--- src/move.c 2022-09-24 19:14:30.234942928 +0100
***************
*** 683,688 ****
--- 683,689 ----
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);
*** ../vim-9.0.0578/src/testdir/test_ins_complete.vim 2022-09-24 14:08:19.896461420 +0100
--- src/testdir/test_ins_complete.vim 2022-09-24 19:18:21.255489759 +0100
***************
*** 547,555 ****
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 term_sendkeys(buf, "\<C-N>")
call VerifyScreenDump(buf, 'Test_pum_with_preview_win', {})
--- 547,554 ----
call writefile(lines, 'Xpreviewscript')
let buf = RunVimInTerminal('-S Xpreviewscript', #{rows: 12})
call term_sendkeys(buf, "Gi\<C-X>\<C-O>")
! call TermWait(buf, 200)
call term_sendkeys(buf, "\<C-N>")
call VerifyScreenDump(buf, 'Test_pum_with_preview_win', {})
***************
*** 2172,2175 ****
--- 2171,2191 ----
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
*** ../vim-9.0.0578/src/version.c 2022-09-24 17:44:18.966404466 +0100
--- src/version.c 2022-09-24 19:19:42.059601525 +0100
***************
*** 701,702 ****
--- 701,704 ----
{ /* Add new patch number below this line */
+ /**/
+ 579,
/**/
--
"Hit any key to continue" is very confusing when you have two keyboards.
/// Bram Moolenaar -- Br...@Moolenaar.net --
http://www.Moolenaar.net \\\
/// \\\
\\\ sponsor Vim, vote for features --
http://www.Vim.org/sponsor/ ///
\\\ help me help AIDS victims --
http://ICCF-Holland.org ///