Patch 8.2.2354
Problem: Crash with a weird combination of autocommands.
Solution: Increment b_nwindows when needed. (closes #7674)
Files: src/ex_cmds.c, src/buffer.c, src/proto/
buffer.pro,
src/testdir/test_autocmd.vim
*** ../vim-8.2.2353/src/ex_cmds.c 2021-01-07 14:45:00.121819781 +0100
--- src/ex_cmds.c 2021-01-15 16:09:52.863124948 +0100
***************
*** 2742,2747 ****
--- 2742,2749 ----
else
{
win_T *the_curwin = curwin;
+ int did_decrement;
+ buf_T *was_curbuf = curbuf;
// Set the w_closing flag to avoid that autocommands close the
// window. And set b_locked for the same reason.
***************
*** 2754,2760 ****
// Close the link to the current buffer. This will set
// oldwin->w_buffer to NULL.
u_sync(FALSE);
! close_buffer(oldwin, curbuf,
(flags & ECMD_HIDE) ? 0 : DOBUF_UNLOAD, FALSE, FALSE);
the_curwin->w_closing = FALSE;
--- 2756,2762 ----
// Close the link to the current buffer. This will set
// oldwin->w_buffer to NULL.
u_sync(FALSE);
! did_decrement = close_buffer(oldwin, curbuf,
(flags & ECMD_HIDE) ? 0 : DOBUF_UNLOAD, FALSE, FALSE);
the_curwin->w_closing = FALSE;
***************
*** 2776,2782 ****
--- 2778,2792 ----
goto theend;
}
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;
+ }
else
{
#ifdef FEAT_SYN_HL
*** ../vim-8.2.2353/src/buffer.c 2020-12-28 18:25:56.796886014 +0100
--- src/buffer.c 2021-01-15 15:04:49.809399903 +0100
***************
*** 492,499 ****
* supposed to close the window but autocommands close all other windows.
*
* When "ignore_abort" is TRUE don't abort even when aborting() returns TRUE.
*/
! void
close_buffer(
win_T *win, // if not NULL, set b_last_cursor
buf_T *buf,
--- 492,501 ----
* supposed to close the window but autocommands close all other windows.
*
* 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.
*/
! int
close_buffer(
win_T *win, // if not NULL, set b_last_cursor
buf_T *buf,
***************
*** 540,546 ****
if (wipe_buf || unload_buf)
{
if (!can_unload_buffer(buf))
! return;
// Wiping out or unloading a terminal buffer kills the job.
free_terminal(buf);
--- 542,548 ----
if (wipe_buf || unload_buf)
{
if (!can_unload_buffer(buf))
! return FALSE;
// Wiping out or unloading a terminal buffer kills the job.
free_terminal(buf);
***************
*** 571,577 ****
// Disallow deleting the buffer when it is locked (already being closed or
// halfway a command that relies on it). Unloading is allowed.
if ((del_buf || wipe_buf) && !can_unload_buffer(buf))
! return;
// check no autocommands closed the window
if (win != NULL && win_valid_any_tab(win))
--- 573,579 ----
// Disallow deleting the buffer when it is locked (already being closed or
// halfway a command that relies on it). Unloading is allowed.
if ((del_buf || wipe_buf) && !can_unload_buffer(buf))
! return FALSE;
// check no autocommands closed the window
if (win != NULL && win_valid_any_tab(win))
***************
*** 600,606 ****
// Autocommands deleted the buffer.
aucmd_abort:
emsg(_(e_auabort));
! return;
}
--buf->b_locked;
if (abort_if_last && one_window())
--- 602,608 ----
// Autocommands deleted the buffer.
aucmd_abort:
emsg(_(e_auabort));
! return FALSE;
}
--buf->b_locked;
if (abort_if_last && one_window())
***************
*** 625,631 ****
#ifdef FEAT_EVAL
// autocmds may abort script processing
if (!ignore_abort && aborting())
! return;
#endif
}
--- 627,633 ----
#ifdef FEAT_EVAL
// autocmds may abort script processing
if (!ignore_abort && aborting())
! return FALSE;
#endif
}
***************
*** 653,659 ****
// Return when a window is displaying the buffer or when it's not
// unloaded.
if (buf->b_nwindows > 0 || !unload_buf)
! return;
// Always remove the buffer when there is no file name.
if (buf->b_ffname == NULL)
--- 655,661 ----
// Return when a window is displaying the buffer or when it's not
// unloaded.
if (buf->b_nwindows > 0 || !unload_buf)
! return FALSE;
// Always remove the buffer when there is no file name.
if (buf->b_ffname == NULL)
***************
*** 683,693 ****
// Autocommands may have deleted the buffer.
if (!bufref_valid(&bufref))
! return;
#ifdef FEAT_EVAL
// autocmds may abort script processing
if (!ignore_abort && aborting())
! return;
#endif
/*
--- 685,695 ----
// Autocommands may have deleted the buffer.
if (!bufref_valid(&bufref))
! return FALSE;
#ifdef FEAT_EVAL
// autocmds may abort script processing
if (!ignore_abort && aborting())
! return FALSE;
#endif
/*
***************
*** 698,704 ****
* deleted buffer.
*/
if (buf == curbuf && !is_curbuf)
! return;
if (win_valid_any_tab(win) && win->w_buffer == buf)
win->w_buffer = NULL; // make sure we don't use the buffer now
--- 700,706 ----
* deleted buffer.
*/
if (buf == curbuf && !is_curbuf)
! return FALSE;
if (win_valid_any_tab(win) && win->w_buffer == buf)
win->w_buffer = NULL; // make sure we don't use the buffer now
***************
*** 755,760 ****
--- 757,763 ----
buf->b_p_bl = FALSE;
}
// NOTE: at this point "curbuf" may be invalid!
+ return TRUE;
}
/*
*** ../vim-8.2.2353/src/proto/
buffer.pro 2020-11-05 19:36:34.706317028 +0100
--- src/proto/
buffer.pro 2021-01-15 15:05:23.437306054 +0100
***************
*** 5,11 ****
void set_bufref(bufref_T *bufref, buf_T *buf);
int bufref_valid(bufref_T *bufref);
int buf_valid(buf_T *buf);
! void 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_freeall(buf_T *buf, int flags);
void free_wininfo(wininfo_T *wip);
--- 5,11 ----
void set_bufref(bufref_T *bufref, buf_T *buf);
int bufref_valid(bufref_T *bufref);
int buf_valid(buf_T *buf);
! 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_freeall(buf_T *buf, int flags);
void free_wininfo(wininfo_T *wip);
*** ../vim-8.2.2353/src/testdir/test_autocmd.vim 2020-12-22 11:40:40.669481249 +0100
--- src/testdir/test_autocmd.vim 2021-01-15 16:01:14.072446087 +0100
***************
*** 500,505 ****
--- 500,525 ----
endfor
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.
func Test_autocmd_bufwipe_in_SessLoadPost2()
tabnew
*** ../vim-8.2.2353/src/version.c 2021-01-15 13:35:26.763953669 +0100
--- src/version.c 2021-01-15 15:02:52.333726768 +0100
***************
*** 752,753 ****
--- 752,755 ----
{ /* Add new patch number below this line */
+ /**/
+ 2354,
/**/
--
Although the scythe isn't pre-eminent among the weapons of war, anyone who
has been on the wrong end of, say, a peasants' revolt will know that in
skilled hands it is fearsome.
-- (Terry Pratchett, Mort)
/// Bram Moolenaar -- Br...@Moolenaar.net --
http://www.Moolenaar.net \\\
/// sponsor Vim, vote for features --
http://www.Vim.org/sponsor/ \\\
\\\ an exciting new programming language --
http://www.Zimbu.org ///
\\\ help me help AIDS victims --
http://ICCF-Holland.org ///