forked from aniani/vim
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.
This commit is contained in:
parent
865bf2ed30
commit
0ff01835a4
@ -2490,7 +2490,8 @@ ins_compl_next_buf(buf_T *buf, int flag)
|
|||||||
|
|
||||||
if (flag == 'w') // just windows
|
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;
|
wp = curwin;
|
||||||
while ((wp = (wp->w_next != NULL ? wp->w_next : firstwin)) != curwin
|
while ((wp = (wp->w_next != NULL ? wp->w_next : firstwin)) != curwin
|
||||||
&& wp->w_buffer->b_scanned)
|
&& wp->w_buffer->b_scanned)
|
||||||
@ -3188,9 +3189,10 @@ enum
|
|||||||
*/
|
*/
|
||||||
typedef struct
|
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
|
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
|
pos_T prev_match_pos; // previous match position
|
||||||
int set_match_pos; // save first_match_pos/last_match_pos
|
int set_match_pos; // save first_match_pos/last_match_pos
|
||||||
pos_T first_match_pos; // first match position
|
pos_T first_match_pos; // first match position
|
||||||
@ -3257,7 +3259,8 @@ process_next_cpt_value(
|
|||||||
st->set_match_pos = TRUE;
|
st->set_match_pos = TRUE;
|
||||||
}
|
}
|
||||||
else if (vim_strchr((char_u *)"buwU", *st->e_cpt) != NULL
|
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.
|
// Scan a buffer, but not the current one.
|
||||||
if (st->ins_buf->b_ml.ml_mfp != NULL) // loaded buffer
|
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
|
static int
|
||||||
ins_compl_get_exp(pos_T *ini)
|
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 i;
|
||||||
int found_new_match;
|
int found_new_match;
|
||||||
int type = ctrl_x_mode;
|
int type = ctrl_x_mode;
|
||||||
|
|
||||||
if (!compl_started)
|
if (!compl_started)
|
||||||
{
|
{
|
||||||
FOR_ALL_BUFFERS(st.ins_buf)
|
buf_T *buf;
|
||||||
st.ins_buf->b_scanned = 0;
|
|
||||||
|
FOR_ALL_BUFFERS(buf)
|
||||||
|
buf->b_scanned = 0;
|
||||||
|
if (!st_cleared)
|
||||||
|
{
|
||||||
|
CLEAR_FIELD(st);
|
||||||
|
st_cleared = TRUE;
|
||||||
|
}
|
||||||
st.found_all = FALSE;
|
st.found_all = FALSE;
|
||||||
st.ins_buf = curbuf;
|
st.ins_buf = curbuf;
|
||||||
st.e_cpt = (compl_cont_status & CONT_LOCAL)
|
vim_free(st.e_cpt_copy);
|
||||||
? (char_u *)"." : curbuf->b_p_cpt;
|
// 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;
|
st.last_match_pos = st.first_match_pos = *ini;
|
||||||
}
|
}
|
||||||
else if (st.ins_buf != curbuf && !buf_valid(st.ins_buf))
|
else if (st.ins_buf != curbuf && !buf_valid(st.ins_buf))
|
||||||
@ -4112,6 +4126,7 @@ ins_compl_next(
|
|||||||
int todo = count;
|
int todo = count;
|
||||||
int advance;
|
int advance;
|
||||||
int started = compl_started;
|
int started = compl_started;
|
||||||
|
buf_T *orig_curbuf = curbuf;
|
||||||
|
|
||||||
// When user complete function return -1 for findstart which is next
|
// When user complete function return -1 for findstart which is next
|
||||||
// time of 'always', compl_shown_match become NULL.
|
// time of 'always', compl_shown_match become NULL.
|
||||||
@ -4144,6 +4159,13 @@ ins_compl_next(
|
|||||||
&num_matches) == -1)
|
&num_matches) == -1)
|
||||||
return -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.
|
// Insert the text of the new completion, or the compl_leader.
|
||||||
if (compl_no_insert && !started)
|
if (compl_no_insert && !started)
|
||||||
{
|
{
|
||||||
|
@ -683,6 +683,7 @@ cursor_valid(void)
|
|||||||
void
|
void
|
||||||
validate_cursor(void)
|
validate_cursor(void)
|
||||||
{
|
{
|
||||||
|
check_cursor();
|
||||||
check_cursor_moved(curwin);
|
check_cursor_moved(curwin);
|
||||||
if ((curwin->w_valid & (VALID_WCOL|VALID_WROW)) != (VALID_WCOL|VALID_WROW))
|
if ((curwin->w_valid & (VALID_WCOL|VALID_WROW)) != (VALID_WCOL|VALID_WROW))
|
||||||
curs_columns(TRUE);
|
curs_columns(TRUE);
|
||||||
|
@ -547,9 +547,8 @@ func Test_pum_with_preview_win()
|
|||||||
|
|
||||||
call writefile(lines, 'Xpreviewscript')
|
call writefile(lines, 'Xpreviewscript')
|
||||||
let buf = RunVimInTerminal('-S Xpreviewscript', #{rows: 12})
|
let buf = RunVimInTerminal('-S Xpreviewscript', #{rows: 12})
|
||||||
call TermWait(buf, 50)
|
|
||||||
call term_sendkeys(buf, "Gi\<C-X>\<C-O>")
|
call term_sendkeys(buf, "Gi\<C-X>\<C-O>")
|
||||||
call TermWait(buf, 100)
|
call TermWait(buf, 200)
|
||||||
call term_sendkeys(buf, "\<C-N>")
|
call term_sendkeys(buf, "\<C-N>")
|
||||||
call VerifyScreenDump(buf, 'Test_pum_with_preview_win', {})
|
call VerifyScreenDump(buf, 'Test_pum_with_preview_win', {})
|
||||||
|
|
||||||
@ -2172,4 +2171,21 @@ func Test_ins_complete_end_of_line()
|
|||||||
bwipe!
|
bwipe!
|
||||||
endfunc
|
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: shiftwidth=2 sts=2 expandtab
|
||||||
|
@ -699,6 +699,8 @@ static char *(features[]) =
|
|||||||
|
|
||||||
static int included_patches[] =
|
static int included_patches[] =
|
||||||
{ /* Add new patch number below this line */
|
{ /* Add new patch number below this line */
|
||||||
|
/**/
|
||||||
|
579,
|
||||||
/**/
|
/**/
|
||||||
578,
|
578,
|
||||||
/**/
|
/**/
|
||||||
|
Loading…
x
Reference in New Issue
Block a user