1
0
forked from aniani/vim

patch 8.2.2421: double free when using autocommand with "argdel"

Problem:    Double free when using autocommand with "argdel". (Houyunsong)
Solution:   Add the arglist_locked flag.
This commit is contained in:
Bram Moolenaar
2021-01-28 14:24:55 +01:00
parent 9a046fd08b
commit 5ed58c7b70
3 changed files with 46 additions and 9 deletions

View File

@@ -17,12 +17,29 @@
#define AL_ADD 2 #define AL_ADD 2
#define AL_DEL 3 #define AL_DEL 3
// This flag is set whenever the argument list is being changed and calling a
// function that might trigger an autocommand.
static int arglist_locked = FALSE;
static int
check_arglist_locked(void)
{
if (arglist_locked)
{
emsg(_(e_cannot_change_arglist_recursively));
return FAIL;
}
return OK;
}
/* /*
* Clear an argument list: free all file names and reset it to zero entries. * Clear an argument list: free all file names and reset it to zero entries.
*/ */
void void
alist_clear(alist_T *al) alist_clear(alist_T *al)
{ {
if (check_arglist_locked() == FAIL)
return;
while (--al->al_ga.ga_len >= 0) while (--al->al_ga.ga_len >= 0)
vim_free(AARGLIST(al)[al->al_ga.ga_len].ae_fname); vim_free(AARGLIST(al)[al->al_ga.ga_len].ae_fname);
ga_clear(&al->al_ga); ga_clear(&al->al_ga);
@@ -126,14 +143,9 @@ alist_set(
int fnum_len) int fnum_len)
{ {
int i; int i;
static int recursive = 0;
if (recursive) if (check_arglist_locked() == FAIL)
{
emsg(_(e_au_recursive));
return; return;
}
++recursive;
alist_clear(al); alist_clear(al);
if (ga_grow(&al->al_ga, count) == OK) if (ga_grow(&al->al_ga, count) == OK)
@@ -152,7 +164,11 @@ alist_set(
// May set buffer name of a buffer previously used for the // May set buffer name of a buffer previously used for the
// argument list, so that it's re-used by alist_add. // argument list, so that it's re-used by alist_add.
if (fnum_list != NULL && i < fnum_len) if (fnum_list != NULL && i < fnum_len)
{
arglist_locked = TRUE;
buf_set_name(fnum_list[i], files[i]); buf_set_name(fnum_list[i], files[i]);
arglist_locked = FALSE;
}
alist_add(al, files[i], use_curbuf ? 2 : 1); alist_add(al, files[i], use_curbuf ? 2 : 1);
ui_breakcheck(); ui_breakcheck();
@@ -163,8 +179,6 @@ alist_set(
FreeWild(count, files); FreeWild(count, files);
if (al == &global_alist) if (al == &global_alist)
arg_had_last = FALSE; arg_had_last = FALSE;
--recursive;
} }
/* /*
@@ -179,6 +193,10 @@ alist_add(
{ {
if (fname == NULL) // don't add NULL file names if (fname == NULL) // don't add NULL file names
return; return;
if (check_arglist_locked() == FAIL)
return;
arglist_locked = TRUE;
#ifdef BACKSLASH_IN_FILENAME #ifdef BACKSLASH_IN_FILENAME
slash_adjust(fname); slash_adjust(fname);
#endif #endif
@@ -187,6 +205,8 @@ alist_add(
AARGLIST(al)[al->al_ga.ga_len].ae_fnum = AARGLIST(al)[al->al_ga.ga_len].ae_fnum =
buflist_add(fname, BLN_LISTED | (set_fnum == 2 ? BLN_CURBUF : 0)); buflist_add(fname, BLN_LISTED | (set_fnum == 2 ? BLN_CURBUF : 0));
++al->al_ga.ga_len; ++al->al_ga.ga_len;
arglist_locked = FALSE;
} }
#if defined(BACKSLASH_IN_FILENAME) || defined(PROTO) #if defined(BACKSLASH_IN_FILENAME) || defined(PROTO)
@@ -334,7 +354,8 @@ alist_add_list(
int i; int i;
int old_argcount = ARGCOUNT; int old_argcount = ARGCOUNT;
if (ga_grow(&ALIST(curwin)->al_ga, count) == OK) if (check_arglist_locked() != FAIL
&& ga_grow(&ALIST(curwin)->al_ga, count) == OK)
{ {
if (after < 0) if (after < 0)
after = 0; after = 0;
@@ -343,6 +364,7 @@ alist_add_list(
if (after < ARGCOUNT) if (after < ARGCOUNT)
mch_memmove(&(ARGLIST[after + count]), &(ARGLIST[after]), mch_memmove(&(ARGLIST[after + count]), &(ARGLIST[after]),
(ARGCOUNT - after) * sizeof(aentry_T)); (ARGCOUNT - after) * sizeof(aentry_T));
arglist_locked = TRUE;
for (i = 0; i < count; ++i) for (i = 0; i < count; ++i)
{ {
int flags = BLN_LISTED | (will_edit ? BLN_CURBUF : 0); int flags = BLN_LISTED | (will_edit ? BLN_CURBUF : 0);
@@ -350,6 +372,7 @@ alist_add_list(
ARGLIST[after + i].ae_fname = files[i]; ARGLIST[after + i].ae_fname = files[i];
ARGLIST[after + i].ae_fnum = buflist_add(files[i], flags); ARGLIST[after + i].ae_fnum = buflist_add(files[i], flags);
} }
arglist_locked = FALSE;
ALIST(curwin)->al_ga.ga_len += count; ALIST(curwin)->al_ga.ga_len += count;
if (old_argcount > 0 && curwin->w_arg_idx >= after) if (old_argcount > 0 && curwin->w_arg_idx >= after)
curwin->w_arg_idx += count; curwin->w_arg_idx += count;
@@ -382,6 +405,9 @@ do_arglist(
int match; int match;
int arg_escaped = TRUE; int arg_escaped = TRUE;
if (check_arglist_locked() == FAIL)
return FAIL;
// Set default argument for ":argadd" command. // Set default argument for ":argadd" command.
if (what == AL_ADD && *str == NUL) if (what == AL_ADD && *str == NUL)
{ {
@@ -776,6 +802,9 @@ ex_argdelete(exarg_T *eap)
int i; int i;
int n; int n;
if (check_arglist_locked() == FAIL)
return;
if (eap->addr_count > 0 || *eap->arg == NUL) if (eap->addr_count > 0 || *eap->arg == NUL)
{ {
// ":argdel" works like ":.argdel" // ":argdel" works like ":.argdel"

View File

@@ -173,6 +173,12 @@ func Test_autocmd_bufunload_with_tabnext()
quit quit
endfunc endfunc
func Test_argdelete_in_next()
au BufNew,BufEnter,BufLeave,BufWinEnter * argdel
call assert_fails('next a b', 'E1156:')
au! BufNew,BufEnter,BufLeave,BufWinEnter *
endfunc
func Test_autocmd_bufwinleave_with_tabfirst() func Test_autocmd_bufwinleave_with_tabfirst()
tabedit tabedit
augroup sample augroup sample

View File

@@ -750,6 +750,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 */
/**/
2421,
/**/ /**/
2420, 2420,
/**/ /**/