0
0
mirror of https://github.com/vim/vim.git synced 2025-09-06 21:53:38 -04:00

patch 8.2.2354: crash with a weird combination of autocommands

Problem:    Crash with a weird combination of autocommands.
Solution:   Increment b_nwindows when needed. (closes #7674)
This commit is contained in:
Bram Moolenaar 2021-01-15 16:22:52 +01:00
parent 17d015b243
commit 797e63b9f2
5 changed files with 46 additions and 11 deletions

View File

@ -492,8 +492,10 @@ can_unload_buffer(buf_T *buf)
* supposed to close the window but autocommands close all other windows. * supposed to close the window but autocommands close all other windows.
* *
* When "ignore_abort" is TRUE don't abort even when aborting() returns TRUE. * When "ignore_abort" is TRUE don't abort even when aborting() returns TRUE.
*
* Return TRUE when we got to the end and b_nwindows was decremented.
*/ */
void int
close_buffer( close_buffer(
win_T *win, // if not NULL, set b_last_cursor win_T *win, // if not NULL, set b_last_cursor
buf_T *buf, buf_T *buf,
@ -540,7 +542,7 @@ close_buffer(
if (wipe_buf || unload_buf) if (wipe_buf || unload_buf)
{ {
if (!can_unload_buffer(buf)) if (!can_unload_buffer(buf))
return; return FALSE;
// Wiping out or unloading a terminal buffer kills the job. // Wiping out or unloading a terminal buffer kills the job.
free_terminal(buf); free_terminal(buf);
@ -571,7 +573,7 @@ close_buffer(
// Disallow deleting the buffer when it is locked (already being closed or // Disallow deleting the buffer when it is locked (already being closed or
// halfway a command that relies on it). Unloading is allowed. // halfway a command that relies on it). Unloading is allowed.
if ((del_buf || wipe_buf) && !can_unload_buffer(buf)) if ((del_buf || wipe_buf) && !can_unload_buffer(buf))
return; return FALSE;
// check no autocommands closed the window // check no autocommands closed the window
if (win != NULL && win_valid_any_tab(win)) if (win != NULL && win_valid_any_tab(win))
@ -600,7 +602,7 @@ close_buffer(
// Autocommands deleted the buffer. // Autocommands deleted the buffer.
aucmd_abort: aucmd_abort:
emsg(_(e_auabort)); emsg(_(e_auabort));
return; return FALSE;
} }
--buf->b_locked; --buf->b_locked;
if (abort_if_last && one_window()) if (abort_if_last && one_window())
@ -625,7 +627,7 @@ aucmd_abort:
#ifdef FEAT_EVAL #ifdef FEAT_EVAL
// autocmds may abort script processing // autocmds may abort script processing
if (!ignore_abort && aborting()) if (!ignore_abort && aborting())
return; return FALSE;
#endif #endif
} }
@ -653,7 +655,7 @@ aucmd_abort:
// Return when a window is displaying the buffer or when it's not // Return when a window is displaying the buffer or when it's not
// unloaded. // unloaded.
if (buf->b_nwindows > 0 || !unload_buf) if (buf->b_nwindows > 0 || !unload_buf)
return; return FALSE;
// Always remove the buffer when there is no file name. // Always remove the buffer when there is no file name.
if (buf->b_ffname == NULL) if (buf->b_ffname == NULL)
@ -683,11 +685,11 @@ aucmd_abort:
// Autocommands may have deleted the buffer. // Autocommands may have deleted the buffer.
if (!bufref_valid(&bufref)) if (!bufref_valid(&bufref))
return; return FALSE;
#ifdef FEAT_EVAL #ifdef FEAT_EVAL
// autocmds may abort script processing // autocmds may abort script processing
if (!ignore_abort && aborting()) if (!ignore_abort && aborting())
return; return FALSE;
#endif #endif
/* /*
@ -698,7 +700,7 @@ aucmd_abort:
* deleted buffer. * deleted buffer.
*/ */
if (buf == curbuf && !is_curbuf) if (buf == curbuf && !is_curbuf)
return; return FALSE;
if (win_valid_any_tab(win) && win->w_buffer == buf) if (win_valid_any_tab(win) && win->w_buffer == buf)
win->w_buffer = NULL; // make sure we don't use the buffer now win->w_buffer = NULL; // make sure we don't use the buffer now
@ -755,6 +757,7 @@ aucmd_abort:
buf->b_p_bl = FALSE; buf->b_p_bl = FALSE;
} }
// NOTE: at this point "curbuf" may be invalid! // NOTE: at this point "curbuf" may be invalid!
return TRUE;
} }
/* /*

View File

@ -2742,6 +2742,8 @@ do_ecmd(
else else
{ {
win_T *the_curwin = curwin; win_T *the_curwin = curwin;
int did_decrement;
buf_T *was_curbuf = curbuf;
// Set the w_closing flag to avoid that autocommands close the // Set the w_closing flag to avoid that autocommands close the
// window. And set b_locked for the same reason. // window. And set b_locked for the same reason.
@ -2754,7 +2756,7 @@ do_ecmd(
// Close the link to the current buffer. This will set // Close the link to the current buffer. This will set
// oldwin->w_buffer to NULL. // oldwin->w_buffer to NULL.
u_sync(FALSE); u_sync(FALSE);
close_buffer(oldwin, curbuf, did_decrement = close_buffer(oldwin, curbuf,
(flags & ECMD_HIDE) ? 0 : DOBUF_UNLOAD, FALSE, FALSE); (flags & ECMD_HIDE) ? 0 : DOBUF_UNLOAD, FALSE, FALSE);
the_curwin->w_closing = FALSE; the_curwin->w_closing = FALSE;
@ -2776,7 +2778,15 @@ do_ecmd(
goto theend; goto theend;
} }
if (buf == curbuf) // already in new buffer if (buf == curbuf) // already in new buffer
{
// close_buffer() has decremented the window count,
// increment it again here and restore w_buffer.
if (did_decrement && buf_valid(was_curbuf))
++was_curbuf->b_nwindows;
if (win_valid_any_tab(oldwin) && oldwin->w_buffer == NULL)
oldwin->w_buffer = was_curbuf;
auto_buf = TRUE; auto_buf = TRUE;
}
else else
{ {
#ifdef FEAT_SYN_HL #ifdef FEAT_SYN_HL

View File

@ -5,7 +5,7 @@ int open_buffer(int read_stdin, exarg_T *eap, int flags);
void set_bufref(bufref_T *bufref, buf_T *buf); void set_bufref(bufref_T *bufref, buf_T *buf);
int bufref_valid(bufref_T *bufref); int bufref_valid(bufref_T *bufref);
int buf_valid(buf_T *buf); int buf_valid(buf_T *buf);
void close_buffer(win_T *win, buf_T *buf, int action, int abort_if_last, int ignore_abort); int close_buffer(win_T *win, buf_T *buf, int action, int abort_if_last, int ignore_abort);
void buf_clear_file(buf_T *buf); void buf_clear_file(buf_T *buf);
void buf_freeall(buf_T *buf, int flags); void buf_freeall(buf_T *buf, int flags);
void free_wininfo(wininfo_T *wip); void free_wininfo(wininfo_T *wip);

View File

@ -500,6 +500,26 @@ func Test_autocmd_bufwipe_in_SessLoadPost()
endfor endfor
endfunc endfunc
" Using :blast and :ball for many events caused a crash, because b_nwindows was
" not incremented correctly.
func Test_autocmd_blast_badd()
let content =<< trim [CODE]
au BufNew,BufAdd,BufWinEnter,BufEnter,BufLeave,BufWinLeave,BufUnload,VimEnter foo* blast
edit foo1
au BufNew,BufAdd,BufWinEnter,BufEnter,BufLeave,BufWinLeave,BufUnload,VimEnter foo* ball
edit foo2
call writefile(['OK'], 'Xerrors')
qall
[CODE]
call writefile(content, 'XblastBall')
call system(GetVimCommand() .. ' --clean -S XblastBall')
call assert_match('OK', readfile('Xerrors')->join())
call delete('XblastBall')
call delete('Xerrors')
endfunc
" SEGV occurs in older versions. " SEGV occurs in older versions.
func Test_autocmd_bufwipe_in_SessLoadPost2() func Test_autocmd_bufwipe_in_SessLoadPost2()
tabnew tabnew

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 */
/**/
2354,
/**/ /**/
2353, 2353,
/**/ /**/