aboutsummaryrefslogtreecommitdiff
path: root/src
diff options
context:
space:
mode:
authorJan Edmund Lazo <jan.lazo@mail.utoronto.ca>2021-05-11 22:17:12 -0400
committerJan Edmund Lazo <jan.lazo@mail.utoronto.ca>2021-05-13 20:47:26 -0400
commitf98433d82a340008fdc51c2093e74400e71101dc (patch)
tree7b381a5f7eb4243226c97655af05e36dc09ef97b /src
parent433807763236427cbe0ff347c1c5b77877aa0aff (diff)
downloadrneovim-f98433d82a340008fdc51c2093e74400e71101dc.tar.gz
rneovim-f98433d82a340008fdc51c2093e74400e71101dc.tar.bz2
rneovim-f98433d82a340008fdc51c2093e74400e71101dc.zip
vim-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 vim/vim#7674) https://github.com/vim/vim/commit/797e63b9f2baa1853e7063aac478d663cd02f207
Diffstat (limited to 'src')
-rw-r--r--src/nvim/buffer.c25
-rw-r--r--src/nvim/ex_cmds.c16
-rw-r--r--src/nvim/testdir/test_autocmd.vim21
3 files changed, 48 insertions, 14 deletions
diff --git a/src/nvim/buffer.c b/src/nvim/buffer.c
index ce4163fccf..5e700dea8a 100644
--- a/src/nvim/buffer.c
+++ b/src/nvim/buffer.c
@@ -407,7 +407,8 @@ bool buf_valid(buf_T *buf)
/// there to be only one window with this buffer. e.g. when
/// ":quit" is supposed to close the window but autocommands
/// close all other windows.
-void close_buffer(win_T *win, buf_T *buf, int action, bool abort_if_last)
+/// @returns true when we got to the end and b_nwindows was decremented.
+bool close_buffer(win_T *win, buf_T *buf, int action, bool abort_if_last)
{
bool unload_buf = (action != 0);
bool del_buf = (action == DOBUF_DEL || action == DOBUF_WIPE);
@@ -444,7 +445,7 @@ void close_buffer(win_T *win, buf_T *buf, int action, bool abort_if_last)
// halfway a command that relies on it). Unloading is allowed.
if (buf->b_locked > 0 && (del_buf || wipe_buf)) {
EMSG(_("E937: Attempt to delete a buffer that is in use"));
- return;
+ return false;
}
if (win != NULL // Avoid bogus clang warning.
@@ -471,13 +472,13 @@ void close_buffer(win_T *win, buf_T *buf, int action, bool abort_if_last)
buf) && !bufref_valid(&bufref)) {
// Autocommands deleted the buffer.
EMSG(_(e_auabort));
- return;
+ return false;
}
buf->b_locked--;
if (abort_if_last && last_nonfloat(win)) {
// Autocommands made this the only window.
EMSG(_(e_auabort));
- return;
+ return false;
}
// When the buffer becomes hidden, but is not unloaded, trigger
@@ -488,17 +489,17 @@ void close_buffer(win_T *win, buf_T *buf, int action, bool abort_if_last)
buf) && !bufref_valid(&bufref)) {
// Autocommands deleted the buffer.
EMSG(_(e_auabort));
- return;
+ return false;
}
buf->b_locked--;
if (abort_if_last && last_nonfloat(win)) {
// Autocommands made this the only window.
EMSG(_(e_auabort));
- return;
+ return false;
}
}
if (aborting()) { // autocmds may abort script processing
- return;
+ return false;
}
}
@@ -525,7 +526,7 @@ void close_buffer(win_T *win, buf_T *buf, int action, bool abort_if_last)
/* Return when a window is displaying the buffer or when it's not
* unloaded. */
if (buf->b_nwindows > 0 || !unload_buf) {
- return;
+ return false;
}
if (buf->terminal) {
@@ -561,11 +562,11 @@ void close_buffer(win_T *win, buf_T *buf, int action, bool abort_if_last)
if (!bufref_valid(&bufref)) {
// Autocommands may have deleted the buffer.
- return;
+ return false;
}
if (aborting()) {
// Autocmds may abort script processing.
- return;
+ return false;
}
/*
@@ -576,7 +577,7 @@ void close_buffer(win_T *win, buf_T *buf, int action, bool abort_if_last)
* deleted buffer.
*/
if (buf == curbuf && !is_curbuf) {
- return;
+ return false;
}
if (win != NULL // Avoid bogus clang warning.
@@ -636,6 +637,8 @@ void close_buffer(win_T *win, buf_T *buf, int action, bool abort_if_last)
buf->b_p_bl = false;
}
}
+ // NOTE: at this point "curbuf" may be invalid!
+ return true;
}
/// Make buffer not contain a file.
diff --git a/src/nvim/ex_cmds.c b/src/nvim/ex_cmds.c
index 349c7d6280..18530a7d58 100644
--- a/src/nvim/ex_cmds.c
+++ b/src/nvim/ex_cmds.c
@@ -2462,6 +2462,7 @@ int do_ecmd(
auto_buf = true;
} else {
win_T *the_curwin = curwin;
+ buf_T *was_curbuf = curbuf;
// Set w_closing to avoid that autocommands close the window.
// Set b_locked for the same reason.
@@ -2475,9 +2476,10 @@ int do_ecmd(
// 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) || curbuf->terminal ? 0 : DOBUF_UNLOAD,
- false);
+ const bool did_decrement = close_buffer(
+ oldwin, curbuf,
+ (flags & ECMD_HIDE) || curbuf->terminal ? 0 : DOBUF_UNLOAD,
+ false);
// Autocommands may have closed the window.
if (win_valid(the_curwin)) {
@@ -2497,6 +2499,14 @@ int do_ecmd(
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 {
// <VN> We could instead free the synblock
diff --git a/src/nvim/testdir/test_autocmd.vim b/src/nvim/testdir/test_autocmd.vim
index fb3aa2b3b0..a2aa1c32c5 100644
--- a/src/nvim/testdir/test_autocmd.vim
+++ b/src/nvim/testdir/test_autocmd.vim
@@ -452,6 +452,27 @@ func Test_autocmd_bufwipe_in_SessLoadPost()
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 assert_match('OK', join(readfile('Xerrors')))
+
+ call delete('XblastBall')
+ call delete('Xerrors')
+endfunc
+
" SEGV occurs in older versions.
func Test_autocmd_bufwipe_in_SessLoadPost2()
tabnew