1
0
forked from aniani/vim

patch 8.0.1420: accessing freed memory in vimgrep

Problem:    Accessing freed memory in vimgrep.
Solution:   Check that the quickfix list is still valid. (Yegappan Lakshmanan,
            closes #2474)
This commit is contained in:
Bram Moolenaar
2017-12-21 20:54:49 +01:00
parent b73fa629d6
commit 3c09722600
4 changed files with 133 additions and 23 deletions

View File

@@ -144,6 +144,7 @@ static int qf_get_fnum(qf_info_T *qi, int qf_idx, char_u *, char_u *);
static char_u *qf_push_dir(char_u *, struct dir_stack_T **, int is_file_stack); static char_u *qf_push_dir(char_u *, struct dir_stack_T **, int is_file_stack);
static char_u *qf_pop_dir(struct dir_stack_T **); static char_u *qf_pop_dir(struct dir_stack_T **);
static char_u *qf_guess_filepath(qf_info_T *qi, int qf_idx, char_u *); static char_u *qf_guess_filepath(qf_info_T *qi, int qf_idx, char_u *);
static int qflist_valid(win_T *wp, int_u qf_id);
static void qf_fmt_text(char_u *text, char_u *buf, int bufsize); static void qf_fmt_text(char_u *text, char_u *buf, int bufsize);
static void qf_clean_dir_stack(struct dir_stack_T **); static void qf_clean_dir_stack(struct dir_stack_T **);
static int qf_win_pos_update(qf_info_T *qi, int old_qf_index); static int qf_win_pos_update(qf_info_T *qi, int old_qf_index);
@@ -177,6 +178,9 @@ static qf_info_T *ll_get_or_alloc_list(win_T *);
static char_u *qf_last_bufname = NULL; static char_u *qf_last_bufname = NULL;
static bufref_T qf_last_bufref = {NULL, 0, 0}; static bufref_T qf_last_bufref = {NULL, 0, 0};
static char *e_loc_list_changed =
N_("E926: Current location list was changed");
/* /*
* Read the errorfile "efile" into memory, line by line, building the error * Read the errorfile "efile" into memory, line by line, building the error
* list. Set the error list's title to qf_title. * list. Set the error list's title to qf_title.
@@ -1927,6 +1931,29 @@ qf_guess_filepath(qf_info_T *qi, int qf_idx, char_u *filename)
return ds_ptr==NULL? NULL: ds_ptr->dirname; return ds_ptr==NULL? NULL: ds_ptr->dirname;
} }
/*
* Returns TRUE if a quickfix/location list with the given identifier exists.
*/
static int
qflist_valid (win_T *wp, int_u qf_id)
{
qf_info_T *qi = &ql_info;
int i;
if (wp != NULL)
{
qi = GET_LOC_LIST(wp); /* Location list */
if (qi == NULL)
return FALSE;
}
for (i = 0; i < qi->qf_listcount; ++i)
if (qi->qf_lists[i].qf_id == qf_id)
return TRUE;
return FALSE;
}
/* /*
* When loading a file from the quickfix, the auto commands may modify it. * When loading a file from the quickfix, the auto commands may modify it.
* This may invalidate the current quickfix entry. This function checks * This may invalidate the current quickfix entry. This function checks
@@ -2343,14 +2370,28 @@ qf_jump_edit_buffer(
else else
{ {
int old_qf_curlist = qi->qf_curlist; int old_qf_curlist = qi->qf_curlist;
int save_qfid = qi->qf_lists[qi->qf_curlist].qf_id;
retval = buflist_getfile(qf_ptr->qf_fnum, retval = buflist_getfile(qf_ptr->qf_fnum,
(linenr_T)1, GETF_SETMARK | GETF_SWITCH, forceit); (linenr_T)1, GETF_SETMARK | GETF_SWITCH, forceit);
if (qi != &ql_info && !win_valid_any_tab(oldwin))
if (qi != &ql_info)
{ {
EMSG(_("E924: Current window was closed")); /*
*abort = TRUE; * Location list. Check whether the associated window is still
*opened_window = FALSE; * present and the list is still valid.
*/
if (!win_valid_any_tab(oldwin))
{
EMSG(_("E924: Current window was closed"));
*abort = TRUE;
*opened_window = FALSE;
}
else if (!qflist_valid(oldwin, save_qfid))
{
EMSG(_(e_loc_list_changed));
*abort = TRUE;
}
} }
else if (old_qf_curlist != qi->qf_curlist else if (old_qf_curlist != qi->qf_curlist
|| !is_qf_entry_present(qi, qf_ptr)) || !is_qf_entry_present(qi, qf_ptr))
@@ -2358,7 +2399,7 @@ qf_jump_edit_buffer(
if (qi == &ql_info) if (qi == &ql_info)
EMSG(_("E925: Current quickfix was changed")); EMSG(_("E925: Current quickfix was changed"));
else else
EMSG(_("E926: Current location list was changed")); EMSG(_(e_loc_list_changed));
*abort = TRUE; *abort = TRUE;
} }
@@ -4065,6 +4106,7 @@ ex_cfile(exarg_T *eap)
qf_info_T *qi = &ql_info; qf_info_T *qi = &ql_info;
#ifdef FEAT_AUTOCMD #ifdef FEAT_AUTOCMD
char_u *au_name = NULL; char_u *au_name = NULL;
int save_qfid;
#endif #endif
int res; int res;
@@ -4122,8 +4164,15 @@ ex_cfile(exarg_T *eap)
if (res >= 0 && qi != NULL) if (res >= 0 && qi != NULL)
qf_list_changed(qi, qi->qf_curlist); qf_list_changed(qi, qi->qf_curlist);
#ifdef FEAT_AUTOCMD #ifdef FEAT_AUTOCMD
save_qfid = qi->qf_lists[qi->qf_curlist].qf_id;
if (au_name != NULL) if (au_name != NULL)
apply_autocmds(EVENT_QUICKFIXCMDPOST, au_name, NULL, FALSE, curbuf); apply_autocmds(EVENT_QUICKFIXCMDPOST, au_name, NULL, FALSE, curbuf);
/*
* Autocmd might have freed the quickfix/location list. Check whether it is
* still valid
*/
if (!qflist_valid(wp, save_qfid))
return;
#endif #endif
if (res > 0 && (eap->cmdidx == CMD_cfile || eap->cmdidx == CMD_lfile)) if (res > 0 && (eap->cmdidx == CMD_cfile || eap->cmdidx == CMD_lfile))
{ {
@@ -4149,8 +4198,11 @@ ex_vimgrep(exarg_T *eap)
char_u *p; char_u *p;
int fi; int fi;
qf_info_T *qi = &ql_info; qf_info_T *qi = &ql_info;
int loclist_cmd = FALSE;
#ifdef FEAT_AUTOCMD #ifdef FEAT_AUTOCMD
int_u save_qfid;
qfline_T *cur_qf_start; qfline_T *cur_qf_start;
win_T *wp;
#endif #endif
long lnum; long lnum;
buf_T *buf; buf_T *buf;
@@ -4204,6 +4256,7 @@ ex_vimgrep(exarg_T *eap)
qi = ll_get_or_alloc_list(curwin); qi = ll_get_or_alloc_list(curwin);
if (qi == NULL) if (qi == NULL)
return; return;
loclist_cmd = TRUE;
} }
if (eap->addr_count > 0) if (eap->addr_count > 0)
@@ -4274,8 +4327,9 @@ ex_vimgrep(exarg_T *eap)
mch_dirname(dirname_start, MAXPATHL); mch_dirname(dirname_start, MAXPATHL);
#ifdef FEAT_AUTOCMD #ifdef FEAT_AUTOCMD
/* Remember the value of qf_start, so that we can check for autocommands /* Remember the current values of the quickfix list and qf_start, so that
* changing the current quickfix list. */ * we can check for autocommands changing the current quickfix list. */
save_qfid = qi->qf_lists[qi->qf_curlist].qf_id;
cur_qf_start = qi->qf_lists[qi->qf_curlist].qf_start; cur_qf_start = qi->qf_lists[qi->qf_curlist].qf_start;
#endif #endif
@@ -4335,6 +4389,18 @@ ex_vimgrep(exarg_T *eap)
using_dummy = FALSE; using_dummy = FALSE;
#ifdef FEAT_AUTOCMD #ifdef FEAT_AUTOCMD
if (loclist_cmd)
{
/*
* Verify that the location list is still valid. An autocmd might
* have freed the location list.
*/
if (!qflist_valid(curwin, save_qfid))
{
EMSG(_(e_loc_list_changed));
goto theend;
}
}
if (cur_qf_start != qi->qf_lists[qi->qf_curlist].qf_start) if (cur_qf_start != qi->qf_lists[qi->qf_curlist].qf_start)
{ {
int idx; int idx;
@@ -4491,6 +4557,13 @@ ex_vimgrep(exarg_T *eap)
if (au_name != NULL) if (au_name != NULL)
apply_autocmds(EVENT_QUICKFIXCMDPOST, au_name, apply_autocmds(EVENT_QUICKFIXCMDPOST, au_name,
curbuf->b_fname, TRUE, curbuf); curbuf->b_fname, TRUE, curbuf);
/*
* The QuickFixCmdPost autocmd may free the quickfix list. Check the list
* is still valid.
*/
wp = loclist_cmd ? curwin : NULL;
if (!qflist_valid(wp, save_qfid))
goto theend;
#endif #endif
/* Jump to first match. */ /* Jump to first match. */
@@ -5543,7 +5616,8 @@ ex_cbuffer(exarg_T *eap)
#endif #endif
/* Must come after autocommands. */ /* Must come after autocommands. */
if (eap->cmdidx == CMD_lbuffer || eap->cmdidx == CMD_lgetbuffer if (eap->cmdidx == CMD_lbuffer
|| eap->cmdidx == CMD_lgetbuffer
|| eap->cmdidx == CMD_laddbuffer) || eap->cmdidx == CMD_laddbuffer)
{ {
qi = ll_get_or_alloc_list(curwin); qi = ll_get_or_alloc_list(curwin);
@@ -5614,14 +5688,6 @@ ex_cexpr(exarg_T *eap)
#endif #endif
int res; int res;
if (eap->cmdidx == CMD_lexpr || eap->cmdidx == CMD_lgetexpr
|| eap->cmdidx == CMD_laddexpr)
{
qi = ll_get_or_alloc_list(curwin);
if (qi == NULL)
return;
}
#ifdef FEAT_AUTOCMD #ifdef FEAT_AUTOCMD
switch (eap->cmdidx) switch (eap->cmdidx)
{ {
@@ -5643,6 +5709,15 @@ ex_cexpr(exarg_T *eap)
} }
#endif #endif
if (eap->cmdidx == CMD_lexpr
|| eap->cmdidx == CMD_lgetexpr
|| eap->cmdidx == CMD_laddexpr)
{
qi = ll_get_or_alloc_list(curwin);
if (qi == NULL)
return;
}
/* Evaluate the expression. When the result is a string or a list we can /* Evaluate the expression. When the result is a string or a list we can
* use it to fill the errorlist. */ * use it to fill the errorlist. */
tv = eval_expr(eap->arg, NULL); tv = eval_expr(eap->arg, NULL);

View File

@@ -1178,10 +1178,3 @@ func Test_nocatch_wipe_dummy_buffer()
call assert_fails('lv½ /x', 'E480') call assert_fails('lv½ /x', 'E480')
au! au!
endfunc endfunc
func Test_wipe_cbuffer()
sv x
au * * bw
lb
au!
endfunc

View File

@@ -3038,3 +3038,43 @@ func Test_lfile_crash()
call assert_fails('lfile', 'E40') call assert_fails('lfile', 'E40')
au! QuickFixCmdPre au! QuickFixCmdPre
endfunc endfunc
" The following test used to crash vim
func Test_lbuffer_crash()
sv Xtest
augroup QF_Test
au!
au * * bw
augroup END
lbuffer
augroup QF_Test
au!
augroup END
endfunc
" The following test used to crash vim
func Test_lexpr_crash()
augroup QF_Test
au!
au * * call setloclist(0, [], 'f')
augroup END
lexpr ""
augroup QF_Test
au!
augroup END
enew | only
endfunc
" The following test used to crash Vim
func Test_lvimgrep_crash()
sv Xtest
augroup QF_Test
au!
au * * call setloclist(0, [], 'f')
augroup END
lvimgrep quickfix test_quickfix.vim
augroup QF_Test
au!
augroup END
enew | only
endfunc

View File

@@ -771,6 +771,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 */
/**/
1420,
/**/ /**/
1419, 1419,
/**/ /**/